The device class model (2.6.3)

References
P. Mochel, The Linux kernel Driver Model Open Source Develeopment Lab
Corbet, Driver porting: device classes, LWN article 31370
G. Kroah-Hartman, The driver model core, part I, Linux Journal 110, June 2003, 28-32
G. Kroah-Hartman, Device classes, Linux Journal 112, August 2003, 36-41

The device class model is an infrastructure that models the relationships among devices, drivers, buses, and program interfaces. It is based on the kobject model. The definitions are in include/linux/device.h, the code is in drivers/base/.

Structures

The bus_type represents a type of bus. There cannot be more than one bus_type for each type of bus (it does not make sense to have two PCI bus types), although the bus_type can have several bus of its type. It has the following fields
bus_type can be inserted and removed from the buses with bus_register() and bus_unregister(). They appear in the sysfs under the bus subdirectory. There are two functions that iterate over the devices and the drivers in a bus type, bus_for_each_dev() and bus_for_each_drv().

A device driver control how to communicate with one or more devices on a specific bus. The struct device_driver has A device driver can registered (driver_register() and driver_unregister()) and manage its reference count (get_driver() and put_driver()). Drivers appear in the sysfs in the subdirectory drivers of their bus type.

The struct class represents a set of "interfaces". It has the following fields hotplug() is called when a device of this class is plugged in the system before the user helper /sbin/hotplug, and can set additional environments. The method release() is called when a device of this class (represented by a class_device structure) reaches zero reference count. The class_release() is called when the class itself reaches zero reference count. When a class is registered with class_register() it shows up in sysfs under the class subdirectory. It is removed with class_unregister(). Its reference count is managed with class_get(), and class_put().

A class_device represents the containmentship of a device in a class. A single device can be associated with several classes, and a class can contain several devices. The class_device structure has class-device_initialize() inserts the class_device in the class_obj_subsys subsystem, and initialize the kobj. class_device_add(), among other things, insert the node in the class children list. class_device_register() is just the sequence of the two. Before inserting the class_device in the class, the fields class, dev and class_id should be set. Removal is accomplished with class_device_del() and class_device_put(), or, in a single step, with class_device_unregister(). Finally there is a class_device_rename() function that changes both class_id and the kpbj name.

class_interface is essentially a collection of two callback methods, one for insertions and the other for removals. It has Interfaces can be inserted in a class class_interface_register(), and removed. A class can have many interfaces and they are linked (using node) in the list interfaces of the class. The function add() is called on the class' class_devices when a new class_device is inserted, or when the interface is inserted. The function remove() is called when devices or the interface are removed.

The struct device contains The device can register/unregister, and there are also functions to initialize, add, del. The devices appear in the sysfs in the general devices subdirectory and in the devices subdirectory of their bus type. The struct device contains only general fields common to all the devices. It is usually embedde in more specific devices, like usb_device, net_device, etc. There is also a sys_device that give a description of system components as sort of virtual devices. The specific device structure can be retrievd from the device pointer through macros (usually named to_xxx_device(), and based on container_of).

To connect a device with a driver there are the functions (see drivers/base/bus.c)

The bus_attribute is just an attribute with custom show() and store() functions. Likewise driver_attribute, class_attribute, class_device_attribute, and device_attribute have their attribute and their show/store functions each. Appributed are easily defined and initialized with macros of the general form XXX_ATTR(name,mode,show,store), where XXX is any of BUS, DRIVER, CLASS, CLASS_DEVICE, DEVICE. The associated sysfs files must still be created, with xxx_create_file(). The files are deleted with xxx_remove_file(), which can be called explicitly, or automatically when the device is unloaded.

How to use it

Classes have been introduced to simplify the bus-device-drivers infrastruture, but they have a sort of independent existance and it is possible to setup a class framework without drivers, bus, and devices. Therefore we discuss their use independently.

If you need to have your own class, you define it, set its name, and register it. Now the class is listed in sysfs. The put a device in the class define a class_device, set its class, dev, class_id, and possibly also the class_data (pointing to whatever private stuff). Now you can call class_device_register(). The field dev is optional, but if you do not set it you miss useful symlinks in the sysfs. Finally if you need to be notified about devices appearing and disappearing, you declare a class_interface and set its add and remove with a suitable pair of your functions.

You can define your bus type; set its name and the match method (can just return 1). Then register it to insert it in the sysfs. Then add a field of type struct device to your device structure. At this point it is useful to define a macro that retrieves a pointer to your structure, from a pointer to the embedded device, say to_my_device(). Use the container_of macro. Set the bus_id and the name of the device. Also set its parent device. Then you can register the device. Your driver should have a device_driver. Before inserting it you must set the name, bus, and the functions probe and remove.


Exercise


Write a simple module that sets up a class with a class_interface. Define a class_device and when you add it to the class, chack that the add() function is invoked.
Write a module that defines a bus type, with a driver. Define a device and attache it to the bus type.

The references have some good code that addresses these exercises. Here is a sketchy module, devclass.c When the module is loaded, the following actions occur,
  the frob class is registered
  the frob class interface is registered (methods add() and remove())
  the frob bus is registered
  the frob driver is registered
  when the frob device is inserted
    the method frob_match() from frob bus is called
    the method frob_probe_dev() from frob driver is called
  finally the frob device is registered
  when the frob device is inserted into the class (registering the frob
    class interface)
    the class interface method frob_add() is called
  eventually the files in the class device are created

On unloading
  the method frob_remove() of the class interface is called
  then frob_release() is called

After loading this module the sysfs hierarchy is cluttered with plenty of "frobs",

  class
    +----- frob-class
             +----- frob-0
                      +----- attr1
                      +----- attr2
                      +----- device -> ../../../devices/frob-bus0
  bus 
    +----- frob-bus
             +----- devices
             |        +----- frob-bus0 -> ../../../devices/frob-bus0
             +----- drivers
                      +----- frob-driver
  devices
    +----- frob-bus0
             +----- detach-state
Add functionalities to this example: files, store() functions, more complex relations, ... . Play with the dynamics of the example, changing the order of the actions.

kobject

References
P. Mochel, The Linux kernel Driver Model Open Source Develeopment Lab
Corbet, The zen of kobjects, LWN article 51437

kobject and its related structures, kset ktype etc., capture the object-oriented design behind several kernel frameworks, and addresses several problems related to the lifecycle of kernel code. These include
Furthermore the kobject model interfaces with the sysfs layer to export kernel data to user land for reading and/or writing. The structures are defined in include/linux/kobject.h and include/linux/sysfs.h. The code is in lib/kobject.c and in the files in fs/sysfs/.

Structures

Let's start with the type, ktype, of kobjects. This is a simple structure with only three fields: a release() function (called when a kobject of this type terminates its life), an array of attributes, and a set of sysfs_ops (called by the sysfs when an attribute is accessed: there are two operations, show() and store()).
A kset is a container of kobjects. It has a kobject itself, to manage the lifecycle of the kset, and a pointer to a ktype that describes the default type of objetcs in the kset. It has also a pointer to a kset_hotplug_ops (which are filter(), name() and hotplug()).
A subsystem is a kset and a semaphore that is used to serialize access to the kset.
Finally a kobject has a name (stored in the struct if it is short, outside if it is too long; in either case k_name points to the name). It can belong to a kset. A kobject cannot belong to more than one kset. It can have a parent kobject and can be of a specific ktype. If it does not have a parent, this is assigned as the kset's kobject when the kboject is added to the sysfs. If it doen not have a ktype it inherits that of the kset.


Methods

The functions of kobject include

The functions of kset include

The functions of a subsystem include

sysfs

sysfs is an in-memory filesystem that aims at exporting properties of kernel components to user access (either in read or write mode). To make it visible to user mode it must be mounted, mount -t sysfs sysfs /mount/point. The de-facto mount point is /sys.
When a subsystem is registered it appears in the sysfs as a directory. When a kobject is added it also appears as a directory, and the attributes show up as files. Attributes have three fields, Attributes can be read or written to by user programs. In the first case the function show() of the struct sysfs_ops is called. The function store is called for the write. These functions are generic for all the attributes of all the kobjects in the subsystem.
Attributes need not belong to a single kobject. For example the driver's name attribute can be shared among all the device kobjects that use that driver. It is also easy to extend the attribute structure with a sort of "inheritance". For example
   struct my_attribute {
     struct attribute attr;
     int    value; // custom field
     ssize_t (*show)( struct kobject *, struct attribute *, char *);
     ssize_t (*store)( struct kobject *, struct attribute *, 
                       const char *, size_t);
   };

   ssize_t my_show( struct kobject * kobj, struct attribute * attr, char * buf)
   {
     if ( attr ) {
       struct my_attribute * myattr = attr;
       return myattr->show( kobj, attr, buf );
     }
     return -EINVAL;
   }

   struct sysfs_ops my_sysfs_ops {
     .show  = my_show,
     .store = my_store
   };
[todo: symbolic links]

[todo: hotplug]


Exercise
Here is a simple example of a module that creates a kobject with a few attributes and show up in the sysfs hierarchy, kobjtest.c.
The code is poor of features and rather brittle. For example try to open an attribute for reading with a program, but delay the read, and unload the module. As an exercise try to add features (e.g, a dynamic attribute) and make it more robust. As a suggestion you might add a semaphore that is initialized to locked, and unlocked by release(). The exit() function should wait until the semaphore is unlocked. Let me know of your enhancements.


Marco Corvi - 2004