| #include <stdint.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <ipxe/io.h> |
| #include <ipxe/isa.h> |
| #include <config/isa.h> |
| |
| FILE_LICENCE ( GPL2_OR_LATER ); |
| |
| /* |
| * 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 ) ? \ |
| (driver)->probe_addrs[(ioidx)] : \ |
| *( isa_extra_probe_addrs + (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ) ) |
| |
| 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; |
| long ioidx; |
| int rc; |
| |
| for_each_table_entry ( driver, ISA_DRIVERS ) { |
| 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.driver_name = driver->name; |
| 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, |
| }; |