xref: /aosp_15_r20/external/pciutils/lib/aos-expansion.c (revision c2e0c6b56a71da9abe8df5c8348fb3eb5c2c9251)
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