When driver authors want to work with the kernel device model, they are
probably wanting to (1) ensure that their devices are represented in
the system hierarchy, or (2) set up some custom attributes in sysfs.
To meet those needs, this article will look at device and attribute
registration. These tasks represent only part of what the device model is
about, but they are a good starting place.
Some time ago, this series looked at a simple
block driver. That driver will now be augmented with simple driver
model and sysfs support. The relevant bits of code will be shown below;
the full source is available here.
The device structure
Once upon a time,
struct device referred to a network interface.
That structure has long since been renamed
net_device, and, in
2.5,
struct device became the "base class" for representing all
devices in the system. The full structure can be seen in
<linux/device.h>; for most drivers, however, there are only
a few fields that are worth worrying about:
char name[DEVICE_NAME_SIZE];
char bus_id[BUS_ID_SIZE];
void *driver_data;
struct device_driver *driver;
The name field is a descriptive name (not something found in
/dev; it can be something like "Barmatic VX773 Frobnicator").
bus_id describes where the device can be found on the bus; for PCI
devices it is a string like "00:09.0". The driver can put
anything it wants into the driver_data field. And driver
describes the driver for this device; we'll get there shortly.
As a general rule, your driver will want to remember more about a device
than can be represented in struct device; this structure just
exists to hold the data common to all devices in the system. So drivers do
not normally deal with bare device structures; instead, this
structure is embedded within something larger. Thus, if you look at the
definition of, say, struct pci_dev or struct usb_device,
you'll find a struct device lurking within.
A general rule has been adopted that struct device is not
the first field in any other structure which contains it. The idea is that
programmers should think carefully about which structure they are dealing
with at any given time, and not just cast pointers back and forth. Going
from the larger structure to struct device is just a matter of
referencing the appropriate field. To go the other way, the
container_of() macro should be used. Thus, for example, a USB
driver could turn a struct device pointer (called, say,
devp) into a struct usb_device pointer with:
container_of(devp, struct usb_device, dev)
Here, "dev" is the name of the struct usb_device field
containing the device structure. Normally, a bus subsystem will
define a macro for doing this conversion; in the USB case, it is
to_usb_device().
The example simple block device (SBD) does not attach to a physical bus, of
course. For this type of device, the kernel exports a "system" bus which
may be used for virtual attachments. Usually, the system bus contains
devices like the processor, interrupt controller, and timer. But we can
attach virtual disks there too.
System bus devices are represented by struct sys_device, defined
as:
struct sys_device {
char *name;
u32 id;
struct device dev;
};
(A couple of fields have been omitted).
Here, the name should be a /dev-style name - it will be
used (along with the id value) to create the device's entry in
sysfs under devices/sys.
The simple block device will be represented as a sys_device.
Before we get to that, however, there is another structure that deserves a
look.
The device driver structure
Device drivers, too, are represented in the device model, and in sysfs.
The relevant structure (again, with a few fields omitted) looks like:
struct device_driver {
char *name;
struct bus_type *bus;
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
};
The name this time around is the name of the driver, of course.
The bus field is normally filled in by the bus-layer logic;
drivers need not worry about initializing it. The various methods provided
in the structure are for the handling of device discovery and power
management tasks. Usually, again, these methods are provided at the bus
level, with bus-specific calls down into the driver itself.
Time to look at some code. The SBD driver sets up its structure as:
static struct device_driver sbd_driver = {
.name = "sbd",
};
A driver for a real device may, at the least, want to add methods for
suspend and resume events. There is nothing in particular that SBD needs
to do in response to such events, however, so no such methods have been
provided.
In the SBD module init code, the driver structure is registered with:
driver_register(&sbd_driver);
It is also necessary to call driver_unregister() in the shutdown
code, of course.
This call is sufficent to create the directory bus/system/sbd in
the sysfs hierarchy. As SBD devices are registered, they will appear as
symbolic links in that directory. There will be no other data there, at
least not yet.
Driver attributes
Suppose we wanted to put something else in the driver sysfs directory?
That can be done through the creation of
driver attributes. The SBD driver
adds a file called
version which contains the version of the
driver code; user-space scripts could query that file to get a sense for
what capabilities might or might not be available.
Each driver attribute requires a name, file permissions, and functions to
format and set the value of the attribute. Generally the functions are
defined first. The SBD function to display the version is:
static ssize_t show_version(struct device_driver *drv, char *buf)
{
strcpy(buf, Version);
return strlen(Version) + 1;
}
The buffer passed into the show function is one full page, so there's
plenty of room. In general, however, values for sysfs attributes should be
short. The convention is that an attribute contains a single value - not
pages of information as can be found in some /proc files.
The store function has the prototype:
ssize_t (*store)(struct device driver *drv,
const char *buf,
size_t count);
The return value is the number of bytes consumed by the operation (usually
count); it can also be one of the usual error codes. Since there
is little point in changing the driver's version string from user space,
SBD provides no store function.
Creating attributes requires filling in a driver_attribute
structure. This is usually done with the DRIVER_ATTR macro:
DRIVER_ATTR(name, mode, show, store);
In the case of the SBD driver, the relevant declaration is:
static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);
This line creates a structure called driver_attr_version; it will
ultimately create a file called version in the driver's sysfs
directory. That file will have read-only permissions, and will call
show_version() when read.
Actually creating the file, however, requires one more step. This line
appears in the module initialization code, immediately after the call to
driver_register():
driver_create_file(&sbd_driver, &driver_attr_version);
There is a driver_remove_file(), but normally it is unnecessary to
call it - the files will be removed automatically when the driver is
unregistered.
Device registration
Now that we are done looking at driver registration, we can get around to
creating our device. SBD is a "system bus" device; the bus-specific device
structure is created as:
static struct sys_device sbd_sys_device = {
.name = "sbd",
.dev = { /* struct device stuff */
.name = "Simple block device",
.driver = &sbd_driver
},
};
The id field defaults to zero, so this device will eventually be
sbd0. Note the assignment of the dev.driver field, which
connects the device with the driver that handles it.
At initialization time, the device is registered with:
sys_device_register(&sbd_sys_device);
sys_device_register() is a wrapper around
device_register() which handles "system bus" details. Once this
call has been made, the sysfs directory device/sys/sbd0 is
created. Two attributes exist there: name contains "Simple block
device", and power contains the device's current power state.
Most importantly, however, the device exists within the device model data
structure, where it can respond to hotplug and power management events.
Devices, too, can have custom attributes. For SBD, an attribute
device contains the device number assigned to the virtual disk;
this value could be used, for example, to create a /dev entry
automatically in user space. The implementation is very similar to the
driver attribute we set up before:
static ssize_t show_devnum(struct device *dev, char *buf)
{
return sprintf(buf, "%02x00", major_num);
}
DEVICE_ATTR(device, S_IRUGO, show_devnum, 0);
...
device_create_file(&sbd_sys_device.dev, &dev_attr_device);
One final step, specific to block devices, is taken in SBD. Before the
virtual disk's gendisk structure is registered with
add_disk(), a pointer to the device structure is stored:
Device.gd->driverfs_dev = &sbd_sys_device.dev;
This assignment causes a couple of extra symbolic links to be created in
sysfs; devices/sys/sbd0/block points to block/sbd0, and
block/sbd0/device points back to devices/sys/sbd0. In
this way, the relationship between the various entries is made explicit.
Going further
This article barely touches on the device model interface. Many details
have necessarily been omitted; many of them will be topics for future
articles. The next article in the series, which will appear soon (promise)
will look at the class interface. Power management also deserves a look,
but that interface remains in flux as of this writing. Expect an article
when the dust settles a bit.
(
Log in to post comments)