diff options
Diffstat (limited to 'src/drivers/bus')
| -rw-r--r-- | src/drivers/bus/eisa.c | 300 | ||||
| -rw-r--r-- | src/drivers/bus/isa.c | 240 | ||||
| -rw-r--r-- | src/drivers/bus/isa_ids.c | 14 | ||||
| -rw-r--r-- | src/drivers/bus/isapnp.c | 604 | ||||
| -rw-r--r-- | src/drivers/bus/mca.c | 276 | ||||
| -rw-r--r-- | src/drivers/bus/pci.c | 6 |
6 files changed, 696 insertions, 744 deletions
diff --git a/src/drivers/bus/eisa.c b/src/drivers/bus/eisa.c index 31c248f07..d9d02d727 100644 --- a/src/drivers/bus/eisa.c +++ b/src/drivers/bus/eisa.c @@ -1,179 +1,185 @@ -#include "string.h" -#include "io.h" -#include "timer.h" -#include "console.h" -#include "dev.h" -#include "eisa.h" - -/* - * Increment a bus_loc structure to the next possible EISA location. - * Leave the structure zeroed and return 0 if there are no more valid - * locations. +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <io.h> +#include <timer.h> +#include <gpxe/eisa.h> + +static struct eisa_driver eisa_drivers[0] + __table_start ( struct eisa_driver, eisa_drivers ); +static struct eisa_driver eisa_drivers_end[0] + __table_end ( struct eisa_driver, eisa_drivers ); + +static void eisabus_remove ( struct root_device *rootdev ); + +/** + * Reset and enable/disable an EISA device * + * @v eisa EISA device + * @v enabled 1=enable, 0=disable */ -static int eisa_next_location ( struct bus_loc *bus_loc ) { - struct eisa_loc *eisa_loc = ( struct eisa_loc * ) bus_loc; - - /* - * Ensure that there is sufficient space in the shared bus - * structures for a struct isa_loc and a struct - * isa_dev, as mandated by bus.h. - * +void eisa_device_enabled ( struct eisa_device *eisa, int enabled ) { + /* Set reset line high for 1000 µs. Spec says 500 µs, but + * this doesn't work for all cards, so we are conservative. */ - BUS_LOC_CHECK ( struct eisa_loc ); - BUS_DEV_CHECK ( struct eisa_device ); - - return ( eisa_loc->slot = ( ++eisa_loc->slot & EISA_MAX_SLOT ) ); -} - -/* - * Fill in parameters for an EISA device based on slot number - * - * Return 1 if device present, 0 otherwise - * - */ -static int eisa_fill_device ( struct bus_dev *bus_dev, - struct bus_loc *bus_loc ) { - struct eisa_loc *eisa_loc = ( struct eisa_loc * ) bus_loc; - struct eisa_device *eisa = ( struct eisa_device * ) bus_dev; - uint8_t present; - - /* Copy slot number to struct eisa, set default values */ - eisa->slot = eisa_loc->slot; - eisa->name = "?"; - - /* Slot 0 is never valid */ - if ( ! eisa->slot ) - return 0; - - /* Set ioaddr */ - eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot ); - - /* Test for board present */ - outb ( 0xff, eisa->ioaddr + EISA_MFG_ID_HI ); - present = inb ( eisa->ioaddr + EISA_MFG_ID_HI ); - if ( present & 0x80 ) { - /* No board present */ - return 0; - } + outb ( EISA_CMD_RESET, eisa->ioaddr + EISA_GLOBAL_CONFIG ); + udelay ( 1000 ); /* Must wait 800 */ - /* Read mfg and product IDs. Yes, the resulting uint16_ts - * will be upside-down. This appears to be by design. + /* Set reset low and write a 1 to ENABLE. Delay again, in + * case the card takes a while to wake up. */ - eisa->mfg_id = ( inb ( eisa->ioaddr + EISA_MFG_ID_LO ) << 8 ) - + present; - eisa->prod_id = ( inb ( eisa->ioaddr + EISA_PROD_ID_LO ) << 8 ) - + inb ( eisa->ioaddr + EISA_PROD_ID_HI ); - - DBG ( "EISA found slot %hhx (base %#hx) ID %hx:%hx (\"%s\")\n", - eisa->slot, eisa->ioaddr, eisa->mfg_id, eisa->prod_id, - isa_id_string ( eisa->mfg_id, eisa->prod_id ) ); + outb ( enabled ? EISA_CMD_ENABLE : 0, + eisa->ioaddr + EISA_GLOBAL_CONFIG ); + udelay ( 1000 ); /* Must wait 800 */ - return 1; + DBG ( "EISA %s device %02x\n", ( enabled ? "enabled" : "disabled" ), + eisa->slot ); } -/* - * Test whether or not a driver is capable of driving the device. +/** + * Probe an EISA device + * + * @v eisa EISA device + * @ret rc Return status code * + * Searches for a driver for the EISA device. If a driver is found, + * its probe() routine is called. */ -static int eisa_check_driver ( struct bus_dev *bus_dev, - struct device_driver *device_driver ) { - struct eisa_device *eisa = ( struct eisa_device * ) bus_dev; - struct eisa_driver *driver - = ( struct eisa_driver * ) device_driver->bus_driver_info; +static int eisa_probe ( struct eisa_device *eisa ) { + struct eisa_driver *driver; + struct eisa_device_id *id; unsigned int i; - - /* Compare against driver's ID list */ - for ( i = 0 ; i < driver->id_count ; i++ ) { - struct eisa_id *id = &driver->ids[i]; - - if ( ( eisa->mfg_id == id->mfg_id ) && - ( ISA_PROD_ID ( eisa->prod_id ) == - ISA_PROD_ID ( id->prod_id ) ) ) { - DBG ( "EISA found ID %hx:%hx (\"%s\") " - "(device %s) matching driver %s\n", - eisa->mfg_id, eisa->prod_id, - isa_id_string ( eisa->mfg_id, - eisa->prod_id ), - id->name, driver->name ); - eisa->name = id->name; - return 1; + int rc; + + DBG ( "Adding EISA device %02x (%04x:%04x (\"%s\") io %x)\n", + eisa->slot, eisa->vendor_id, eisa->prod_id, + isa_id_string ( eisa->vendor_id, eisa->prod_id ), eisa->ioaddr ); + + for ( driver = eisa_drivers; driver < eisa_drivers_end; driver++ ) { + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( id->vendor_id != eisa->vendor_id ) + continue; + if ( ISA_PROD_ID ( id->prod_id ) != + ISA_PROD_ID ( eisa->prod_id ) ) + continue; + eisa->driver = driver; + eisa->driver_name = id->name; + DBG ( "...using driver %s\n", eisa->driver_name ); + if ( ( rc = driver->probe ( eisa, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; } } - /* No device found */ - return 0; + DBG ( "...no driver found\n" ); + return -ENOTTY; } -/* - * Describe an EISA device +/** + * Remove an EISA device * + * @v eisa EISA device */ -static char * eisa_describe_device ( struct bus_dev *bus_dev ) { - struct eisa_device *eisa = ( struct eisa_device * ) bus_dev; - static char eisa_description[] = "EISA 00"; - - sprintf ( eisa_description + 5, "%hhx", eisa->slot ); - return eisa_description; +static void eisa_remove ( struct eisa_device *eisa ) { + eisa->driver->remove ( eisa ); + DBG ( "Removed EISA device %02x\n", eisa->slot ); } -/* - * Name an EISA device +/** + * Probe EISA root bus * - */ -static const char * eisa_name_device ( struct bus_dev *bus_dev ) { - struct eisa_device *eisa = ( struct eisa_device * ) bus_dev; - - return eisa->name; -} - -/* - * EISA bus operations table + * @v rootdev EISA bus root device * + * Scans the EISA bus for devices and registers all devices it can + * find. */ -struct bus_driver eisa_driver __bus_driver = { - .name = "EISA", - .next_location = eisa_next_location, - .fill_device = eisa_fill_device, - .check_driver = eisa_check_driver, - .describe_device = eisa_describe_device, - .name_device = eisa_name_device, -}; +static int eisabus_probe ( struct root_device *rootdev ) { + struct eisa_device *eisa = NULL; + unsigned int slot; + int rc; + + for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) { + /* Allocate struct eisa_device */ + if ( ! eisa ) + eisa = malloc ( sizeof ( *eisa ) ); + if ( ! eisa ) { + rc = -ENOMEM; + goto err; + } + memset ( eisa, 0, sizeof ( *eisa ) ); + eisa->slot = slot; + eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot ); + + /* Test for board present */ + outb ( 0xff, eisa->ioaddr + EISA_VENDOR_ID ); + eisa->vendor_id = + le16_to_cpu ( inw ( eisa->ioaddr + EISA_VENDOR_ID ) ); + eisa->prod_id = + le16_to_cpu ( inw ( eisa->ioaddr + EISA_PROD_ID ) ); + if ( eisa->vendor_id & 0x80 ) { + /* No board present */ + continue; + } -/* - * Fill in a nic structure - * - */ -void eisa_fill_nic ( struct nic *nic, struct eisa_device *eisa ) { + /* Add to device hierarchy */ + snprintf ( eisa->dev.name, sizeof ( eisa->dev.name ), + "EISA%02x", slot ); + eisa->dev.desc.bus_type = BUS_TYPE_EISA; + eisa->dev.desc.vendor = eisa->vendor_id; + eisa->dev.desc.device = eisa->prod_id; + eisa->dev.parent = &rootdev->dev; + list_add ( &eisa->dev.siblings, &rootdev->dev.children ); + INIT_LIST_HEAD ( &eisa->dev.children ); + + /* Look for a driver */ + if ( eisa_probe ( eisa ) == 0 ) { + /* eisadev registered, we can drop our ref */ + eisa = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &eisa->dev.siblings ); + } + } - /* Fill in ioaddr and irqno */ - nic->ioaddr = eisa->ioaddr; - nic->irqno = 0; + free ( eisa ); + return 0; - /* Fill in DHCP device ID structure */ - nic->dhcp_dev_id.bus_type = ISA_BUS_TYPE; - nic->dhcp_dev_id.vendor_id = htons ( eisa->mfg_id ); - nic->dhcp_dev_id.device_id = htons ( eisa->prod_id ); + err: + free ( eisa ); + eisabus_remove ( rootdev ); + return rc; } -/* - * Reset and enable/disable an EISA device +/** + * Remove EISA root bus * + * @v rootdev EISA bus root device */ -void eisa_device_enabled ( struct eisa_device *eisa, int enabled ) { - /* Set reset line high for 1000 µs. Spec says 500 µs, but - * this doesn't work for all cards, so we are conservative. - */ - outb ( EISA_CMD_RESET, eisa->ioaddr + EISA_GLOBAL_CONFIG ); - udelay ( 1000 ); /* Must wait 800 */ +static void eisabus_remove ( struct root_device *rootdev ) { + struct eisa_device *eisa; + struct eisa_device *tmp; + + list_for_each_entry_safe ( eisa, tmp, &rootdev->dev.children, + dev.siblings ) { + eisa_remove ( eisa ); + list_del ( &eisa->dev.siblings ); + free ( eisa ); + } +} - /* Set reset low and write a 1 to ENABLE. Delay again, in - * case the card takes a while to wake up. - */ - outb ( enabled ? EISA_CMD_ENABLE : 0, - eisa->ioaddr + EISA_GLOBAL_CONFIG ); - udelay ( 1000 ); /* Must wait 800 */ +/** EISA bus root device driver */ +static struct root_driver eisa_root_driver = { + .probe = eisabus_probe, + .remove = eisabus_remove, +}; - DBG ( "EISA %s device %hhx\n", ( enabled ? "enabled" : "disabled" ), - eisa->slot ); -} +/** EISA bus root device */ +struct root_device eisa_root_device __root_device = { + .dev = { .name = "EISA" }, + .driver = &eisa_root_driver, +}; diff --git a/src/drivers/bus/isa.c b/src/drivers/bus/isa.c index d8b88fdb2..a4105fd00 100644 --- a/src/drivers/bus/isa.c +++ b/src/drivers/bus/isa.c @@ -1,8 +1,10 @@ -#include "string.h" -#include "console.h" -#include "config/isa.h" -#include "dev.h" -#include "isa.h" +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <io.h> +#include <gpxe/isa.h> /* * isa.c implements a "classical" port-scanning method of ISA device @@ -31,161 +33,143 @@ static isa_probe_addr_t isa_extra_probe_addrs[] = { ISA_PROBE_ADDRS #endif }; -#define isa_extra_probe_addr_count \ +#define ISA_EXTRA_PROBE_ADDR_COUNT \ ( sizeof ( isa_extra_probe_addrs ) / sizeof ( isa_extra_probe_addrs[0] ) ) +#define ISA_IOIDX_MIN( driver ) ( -ISA_EXTRA_PROBE_ADDR_COUNT ) #ifdef ISA_PROBE_ONLY -#define ISA_PROBE_ADDR_COUNT(driver) ( isa_extra_probe_addr_count ) +#define ISA_IOIDX_MAX( driver ) ( -1 ) #else -#define ISA_PROBE_ADDR_COUNT(driver) \ - ( isa_extra_probe_addr_count + (driver)->addr_count ) +#define ISA_IOIDX_MAX( driver ) ( (int) (driver)->addr_count - 1 ) #endif -/* - * Symbols defined by linker - * - */ +#define ISA_IOADDR( driver, ioidx ) \ + ( ( (ioidx) < 0 ) ? \ + isa_extra_probe_addrs[ (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ] : \ + (driver)->probe_addrs[(ioidx)] ) + static struct isa_driver isa_drivers[0] __table_start ( struct isa_driver, isa_driver ); static struct isa_driver isa_drivers_end[0] __table_end ( struct isa_driver, isa_driver ); -/* - * Increment a bus_loc structure to the next possible ISA location. - * Leave the structure zeroed and return 0 if there are no more valid - * locations. - * - * There is no sensible concept of a device location on an ISA bus, so - * we use the probe address list for each ISA driver to define the - * list of ISA locations. - * - */ -static int isa_next_location ( struct bus_loc *bus_loc ) { - struct isa_loc *isa_loc = ( struct isa_loc * ) bus_loc; - struct isa_driver *driver; - - /* - * Ensure that there is sufficient space in the shared bus - * structures for a struct isa_loc and a struct - * isa_dev, as mandated by bus.h. - * - */ - BUS_LOC_CHECK ( struct isa_loc ); - BUS_DEV_CHECK ( struct isa_device ); - - /* Move to next probe address within this driver */ - driver = &isa_drivers[isa_loc->driver]; - if ( ++isa_loc->probe_idx < ISA_PROBE_ADDR_COUNT ( driver ) ) - return 1; - - /* Move to next driver */ - isa_loc->probe_idx = 0; - if ( ( ++isa_loc->driver, ++driver ) < isa_drivers_end ) - return 1; - - isa_loc->driver = 0; - return 0; -} +static void isabus_remove ( struct root_device *rootdev ); -/* - * Fill in parameters (vendor & device ids, class, membase etc.) for - * an ISA device based on bus_loc. - * - * Returns 1 if a device was found, 0 for no device present. +/** + * Probe an ISA device * + * @v isa ISA device + * @ret rc Return status code */ -static int isa_fill_device ( struct bus_dev *bus_dev, - struct bus_loc *bus_loc ) { - struct isa_loc *isa_loc = ( struct isa_loc * ) bus_loc; - struct isa_device *isa = ( struct isa_device * ) bus_dev; - signed int driver_probe_idx; - - /* Fill in struct isa from struct isa_loc */ - isa->driver = &isa_drivers[isa_loc->driver]; - driver_probe_idx = isa_loc->probe_idx - isa_extra_probe_addr_count; - if ( driver_probe_idx < 0 ) { - isa->ioaddr = isa_extra_probe_addrs[isa_loc->probe_idx]; - } else { - isa->ioaddr = isa->driver->probe_addrs[driver_probe_idx]; - } +static int isa_probe ( struct isa_device *isa ) { + int rc; + + DBG ( "Trying ISA driver %s at I/O %04x\n", + isa->driver->name, isa->ioaddr ); - /* Call driver's probe_addr method to determine if a device is - * physically present - */ - if ( isa->driver->probe_addr ( isa->ioaddr ) ) { - isa->name = isa->driver->name; - isa->mfg_id = isa->driver->mfg_id; - isa->prod_id = isa->driver->prod_id; - DBG ( "ISA found %s device at address %hx\n", - isa->name, isa->ioaddr ); - return 1; + if ( ( rc = isa->driver->probe ( isa ) ) != 0 ) { + DBG ( "...probe failed\n" ); + return rc; } + DBG ( "...device found\n" ); return 0; } -/* - * Test whether or not a driver is capable of driving the specified - * device. +/** + * Remove an ISA device * + * @v isa ISA device */ -int isa_check_driver ( struct bus_dev *bus_dev, - struct device_driver *device_driver ) { - struct isa_device *isa = ( struct isa_device * ) bus_dev; - struct isa_driver *driver - = ( struct isa_driver * ) device_driver->bus_driver_info; - - return ( driver == isa->driver ); +static void isa_remove ( struct isa_device *isa ) { + isa->driver->remove ( isa ); + DBG ( "Removed ISA%04x\n", isa->ioaddr ); } -/* - * Describe a ISA device +/** + * Probe ISA root bus + * + * @v rootdev ISA bus root device * + * Scans the ISA bus for devices and registers all devices it can + * find. */ -static char * isa_describe_device ( struct bus_dev *bus_dev ) { - struct isa_device *isa = ( struct isa_device * ) bus_dev; - static char isa_description[] = "ISA 0000 (00)"; +static int isabus_probe ( struct root_device *rootdev ) { + struct isa_device *isa = NULL; + struct isa_driver *driver; + int ioidx; + int rc; + + for ( driver = isa_drivers ; driver < isa_drivers_end ; driver++ ) { + for ( ioidx = ISA_IOIDX_MIN ( driver ) ; + ioidx <= ISA_IOIDX_MAX ( driver ) ; ioidx++ ) { + /* Allocate struct isa_device */ + if ( ! isa ) + isa = malloc ( sizeof ( *isa ) ); + if ( ! isa ) { + rc = -ENOMEM; + goto err; + } + memset ( isa, 0, sizeof ( *isa ) ); + isa->driver = driver; + isa->ioaddr = ISA_IOADDR ( driver, ioidx ); + + /* Add to device hierarchy */ + snprintf ( isa->dev.name, sizeof ( isa->dev.name ), + "ISA%04x", isa->ioaddr ); + isa->dev.desc.bus_type = BUS_TYPE_ISA; + isa->dev.desc.vendor = driver->vendor_id; + isa->dev.desc.device = driver->prod_id; + isa->dev.parent = &rootdev->dev; + list_add ( &isa->dev.siblings, + &rootdev->dev.children ); + INIT_LIST_HEAD ( &isa->dev.children ); + + /* Try probing at this I/O address */ + if ( isa_probe ( isa ) == 0 ) { + /* isadev registered, we can drop our ref */ + isa = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &isa->dev.siblings ); + } + } + } - sprintf ( isa_description + 4, "%hx (%hhx)", isa->ioaddr, - isa->driver - isa_drivers ); - return isa_description; + free ( isa ); + return 0; + + err: + free ( isa ); + isabus_remove ( rootdev ); + return rc; } -/* - * Name a ISA device +/** + * Remove ISA root bus * + * @v rootdev ISA bus root device */ -static const char * isa_name_device ( struct bus_dev *bus_dev ) { - struct isa_device *isa = ( struct isa_device * ) bus_dev; - - return isa->name; +static void isabus_remove ( struct root_device *rootdev ) { + struct isa_device *isa; + struct isa_device *tmp; + + list_for_each_entry_safe ( isa, tmp, &rootdev->dev.children, + dev.siblings ) { + isa_remove ( isa ); + list_del ( &isa->dev.siblings ); + free ( isa ); + } } -/* - * ISA bus operations table - * - */ -struct bus_driver isa_driver __bus_driver = { - .name = "ISA", - .next_location = isa_next_location, - .fill_device = isa_fill_device, - .check_driver = isa_check_driver, - .describe_device = isa_describe_device, - .name_device = isa_name_device, +/** ISA bus root device driver */ +static struct root_driver isa_root_driver = { + .probe = isabus_probe, + .remove = isabus_remove, }; -/* - * Fill in a nic structure - * - */ -void isa_fill_nic ( struct nic *nic, struct isa_device *isa ) { - - /* Fill in ioaddr and irqno */ - nic->ioaddr = isa->ioaddr; - nic->irqno = 0; - - /* Fill in DHCP device ID structure */ - nic->dhcp_dev_id.bus_type = ISA_BUS_TYPE; - nic->dhcp_dev_id.vendor_id = htons ( isa->mfg_id ); - nic->dhcp_dev_id.device_id = htons ( isa->prod_id ); -} +/** ISA bus root device */ +struct root_device isa_root_device __root_device = { + .dev = { .name = "ISA" }, + .driver = &isa_root_driver, +}; diff --git a/src/drivers/bus/isa_ids.c b/src/drivers/bus/isa_ids.c index a175ed459..73101584f 100644 --- a/src/drivers/bus/isa_ids.c +++ b/src/drivers/bus/isa_ids.c @@ -1,15 +1,15 @@ -#include "stdint.h" -#include "byteswap.h" -#include "console.h" -#include "isa_ids.h" +#include <stdint.h> +#include <stdio.h> +#include <byteswap.h> +#include <gpxe/isa_ids.h> /* * EISA and ISAPnP IDs are actually mildly human readable, though in a * somewhat brain-damaged way. * */ -char * isa_id_string ( uint16_t vendor, uint16_t product ) { - static unsigned char buf[7]; +char * isa_id_string ( unsigned int vendor, unsigned int product ) { + static char buf[7]; int i; /* Vendor ID is a compressed ASCII string */ @@ -20,7 +20,7 @@ char * isa_id_string ( uint16_t vendor, uint16_t product ) { } /* Product ID is a 4-digit hex string */ - sprintf ( &buf[3], "%hx", bswap_16 ( product ) ); + sprintf ( &buf[3], "%04x", bswap_16 ( product ) ); return buf; } diff --git a/src/drivers/bus/isapnp.c b/src/drivers/bus/isapnp.c index 41c9cb289..96bc60ec9 100644 --- a/src/drivers/bus/isapnp.c +++ b/src/drivers/bus/isapnp.c @@ -60,80 +60,77 @@ * */ -#include "string.h" -#include "timer.h" -#include "io.h" -#include "console.h" -#include "dev.h" -#include "isapnp.h" +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <io.h> +#include <timer.h> +#include <gpxe/isapnp.h> /** * ISAPnP Read Port address. * + * ROM prefix may be able to set this address, which is why this is + * non-static. */ uint16_t isapnp_read_port; -/** - * Highest assigned CSN. - * - * Note that @b we do not necessarily assign CSNs; it could be done by - * the PnP BIOS instead. We therefore set this only when we first try - * to Wake[CSN] a device and find that there's nothing there. Page 16 - * (PDF page 22) of the ISAPnP spec states that "Valid Card Select - * Numbers for identified ISA cards range from 1 to 255 and must be - * assigned sequentially starting from 1", so we are (theoretically, - * at least) safe to assume that there are no ISAPnP cards at CSNs - * higher than the first unused CSN. - * - */ -static uint8_t isapnp_max_csn = 0xff; +static struct isapnp_driver isapnp_drivers[0] + __table_start ( struct isapnp_driver, isapnp_drivers ); +static struct isapnp_driver isapnp_drivers_end[0] + __table_end ( struct isapnp_driver, isapnp_drivers ); + +static void isapnpbus_remove ( struct root_device *rootdev ); /* * ISAPnP utility functions * */ -#define ISAPNP_CARD_ID_FMT "ID %hx:%hx (\"%s\") serial %x" +#define ISAPNP_CARD_ID_FMT "ID %04x:%04x (\"%s\") serial %lx" #define ISAPNP_CARD_ID_DATA(identifier) \ (identifier)->vendor_id, (identifier)->prod_id, \ isa_id_string ( (identifier)->vendor_id, (identifier)->prod_id ), \ (identifier)->serial -#define ISAPNP_DEV_ID_FMT "ID %hx:%hx (\"%s\")" +#define ISAPNP_DEV_ID_FMT "ID %04x:%04x (\"%s\")" #define ISAPNP_DEV_ID_DATA(isapnp) \ (isapnp)->vendor_id, (isapnp)->prod_id, \ isa_id_string ( (isapnp)->vendor_id, (isapnp)->prod_id ) -static inline void isapnp_write_address ( uint8_t address ) { +static inline void isapnp_write_address ( unsigned int address ) { outb ( address, ISAPNP_ADDRESS ); } -static inline void isapnp_write_data ( uint8_t data ) { +static inline void isapnp_write_data ( unsigned int data ) { outb ( data, ISAPNP_WRITE_DATA ); } -static inline uint8_t isapnp_read_data ( void ) { +static inline unsigned int isapnp_read_data ( void ) { return inb ( isapnp_read_port ); } -static inline void isapnp_write_byte ( uint8_t address, uint8_t value ) { +static inline void isapnp_write_byte ( unsigned int address, + unsigned int value ) { isapnp_write_address ( address ); isapnp_write_data ( value ); } -static inline uint8_t isapnp_read_byte ( uint8_t address ) { +static inline unsigned int isapnp_read_byte ( unsigned int address ) { isapnp_write_address ( address ); return isapnp_read_data (); } -static inline uint16_t isapnp_read_word ( uint8_t address ) { +static inline unsigned int isapnp_read_word ( unsigned int address ) { /* Yes, they're in big-endian order */ return ( ( isapnp_read_byte ( address ) << 8 ) - + isapnp_read_byte ( address + 1 ) ); + | isapnp_read_byte ( address + 1 ) ); } /** Inform cards of a new read port address */ static inline void isapnp_set_read_port ( void ) { - isapnp_write_byte ( ISAPNP_READPORT, isapnp_read_port >> 2 ); + isapnp_write_byte ( ISAPNP_READPORT, ( isapnp_read_port >> 2 ) ); } /** @@ -141,7 +138,6 @@ static inline void isapnp_set_read_port ( void ) { * * Only cards currently in the Sleep state will respond to this * command. - * */ static inline void isapnp_serialisolation ( void ) { isapnp_write_address ( ISAPNP_SERIALISOLATION ); @@ -152,7 +148,6 @@ static inline void isapnp_serialisolation ( void ) { * * All cards will respond to this command, regardless of their current * state. - * */ static inline void isapnp_wait_for_key ( void ) { isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY ); @@ -163,7 +158,6 @@ static inline void isapnp_wait_for_key ( void ) { * * Only cards currently in the Sleep state will respond to this * command. - * */ static inline void isapnp_reset_csn ( void ) { isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN ); @@ -179,17 +173,16 @@ static inline void isapnp_reset_csn ( void ) { * Only cards currently in the Sleep, Isolation, or Config states will * respond to this command. The card that has the specified CSN will * enter the Config state, all other cards will enter the Sleep state. - * */ static inline void isapnp_wake ( uint8_t csn ) { isapnp_write_byte ( ISAPNP_WAKE, csn ); } -static inline uint8_t isapnp_read_resourcedata ( void ) { +static inline unsigned int isapnp_read_resourcedata ( void ) { return isapnp_read_byte ( ISAPNP_RESOURCEDATA ); } -static inline uint8_t isapnp_read_status ( void ) { +static inline unsigned int isapnp_read_status ( void ) { return isapnp_read_byte ( ISAPNP_STATUS ); } @@ -197,38 +190,35 @@ static inline uint8_t isapnp_read_status ( void ) { * Assign a Card Select Number to a card, and enter the Config state. * * @v csn Card Select Number - * @ret None - - * @err None - * * Only cards in the Isolation state will respond to this command. * The isolation protocol is designed so that only one card will * remain in the Isolation state by the time the isolation protocol * completes. - * */ -static inline void isapnp_write_csn ( uint8_t csn ) { +static inline void isapnp_write_csn ( unsigned int csn ) { isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn ); } -static inline void isapnp_logicaldevice ( uint8_t logdev ) { +static inline void isapnp_logicaldevice ( unsigned int logdev ) { isapnp_write_byte ( ISAPNP_LOGICALDEVICENUMBER, logdev ); } -static inline void isapnp_activate ( uint8_t logdev ) { +static inline void isapnp_activate ( unsigned int logdev ) { isapnp_logicaldevice ( logdev ); isapnp_write_byte ( ISAPNP_ACTIVATE, 1 ); } -static inline void isapnp_deactivate ( uint8_t logdev ) { +static inline void isapnp_deactivate ( unsigned int logdev ) { isapnp_logicaldevice ( logdev ); isapnp_write_byte ( ISAPNP_ACTIVATE, 0 ); } -static inline uint16_t isapnp_read_iobase ( unsigned int index ) { +static inline unsigned int isapnp_read_iobase ( unsigned int index ) { return isapnp_read_word ( ISAPNP_IOBASE ( index ) ); } -static inline uint8_t isapnp_read_irqno ( unsigned int index ) { +static inline unsigned int isapnp_read_irqno ( unsigned int index ) { return isapnp_read_byte ( ISAPNP_IRQNO ( index ) ); } @@ -242,7 +232,6 @@ static void isapnp_delay ( void ) { * @v lfsr Current value of the LFSR * @v input_bit Current input bit to the LFSR * @ret lfsr Next value of the LFSR - * @err None - * * This routine implements the linear feedback shift register as * described in Appendix B of the PnP ISA spec. The hardware @@ -250,9 +239,9 @@ static void isapnp_delay ( void ) { * think this is probably the smallest possible implementation in * software. Six instructions when input_bit is a constant 0 (for * isapnp_send_key). :) - * */ -static inline uint8_t isapnp_lfsr_next ( uint8_t lfsr, int input_bit ) { +static inline unsigned int isapnp_lfsr_next ( unsigned int lfsr, + unsigned int input_bit ) { register uint8_t lfsr_next; lfsr_next = lfsr >> 1; @@ -265,11 +254,10 @@ static inline uint8_t isapnp_lfsr_next ( uint8_t lfsr, int input_bit ) { * * Sending the key causes all ISAPnP cards that are currently in the * Wait for Key state to transition into the Sleep state. - * */ static void isapnp_send_key ( void ) { unsigned int i; - uint8_t lfsr; + unsigned int lfsr; isapnp_delay(); isapnp_write_address ( 0x00 ); @@ -285,19 +273,17 @@ static void isapnp_send_key ( void ) { /** * Compute ISAPnP identifier checksum * - * @v identifier ISAPnP identifier - * @ret checksum Expected checksum value - * @err None - - * + * @v identifier ISAPnP identifier + * @ret checksum Expected checksum value */ -static uint8_t isapnp_checksum ( struct isapnp_identifier *identifier ) { - int i, j; - uint8_t lfsr; - uint8_t byte; +static unsigned int isapnp_checksum ( struct isapnp_identifier *identifier ) { + unsigned int i, j; + unsigned int lfsr; + unsigned int byte; lfsr = ISAPNP_LFSR_SEED; for ( i = 0 ; i < 8 ; i++ ) { - byte = ( (char *) identifier )[i]; + byte = * ( ( ( uint8_t * ) identifier ) + i ); for ( j = 0 ; j < 8 ; j++ ) { lfsr = isapnp_lfsr_next ( lfsr, byte ); byte >>= 1; @@ -309,17 +295,18 @@ static uint8_t isapnp_checksum ( struct isapnp_identifier *identifier ) { /* * Read a byte of resource data from the current location * + * @ret byte Byte of resource data */ -static inline uint8_t isapnp_peek_byte ( void ) { - int i; +static inline unsigned int isapnp_peek_byte ( void ) { + unsigned int i; /* Wait for data to be ready */ - for ( i = 0 ; i < 20 ; i ++ ) { + for ( i = 0 ; i < 20 ; i++ ) { if ( isapnp_read_status() & 0x01 ) { /* Byte ready - read it */ return isapnp_read_resourcedata(); } - isapnp_delay (); + isapnp_delay(); } /* Data never became ready - return 0xff */ return 0xff; @@ -330,22 +317,18 @@ static inline uint8_t isapnp_peek_byte ( void ) { * * @v buf Buffer in which to store data, or NULL * @v bytes Number of bytes to read - * @ret None - - * @err None - * * Resource data is read from the current location. If #buf is NULL, * the data is discarded. - * */ -static void isapnp_peek ( uint8_t *buf, size_t bytes ) { +static void isapnp_peek ( void *buf, size_t len ) { unsigned int i; - uint8_t byte; + unsigned int byte; - for ( i = 0 ; i < bytes ; i++) { + for ( i = 0 ; i < len ; i++) { byte = isapnp_peek_byte(); - if ( buf ) { - buf[i] = byte; - } + if ( buf ) + * ( ( uint8_t * ) buf + i ) = byte; } } @@ -354,40 +337,59 @@ static void isapnp_peek ( uint8_t *buf, size_t bytes ) { * * @v wanted_tag The tag that we're looking for * @v buf Buffer in which to store the tag's contents - * @ret True Tag was found - * @ret False Tag was not found - * @err None - + * @v len Length of buffer + * @ret rc Return status code * * Scan through the resource data until we find a particular tag, and - * read its contents into a buffer. It is the caller's responsibility - * to ensure that #buf is large enough to contain a tag of the - * requested size. - * + * read its contents into a buffer. */ -static int isapnp_find_tag ( uint8_t wanted_tag, uint8_t *buf ) { - uint8_t tag; - uint16_t len; +static int isapnp_find_tag ( unsigned int wanted_tag, void *buf, size_t len ) { + unsigned int tag; + unsigned int tag_len; DBG2 ( "ISAPnP read tag" ); do { tag = isapnp_peek_byte(); if ( ISAPNP_IS_SMALL_TAG ( tag ) ) { - len = ISAPNP_SMALL_TAG_LEN ( tag ); + tag_len = ISAPNP_SMALL_TAG_LEN ( tag ); tag = ISAPNP_SMALL_TAG_NAME ( tag ); } else { - len = isapnp_peek_byte() + ( isapnp_peek_byte() << 8 ); + tag_len = ( isapnp_peek_byte() + + ( isapnp_peek_byte() << 8 ) ); tag = ISAPNP_LARGE_TAG_NAME ( tag ); } - DBG2 ( " %hhx (%hhx)", tag, len ); + DBG2 ( " %02x (%02x)", tag, tag_len ); if ( tag == wanted_tag ) { + if ( len > tag_len ) + len = tag_len; isapnp_peek ( buf, len ); DBG2 ( "\n" ); - return 1; + return 0; } else { - isapnp_peek ( NULL, len ); + isapnp_peek ( NULL, tag_len ); } } while ( tag != ISAPNP_TAG_END ); DBG2 ( "\n" ); + return -ENOENT; +} + +/** + * Find specified Logical Device ID tag + * + * @v logdev Logical device ID + * @v logdevid Logical device ID structure to fill in + * @ret rc Return status code + */ +static int isapnp_find_logdevid ( unsigned int logdev, + struct isapnp_logdevid *logdevid ) { + unsigned int i; + int rc; + + for ( i = 0 ; i <= logdev ; i++ ) { + if ( ( rc = isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, logdevid, + sizeof ( *logdevid ) ) ) != 0 ) + return rc; + } return 0; } @@ -401,37 +403,36 @@ static int isapnp_find_tag ( uint8_t wanted_tag, uint8_t *buf ) { * * The state diagram on page 18 (PDF page 24) of the PnP ISA spec * gives the best overview of what happens here. - * */ static int isapnp_try_isolate ( void ) { struct isapnp_identifier identifier; unsigned int i, j; unsigned int seen_55aa, seen_life; unsigned int csn = 0; - uint16_t data; - uint8_t byte; + unsigned int data; + unsigned int byte; - DBG ( "ISAPnP attempting isolation at read port %hx\n", + DBG ( "ISAPnP attempting isolation at read port %04x\n", isapnp_read_port ); /* Place all cards into the Sleep state, whatever state * they're currently in. */ - isapnp_wait_for_key (); - isapnp_send_key (); + isapnp_wait_for_key(); + isapnp_send_key(); /* Reset all assigned CSNs */ - isapnp_reset_csn (); + isapnp_reset_csn(); isapnp_delay(); isapnp_delay(); /* Place all cards into the Isolation state */ isapnp_wait_for_key (); - isapnp_send_key (); + isapnp_send_key(); isapnp_wake ( 0x00 ); /* Set the read port */ - isapnp_set_read_port (); + isapnp_set_read_port(); isapnp_delay(); while ( 1 ) { @@ -442,7 +443,7 @@ static int isapnp_try_isolate ( void ) { */ /* Initiate serial isolation */ - isapnp_serialisolation (); + isapnp_serialisolation(); isapnp_delay(); /* Read identifier serially via the ISAPnP read port. */ @@ -451,9 +452,9 @@ static int isapnp_try_isolate ( void ) { for ( i = 0 ; i < 9 ; i++ ) { byte = 0; for ( j = 0 ; j < 8 ; j++ ) { - data = isapnp_read_data (); + data = isapnp_read_data(); isapnp_delay(); - data = ( data << 8 ) | isapnp_read_data (); + data = ( ( data << 8 ) | isapnp_read_data() ); isapnp_delay(); byte >>= 1; if ( data != 0xffff ) { @@ -464,7 +465,7 @@ static int isapnp_try_isolate ( void ) { } } } - ( (char *) &identifier )[i] = byte; + *( ( ( uint8_t * ) &identifier ) + i ) = byte; } /* If we didn't see any 55aa patterns, stop here */ @@ -487,8 +488,8 @@ static int isapnp_try_isolate ( void ) { /* If the checksum was invalid stop here */ if ( identifier.checksum != isapnp_checksum ( &identifier) ) { DBG ( "ISAPnP found malformed card " - ISAPNP_CARD_ID_FMT "\n with checksum %hhx " - "(should be %hhx), trying new read port\n", + ISAPNP_CARD_ID_FMT "\n with checksum %02x " + "(should be %02x), trying new read port\n", ISAPNP_CARD_ID_DATA ( &identifier ), identifier.checksum, isapnp_checksum ( &identifier) ); @@ -499,7 +500,7 @@ static int isapnp_try_isolate ( void ) { /* Give the device a CSN */ csn++; DBG ( "ISAPnP found card " ISAPNP_CARD_ID_FMT - ", assigning CSN %hhx\n", + ", assigning CSN %02x\n", ISAPNP_CARD_ID_DATA ( &identifier ), csn ); isapnp_write_csn ( csn ); @@ -513,11 +514,11 @@ static int isapnp_try_isolate ( void ) { } /* Place all cards in Wait for Key state */ - isapnp_wait_for_key (); + isapnp_wait_for_key(); /* Return number of cards found */ if ( csn > 0 ) { - DBG ( "ISAPnP found %d cards at read port %hx\n", + DBG ( "ISAPnP found %d cards at read port %04x\n", csn, isapnp_read_port ); } return csn; @@ -539,281 +540,222 @@ static void isapnp_isolate ( void ) { continue; /* If we detect any ISAPnP cards at this location, stop */ - if ( isapnp_try_isolate () >= 0 ) + if ( isapnp_try_isolate() >= 0 ) return; } } /** - * Increment a #bus_loc structure to the next possible ISAPnP - * location. + * Activate or deactivate an ISAPnP device. * - * @v bus_loc Bus location - * @ret True #bus_loc contains a valid ISAPnP location - * @ret False There are no more valid ISAPnP locations + * @v isapnp ISAPnP device + * @v activation True to enable, False to disable the device + * @ret None - * @err None - * - * If there are no more valid locations, the #bus_loc structure will - * be zeroed. - * - */ -static int isapnp_next_location ( struct bus_loc *bus_loc ) { - struct isapnp_loc *isapnp_loc = ( struct isapnp_loc * ) bus_loc; - - /* - * Ensure that there is sufficient space in the shared bus - * structures for a struct isapnp_loc and a struct isapnp_dev, - * as mandated by bus.h. - * - */ - BUS_LOC_CHECK ( struct isapnp_loc ); - BUS_DEV_CHECK ( struct isapnp_device ); - - return ( ++isapnp_loc->logdev ? 1 : ++isapnp_loc->csn ); -} - -/** - * Fill in parameters for an ISAPnP device based on CSN. - * - * @v bus_dev Bus device to be filled in - * @v bus_loc Bus location as filled in by isapnp_next_location() - * @ret True A device is present at this location - * @ret False No device is present at this location - * @err None - + * This routine simply activates the device in its current + * configuration, or deactivates the device. It does not attempt any + * kind of resource arbitration. * */ -static int isapnp_fill_device ( struct bus_dev *bus_dev, - struct bus_loc *bus_loc ) { - struct isapnp_device *isapnp = ( struct isapnp_device * ) bus_dev; - struct isapnp_loc *isapnp_loc = ( struct isapnp_loc * ) bus_loc; - unsigned int i; - struct isapnp_identifier identifier; - struct isapnp_logdevid logdevid; - static struct { - uint8_t csn; - uint8_t first_nonexistent_logdev; - } cache = { 0, 0 }; - - /* Copy CSN and logdev to isapnp_device, set default values */ - isapnp->csn = isapnp_loc->csn; - isapnp->logdev = isapnp_loc->logdev; - isapnp->name = "?"; - - /* CSN 0 is never valid, but may be passed in */ - if ( ! isapnp->csn ) - return 0; - - /* Check to see if we are already past the maximum CSN */ - if ( isapnp->csn > isapnp_max_csn ) - return 0; - - /* Check cache to see if we are already past the highest - * logical device of this CSN - */ - if ( ( isapnp->csn == cache.csn ) && - ( isapnp->logdev >= cache.first_nonexistent_logdev ) ) - return 0; - - /* Perform isolation if it hasn't yet been done */ - if ( ! isapnp_read_port ) - isapnp_isolate(); - - /* Wake the card */ +void isapnp_device_activation ( struct isapnp_device *isapnp, + int activation ) { + /* Wake the card and select the logical device */ isapnp_wait_for_key (); isapnp_send_key (); isapnp_wake ( isapnp->csn ); - - /* Read the card identifier */ - isapnp_peek ( ( char * ) &identifier, sizeof ( identifier ) ); - - /* Need to return 0 if no device exists at this CSN */ - if ( identifier.vendor_id & 0x80 ) { - isapnp_max_csn = isapnp->csn - 1; - return 0; - } - - /* Find the Logical Device ID tag corresponding to this device */ - for ( i = 0 ; i <= isapnp->logdev ; i++ ) { - if ( ! isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, - ( char * ) &logdevid ) ) { - /* No tag for this device */ - if ( isapnp->logdev == 0 ) { - DBG ( "ISAPnP found no device %hhx.0 on card " - ISAPNP_CARD_ID_FMT "\n", isapnp->csn, - ISAPNP_CARD_ID_DATA ( &identifier ) ); - } - cache.csn = isapnp->csn; - cache.first_nonexistent_logdev = isapnp->logdev; - return 0; - } - } - - /* Read information from logdevid structure */ - isapnp->vendor_id = logdevid.vendor_id; - isapnp->prod_id = logdevid.prod_id; - - /* Select the logical device */ isapnp_logicaldevice ( isapnp->logdev ); - /* Read the current ioaddr and irqno */ - isapnp->ioaddr = isapnp_read_iobase ( 0 ); - isapnp->irqno = isapnp_read_irqno ( 0 ); + /* Activate/deactivate the logical device */ + isapnp_activate ( activation ); + isapnp_delay(); /* Return all cards to Wait for Key state */ isapnp_wait_for_key (); - DBG ( "ISAPnP found device %hhx.%hhx " ISAPNP_DEV_ID_FMT - ", base %hx irq %d\n", isapnp->csn, isapnp->logdev, - ISAPNP_DEV_ID_DATA ( isapnp ), isapnp->ioaddr, isapnp->irqno ); - DBG ( " on card " ISAPNP_CARD_ID_FMT "\n", - ISAPNP_CARD_ID_DATA ( &identifier ) ); - - return 1; + DBG ( "ISAPnP %s device %02x:%02x\n", + ( activation ? "activated" : "deactivated" ), + isapnp->csn, isapnp->logdev ); } /** - * Test whether or not a driver is capable of driving the device. + * Probe an ISAPnP device * - * @v bus_dev Bus device as filled in by isapnp_fill_device() - * @v device_driver Device driver - * @ret True Driver is capable of driving this device - * @ret False Driver is not capable of driving this device - * @err None - + * @v isapnp ISAPnP device + * @ret rc Return status code * + * Searches for a driver for the ISAPnP device. If a driver is found, + * its probe() routine is called. */ -static int isapnp_check_driver ( struct bus_dev *bus_dev, - struct device_driver *device_driver ) { - struct isapnp_device *isapnp = ( struct isapnp_device * ) bus_dev; - struct isapnp_driver *driver - = ( struct isapnp_driver * ) device_driver->bus_driver_info; +static int isapnp_probe ( struct isapnp_device *isapnp ) { + struct isapnp_driver *driver; + struct isapnp_device_id *id; unsigned int i; - - /* Compare against driver's ID list */ - for ( i = 0 ; i < driver->id_count ; i++ ) { - struct isapnp_id *id = &driver->ids[i]; - - if ( ( isapnp->vendor_id == id->vendor_id ) && - ( ISA_PROD_ID ( isapnp->prod_id ) == - ISA_PROD_ID ( id->prod_id ) ) ) { - DBG ( "ISAPnP found ID %hx:%hx (\"%s\") (device %s) " - "matching driver %s\n", - isapnp->vendor_id, isapnp->prod_id, - isa_id_string( isapnp->vendor_id, - isapnp->prod_id ), - id->name, device_driver->name ); - isapnp->name = id->name; - return 1; + int rc; + + DBG ( "Adding ISAPnP device %02x:%02x (%04x:%04x (\"%s\") " + "io %x irq %d)\n", isapnp->csn, isapnp->logdev, + isapnp->vendor_id, isapnp->prod_id, + isa_id_string ( isapnp->vendor_id, isapnp->prod_id ), + isapnp->ioaddr, isapnp->irqno ); + + for ( driver = isapnp_drivers; driver < isapnp_drivers_end; driver++ ){ + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( id->vendor_id != isapnp->vendor_id ) + continue; + if ( ISA_PROD_ID ( id->prod_id ) != + ISA_PROD_ID ( isapnp->prod_id ) ) + continue; + isapnp->driver = driver; + isapnp->driver_name = id->name; + DBG ( "...using driver %s\n", isapnp->driver_name ); + if ( ( rc = driver->probe ( isapnp, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; } } - return 0; + DBG ( "...no driver found\n" ); + return -ENOTTY; } /** - * Describe an ISAPnP device. - * - * @v bus_dev Bus device as filled in by isapnp_fill_device() - * @ret string Printable string describing the device - * @err None - - * - * The string returned by isapnp_describe_device() is valid only until - * the next call to isapnp_describe_device(). + * Remove an ISAPnP device * + * @v isapnp ISAPnP device */ -static char * isapnp_describe_device ( struct bus_dev *bus_dev ) { - struct isapnp_device *isapnp = ( struct isapnp_device * ) bus_dev; - static char isapnp_description[] = "ISAPnP 00:00"; - - sprintf ( isapnp_description + 7, "%hhx:%hhx", - isapnp->csn, isapnp->logdev ); - return isapnp_description; +static void isapnp_remove ( struct isapnp_device *isapnp ) { + isapnp->driver->remove ( isapnp ); + DBG ( "Removed ISAPnP device %02x:%02x\n", + isapnp->csn, isapnp->logdev ); } /** - * Name an ISAPnP device. + * Probe ISAPnP root bus * - * @v bus_dev Bus device as filled in by isapnp_fill_device() - * @ret string Printable string naming the device - * @err None - - * - * The string returned by isapnp_name_device() is valid only until the - * next call to isapnp_name_device(). + * @v rootdev ISAPnP bus root device * + * Scans the ISAPnP bus for devices and registers all devices it can + * find. */ -static const char * isapnp_name_device ( struct bus_dev *bus_dev ) { - struct isapnp_device *isapnp = ( struct isapnp_device * ) bus_dev; - - return isapnp->name; -} +static int isapnpbus_probe ( struct root_device *rootdev ) { + struct isapnp_device *isapnp = NULL; + struct isapnp_identifier identifier; + struct isapnp_logdevid logdevid; + unsigned int csn; + unsigned int logdev; + int rc; -/* - * ISAPnP bus operations table - * - */ -struct bus_driver isapnp_driver __bus_driver = { - .name = "ISAPnP", - .next_location = isapnp_next_location, - .fill_device = isapnp_fill_device, - .check_driver = isapnp_check_driver, - .describe_device = isapnp_describe_device, - .name_device = isapnp_name_device, -}; + /* Perform isolation if it hasn't yet been done */ + if ( ! isapnp_read_port ) + isapnp_isolate(); -/** - * Activate or deactivate an ISAPnP device. - * - * @v isapnp ISAPnP device - * @v activation True to enable, False to disable the device - * @ret None - - * @err None - - * - * This routine simply activates the device in its current - * configuration, or deactivates the device. It does not attempt any - * kind of resource arbitration. - * - */ -void isapnp_device_activation ( struct isapnp_device *isapnp, - int activation ) { - /* Wake the card and select the logical device */ - isapnp_wait_for_key (); - isapnp_send_key (); - isapnp_wake ( isapnp->csn ); - isapnp_logicaldevice ( isapnp->logdev ); + for ( csn = 1 ; csn <= 0xff ; csn++ ) { + for ( logdev = 0 ; logdev <= 0xff ; logdev++ ) { - /* Activate/deactivate the logical device */ - isapnp_activate ( activation ); - isapnp_delay(); + /* Allocate struct isapnp_device */ + if ( ! isapnp ) + isapnp = malloc ( sizeof ( *isapnp ) ); + if ( ! isapnp ) { + rc = -ENOMEM; + goto err; + } + memset ( isapnp, 0, sizeof ( *isapnp ) ); + isapnp->csn = csn; + isapnp->logdev = logdev; + + /* Wake the card */ + isapnp_wait_for_key(); + isapnp_send_key(); + isapnp_wake ( csn ); + + /* Read the card identifier */ + isapnp_peek ( &identifier, sizeof ( identifier ) ); + + /* No card with this CSN; stop here */ + if ( identifier.vendor_id & 0x80 ) + goto done; + + /* Find the Logical Device ID tag */ + if ( ( rc = isapnp_find_logdevid ( logdev, + &logdevid ) ) != 0){ + /* No more logical devices; go to next CSN */ + break; + } + + /* Select the logical device */ + isapnp_logicaldevice ( logdev ); + + /* Populate struct isapnp_device */ + isapnp->vendor_id = logdevid.vendor_id; + isapnp->prod_id = logdevid.prod_id; + isapnp->ioaddr = isapnp_read_iobase ( 0 ); + isapnp->irqno = isapnp_read_irqno ( 0 ); + + /* Return all cards to Wait for Key state */ + isapnp_wait_for_key(); + + /* Add to device hierarchy */ + snprintf ( isapnp->dev.name, + sizeof ( isapnp->dev.name ), + "ISAPnP%02x:%02x", csn, logdev ); + isapnp->dev.desc.bus_type = BUS_TYPE_ISAPNP; + isapnp->dev.desc.vendor = isapnp->vendor_id; + isapnp->dev.desc.device = isapnp->prod_id; + isapnp->dev.parent = &rootdev->dev; + list_add ( &isapnp->dev.siblings, + &rootdev->dev.children ); + INIT_LIST_HEAD ( &isapnp->dev.children ); + + /* Look for a driver */ + if ( isapnp_probe ( isapnp ) == 0 ) { + /* isapnpdev registered, we can drop our ref */ + isapnp = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &isapnp->dev.siblings ); + } + } + } - /* Return all cards to Wait for Key state */ - isapnp_wait_for_key (); + done: + free ( isapnp ); + return 0; - DBG ( "ISAPnP %s device %hhx.%hhx\n", - ( activation ? "activated" : "deactivated" ), - isapnp->csn, isapnp->logdev ); + err: + free ( isapnp ); + isapnpbus_remove ( rootdev ); + return rc; } /** - * Fill in a nic structure. - * - * @v nic NIC structure to be filled in - * @v isapnp ISAPnP device - * @ret None - - * @err None - - * - * This fills in generic NIC parameters (e.g. I/O address and IRQ - * number) that can be determined directly from the ISAPnP device, - * without any driver-specific knowledge. + * Remove ISAPnP root bus * + * @v rootdev ISAPnP bus root device */ -void isapnp_fill_nic ( struct nic *nic, struct isapnp_device *isapnp ) { - - /* Fill in ioaddr and irqno */ - nic->ioaddr = isapnp->ioaddr; - nic->irqno = isapnp->irqno; - - /* Fill in DHCP device ID structure */ - nic->dhcp_dev_id.bus_type = ISA_BUS_TYPE; - nic->dhcp_dev_id.vendor_id = htons ( isapnp->vendor_id ); - nic->dhcp_dev_id.device_id = htons ( isapnp->prod_id ); +static void isapnpbus_remove ( struct root_device *rootdev ) { + struct isapnp_device *isapnp; + struct isapnp_device *tmp; + + list_for_each_entry_safe ( isapnp, tmp, &rootdev->dev.children, + dev.siblings ) { + isapnp_remove ( isapnp ); + list_del ( &isapnp->dev.siblings ); + free ( isapnp ); + } } +/** ISAPnP bus root device driver */ +static struct root_driver isapnp_root_driver = { + .probe = isapnpbus_probe, + .remove = isapnpbus_remove, +}; + +/** ISAPnP bus root device */ +struct root_device isapnp_root_device __root_device = { + .dev = { .name = "ISAPnP" }, + .driver = &isapnp_root_driver, +}; diff --git a/src/drivers/bus/mca.c b/src/drivers/bus/mca.c index 630442951..375a68344 100644 --- a/src/drivers/bus/mca.c +++ b/src/drivers/bus/mca.c @@ -5,157 +5,177 @@ * */ -#include "string.h" -#include "io.h" -#include "console.h" -#include "dev.h" -#include "mca.h" - -/* - * Increment a bus_loc structure to the next possible MCA location. - * Leave the structure zeroed and return 0 if there are no more valid - * locations. +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <io.h> +#include <timer.h> +#include <gpxe/mca.h> + +static struct mca_driver mca_drivers[0] + __table_start ( struct mca_driver, mca_drivers ); +static struct mca_driver mca_drivers_end[0] + __table_end ( struct mca_driver, mca_drivers ); + +static void mcabus_remove ( struct root_device *rootdev ); + +/** + * Probe an MCA device * - */ -static int mca_next_location ( struct bus_loc *bus_loc ) { - struct mca_loc *mca_loc = ( struct mca_loc * ) bus_loc; - - /* - * Ensure that there is sufficient space in the shared bus - * structures for a struct mca_loc and a struct - * mca_dev, as mandated by bus.h. - * - */ - BUS_LOC_CHECK ( struct mca_loc ); - BUS_DEV_CHECK ( struct mca_device ); - - return ( mca_loc->slot = ( ++mca_loc->slot & MCA_MAX_SLOT_NR ) ); -} - -/* - * Fill in parameters for an MCA device based on slot number + * @v mca MCA device + * @ret rc Return status code * + * Searches for a driver for the MCA device. If a driver is found, + * its probe() routine is called. */ -static int mca_fill_device ( struct bus_dev *bus_dev, - struct bus_loc *bus_loc ) { - struct mca_loc *mca_loc = ( struct mca_loc * ) bus_loc; - struct mca_device *mca = ( struct mca_device * ) bus_dev; - unsigned int i, seen_non_ff; - - /* Store slot in struct mca, set default values */ - mca->slot = mca_loc->slot; - mca->name = "?"; - - /* Make sure motherboard setup is off */ - outb_p ( 0xff, MCA_MOTHERBOARD_SETUP_REG ); - - /* Select the slot */ - outb_p ( 0x8 | ( mca->slot & 0xf ), MCA_ADAPTER_SETUP_REG ); - - /* Read the POS registers */ - seen_non_ff = 0; - for ( i = 0 ; i < ( sizeof ( mca->pos ) / sizeof ( mca->pos[0] ) ) ; - i++ ) { - mca->pos[i] = inb_p ( MCA_POS_REG ( i ) ); - if ( mca->pos[i] != 0xff ) - seen_non_ff = 1; - } - - /* If all POS registers are 0xff, this means there's no device - * present - */ - if ( ! seen_non_ff ) - return 0; - - /* Kill all setup modes */ - outb_p ( 0, MCA_ADAPTER_SETUP_REG ); +static int mca_probe ( struct mca_device *mca ) { + struct mca_driver *driver; + struct mca_device_id *id; + unsigned int i; + int rc; - DBG ( "MCA found slot %d id %hx " - "(POS %hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx)\n", + DBG ( "Adding MCA slot %02x (ID %04x POS " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)\n", mca->slot, MCA_ID ( mca ), mca->pos[0], mca->pos[1], mca->pos[2], mca->pos[3], mca->pos[4], mca->pos[5], mca->pos[6], mca->pos[7] ); - return 1; -} - -/* - * Test whether or not a driver is capable of driving the device. - * - */ -static int mca_check_driver ( struct bus_dev *bus_dev, - struct device_driver *device_driver ) { - struct mca_device *mca = ( struct mca_device * ) bus_dev; - struct mca_driver *driver - = ( struct mca_driver * ) device_driver->bus_driver_info; - unsigned int i; - - /* Compare against driver's ID list */ - for ( i = 0 ; i < driver->id_count ; i++ ) { - struct mca_id *id = &driver->ids[i]; - - if ( MCA_ID ( mca ) == id->id ) { - DBG ( "MCA found ID %hx (device %s) " - "matching driver %s\n", - id->name, id->id, device_driver->name ); - mca->name = id->name; - return 1; + for ( driver = mca_drivers; driver < mca_drivers_end; driver++ ){ + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( id->id != MCA_ID ( mca ) ) + continue; + mca->driver = driver; + mca->driver_name = id->name; + DBG ( "...using driver %s\n", mca->driver_name ); + if ( ( rc = driver->probe ( mca, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; } } - /* No device found */ - return 0; + DBG ( "...no driver found\n" ); + return -ENOTTY; } -/* - * Describe an MCA device +/** + * Remove an MCA device * + * @v mca MCA device */ -static char * mca_describe_device ( struct bus_dev *bus_dev ) { - struct mca_device *mca = ( struct mca_device * ) bus_dev; - static char mca_description[] = "MCA 00"; - - sprintf ( mca_description + 4, "%hhx", mca->slot ); - return mca_description; +static void mca_remove ( struct mca_device *mca ) { + mca->driver->remove ( mca ); + DBG ( "Removed MCA device %02x\n", mca->slot ); } -/* - * Name an MCA device +/** + * Probe MCA root bus + * + * @v rootdev MCA bus root device * + * Scans the MCA bus for devices and registers all devices it can + * find. */ -static const char * mca_name_device ( struct bus_dev *bus_dev ) { - struct mca_device *mca = ( struct mca_device * ) bus_dev; +static int mcabus_probe ( struct root_device *rootdev ) { + struct mca_device *mca = NULL; + unsigned int slot; + int seen_non_ff; + unsigned int i; + int rc; + + for ( slot = 0 ; slot <= MCA_MAX_SLOT_NR ; slot++ ) { + /* Allocate struct mca_device */ + if ( ! mca ) + mca = malloc ( sizeof ( *mca ) ); + if ( ! mca ) { + rc = -ENOMEM; + goto err; + } + memset ( mca, 0, sizeof ( *mca ) ); + mca->slot = slot; + + /* Make sure motherboard setup is off */ + outb_p ( 0xff, MCA_MOTHERBOARD_SETUP_REG ); + + /* Select the slot */ + outb_p ( 0x8 | ( mca->slot & 0xf ), MCA_ADAPTER_SETUP_REG ); + + /* Read the POS registers */ + seen_non_ff = 0; + for ( i = 0 ; i < ( sizeof ( mca->pos ) / + sizeof ( mca->pos[0] ) ) ; i++ ) { + mca->pos[i] = inb_p ( MCA_POS_REG ( i ) ); + if ( mca->pos[i] != 0xff ) + seen_non_ff = 1; + } - return mca->name; + /* Kill all setup modes */ + outb_p ( 0, MCA_ADAPTER_SETUP_REG ); + + /* If all POS registers are 0xff, this means there's no device + * present + */ + if ( ! seen_non_ff ) + continue; + + /* Add to device hierarchy */ + snprintf ( mca->dev.name, sizeof ( mca->dev.name ), + "MCA%02x", slot ); + mca->dev.desc.bus_type = BUS_TYPE_MCA; + mca->dev.desc.vendor = GENERIC_MCA_VENDOR; + mca->dev.desc.device = MCA_ID ( mca ); + mca->dev.parent = &rootdev->dev; + list_add ( &mca->dev.siblings, &rootdev->dev.children ); + INIT_LIST_HEAD ( &mca->dev.children ); + + /* Look for a driver */ + if ( mca_probe ( mca ) == 0 ) { + /* mcadev registered, we can drop our ref */ + mca = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &mca->dev.siblings ); + } + } + + free ( mca ); + return 0; + + err: + free ( mca ); + mcabus_remove ( rootdev ); + return rc; } -/* - * MCA bus operations table +/** + * Remove MCA root bus * + * @v rootdev MCA bus root device */ -struct bus_driver mca_driver __bus_driver = { - .name = "MCA", - .next_location = mca_next_location, - .fill_device = mca_fill_device, - .check_driver = mca_check_driver, - .describe_device = mca_describe_device, - .name_device = mca_name_device, +static void mcabus_remove ( struct root_device *rootdev ) { + struct mca_device *mca; + struct mca_device *tmp; + + list_for_each_entry_safe ( mca, tmp, &rootdev->dev.children, + dev.siblings ) { + mca_remove ( mca ); + list_del ( &mca->dev.siblings ); + free ( mca ); + } +} + +/** MCA bus root device driver */ +static struct root_driver mca_root_driver = { + .probe = mcabus_probe, + .remove = mcabus_remove, }; -/* - * Fill in a nic structure - * - */ -void mca_fill_nic ( struct nic *nic, struct mca_device *mca ) { - - /* ioaddr and irqno must be read in a device-dependent way - * from the POS registers - */ - nic->ioaddr = 0; - nic->irqno = 0; - - /* Fill in DHCP device ID structure */ - nic->dhcp_dev_id.bus_type = MCA_BUS_TYPE; - nic->dhcp_dev_id.vendor_id = htons ( GENERIC_MCA_VENDOR ); - nic->dhcp_dev_id.device_id = htons ( MCA_ID ( mca ) ); -} +/** MCA bus root device */ +struct root_device mca_root_device __root_device = { + .dev = { .name = "MCA" }, + .driver = &mca_root_driver, +}; diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index e8f34fba3..577691f5e 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -283,9 +283,9 @@ static int pcibus_probe ( struct root_device *rootdev ) { "PCI%02x:%02x.%x", bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) ); pci->dev.desc.bus_type = BUS_TYPE_PCI; - pci->dev.desc.pci.busdevfn = PCI_BUSDEVFN (bus, devfn); - pci->dev.desc.pci.vendor = pci->vendor; - pci->dev.desc.pci.device = pci->device; + pci->dev.desc.location = PCI_BUSDEVFN (bus, devfn); + pci->dev.desc.vendor = pci->vendor; + pci->dev.desc.device = pci->device; pci->dev.parent = &rootdev->dev; list_add ( &pci->dev.siblings, &rootdev->dev.children); INIT_LIST_HEAD ( &pci->dev.children ); |
