1 /*
2 * The PCI Library -- Configuration Access via AmigaOS 4.x expansion.library
3 *
4 * Copyright (c) 2024 Olrick Lefebvre <[email protected]>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #define _GNU_SOURCE
12
13 #include <proto/exec.h>
14 #include <exec/types.h>
15 #include <proto/expansion.h>
16 #include <interfaces/expansion.h>
17
18
19 // have to undef PCI values to avoid redefine warning
20 #undef PCI_BASE_ADDRESS_MEM_MASK
21 #undef PCI_BASE_ADDRESS_IO_MASK
22 #undef PCI_ROM_ADDRESS_MASK
23 #include <expansion/pci.h>
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <sys/unistd.h>
30
31 #include "internal.h"
32
33
34 // custom Amiga x.y version tag
35 #define VERSTAG "\0$VER: pciutils " PCILIB_VERSION " (" PCILIB_DATE_AMIGAOS ") AmigaOS4 port"
36
37
38 /*** AmigaOS access support ***/
39
40 typedef struct _PCIAccess {
41 struct ExpansionBase *expansion;
42 struct PCIIFace *ipci;
43 } PCIAccess;
44
45 static void
aos_close_pci_interface(struct pci_access * a)46 aos_close_pci_interface(struct pci_access *a)
47 {
48 PCIAccess *pci = a->backend_data;
49
50 if (pci) {
51 if (pci->expansion) {
52 if (pci->ipci) {
53 IExec->DropInterface((struct Interface *)pci->ipci);
54 pci->ipci = NULL;
55 }
56 IExec->CloseLibrary((struct Library *)pci->expansion);
57 pci->expansion = NULL;
58 }
59 pci_mfree(pci);
60 a->backend_data = NULL;
61 }
62 }
63
64 static BOOL
aos_open_pci_interface(struct pci_access * a)65 aos_open_pci_interface(struct pci_access *a)
66 {
67 PCIAccess *pci;
68 BOOL res = FALSE;
69
70 if (NULL == a->backend_data) {
71 pci = pci_malloc(a, sizeof(PCIAccess));
72 a->backend_data = pci;
73 pci->expansion = (struct ExpansionBase *)IExec->OpenLibrary("expansion.library", 0);
74 if(NULL == pci->expansion) {
75 a->warning("Unable to open expansion.library");
76 aos_close_pci_interface(a);
77 } else {
78 pci->ipci = (struct PCIIFace *)IExec->GetInterface((struct Library *)pci->expansion, "pci", 1, TAG_DONE);
79 if(NULL == pci->ipci) {
80 a->warning("Unable to obtain pci interface");
81 aos_close_pci_interface(a);
82 } else {
83 res = TRUE;
84 }
85 }
86 } else {
87 res = TRUE; // already opened
88 }
89
90 return res;
91 }
92
93 static int
aos_expansion_detect(struct pci_access * a)94 aos_expansion_detect(struct pci_access *a)
95 {
96 int res = FALSE;
97 struct PCIDevice *device = NULL;
98 PCIAccess *pci;
99
100 if(TRUE == aos_open_pci_interface(a)) {
101 pci = a->backend_data;
102
103 // Try to read PCI first device
104 device = pci->ipci->FindDeviceTags(FDT_Index, 0);
105 if(NULL == device) {
106 a->warning("AmigaOS Expansion PCI interface cannot find any device");
107 aos_close_pci_interface(a);
108 } else {
109 pci->ipci->FreeDevice(device);
110 res = TRUE;
111 }
112 }
113
114 return res;
115 }
116
117 static void
aos_expansion_init(struct pci_access * a)118 aos_expansion_init(struct pci_access *a)
119 {
120 // to avoid flushing of version tag
121 static STRPTR USED ver = (STRPTR)VERSTAG;
122
123 if (!aos_open_pci_interface(a)) {
124 a->debug("\n");
125 a->error("AmigaOS Expansion PCI interface cannot be accessed.");
126 }
127 }
128
129 static void
aos_expansion_cleanup(struct pci_access * a)130 aos_expansion_cleanup(struct pci_access *a)
131 {
132 aos_close_pci_interface(a);
133 }
134
135 static void
aos_expansion_scan(struct pci_access * a)136 aos_expansion_scan(struct pci_access *a)
137 {
138 struct PCIDevice *device = NULL;
139 PCIAccess *pci = NULL;
140 UBYTE bus_num;
141 UBYTE dev_num;
142 UBYTE fn_num;
143 struct pci_dev *d;
144 int found_devs = 0;
145
146 pci = a->backend_data;
147
148 // X1000 has a bug which left shifts secondary bus by one bit, so we don't scan but get all devices identified by the system
149 device = pci->ipci->FindDeviceTags(FDT_Index, found_devs);
150 while (device) {
151 d = pci_alloc_dev(a);
152 d->domain = 0; // only one domain for AmigaOS
153 device->GetAddress(&bus_num, &dev_num, &fn_num);
154 d->bus = bus_num;
155 d->dev = dev_num;
156 d->func = fn_num;
157 d->backend_data = device;
158 d->vendor_id = device->ReadConfigWord(PCI_VENDOR_ID);
159 d->device_id = device->ReadConfigWord(PCI_DEVICE_ID);
160 d->known_fields = PCI_FILL_IDENT;
161 d->hdrtype = device->ReadConfigByte(PCI_HEADER_TYPE) & ~PCI_HEADER_TYPE_MULTIFUNCTION;
162 pci_link_dev(a, d);
163 a->debug(" Found device %02x:%02x.%d %04x:%04x\n", d->bus, d->dev, d->func, d->vendor_id, d->device_id);
164
165 found_devs++;
166 device = pci->ipci->FindDeviceTags(FDT_Index, found_devs);
167 }
168 }
169
170 static int
aos_expansion_read(struct pci_dev * d,int pos,byte * buf,int len)171 aos_expansion_read(struct pci_dev *d, int pos, byte *buf, int len)
172 {
173 int res = FALSE;
174 byte *ptr = buf;
175 if (d->backend_data) {
176 for (int i = 0; i < len; i++) {
177 // byte by byte to avoid endianness troubles
178 *ptr = ((struct PCIDevice *)(d->backend_data))->ReadConfigByte(pos + i);
179 ptr++;
180 res = TRUE;
181 }
182 }
183
184 return res;
185 }
186
187 static int
aos_expansion_write(struct pci_dev * d,int pos,byte * buf,int len)188 aos_expansion_write(struct pci_dev *d, int pos, byte *buf, int len)
189 {
190 int res = FALSE;
191 byte *ptr = buf;
192
193 if (d->backend_data) {
194 for (int i = 0; i < len; i++) {
195 // byte by byte to avoid endianness troubles
196 ((struct PCIDevice *)(d->backend_data))->WriteConfigByte(pos + i, *ptr);
197 ptr++;
198 res = TRUE;
199 }
200 }
201
202 return res;
203 }
204
205 static void
aos_expansion_init_dev(struct pci_dev * d)206 aos_expansion_init_dev(struct pci_dev *d)
207 {
208 d->backend_data = NULL; // struct PCIDevice * to be obtained
209 }
210
211 static void
aos_expansion_cleanup_dev(struct pci_dev * d)212 aos_expansion_cleanup_dev(struct pci_dev *d)
213 {
214 PCIAccess *pci;
215
216 if (d->backend_data && d->access->backend_data) {
217 pci = d->access->backend_data;
218 pci->ipci->FreeDevice((struct PCIDevice *)d->backend_data);
219 d->backend_data = NULL;
220 }
221 }
222
223 struct pci_methods pm_aos_expansion = {
224 .name = "aos-expansion",
225 .help = "The Expansion.library on AmigaOS 4.x",
226 .detect = aos_expansion_detect, // detect, mandatory because called without check
227 .init = aos_expansion_init, // init, called once access chosen, eventually after detect
228 .cleanup = aos_expansion_cleanup, // cleanup, called at the end
229 .scan = aos_expansion_scan,
230 .fill_info = pci_generic_fill_info,
231 .read = aos_expansion_read,
232 .write = aos_expansion_write,
233 .init_dev = aos_expansion_init_dev,
234 .cleanup_dev = aos_expansion_cleanup_dev,
235 };
236