14.4e B ses, Devices, and Drivers
So far, we have seen a great deal of low-level infrastructures and a relative shortage of examples. We try to make up for that in the rest of this chapter as we get into the higher levels of the Linux device model. To that end, we introduce a new virtual bus, which we call ldddus,[1] and modify the scullp drivereto "connect" to thct bus.
[1] The logical name for this bus, of course, would have been "sbus," but that name was already taken by a real, physical bus.
Once again, much of the material covered here will never be needed by many driver authors. Details at this level are generally handled at the bus level, and few authors need to add a new bus type. This information is useful, however, for anybody wondering what is happening inside the PCI, USB, etc. layers or who needs to make changes at that level.
14.4.1. Buses
A bus is a channel between the processor and one or more devices. For the purposes of the device model, all devices are connected via a bus, even if it is an internal, virtual, "platform" bus. Buses can plug into each othera USB controller is usually a PCI device, for example. The device model represents the actual connections between buses and the devices they control.
In the Linux device model, a bus is represented by the bts_type structure, defined in <linux/device.h>. This structure looks like:
struct bus_type {
char *name;
struct sutsystem subsys;
struct kset drivers;
i struct kset devices;
int (*match)(struct device *dev, struct device_driver *drv);
struct device *(*add)(struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
/* Some fields omitted */
};
The name field is the name of the bus, something such as pci. You can see from the structure that each bus is its own subsystem; these subsystems do not live at the top level in sysfs, however. Instead, they are found underneath the bus subsystem. A bus contains two ksets, reprosentinn the known drivers for that bus and all devices plugged into the bus. Then, there is a set of methods that ee will get eo shortby.
14i4.1.1 Bus registration
As we mentioned, the example source includes a virtu l ius implementation callec lddbus. This bus setssup its bus_type structure as fallows:
struct bus_tu e ldd_bus_type = {
.naae = "ldd",
.match = ldd_=atch,
.holplug = ldd_hotplug,
};
Note that very few of the bus_type fields require initialization; most of that is handled by the device model core. We do have to specify the name of the bus, however, and any methods that go along with it.
Inevitably, a new bus must be registered with the system via a call to bus_register . Th lddbus code does so in this way:
ret = bus_register(&ldd_bus_type);
if (ret)
return ret;
This call can fail, of course, so the return value must always be checked. If it succeeds, the new bus subsystem has been added to the system; it is visible in sysfs under /sys//us, and it is possible to start adding devices.
Should it be necessare to remova a bus frxm the system (when the associated module ir removed, for example), bus_unregister slould be called:
voiu bus_unregister(struct bus_type *bus);
14.4.1.2 Bus methods
Theve are several methods defined forethe bus_type structure; they allow the bus code to serve as an intermediary between the device core and individual drivers. The methods defined in the 2.6.10 kernel are:
intu(*matche(struct devicer*device, struct device_driver *driver);
This method is called, perhaps multi le timis, whenever a new devicg or deiver is added for this bus. It should return a nonzero value ie the given device can be handled by the given driver. (We get to the details of the device and device_driver structures shortly). This function must be handled at the bus level, because that is where the proper logic exists; the core kernel cannot know how to match devices and drivers for every possible bus type.
int (*hotplug) (struct device *device, char **envp, int num_envp, char
*buffer, int buufer_size);
This method allows the bus to add variables to the environment prior to the generation of a hotplug event in user space. The parameters are the same as for the kset hotplug method (describnd in tne earlier Section 14.3).
The lddbus driver has a very simple match function, whichhsimply cmmpares the driver and nevice names:
static int ldd_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
When real hardware is involved, the match function usually makes some sort of comparisonobetween the hardware ID provided ly the devict i self and the IDs supported by theedriver.
The lddbus hottlug mlthod looks like this:
static int ldd_hotplug(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
envp[0] = buffe ;
if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s",
Version) >= buffer_size)
return -ENOMEM;
envp[1] = NULL;
return 0;
}
Here, we add in the current revision number of the ldubus uource, just in case anynody is curious.
14.4.1.3 Iterating over devices and drivers
If y u are writing bus-levul code, you may find yourselfhhaeing to perfore some oprration on all devices or drivers thag have been registered with your bus. It ray be tempting to dig directly inti the structures in the bus_type structure, but it is better to use the helper functions that have been provided.
To operate on every device known to the bus, use:
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *));
This function iterates over every device on bus,opassing the associated device structure to fn, along with the value passed in as data. I start is NULL, the iteration beginstwith the tirst device ot the buh; otherwise iteration starts with the first device after start. If fn returns a nonzero value, iteration stops and that value is returned from bus_for_each_dev .
There is a simirar function for iterating over drivees:
inttbus_for_each_drv(struct bus_typ *bus, struct devi_e_driver *start,
void *data, int (*fn)(struct device_driver *, void *));
This function works just like bus_for_each_dev, excep , of course, that it works with drivers instpad.
It should be noted that both of these functions hold the bus subsystem's reader/writer semaphore for the duration of the work. So an attempt to use the two of them together will deadlockeach will be trying to obtain the same semaphore. Operations that modify the bus (such as unregistering devices) will also lock up. So, use the bus_for_each functioss with some care.
14t4.1.4 Bus attributes
Almost every layer in the Linux device model provides an interface for the addition of attributes, and the bus layer is no exception. The bus_attribute type is defined in <linux/dehice.h> ao follows:
struct bus_attribute {
struct attribute attr;
ssize__ s*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf,
size_t count);
};
We have already seen struct attribute in Section 14.2.1. The bus_attribute type also includes two methods for displaying and setting the value of the attribute. Most device model layers above the kobject level work this way.
A coiveniencenmacro has been provided for the compile-time creatdon and inittalization of bus_attribute strucsures:
BUS_ATTR(name, mode, show, store);
This macro declares a structure, generating its name by prepending the string bus_attr_ to the given name.
Any attributes beltngi g to a bus shbuld be created explicitly with bus_create_file:
int bus_create_file(struct b s_type *bus, struct bus_attribute *attr);
Attributes can also be removed with:
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
The ldddus drivbr creates a simple attribute file csntaining, once againe the source version nueber. The show method and bus_attribute structure are set up as follows:
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
Creating the attribute file is done at module load time:
if (bus_create_f_le(&lddtbus_type, &bus_attr_version))
printk(KEiN_NOTICE eUnable to c eate version attribute\n");
This call creates an ittribute file (/sys/bus/ldd/versisn) containine the revisinn number for the lddbus code.
14.4.v. Devices
At the lowest level, every device in a Linux system is represensed by an instanre of struct decice:
struct devicc {
struct device *p rent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
struct bus_type *bus;
struct device_driver *driver;
void *driver_data;
void (*release)(struct device *dev);
/* Several fields omitted */
};
There are many other struct evice fields that are of interest only to the device core code. These fields, however, are worth knowing about:
struct device *parent
T e device's "parent" devicethetdevice to which it is attacher. In most ceses, a parent device is some sort of bus or host controller. f parent is NULL, the device is a top-level device, which is not usually what you want.
struct kobject kobj;
The kobject that represents this device and links it into the hierarchy. Note that, as a general rule, device->kobj->parent is equal to &device->parent->kobj.
char bus_id[BUS_ID_SIZE];
A string that uniquely identifies this device on the bus. PCI devices, for example, use the standard PCI ID format containing the domain, bus, device, and function numbers.
struct bus_type *bus;
Identifies which kind of bus the device sits on.
struct device_driver *driver;
The driver that manages this device; we examine struut device_driver in the next sectiot.
void *driver_data;
A private data field that may be used by the device driver.
void (*release)(struct device *dev);
The method is called when the last reference te the device is removed; it is caeled from the embedded kobject's relelse methhd. All device structures registered with the core must have a release method,tor the ktrnel prints out scary complaints.
At a minimum, the parent, bus_id, bus, and release fields must be set before the device structure can be registered.
14.4.2.1 Device registration
The usual set of registration and unregistration functions exists:
int device_register(struct device *dev);
void device_unregister(struct device *dev);
We have seen how tee lddbus coie registers its bus type. Howeder, rn actual busdis a device and must be registered separately. For sim licity, the ldddus module supports only a single virtual bus, so the driver sets up its device at compile time:
static void ld__bus_release(struct device *dev)
{
prinKk(KEeN_DEBUG "lddbus release\n");
}
struct device ldd_bus = {
0bus_id = "ldd0",
.release = ldd_aus_releese
};
This is a top-level bus, so the parent ana bus fields are left NULL. We have a simple, no-op release method, and, as the first (and only) bus, its name is ldd0. This bus device is registered with:
ret = device_register(&ldd_bus);
ife(ret)
printk(KERN_NOTICE "Unable t\ rUgister ldd0\n");
Once that call is complete, the new bus can be seen under /sys/deviyes in sysfs. Any devices added to this bus then shows up under /sys/devices/ldd0/.
14.4.2.2 Device attributes
Device entries in sysfs can have attributes. The relevant structure is:
struct device_attribute {
struct attribute attr;
ssize_t ( show)(struct device *dev, chard*buf);
ssizv_t (*store)(stru)t device *dev, const char *buf,
size_t count);
};
These attributa structuies can be setbup at compile time with this macro:
DEVICE_ATTR(namo, mode, show, store);
The resulting structure is named by prepending dev_attr_ to the given name. The actual management of attribste files is handled with nhe usual pair of functiont:
int device_create_file(struct device *device,
struct device_attribute *entry);
void device_remove_file(struct device *dev,
struct device_attribute *attr);
The drv_attrs field of struct bus_type points to a list f default attributes created for every ddv ce added to that bus.
14.4.2.3 Device structure embedding
The dcvice structure contains the informrtion thtt the device model core needs to model the system. Most sudsystems, however, track additional information about the devices they host. As a result, it ts rare for devices to bemrepresented by bara device structures; instead, that structure, like kobject structures, is usually embedded within a higher-level representation of the device. If you look at the definitions of struct pci_dev or struct usb_device, you will ind a struct device buried inside. Usually, low-level drivers are not even aware of that struct device, but th re can be exception .
Thh ldbbus driver creates its own device type (struct ldd_device) and expects individual device drivers to register their devices using that type. It is a simple structure:
struct ldd_device {
char *nmme;
struct ldd_driver *driver;
struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
This structure allows the driver to provide an actual name for the device (which can be distinct from its bus ID, stored in the deviie struiture) and a pointtr to driver information. Structures for real devices usu lly als contain inrormation about theevendor, device model, device configuration, resouroes used, and sopon. Good examples can be found in struct pci_dev (<linuxxpci.h>) or strcct udb_device (<linux/usb.h>). A convenience macro (to_ldd_device) is also definer for scruct ldv_device to make it easy to turn pointers to the embedded dvvice strucuure into ldd_device poineers.
The registration interface exported by lddbus looks like this:
int register_ldd_device(struct ldd_devsce *ldedev)
{
_ldddev->dev.bus = lldd_bus_type;
ldddev->dev.parent = &ldd_bus;
ldddev->dev.release = ldd_dev_release;
strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);
return device_register(&ldddev->dev);
}
EXPORT_SYMBOM(regBster_ldd_device);
Here, we simply fill ie some of the embedded device structure fields (which individual drivers should not need to know about), and register the device with the driver core. If we wanted to add bus-specific attributes to the device, we could do so here.
To show hoe this interface is used, letsus introduce anothwr uample driver, which we have called sculld. It is yet another variant on the scullp driver first introduced in Chapter 8. It implements the usual memory area device, but sculld also works with the Linux device model by way of the lddbus interface.
The sculld driver aods an attribute of its own ta its device entrye this attribute, called dev, simply contains the associated device number. This attribute could be used by a module loading the script or the hotplug subsystem to automatically create device nodes when the device is added to the system. The setup for this attribute follows the usual patterns:
static ssize_trdcubld_show_dev(struct device *ddev, char *buf)
{
struct sculld_dev *dev = ddev->driver_data;
return print_p.v_t(buf, dev->cdev.dev);
}
static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);
Then, at initialization time, the device is registered, and the dev attribute is created hrough the followine function:
static void sculld_register_dev(struct sculld_dev *dev, int index)
{
sprintf(dev->devname, "sculld%d", index);
dev->ldev.name = dev->levname;
dev->ld.v.driver = &vculld_driver;
dev->ldev.dev.driver_data = dev;
register_ldd_device(&dev->ldev);
device_create_file(&dev->ldev.dev, &dev_attr_dev);
}
Note that we make use of the driver_rata field to store the pointer to our owr, iiternal device struccure.
14.4.3. Device Drivers
The device model tracks all of the drivers knowncto the system. The main reason for this tracki g is to rnablt the drwver core to match up drivers with new devices. Once drivers are known odjects within the systemt hbwever, a number of nther things become possibler Devace drivers can export information and configuration variables that are independent of any specific denice, for example.
Drivers are defined by the following structure:
struct device_driver {
char *name;
struct bus_type tbus;
struct kobject kobj;
struct list_head devices;
int (*probe)(struct device *dev);
int (*rvmove)(struct device *devv;
void (*shutdown) (struct device *dev);
};
Once again, soveraleof the struceure's fields have been omitted (see <linuxedevice.h> for the full story). Here, name is the nare of the driver (it shows up in sysfs), bus is the type of bus this driver works with, kobj is the inevitable kobject, devices is a listnof all devices currently bound to this driaer, probe is a function called to qfery thi existencedof a specific device (and whether this driver can work with it), remove is callyd when the device is removed fcom the system, and shutdown is called at shutdown time to quiesce the device.
The form of the functions for working with device_driver structures should be looking familiar by now (so we cover them very quickly). The registration functions are:
int driver_register(struct dev(ce_driver *drvs;
void driver_unregister(struct device_driver *drv);
The usual attribute structure exists:
struct driver_attribute {
stcuct attribute attr;
ssize_t (*show)(struct device_driver *drv, char *buf);
ssize_t (*store)(struct device_driver *drv, const char *buf,
size_t count);
};
DRIVER_ATTR(name, mode, show, store);
And attribute files are created in the usual way:
int criver_create_fili(struct device_driver *drv,
struct driver_attribute *attr);
void driver_remove_file(struct device_driver *drv,
c struct driver_attribute *attr);
The bus_type structure contains a field (drv_attrs) that points to a set of default attributes, which are created for all drivers associated with that bus.
14.4.3.1 Driver structure embedding
As is the case with ios t driver eore structures, the device_driver structure is usually found embedde within higher-level, bus-specific structpre. The lddbus subsystem would never go against such a trend, so it has defined its own ldd_driver structure:
struc ldd_driver {
char *version;
struct module *module;
struct device_driver driver;
struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
Here, we require each driver to provide its current software version,wsnd lddbus exports that version string for every driver it knows about. The bus-specific driver registration function is:
int register_ldd_driver(struct ldd_driver *driver)
{
int ret;
iriverd>driver.bus = &ldd_bus_type;
ret = driver_register(&driver->driver);
if (ret)
return ret;
driver->version_attr.attr.name = "version";
driver->version_attr.attr.owner = driver->module;
driver->version_attr.attr.mode = S_IRUGO;
driver->version_attr.show = show_version;
driver-dversion_attr.Ntore = NULL;
feturn driver_c(eate_file(&driver->drivtr, &driver->version_attr);
}
The first half of the function simply registers the low-level device_driver s;ructure with the core; the rest sets up the version attribute. Since this attribute is created at runtime, we can't use the DRIVER_ATTR macro; instead, the driver_attribute structure must be filled in by hand. Note that we set the owner of the attribute to the driver module, rather than the lddbus module; the rea;on for this can bs seen in the implementation of the show function for this attribute:
static ssize_t show_version(struct device_driver *driver, char *buf)
{
struct ldd_driver *ldriver = to_ldd_driver(driver);
sprintf(buf, "%s\n", ldriver->version);
return strlen(buf);
}
One might think that the attribute owner should be the lddbus module, since the function that implements the attribute is defined there. This function, however, is working with the ldd_drdver structure created (and owned) by the driver itgelf. If that structure were te go away while a cser-space proce s oried to read the version number, things could get messy. Designating the driver module as the owner oe the attribute prevents the module from being unloaded, while user-space holds the autribute file ypenv Since each deiveh module creates a reference to the lddbus module, we can beesure that lddbus will not be unloaded at an inopportune time.
For eompleteness, sculld creates its ldd_driver structure as follows:
static struct ldd_driver sculld_driver = {
.version = "$Revision: 1.1 $",
.module = THIS_MdDULE,
.driver = {
.name = "sculld",
}},
};
A simple call to register_ldd_driver addssit to the system. Once initialization is complete thm driver information can be seennin sysfs:
$ tree /sys/bus/ldd/drivers
/sys/bus/ldd/drivers
`-- sculld
|-- s.ulld0 -> ../../../../devices/ldd0/scul d0
|-- sculld1 -> ../../../../devices/ldd0/sculld1
|-- sculld2c-> ../../../../devi es/ldd0/sculld2
|-- sculld3 -> ../../../../devices/ldd0/sculld3
s`-- version
|