2007-03-10 19:08:33 +01:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
2008-10-12 01:52:30 +02:00
|
|
|
#include <gpxe/io.h>
|
2007-03-10 19:08:33 +01:00
|
|
|
#include <gpxe/isa.h>
|
2005-04-14 20:46:43 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
2005-04-22 04:28:16 +02:00
|
|
|
* The ISA probe address list can be overridden by config.h; if the
|
2005-04-16 11:30:48 +02:00
|
|
|
* user specifies ISA_PROBE_ADDRS then that list will be used first.
|
2005-04-26 14:30:14 +02:00
|
|
|
* (If ISA_PROBE_ONLY is defined, the driver's own list will never be
|
|
|
|
* used).
|
2005-04-14 20:46:43 +02:00
|
|
|
*/
|
|
|
|
|
2005-04-18 13:00:42 +02:00
|
|
|
/*
|
|
|
|
* User-supplied probe address list
|
|
|
|
*
|
|
|
|
*/
|
2005-04-22 04:28:16 +02:00
|
|
|
static isa_probe_addr_t isa_extra_probe_addrs[] = {
|
2005-04-18 14:43:18 +02:00
|
|
|
#ifdef ISA_PROBE_ADDRS
|
2005-04-22 04:28:16 +02:00
|
|
|
ISA_PROBE_ADDRS
|
2005-04-18 13:00:42 +02:00
|
|
|
#endif
|
|
|
|
};
|
2007-03-10 19:08:33 +01:00
|
|
|
#define ISA_EXTRA_PROBE_ADDR_COUNT \
|
2005-04-18 13:00:42 +02:00
|
|
|
( sizeof ( isa_extra_probe_addrs ) / sizeof ( isa_extra_probe_addrs[0] ) )
|
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
#define ISA_IOIDX_MIN( driver ) ( -ISA_EXTRA_PROBE_ADDR_COUNT )
|
2005-04-22 04:28:16 +02:00
|
|
|
#ifdef ISA_PROBE_ONLY
|
2007-03-10 19:08:33 +01:00
|
|
|
#define ISA_IOIDX_MAX( driver ) ( -1 )
|
2005-04-22 04:28:16 +02:00
|
|
|
#else
|
2007-03-10 19:08:33 +01:00
|
|
|
#define ISA_IOIDX_MAX( driver ) ( (int) (driver)->addr_count - 1 )
|
2005-04-22 04:28:16 +02:00
|
|
|
#endif
|
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
#define ISA_IOADDR( driver, ioidx ) \
|
|
|
|
( ( (ioidx) < 0 ) ? \
|
|
|
|
isa_extra_probe_addrs[ (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ] : \
|
|
|
|
(driver)->probe_addrs[(ioidx)] )
|
|
|
|
|
2007-01-10 05:22:09 +01:00
|
|
|
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 );
|
2005-04-26 14:30:14 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
static void isabus_remove ( struct root_device *rootdev );
|
2005-04-22 04:28:16 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
/**
|
|
|
|
* Probe an ISA device
|
2005-04-22 04:28:16 +02:00
|
|
|
*
|
2007-03-10 19:08:33 +01:00
|
|
|
* @v isa ISA device
|
|
|
|
* @ret rc Return status code
|
2005-04-22 04:28:16 +02:00
|
|
|
*/
|
2007-03-10 19:08:33 +01:00
|
|
|
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 );
|
2005-04-26 14:30:14 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
if ( ( rc = isa->driver->probe ( isa ) ) != 0 ) {
|
|
|
|
DBG ( "...probe failed\n" );
|
|
|
|
return rc;
|
2005-04-14 20:46:43 +02:00
|
|
|
}
|
2005-04-26 14:30:14 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
DBG ( "...device found\n" );
|
2005-04-26 14:30:14 +02:00
|
|
|
return 0;
|
2005-04-22 04:28:16 +02:00
|
|
|
}
|
2005-04-14 20:46:43 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
/**
|
|
|
|
* Remove an ISA device
|
2005-04-22 04:28:16 +02:00
|
|
|
*
|
2007-03-10 19:08:33 +01:00
|
|
|
* @v isa ISA device
|
2005-04-22 04:28:16 +02:00
|
|
|
*/
|
2007-03-10 19:08:33 +01:00
|
|
|
static void isa_remove ( struct isa_device *isa ) {
|
|
|
|
isa->driver->remove ( isa );
|
|
|
|
DBG ( "Removed ISA%04x\n", isa->ioaddr );
|
2005-04-22 04:28:16 +02:00
|
|
|
}
|
2005-04-14 20:46:43 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
/**
|
|
|
|
* Probe ISA root bus
|
|
|
|
*
|
|
|
|
* @v rootdev ISA bus root device
|
2005-04-22 04:28:16 +02:00
|
|
|
*
|
2007-03-10 19:08:33 +01:00
|
|
|
* Scans the ISA bus for devices and registers all devices it can
|
|
|
|
* find.
|
2005-04-22 04:28:16 +02:00
|
|
|
*/
|
2007-03-10 19:08:33 +01: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 )
|
2007-07-07 01:56:27 +02:00
|
|
|
isa = malloc ( sizeof ( *isa ) );
|
2007-03-10 19:08:33 +01:00
|
|
|
if ( ! isa ) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
2007-07-07 01:56:27 +02:00
|
|
|
memset ( isa, 0, sizeof ( *isa ) );
|
2007-03-10 19:08:33 +01:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-04-22 04:28:16 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
free ( isa );
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
free ( isa );
|
|
|
|
isabus_remove ( rootdev );
|
|
|
|
return rc;
|
2005-04-14 20:46:43 +02:00
|
|
|
}
|
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
/**
|
|
|
|
* Remove ISA root bus
|
2005-04-14 20:46:43 +02:00
|
|
|
*
|
2007-03-10 19:08:33 +01:00
|
|
|
* @v rootdev ISA bus root device
|
2005-04-14 20:46:43 +02:00
|
|
|
*/
|
2007-03-10 19:08:33 +01:00
|
|
|
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 );
|
|
|
|
}
|
2005-04-22 04:28:16 +02:00
|
|
|
}
|
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
/** ISA bus root device driver */
|
|
|
|
static struct root_driver isa_root_driver = {
|
|
|
|
.probe = isabus_probe,
|
|
|
|
.remove = isabus_remove,
|
2005-04-22 04:28:16 +02:00
|
|
|
};
|
2005-04-14 20:46:43 +02:00
|
|
|
|
2007-03-10 19:08:33 +01:00
|
|
|
/** ISA bus root device */
|
|
|
|
struct root_device isa_root_device __root_device = {
|
|
|
|
.dev = { .name = "ISA" },
|
|
|
|
.driver = &isa_root_driver,
|
|
|
|
};
|