Previous articles in this series have shown how the device model maintains a data
structure representing the physical structure of the host system. There is
more to know about a system than how it is plugged together, however;
indeed, most of the time, user space really does not care about physical
connections. Users (and the applications they run) are much more
interested in questions like "what disks
does this system have" or "where is the mouse?"
To help with this sort of resource discovery issue, the driver model
exports a "class" interface. Devices, once registered, can be associated
with one or more classes which describe the function(s) performed by the
device. Class memberships show up under the /sys/class sysfs
directory, and, of course, can be decorated with all kinds of attributes.
There are also mechanisms which provide notification - both within and
outside of the kernel - when a device joins or leaves a class. The class
interface can also be the easiest way for a driver to make arbitrary
attributes available via sysfs.
For many (if not most) drivers, class membership will be handled
automatically in the higher layers. Block devices, for example, are
associated with the "block" class when their associated gendisk
structures are registered. (This class currently appears in
/sys/block, incidentally; it will likely move to
/sys/class/block at some point). Occasionally, however, it can be
necessary to explicitly associate a device with a specific class. This
article describes how to do that, and - though remaining superficial - it
provides more information than is really needed in order to, with luck,
provide an understanding of how the class system works.
For those wishing for a hands-on example, the full source for a version of the "simple block
driver" module that understands classes is available.
Creating a class
It is a rare device which exists in a unique class of its own; as a result,
drivers will almost never create their own classes. Should the need arise,
however, the process is simple. The first step is the creation of a
struct class (defined in <linux/device.h>).
There are two necessary fields, being the name and a pointer to a "release"
function; the SBD driver sets up its class as:
static struct class sbd_class = {
.name = "sbd",
.release = sbd_class_release
};
The name is, of course, how this class will show up under
/sys/class. We will get to the release function shortly, after we
have looked at class devices.
Beyond that, there is only one other thing that a class definition can
provide: a "hotplug" function:
int (*hotplug)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
The addition of a device to a class creates a hotplug event. Before
/sbin/hotplug is called to respond to that event, the class's
hotplug() method (if any) will be called. That method can add
variables to the environment that is passed to /sbin/hotplug; they
should be put into buffer (respecting the given
buffer_size) with pointers set into envp (but no more
than num_envp of them, and with a NULL pointer to
terminate the list). The return value should be zero, or the
usual negative error code.
Classes need to be registered, of course:
int class_register(struct class *cls);
The return value will be zero of all goes well. The void function
class_unregister() will do exactly what one would expect.
Class devices
If your device type lacks a specific registration function of its own (such
as add_disk() or register_netdev()), or if you have
created your own custom class, you may find
yourself adding your device(s) to a class explicitly. Membership in a
class is represented by an instance of struct class_device. There
are three fields that should normally be filled in:
struct class *class;
struct device *dev;
char class_id[BUS_ID_SIZE];
The class pointer, of course, should be aimed at the proper class
structure. The dev pointer is optional; it is used to create the
device and driver symbolic links in the device's class
entry in sysfs. Since
user-space processes looking to discover devices of a particular class
probably want to have that pointer, you should make it easy for them. The
class_id is a string which is unique within the class - it
becomes, of course, the name of the device's sysfs entry.
Once the class_device structure has been set up, it can be added
to the class with:
int class_device_register(struct class_device *class_dev);
class_device_unregister() can be used at module unload time.
Once you register a class device, it becomes available to the world as a
whole. If your class device is allocated dynamically, you must be very
careful about when you free it. Remember that user-space processes can
retain references to your device via your sysfs attributes; you must not
free the class device until all of those references are gone.
That, of course, is the purpose of the release function stored in
struct class. This function has a simple prototype:
void release_fn(struct class_device *cd);
This function is called when the last reference to the given device goes
away; it should respond by freeing the device. That call will typically
happen when you call class_device_unregister() on the device, but
it could happen later if other references persist.
Please note that, if your class device structure is dynamically allocated,
or it embedded within another, dynamic structure, you must use a
release function to free that structure or your code is buggy.
Class device attributes
Attributes are easily added to a class device entry. If the attribute is
to be readable, it will need a "show" function to respond to reads; the
function used to export the driver version in SBD looks like:
static ssize_t show_version(struct class_device *cd, char *buf)
{
sprintf(buf, "%s\n", Version);
return strlen(buf) + 1;
}
If the attribute is to be writable, you will need a store function too:
ssize_t (*store)(struct class_device *, const char *buf, size_t count);
These functions are then bundled into an attribute structure with:
CLASS_DEVICE_ATTR(name, mode, show, store);
The name should not be a quoted string; it is joined in the macro
to create a structure called class_device_attr_name.
The final step is to create the actual device attribute, using:
int class_device_create_file(struct class_device *,
struct class_device_attribute *);
You can call class_device_remove_file() to get rid of an
attribute, but that is also done automatically for you when a device is
removed from a class.
Interfaces
The term "interface," as used within the device model, is a bit confusing.
A better way to think of interfaces is as a sort of constructor and
destructor mechanism for class device entries. An interface provides
add() and remove() methods which are called as devices
are added to (and removed from) a class; their usual purpose is to add
class-specific attributes to the class device entry. They can, however,
perform any other kernel function that might be useful in response to class
device events.
Briefly, the creation of an interface requires the creation of a
class_interface structure, which needs to have the following
fields filled in:
struct class *class;
int (*add) (struct class_device *);
void (*remove) (struct class_device *);
Once the interface is set up with:
int class_interface_register(struct class_interface *);
The add() and remove() functions will be called when
devices are added to (or removed from) the given class. A call to
class_interface_unregister() undoes the registration. No comments have been posted.
Post one now
|