/* * The PCI Library -- Configuration Access via AmigaOS 4.x expansion.library * * Copyright (c) 2024 Olrick Lefebvre * * 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 #include #include #include // 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 #include #include #include #include #include #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 = 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 = { .name = "aos-expansion", .help = "The Expansion.library on AmigaOS 4.x", .detect = aos_expansion_detect, // detect, mandatory because called without check .init = aos_expansion_init, // init, called once access chosen, eventually after detect .cleanup = aos_expansion_cleanup, // cleanup, called at the end .scan = aos_expansion_scan, .fill_info = pci_generic_fill_info, .read = aos_expansion_read, .write = aos_expansion_write, .init_dev = aos_expansion_init_dev, .cleanup_dev = aos_expansion_cleanup_dev, };