New Device Framework
A flexible mechanism for device drivers in FreeBSD
Releases of FreeBSD from version 3.0 and later include a new software architecture for developing device drivers. The new system includes support for dynamic device arrival and is flexible enough to allow most if not all of the different categories of device to be tied together into a coherent system.
The device class has two roles. The first is to maintain a mapping from name+unit to a device. Each named driver (sio, ed or whatever) uses a device class with the same name and the class object contains a table which maps the unit number to the device instance. The system provides functions to lookup device classes by name, devclass_find(const char *classname) and to lookup a device given its unit number, devclass_get_device(devclass_t dc, int unit).
The second role is to store lists of drivers. All the drivers for a particular bus type are stored in the devclass for that bus type (e.g. the devclass with the name "pci" would keep the drivers for pci hardware). This list is used at probe time to match drivers to devices.
Each piece of hardware in the system is represented by a device object (or possibly several for complicated hardware). The devices are organised into a tree structure where interior nodes of the tree represent 'busses' (i.e. devices which have other devicesattached to them) and leaves of the tree represent simple hardware.
A device is typically created by the driver of its parent device and goes through a number of states during its lifetime.
Each new device starts off in the NOTPRESENT this state, no driver is assigned to the device. To bring the device into the system, device_probe_and_attach() is called, either directly or via bus_generic_attach().
To match a driver to a new device, the system searches through the list of drivers in the parent device's class. If the new device has a name, only drivers with a matching name are considered, otherwise all drivers are tried. The driver of the device is set to each candidate in turn (which involves allocating a suitably sized softc structure for the driver's use) and the DEVICE_PROBE method is called. If the probe succeeds, the device enters the ALIVE state, representing devices which actually exist but which have not completed their initialisation.
Directly after entering the ALIVE state, the DEVICE_ATTACH method of the driver is called. This method should complete the initialisation of the device, possibly adding child devices and recursing via bus_generic_attach(). If the attach is successful, the device enters the ATTACHED state and is ready for use, otherwise the driver is removed from the device and it returns to the NOTPRESENT state.
Devices which are in the ATTACHED state can enter the BUSY state when they are in use. This can be used to prevent active drivers from being detached from the device. To enter the BUSY state, the driver calls device_busy() and to leave it device_unbusy(). The system maintains a count of how many times device_busy() is called and the device only returns to the ATTACHED state after the count reaches zero.
A device in the ATTACHED state can return to the NOTPRESENT state (perhaps to replace the driver or in response to a dynamic device removal type event). The driver's DEVICE_DETACH method is called and the softc memory is reclaimed.
To remove a device from the system, it must first be in the NOTPRESENT state. The system enforces this by calling the DEVICE_DETACH method when the device is deleted (if appropriate). If the device is currently in the BUSY state, the delete attempt will return with an error.