FreeBSD Architecture Handbook
Prev | Chapter 10 ISA Device Drivers | Next

# 10.2 Basic Information

A typical ISA driver would need the following include files:

    #include <sys/module.h>
    #include <sys/bus.h>
    #include <machine/bus.h>
    #include <machine/resource.h>
    #include <sys/rman.h>

    #include <isa/isavar.h>
    #include <isa/pnpvar.h>

They describe the things specific to the ISA and generic bus subsystem.

The bus subsystem is implemented in an object-oriented fashion, its main structures are accessed by associated method functions.

The list of bus methods implemented by an ISA driver is like one for any other bus. For a hypothetical driver named “xxx” they would be:

- `static void xxx_isa_identify (driver_t *, device_t);` Normally used for bus drivers, not device drivers. But for ISA devices this method may have special use: if the device provides some device-specific (non-PnP) way to auto-detect devices this routine may implement it.

- `static int xxx_isa_probe (device_t dev);` Probe for a device at a known (or PnP) location. This routine can also accommodate device-specific auto-detection of parameters for partially configured devices.

- `static int xxx_isa_attach (device_t dev);` Attach and initialize device.

- `static int xxx_isa_detach (device_t dev);` Detach device before unloading the driver module.

- `static int xxx_isa_shutdown (device_t dev);` Execute shutdown of the device before system shutdown.

- `static int xxx_isa_suspend (device_t dev);` Suspend the device before the system goes to the power-save state. May also abort transition to the power-save state.

- `static int xxx_isa_resume (device_t dev);` Resume the device activity after return from power-save state.

`xxx_isa_probe()` and `xxx_isa_attach()` are mandatory, the rest of the routines are optional, depending on the device's needs.

The driver is linked to the system with the following set of descriptions.

        /* table of supported bus methods */
        static device_method_t xxx_isa_methods[] = {
            /* list all the bus method functions supported by the driver */
            /* omit the unsupported methods */
            DEVMETHOD(device_identify,  xxx_isa_identify),
            DEVMETHOD(device_probe,     xxx_isa_probe),
            DEVMETHOD(device_attach,    xxx_isa_attach),
            DEVMETHOD(device_detach,    xxx_isa_detach),
            DEVMETHOD(device_shutdown,  xxx_isa_shutdown),
            DEVMETHOD(device_suspend,   xxx_isa_suspend),
            DEVMETHOD(device_resume,    xxx_isa_resume),

    	DEVMETHOD_END
        };

        static driver_t xxx_isa_driver = {
            "xxx",
            xxx_isa_methods,
            sizeof(struct xxx_softc),
        };

        static devclass_t xxx_devclass;

        DRIVER_MODULE(xxx, isa, xxx_isa_driver, xxx_devclass,
            load_function, load_argument);

Here struct `xxx_softc` is a device-specific structure that contains private driver data and descriptors for the driver's resources. The bus code automatically allocates one softc descriptor per device as needed.

If the driver is implemented as a loadable module then `load_function()` is called to do driver-specific initialization or clean-up when the driver is loaded or unloaded and load_argument is passed as one of its arguments. If the driver does not support dynamic loading (in other words it must always be linked into the kernel) then these values should be set to 0 and the last definition would look like:

     DRIVER_MODULE(xxx, isa, xxx_isa_driver,
           xxx_devclass, 0, 0);

If the driver is for a device which supports PnP then a table of supported PnP IDs must be defined. The table consists of a list of PnP IDs supported by this driver and human-readable descriptions of the hardware types and models having these IDs. It looks like:

        static struct isa_pnp_id xxx_pnp_ids[] = {
            /* a line for each supported PnP ID */
            { 0x12345678,   "Our device model 1234A" },
            { 0x12345679,   "Our device model 1234B" },
            { 0,        NULL }, /* end of table */
        };

If the driver does not support PnP devices it still needs an empty PnP ID table, like:

        static struct isa_pnp_id xxx_pnp_ids[] = {
            { 0,        NULL }, /* end of table */
        };

Prev | Home | Next
ISA Device Drivers | Up | `device_t` Pointer