The 2.5 kernel implements a unified device driver model that will make driver development for 2.6 easier.
In the 2.5 Linux kernel development series, a unified device driver model framework was created by Pat Mochel. This framework consists of a number of common structures and functions all device driver subsystems have been converted to use. It also consists of some generic structures that are starting to be used outside of the driver code by other parts of the kernel. This article discusses parts of the driver model and provides an example of how to convert a specific device driver subsystem to the driver model.
The driver framework breaks all things down into buses, devices and classes. Using these primitives, it controls how drivers are matched up with physical and virtual devices, and it shows the user how all of these things are interconnected.
A bus can be described as something with devices connected to it. Examples of buses are PCI, USB, i2c, PCMCIA and SCSI. Usually only one bus driver controls the activity on a bus, and it provides a type of bridge from the bus it is on to the bus it controls.
An example of a bridge is a USB controller that lives on the PCI bus. It talks to the PCI bus as a PCI device and looks to the kernel as a PCI driver. But it controls all access to that specific USB bus, talking to the different USB devices plugged in to it.
Buses are represented in the kernel with the struct bus_type definition, found in include/linux/device.h. All buses in the system are shown to the user in subdirectories of the sysfs directory /sys/bus/.
Devices are physical or virtual devices that reside on a bus. They are represented by the struct device definition and are created by the bus when the bus sees they are present in the system. Usually only one driver controls a specific device at one time. They can be seen in the /sys/devices directory as a giant tree of all devices in the system or in the /sys/bus/BUS_TYPE/devices/ directory for a specific type of device.
Devices also have drivers assigned to them that control how to talk to the device across a specific bus. Some drivers know how to talk to multiple buses, such as the Tulip network driver, which can talk to PCI and ISA Tulip devices. All drivers are represented by the struct device_driver definition. They can be seen in sysfs at /sys/bus/BUS_TYPE/drivers/. Drivers register with a specific bus and export a list of different types of devices they can support. The bus matches the devices and drivers based on this list of exported devices. The list also is exported to user space so the /sbin/hotplug tools can be used to match drivers to devices that do not have drivers already loaded. See my article, “Hot Plug”, in the April 2002 issue of Linux Journal for more information on this interface and how it works [also available at www.linuxjournal.com/article/5604].
Classes here do not take the general object-oriented definition but, rather, are things that provide a function to the user. They are not bus- or device-specific things but functionally look to the user as the same type of device. Examples of classes are audio devices; pointing devices, such as mice and touchpads; keyboards; joysticks; IDE disks; and tty devices. The kernel always has had these kinds of devices, and they traditionally have been grouped together by major/minor number range, so the user can access them easily. Classes are represented in the kernel with the struct device_class definition, and they can be seen as subdirectories of the sysfs directory /sys/class/.
For a description of the whole driver model, along with an introduction to the structures below the driver model that do all of the real work, see the thorough document at www.kernel.org/pub/linux/kernel/people/mochel/doc/lca/driver-model-lca2003.tar.gz. It was written by Pat Mochel for the 2003 Linux.Conf.Au conference.
All of the above descriptions sound great on paper, but how does the driver model actually affect the kernel code? To show this, let us walk through how the i2c driver subsystem was modified to support this driver model.
The i2c code has lived outside of the main kernel tree for a long time, and it was offered as a patch for the 2.0, 2.2 and 2.4 kernels. It also was the subject of “Using the i2c Bus”, by Simon G. Vogl, one of the main authors of the code [LJ, March 1997, www.linuxjournal.com/article/1342]. In the 2.4 development cycle, a number of the i2c core files and a few i2c bus drivers were accepted into the main kernel. In the 2.5 development cycle, a few more drivers were added; hopefully, all of them eventually will migrate into the main tree. For a good description of the i2c code, what devices it supports and how to use it, see the main development site at secure.netroedge.com/~lm78/index.html.
When loaded, the i2c bus drivers, which talk to the i2c controller chips, export a number of files in the /proc/bus directory. When an i2c device driver is loaded and bound to an i2c device, it exports files and directories in the /proc/sys/dev/sensors directory. By moving the representation of the devices and buses to the kernel driver core, all of these separate files can be shown in their proper places in /sys.
The main i2c bus subsystem needs to be declared in the kernel and registered with the driver core. To accomplish this, the following code was added to drivers/i2c/i2c-core.c:
static int i2c_device_match(struct device *dev, struct device_driver *drv) { return 1; } struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, };
The name field says what the bus should be called, and the match field points to our match function. Right now, the match function is left alone, always returning 1 whenever the driver core wants to try to match a driver with a device. This logic will be modified at a later time.
Then, in the i2c core startup code, the i2c_bus_type is registered with a call to:
bus_register(&i2c_bus_type);
When the i2c core is shut down, a call is added to unregister this bus:
bus_unregister(&i2c_bus_type);When the above code runs, the following tree is created in sysfs:
$ tree /sys/bus/i2c/ /sys/bus/i2c/ |-- devices '-- driversWhen the i2c core is removed from the system, the above directories are removed. This is all that is needed to create the i2c bus.
An i2c bus by itself is pretty boring. Now, the i2c bus adapter drivers need to be modified to register themselves with this bus. To do this, a struct device variable is added to the struct i2c_adapter structure:
struct i2c_adapter { ..... struct device dev; };
A to_i2c_adapter() macro is defined as:
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)This macro is used by the i2c core to get a pointer to a real i2c_adapter structure whenever the driver core passes it a pointer to a struct device.
The struct device in the i2c_adapter is a whole variable declared within the structure, not merely a pointer. This is done so when the driver core passes a pointer to a struct device, the i2c code can use the to_i2c_adapter() macro to get a pointer to the real i2c_adapter structure.
The individual struct i2c_driver variables are declared in the different i2c bus drivers. For example, in the i2c-piix4.c driver, there is a variable called piix4_adapter of type struct i2c_driver. This variable is passed to the i2c core in the i2c_add_adapter() function, when a PIIX4 adapter is seen by the i2c-piix4 driver.
In the i2c-piix4.c driver, before i2c_add_adapter() is called, a pointer to the parent device of the PIIX4 adapter needs to be saved within the i2c_driver structure. This is done with a single line of code:
piix4_adapter.dev.parent = &dev->dev;
dev is a pointer to the struct pci_dev that is passed to the i2c-piix4 driver's PCI probe function; the PIIX4 is a PCI-based device.
To link the i2c_driver variable to the sysfs tree, the following lines of code are added to the i2c_add_adapter() function:
/* add the adapter to the driver core. * The parent pointer should already * have been set up. */ sprintf(adap->dev.bus_id, "i2c-%d", i); strcpy(adap->dev.name, "i2c controller"); device_register(&adap->dev);
With this code, when the PIIX4 device is detected by the driver, an i2c bus tree is created and linked to the controlling PCI device:
$ tree /sys/devices/pci0/00:07.3/i2c-0 /sys/devices/pci0/00:07.3/i2c-0 |-- name `-- powerWhen the i2c-piix4 driver is unloaded, the i2c_del_adapter() function is called. The following line of code is added to clean up the i2c bus device:
/* clean up the sysfs representation */ device_unregister(&adap->dev);
The i2c bus has a number of different drivers that control access to a wide range of i2c devices that live on the i2c bus. These drivers are declared with a struct i2c_driver structure. Within this structure, a struct device_driver variable is added to allow these drivers to be registered with the driver core:
struct i2c_driver { ..... struct device_driver driver; };
And, a to_i2c_driver() macro is defined as:
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)An i2c driver registers itself with the i2c core in a call to i2c_add_driver(). To add driver core support for i2c drivers, the following lines of code are added to this function:
/* add the driver to the list of *i2c drivers in the driver core */ driver->driver.name = driver->name; driver->driver.bus = &i2c_bus_type; driver->driver.probe = i2c_device_probe; driver->driver.remove = i2c_device_remove; retval = driver_register(&driver->driver); if (retval) return retval;This sets up the driver core structure to have the same name as the driver and a bus type of i2c_bus_type; the probe and remove functions are set to local i2c functions. For now, these functions are declared as:
int i2c_device_probe(struct device *dev) { return -ENODEV; } int i2c_device_remove(struct device *dev) { return 0; }because no i2c device support has been added yet. These functions will be called when an i2c device is added or removed from the driver core, but that will be described in the next column.
When the i2c_add_driver() is called, the driver is registered with the i2c_bus_type, and it shows up in sysfs as:
$ tree /sys/bus/i2c/ /sys/bus/i2c/ |-- devices `-- drivers |-- EEPROM READER `-- W83781D sensors
To remove an i2c driver from the system, the i2c_del_driver() function is called. In order to remove the i2c driver from the driver core that was registered with the call to driver_register, the following line of code is added to this function:
driver_unregister(&driver->driver);
We have covered the basics of the new driver core, and to help understand how this driver model affects different subsystems, we covered the changes needed to convert the i2c core to support the kernel core bus and driver model. In the next Driving Me Nuts column, we will cover how to add i2c device support and how the probe() and remove() functions should look.