aboutsummaryrefslogtreecommitdiffstats
path: root/lib/aos-expansion.c
blob: 7f927a477b81d14ccae447a8fbbe2168bfcf1669 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
 *	The PCI Library -- Configuration Access via AmigaOS 4.x expansion.library
 *
 *	Copyright (c) 2024 Olrick Lefebvre <olrick.lefebvre@olrick.fr>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL v2+.
 *
 *	SPDX-License-Identifier: GPL-2.0-or-later
 */

#define _GNU_SOURCE

#include <proto/exec.h>
#include <exec/types.h>
#include <proto/expansion.h>
#include <interfaces/expansion.h>


// have to undef PCI values to avoid redefine warning
#undef PCI_BASE_ADDRESS_MEM_MASK
#undef PCI_BASE_ADDRESS_IO_MASK
#undef PCI_ROM_ADDRESS_MASK
#include <expansion/pci.h>

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/unistd.h>

#include "internal.h"


// custom Amiga x.y version tag
#define VERSTAG "\0$VER: pciutils " PCILIB_VERSION " (" PCILIB_DATE_AMIGAOS ") AmigaOS4 port"


/*** AmigaOS access support ***/

typedef struct _PCIAccess {
	struct ExpansionBase *expansion;
	struct PCIIFace *ipci;
} PCIAccess;

static void 
aos_close_pci_interface(struct pci_access *a)
{
	PCIAccess *pci;

	pci = (PCIAccess *)a->backend_data;
	if (pci) {
		if (pci->expansion) {
			if (pci->ipci) {
				IExec->DropInterface((struct Interface *)pci->ipci);
				pci->ipci = NULL;
			}
			IExec->CloseLibrary((struct Library *)pci->expansion);
			pci->expansion = NULL;
		}
		pci_mfree(pci);
		a->backend_data = NULL;
	}
}

static BOOL 
aos_open_pci_interface(struct pci_access *a)
{
	PCIAccess *pci;
	BOOL res = FALSE;

	if (NULL == a->backend_data) {
		pci = pci_malloc(a, sizeof(PCIAccess));
		a->backend_data = pci;
		pci->expansion = (struct ExpansionBase *)IExec->OpenLibrary("expansion.library", 0);
		if(NULL == pci->expansion) {
			a->warning("Unable to open expansion.library");
			aos_close_pci_interface(a);
		} else {
			pci->ipci = (struct PCIIFace *)IExec->GetInterface((struct Library *)pci->expansion, "pci", 1, TAG_DONE);
			if(NULL == pci->ipci) {
				a->warning("Unable to obtain pci interface");
				aos_close_pci_interface(a);			
			} else {
				res = TRUE;
			}
		}
	} else {
		res = TRUE;  // already opened
	}

	return res;
}

static int 
aos_expansion_detect(struct pci_access *a)
{
	int res = FALSE;
	struct PCIDevice *device = NULL;
	PCIAccess *pci;

	if(TRUE == aos_open_pci_interface(a)) {	
		pci = a->backend_data;

		// Try to read PCI first device
		device = pci->ipci->FindDeviceTags(FDT_Index, 0);
		if(NULL == device) {
			a->warning("AmigaOS Expansion PCI interface cannot find any device");
			aos_close_pci_interface(a);
		} else {
			pci->ipci->FreeDevice(device);
			res = TRUE;
		}
	}
	
	return res;
}

static void 
aos_expansion_init(struct pci_access *a)
{
	// to avoid flushing of version tag
	static STRPTR USED ver = (STRPTR)VERSTAG;

	if (!aos_open_pci_interface(a)) {
		a->debug("\n");
		a->error("AmigaOS Expansion PCI interface cannot be accessed.");
	}
}

static void 
aos_expansion_cleanup(struct pci_access *a)
{
	aos_close_pci_interface(a);
}

static void 
aos_expansion_scan(struct pci_access *a)
{
	struct PCIDevice *device = NULL;
	PCIAccess *pci = NULL;
	UBYTE bus_num;
	UBYTE dev_num;
	UBYTE fn_num;
	struct pci_dev *d;
	int found_devs = 0;

	pci = a->backend_data;

	// 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
	device = pci->ipci->FindDeviceTags(FDT_Index, found_devs);
	while (device) {
		d = pci_alloc_dev(a);
		d->domain = 0; // only one domain for AmigaOS
		device->GetAddress(&bus_num, &dev_num, &fn_num);
		d->bus = bus_num;
		d->dev = dev_num;
		d->func = fn_num;
		d->backend_data = device;
		d->vendor_id = device->ReadConfigWord(PCI_VENDOR_ID);
		d->device_id = device->ReadConfigWord(PCI_DEVICE_ID);
		d->known_fields = PCI_FILL_IDENT;
		d->hdrtype = device->ReadConfigByte(PCI_HEADER_TYPE) & ~PCI_HEADER_TYPE_MULTIFUNCTION;
		pci_link_dev(a, d);
		a->debug("  Found device %02x:%02x.%d %04x:%04x\n", d->bus, d->dev, d->func, d->vendor_id, d->device_id);

		found_devs++;
		device = pci->ipci->FindDeviceTags(FDT_Index, found_devs);
	}
}

static int 
aos_expansion_read(struct pci_dev *d, int pos, byte *buf, int len)
{
	int res = FALSE;
	byte *ptr = buf;
	if (d->backend_data) {
		for (int i = 0; i < len; i++) {
			// byte by byte to avoid endianness troubles
			*ptr = ((struct PCIDevice *)(d->backend_data))->ReadConfigByte(pos + i);
			ptr++;
			res = TRUE;
		}
	}

	return res;
}

static int 
aos_expansion_write(struct pci_dev *d, int pos, byte *buf, int len)
{
	int res = FALSE;
	byte *ptr = buf;

	if (d->backend_data) {
		for (int i = 0; i < len; i++) {
			// byte by byte to avoid endianness troubles
			((struct PCIDevice *)(d->backend_data))->WriteConfigByte(pos + i, *ptr);
			ptr++;
			res = TRUE;
		}
	}

	return res;
}

static void 
aos_expansion_init_dev(struct pci_dev *d)
{
	d->backend_data = NULL; // struct PCIDevice * to be obtained
}

static void 
aos_expansion_cleanup_dev(struct pci_dev *d)
{
	PCIAccess *pci;

	if (d->backend_data && d->access->backend_data) {
		pci = d->access->backend_data;
		pci->ipci->FreeDevice((struct PCIDevice *)d->backend_data);
		d->backend_data = NULL;
	}
}

struct pci_methods pm_aos_expansion = {
	"aos-expansion",
	"The Expansion.library on AmigaOS 4.x",
	NULL,			// config, called after allocation of pci_access, if assigned
	aos_expansion_detect,	// detect, mandatory because called without check
	aos_expansion_init,	// init, called once access chosen, eventually after detect
	aos_expansion_cleanup,	// cleanup, called at the end
	aos_expansion_scan,
	pci_generic_fill_info,
	aos_expansion_read,
	aos_expansion_write,
	NULL,			// read_vpd
	aos_expansion_init_dev,
	aos_expansion_cleanup_dev,
};