diff options
Diffstat (limited to 'gpxe/src/drivers/bus/isa.c')
-rw-r--r-- | gpxe/src/drivers/bus/isa.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/gpxe/src/drivers/bus/isa.c b/gpxe/src/drivers/bus/isa.c new file mode 100644 index 00000000..a4105fd0 --- /dev/null +++ b/gpxe/src/drivers/bus/isa.c @@ -0,0 +1,175 @@ +#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 + * detection. The driver must provide a list of probe addresses + * (probe_addrs), together with a function (probe_addr) that can be + * used to test for the physical presence of a device at any given + * address. + * + * Note that this should probably be considered the "last resort" for + * device probing. If the card supports ISAPnP or EISA, use that + * instead. Some cards (e.g. the 3c509) implement a proprietary + * ISAPnP-like mechanism. + * + * The ISA probe address list can be overridden by config.h; if the + * user specifies ISA_PROBE_ADDRS then that list will be used first. + * (If ISA_PROBE_ONLY is defined, the driver's own list will never be + * used). + */ + +/* + * User-supplied probe address list + * + */ +static isa_probe_addr_t isa_extra_probe_addrs[] = { +#ifdef ISA_PROBE_ADDRS + ISA_PROBE_ADDRS +#endif +}; +#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_IOIDX_MAX( driver ) ( -1 ) +#else +#define ISA_IOIDX_MAX( driver ) ( (int) (driver)->addr_count - 1 ) +#endif + +#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 ); + +static void isabus_remove ( struct root_device *rootdev ); + +/** + * Probe an ISA device + * + * @v isa ISA device + * @ret rc Return status code + */ +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 ); + + if ( ( rc = isa->driver->probe ( isa ) ) != 0 ) { + DBG ( "...probe failed\n" ); + return rc; + } + + DBG ( "...device found\n" ); + return 0; +} + +/** + * Remove an ISA device + * + * @v isa ISA device + */ +static void isa_remove ( struct isa_device *isa ) { + isa->driver->remove ( isa ); + DBG ( "Removed ISA%04x\n", isa->ioaddr ); +} + +/** + * Probe ISA root bus + * + * @v rootdev ISA bus root device + * + * Scans the ISA bus for devices and registers all devices it can + * find. + */ +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 ); + } + } + } + + free ( isa ); + return 0; + + err: + free ( isa ); + isabus_remove ( rootdev ); + return rc; +} + +/** + * Remove ISA root bus + * + * @v rootdev ISA bus root device + */ +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 root device driver */ +static struct root_driver isa_root_driver = { + .probe = isabus_probe, + .remove = isabus_remove, +}; + +/** ISA bus root device */ +struct root_device isa_root_device __root_device = { + .dev = { .name = "ISA" }, + .driver = &isa_root_driver, +}; |