diff --git a/src/kernel/core/addons/bus_managers/Jamfile b/src/kernel/core/addons/bus_managers/Jamfile index e7d4d62370..45b01db8f2 100644 --- a/src/kernel/core/addons/bus_managers/Jamfile +++ b/src/kernel/core/addons/bus_managers/Jamfile @@ -13,17 +13,15 @@ KernelLd -Bdynamic -shared : : - addons/kernel/bus_managers/config_manager + addons/kernel/config_manager ; KernelStaticLibrary libbus : <$(SOURCE_GRIST)>bus_init.c <$(SOURCE_GRIST)>bus_man.c - - <$(SOURCE_GRIST)>pci/pci.c - <$(SOURCE_GRIST)>pci/pci_bus.c : - -fno-pic + -fno-pic -D_KERNEL_MODE ; SubInclude OBOS_TOP src kernel core addons bus_managers isa ; +SubInclude OBOS_TOP src kernel core addons bus_managers pci ; diff --git a/src/kernel/core/addons/bus_managers/bus_init.c b/src/kernel/core/addons/bus_managers/bus_init.c index 1b70cb091e..c1382467e0 100755 --- a/src/kernel/core/addons/bus_managers/bus_init.c +++ b/src/kernel/core/addons/bus_managers/bus_init.c @@ -6,19 +6,19 @@ #include #include #include - -#ifdef ARCH_x86 -#include -#endif +#include int bus_init(kernel_args *ka) { + struct config_manager_for_driver_module_info *cfm; + bus_man_init(ka); -#ifdef ARCH_x86 - pci_bus_init(ka); -#endif - + if (get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME, + (module_info**)&cfm) == 0) { + dprintf("bus_init: loaded the config_manager\n"); + } + #if 0 { id_list *vendor_ids; diff --git a/src/kernel/core/addons/bus_managers/config_manager.c b/src/kernel/core/addons/bus_managers/config_manager.c index e340ecc4c0..abab63bfcd 100644 --- a/src/kernel/core/addons/bus_managers/config_manager.c +++ b/src/kernel/core/addons/bus_managers/config_manager.c @@ -9,22 +9,147 @@ #include #include #include +#include +#include -#define B_CONFIG_MANAGER_FOR_BUS_MODULE_NAME "bus_managers/config_manager/driver/v1" +/* This is normally turned off. Turning it on (1) will provide a more + * verbose description of the PCI devices found, though most of the extra + * information is only useful if you're working on this code. + */ +#define THE_FULL_MONTY 0 -static status_t get_next_device_info(bus_type bus, uint64 *cookie, - struct device_info *info, uint32 len) +#define B_CONFIG_MANAGER_FOR_BUS_MODULE_NAME "bus_managers/config_manager/bus/v1" + +static status_t cfdm_get_next_device_info(bus_type bus, uint64 *cookie, + struct device_info *info, uint32 len) { dprintf("get_next_device_info(bus = %d, cookie = %lld)\n", bus, *cookie); + return 0; } +static char *decode_class_base(uint8 base) +{ + switch(base) { + case 0x00: return "legacy"; + case 0x01: return "mass storage controller"; + case 0x02: return "network controller"; + case 0x03: return "display controller"; + case 0x04: return "multimedia device"; + case 0x05: return "memory controller"; + case 0x06: return "bridge controller"; + case 0x07: return "simple comms controller"; + case 0x08: return "base system peripheral"; + case 0x09: return "input device"; + case 0x0a: return "docking station"; + case 0x0b: return "processor"; + case 0x0c: return "serial bus controller"; + case 0x0d: return "wireless"; + case 0x0e: return "intelligent i/o ??"; + case 0x0f: return "satellite"; + case 0x10: return "encryption"; + case 0x11: return "signal processing"; + default: return "unknown"; + } +} + +/* XXX - these are only listed as they're the ones that my system has! + * I'm committing mainly so I don't have to keep adjusting this file each + * time I commit a change. I'm NOT suggesting others add their codes here. + * The level of detail offered is useful if you're working on this, otherwise + * it's just nice to have. Adding all the data we need will add over 270k to + * the build! + */ +static char *decode_vendor(uint16 vendor) +{ + switch(vendor) { + case 0x102b: return "Matrox"; + case 0x10b7: return "3Com"; + case 0x11ad: return "Lite-On"; + case 0x1385: return "NetGear"; + case 0x8086: return "Intel"; + default: return "unknown"; + } +} + +static char *decode_device(uint16 dev) +{ + switch(dev) { + case 0x0002: return "NGMC169B, 10/100 Ethernet (NetGear FA310TX)"; + case 0x051a: return "MGA 1064SG, Hurricane/Cyclone 64-bit graphics chip"; + case 0x7110: return "82371AB/EB/MB, PIIX4/4E/4M ISA Bridge"; + case 0x7111: return "82371AB/EB/MB, PIIX4/4E/4M IDE Controller"; + case 0x7112: return "82371AB/EB/MB, PIIX4/4E/4M USB Interface"; + case 0x7113: return "82371AB/EB/MB, PIIX4/4E/4M Power Management Controller"; + case 0x7190: return "82443BX/ZX, 440BX/ZX AGPset Host Bridge"; + case 0x7191: return "82443BX/ZX, 440BX/ZX AGPset PCI-to-PCI bridge"; + case 0x7192: return "82443BX/ZX, 440BX/ZX chipset Host-to-PCI Bridge"; + case 0x9055: return "3C905B-TX, Fast Etherlink 10/100 PCI TX NIC"; + default: return "unknown"; + } +} + +static void show_pci_details(struct pci_info *p) +{ + dprintf("PCI device found:\n"); + dprintf("\tvendor id : %02x [%s]\n", p->vendor_id, decode_vendor(p->vendor_id)); + dprintf("\tdevice id : %02x [%s]\n", p->device_id, decode_device(p->device_id)); + +#if THE_FULL_MONTY + dprintf("\tbus : %d\n", p->bus); + dprintf("\tdevice : %d\n", p->device); + dprintf("\tfunction : %d\n", p->function); + dprintf("\trevision : %02x\n", p->revision); + dprintf("\tclass_api : %02x\n", p->class_api); + dprintf("\tclass_sub : %02x\n", p->class_sub); +#endif + + dprintf("\tclass_base : %02x [%s]\n", p->class_base, decode_class_base(p->class_base)); + +#if THE_FULL_MONTY + dprintf("\tline_size : %02x\n", p->line_size); + dprintf("\theader_type : %02x\n", p->header_type); + + if (p->header_type == 0) { + dprintf("Header Type 0\n"); + dprintf("\tcardbus_cis : %08lx\n", p->u.h0.cardbus_cis); + dprintf("\tsubsystem_id : %04x\n", p->u.h0.subsystem_id); + dprintf("\tsubsystem_vendor_id : %04x [%s]\n", p->u.h0.subsystem_vendor_id, + decode_vendor(p->u.h0.subsystem_vendor_id)); + dprintf("\trom_base_pci : %08lx\n", p->u.h0.rom_base_pci); + } else if (p->header_type == 1) { + dprintf("Header Type 1 (PCI-PCI bridge)\n"); + dprintf("\trom_base_pci : %08lx\n", p->u.h1.rom_base_pci); + } +#endif +} + +static status_t test_me(void) +{ + pci_module_info *pcim; + pci_info apci; + long index = 0; + + if (get_module(B_PCI_MODULE_NAME, (module_info**)&pcim) != 0) { + dprintf("config_manager: test_me: failed to load PCI module\n"); + return -1; + } + + while (pcim->get_nth_pci_info(index++, &apci) == 0) { + show_pci_details(&apci); + } + + put_module(B_PCI_MODULE_NAME); + return 0; +} + /* device_modules */ static int cfdm_std_ops(int32 op, ...) { switch(op) { case B_MODULE_INIT: dprintf( "config_manager: device modules: init\n" ); + test_me(); break; case B_MODULE_UNINIT: dprintf( "config_manager: device modules: uninit\n" ); @@ -55,11 +180,11 @@ static int cfbm_std_ops(int32 op, ...) struct config_manager_for_driver_module_info cfdm = { { B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME, - 0, + B_KEEP_LOADED, cfdm_std_ops }, - NULL, /* get_next_device_info */ + &cfdm_get_next_device_info, /* get_next_device_info */ NULL, /* get_device_info_for */ NULL, /* get_size_of_current_configuration_for */ NULL, /* get_current_configuration_for */ @@ -74,7 +199,7 @@ struct config_manager_for_driver_module_info cfdm = { struct config_manager_for_driver_module_info cfbm = { { B_CONFIG_MANAGER_FOR_BUS_MODULE_NAME, - 0, + B_KEEP_LOADED, cfbm_std_ops }, diff --git a/src/kernel/core/addons/bus_managers/isa/Jamfile b/src/kernel/core/addons/bus_managers/isa/Jamfile index 189c5edb7a..f68b3f04bb 100644 --- a/src/kernel/core/addons/bus_managers/isa/Jamfile +++ b/src/kernel/core/addons/bus_managers/isa/Jamfile @@ -1,3 +1,17 @@ SubDir OBOS_TOP src kernel core addons bus_managers isa ; -KernelObjects isa.c : -fno-pic ; +KernelObjects isa.c : -fno-pic -D_KERNEL_MODE ; + +KernelLd isa + : + <$(SOURCE_GRIST)>isa.o + kernel.so + : + $(OBOS_TOP)/src/kernel/core/addons/ldscripts/$(OBOS_ARCH)/addon.ld + : + -Bdynamic -shared + : + : + addons/kernel/isa + ; + diff --git a/src/kernel/core/addons/bus_managers/isa/isa.c b/src/kernel/core/addons/bus_managers/isa/isa.c index 3daf53c9ba..e70ee82597 100755 --- a/src/kernel/core/addons/bus_managers/isa/isa.c +++ b/src/kernel/core/addons/bus_managers/isa/isa.c @@ -17,32 +17,32 @@ static status_t isa_rescan(void) static uint8 isa_read_io_8(int mapped_io_addr) { - return in8( mapped_io_addr ); + return in8(mapped_io_addr); } static void isa_write_io_8(int mapped_io_addr, uint8 value) { - out8( value, mapped_io_addr ); + out8(value, mapped_io_addr); } static uint16 isa_read_io_16( int mapped_io_addr ) { - return in16( mapped_io_addr ); + return in16(mapped_io_addr); } static void isa_write_io_16( int mapped_io_addr, uint16 value ) { - out16( value, mapped_io_addr ); + out16(value, mapped_io_addr); } static uint32 isa_read_io_32( int mapped_io_addr ) { - return in32( mapped_io_addr ); + return in32(mapped_io_addr); } static void isa_write_io_32( int mapped_io_addr, uint32 value ) { - out32( value, mapped_io_addr ); + out32(value, mapped_io_addr); } static void *ram_address(const void *physical_address_in_system_memory) diff --git a/src/kernel/core/addons/bus_managers/pci/Jamfile b/src/kernel/core/addons/bus_managers/pci/Jamfile index c5f963366b..1a766e0b1b 100644 --- a/src/kernel/core/addons/bus_managers/pci/Jamfile +++ b/src/kernel/core/addons/bus_managers/pci/Jamfile @@ -1,3 +1,18 @@ SubDir OBOS_TOP src kernel core addons bus_managers pci ; KernelStaticLibrary libbuspci : pci.c pci_bus.c ; + +KernelLd + pci + : + <$(SOURCE_GRIST)>pci.o + <$(SOURCE_GRIST)>pci_bus.o + kernel.so + : + $(OBOS_TOP)/src/kernel/core/addons/ldscripts/$(OBOS_ARCH)/addon.ld + : + -Bdynamic -shared + : + : + addons/kernel/pci + ; diff --git a/src/kernel/core/addons/bus_managers/pci/pci.c b/src/kernel/core/addons/bus_managers/pci/pci.c index de15d102ab..ba71c37b3a 100755 --- a/src/kernel/core/addons/bus_managers/pci/pci.c +++ b/src/kernel/core/addons/bus_managers/pci/pci.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -25,17 +25,343 @@ #include "pci_p.h" // private includes +struct found_pci_device { + struct found_pci_device *next; + struct found_pci_device *prev; + pci_info *info; +}; + +static struct found_pci_device pci_dev_list; + struct pci_config { struct pci_config *next; char *full_path; struct pci_cfg *cfg; }; +/* The pci_mode we're using, defaults to 1 as this is more common */ +static int pci_mode = 1; + +/* If we have configuration mechanism one we have devices 0 - 32 per bus, if + * we only have configuration mechanism two we have devices 0 - 16 + * XXX - set this when we determine which configuration mechanism we have + */ +static int bus_max_devices = 32; + struct pci_config *pci_list; +/* PCI has 2 Configuration Mechanisms. We need to decide which one the + * PCI Host Bridge is speaking and then speak to it correctly. This is decided + * in set_pci_mechanism() where the pci_mode value is set to the appropriate + * value and the bus_max_devices value is set correctly. + * + * Mechanism 1 + * =========== + * Mechanism 1 is the more common one found on modern computers, so presently + * that's the one that has been tested and added. + * + * This has 2 ranges, one for addressing and for data. Basically we write in the details of + * the configuration information we want (bus, device and function) and then either + * read or write from the data port. + * + * Apparently most modern hardware no longer has Configuration Type #2. + * + * XXX - add code for Mechanism Two + * + * + */ + +#define CONFIG_REQ_PORT 0xCF8 +#define CONFIG_DATA_PORT 0xCFC + +#define CONFIG_REQ(bus, device, func, offs) (0x80000000 | (bus << 16) | \ + (((device << 3) | (func & 0x07)) << 8) | \ + (offs & ~3)) + +static uint32 read_pci_config(uchar bus, uchar device, uchar function, uchar offset, uchar size) +{ + if (pci_mode == 1) { + /* write request details */ + out32(CONFIG_REQ(bus, device, function, offset), 0xCF8); + /* Now read data back from the data port... + * offset for 1 byte can be 1,2 or 3 + * offset for 2 bytes can be 1 or 2 + */ + switch (size) { + case 1: + return in8 (CONFIG_DATA_PORT + (offset & 3)); + case 2: + return in16(CONFIG_DATA_PORT + (offset & 2)); + case 4: + return in32(CONFIG_DATA_PORT + offset); + default: + dprintf("read_pci_config: called for %d bytes!!\n", size); + } + } else if (pci_mode == 2) { + dprintf("PCI: Config Mechanism 2 not yet supported!\n"); + } else + dprintf("PCI: Config Mechanism %d isn't known!\n", pci_mode); + + return 0; +} + +static void write_pci_config(uchar bus, uchar device, uchar function, uchar offset, + uchar size, uint32 value) +{ + if (pci_mode == 1) { + /* write request details */ + out32(CONFIG_REQ(bus, device, function, offset), 0xCF8); + /* Now read data back from the data port... + * offset for 1 byte can be 1,2 or 3 + * offset for 2 bytes can be 1 or 2 + */ + switch (size) { + case 1: + out8 (value, CONFIG_DATA_PORT + (offset & 3)); + case 2: + out16(value, CONFIG_DATA_PORT + (offset & 2)); + case 4: + out32(value, CONFIG_DATA_PORT + offset); + default: + dprintf("read_pci_config: called for %d bytes!!\n", size); + } + } else if (pci_mode == 2) { + dprintf("PCI: Config Mechanism 2 not yet supported\n"); + } else + dprintf("PCI: Config Mechanism %d isn't known!\n", pci_mode); + + return; +} + +/* set_pci_mechanism() + * Try to determine which configuration mechanism the PCI Host Bridge + * wants to deal with. + * XXX - we really should add code to detect and use a PCI BIOS if one + * exists, and this code then becomes the fallback. For now we'll + * just use this. + */ +static int set_pci_mechanism(void) +{ + uint32 ckval = 0x80000000; + /* Start by looking for the older and more limited mechanism 2 + * as the test will probably work for mechanism 1 as well. + * + * This code copied/adapted from OpenBSD + */ +#define PCI_MODE2_ENABLE 0x0cf8 +#define PCI_MODE2_FORWARD 0x0cfa + out8(0, PCI_MODE2_ENABLE); + out8(0, PCI_MODE2_FORWARD); + if (in8(PCI_MODE2_ENABLE) == 0 && + in8(PCI_MODE2_FORWARD) == 0) { + dprintf("PCI_Mechanism 2 test passed\n"); + bus_max_devices = 16; + pci_mode = 2; + return 0; + } + + /* If we get here, the first test (for mechanism 2) failed, so there + * is a good chance this one will pass. Basically enable then disable and + * make sure we have the same values. + */ +#define PCI_MODE1_ADDRESS 0x0cf8 + out32(ckval, PCI_MODE1_ADDRESS); + if (in32(PCI_MODE1_ADDRESS) == ckval) { + out32(0, PCI_MODE1_ADDRESS); + if (in32(PCI_MODE1_ADDRESS) == 0) { + dprintf("PCI_Mechanism 1 test passed\n"); + bus_max_devices = 32; + pci_mode = 1; + return 0; + } + } + + dprintf("PCI: Failed to find a valid PCI Configuration Mechanism!\n" + "PCI: disabled\n"); + pci_mode = 0; + + return -1; +} + +/* check_pci() + * Basically PCI bus #0 "should" contain a PCI Host Bridge. + * This can be identified by the 8 bit pci class base of 0x06. + * + * XXX - this is pretty simplistic and needs improvement. In particular + * some Intel & Compaq bridges don't have the correct class base set. + * Need to review these. For the time being if this sanity check + * fails it won't be a hanging offense, but it will generate a + * message asking for the info to be sent to the kernel list so we + * can refine this code. :) Assuming anyone ever looks at the + * debug output! + * + * returns 0 if PCI seems to be OK + * -1 if PCI fails the test + */ +static int check_pci(void) +{ + int dev = 0; + + /* Scan through the first 16 devices on bus 0 looking for + * a PCI Host Bridge + */ + for (dev = 0; dev < bus_max_devices; dev++) { + uint8 val = read_pci_config(0, dev, 0, PCI_class_base, 1); + if (val == 0x06) + return 0; + } + /* Bit wordy, but it needs to be :( */ + dprintf("*** PCI Warning! ***\n" + "The PCI sanity check appears to have failed on your system.\n" + "This is probably due to the test being used, so please email\n" + "\topen-beos-kernel-devel@lists.sourceforge.net\n" + "Your assistance will help improve this test :)\n" + "***\n" + "PCI will attempt to continue normally.\n"); + return -1; +} + + +static void scan_pci(void) +{ + int bus, dev, func; + + /* We can have up to 255 busses */ + for(bus = 0; bus < 255; bus++) { + /* Each bus can have up to 32 devices on it */ + for(dev = 0; dev < bus_max_devices; dev++) { + /* Each device can have up to 8 functions */ + for (func = 0; func < 8; func++) { + pci_info *pcii = NULL; + struct found_pci_device *npcid = NULL; + uint16 val = read_pci_config(bus, dev, func, 0, 2); + /* If we get 0xffff then there is noe device here. As there can't + * be any gaps in function allocation this tells us that we + * can move onto the next device/bus + */ + if (val == 0xffff) + break; + + /* At present we will add a device to our list if we get here, + * but we may want to review if we need to add 8 version of the + * same device if only the functions differ? + */ + if ((pcii = (pci_info*)kmalloc(sizeof(pci_info))) == NULL) { + dprintf("Failed to get memory for a pic_info structure in scan_pci\n"); + return; + } + if ((npcid = (struct found_pci_device*)kmalloc(sizeof(struct found_pci_device))) == NULL) { + kfree(pcii); + dprintf("scan_pci: failed to kmalloc memory for found_pci_device structure\n"); + return; + } + + pcii->vendor_id = val; + pcii->device_id = read_pci_config(bus, dev, func, PCI_device_id, 2); + pcii->bus = bus; + pcii->device = dev; + pcii->function = func; + pcii->revision = read_pci_config(bus, dev, func, PCI_revision, 1); + pcii->class_api = read_pci_config(bus, dev, func, PCI_class_api, 1); + pcii->class_sub = read_pci_config(bus, dev, func, PCI_class_sub, 1); + pcii->class_base = read_pci_config(bus, dev, func, PCI_class_base, 1); + pcii->line_size = read_pci_config(bus, dev, func, PCI_line_size, 1); + pcii->latency = read_pci_config(bus, dev, func, PCI_latency, 1); + pcii->header_type = read_pci_config(bus, dev, func, PCI_header_type, 1); + + if (pcii->header_type == 0) { + /* header type 0 */ + pcii->u.h0.cardbus_cis = read_pci_config(bus, dev, func, PCI_cardbus_cis, 4); + pcii->u.h0.subsystem_id = read_pci_config(bus, dev, func, PCI_subsystem_id, 2); + pcii->u.h0.subsystem_vendor_id = read_pci_config(bus, dev, func, PCI_subsystem_vendor_id, 2); + pcii->u.h0.rom_base_pci = read_pci_config(bus, dev, func, PCI_rom_base, 4); + } else if (pcii->header_type == 1) { + /* header_type 1 */ + /* bridge */ + pcii->u.h1.rom_base_pci = read_pci_config(bus, dev, func, PCI_bridge_rom_base, 4); + } else if (pcii->header_type == 0x80) { + /* ??? */ + } + + npcid->info = pcii; + /* Add the device to the list */ + insque(npcid, &pci_dev_list); + } + } + } +} + +/* XXX - check the return values from this function. */ +static long get_nth_pci_info(long index, pci_info *copyto) +{ + /* We copy the index and then decrement it. + * We also set the start found_pci_device pointer to + * the first device found and inserted. + * XXX - see discussion below. + */ + long iter = index; + struct found_pci_device *fpd = pci_dev_list.prev; + + /* iterate through the list until we have iter == 0 + * NB we go in "reverse" as the devices are inserted into the + * list at the start, so going "forward" would give us the last + * device first. + * XXX - do we want to reverse this decision and go "forwards"? + * this should reduce the number of tries we make to find + * a "device" as the system devices are the first ones found + * and therefore the last ones to be returned. + * XXX - if we do decide to reverse the order we need to be very + * careful about which function # gets found first and returned. + */ + while (iter-- > 0 && fpd && fpd != &pci_dev_list) + fpd = fpd->prev; + + /* 2 cases we bail here. + * 1) we don't have enough devices to fulfill the request + * e.g. user asked for dev #31 but we only have 30 + * 2) The found_device_structure has a NULL pointer for + * info, which would cause a segfault when we try to memcpy! + */ + if (iter > 0 || !fpd->info) + return B_DEV_ID_ERROR; + + memcpy(copyto, fpd->info, sizeof(pci_info)); + return 0; +} + +/* I/O routines */ +static uint8 pci_read_io_8(int mapped_io_addr) +{ + return in8(mapped_io_addr); +} + +static void pci_write_io_8(int mapped_io_addr, uint8 value) +{ + out8(value, mapped_io_addr); +} + +static uint16 pci_read_io_16(int mapped_io_addr) +{ + return in16(mapped_io_addr); +} + +static void pci_write_io_16(int mapped_io_addr, uint16 value) +{ + out16(value, mapped_io_addr); +} + +static uint32 pci_read_io_32(int mapped_io_addr ) +{ + return in32(mapped_io_addr); +} + +static void pci_write_io_32(int mapped_io_addr, uint32 value) +{ + out32(value, mapped_io_addr); +} + static int pci_open(const char *name, uint32 flags, void * *_cookie) { -// struct pci_cookie *cookie; struct pci_config *c = (struct pci_config *)kmalloc(sizeof(struct pci_config)); /* name points at the devfs_vnode so this is safe as we have to have a shorter @@ -185,11 +511,33 @@ int pci_bus_init(kernel_args *ka) return 0; } +static void pci_module_init(void) +{ + /* init the double linked list */ + pci_dev_list.next = pci_dev_list.prev = &pci_dev_list; + + /* Determine how we communicate with the PCI Host Bridge */ + if (set_pci_mechanism() == -1) { + return; + } + + /* Check PCI is valid and we can use it. + * Presently we don't do anything with this other than run it and + * inform the user if we pass/fail. If we fail we print a big message + * asking the user to get in touch to let us know about their system so + * we can refine the test. + */ + check_pci(); + /* Get our initial list of devices */ + scan_pci(); +} + static int std_ops(int32 op, ...) { switch(op) { case B_MODULE_INIT: dprintf( "PCI: init\n" ); + pci_module_init(); break; case B_MODULE_UNINIT: dprintf( "PCI: uninit\n" ); @@ -207,17 +555,23 @@ struct pci_module_info pci_module = { B_KEEP_LOADED, std_ops }, -NULL// &pci_rescan + NULL// &pci_rescan }, -NULL,// &read_io_8, -NULL,// &write_io_8, -NULL,// &read_io_16, -NULL,// &write_io_16, -NULL,// &read_io_32, -NULL,// &write_io_32, -NULL,// &get_nth_pci_info, -NULL,// &read_pci_config, -NULL,// &write_pci_config, -NULL,// &ram_address + &pci_read_io_8, + &pci_write_io_8, + &pci_read_io_16, + &pci_write_io_16, + &pci_read_io_32, + &pci_write_io_32, + &get_nth_pci_info, + &read_pci_config, + &write_pci_config, + NULL,// &ram_address }; + +module_info *modules[] = { + (module_info *)&pci_module, + NULL +}; +