Poster of Linux kernelThe best gift for a Linux geek
 Linux kernel map 
⇦ prev ⇱ home next ⇨

14.5. Classes

The final device model concept we examine in this chapter is the class. A class is a higher-level view of a device that abstracts out low-level implementation details. Drivers may see a SCSI disk or an ATA disk, but, at the class level, they are all simply disks. Classes allow user space to work with devices based on what they do, rather than how they are connected or how they work.

Almost all classes show up in sysfs under /sys/class. Thus, for example, all network interfaces can be found under /sys/class/net, regardless of the type of interface. Input devices can be found in /sys/class/input, and serial devices are in /sys/class/tty. The one exception is block devices, which can be found under /sys/block for historical reasons.

Class membership is usually handled by high-level code without the need for explicit support from drivers. When the sbull driver (see Chapter 16) creates a virtual disk device, it automatically appears in /sys/block. The snull network driver (see Chapter 17) does not have to do anything special for its interfaces to be represented in /sys/class/net. There will be times, however, when drivers end up dealing with classes directly.

In many cases, the class subsystem is the best way of exporting information to user space. When a subsystem creates a class, it owns the class entirely, so there is no need to worry about which module owns the attributes found there. It also takes very little time wandering around in the more hardware-oriented parts of sysfs to realize that it can be an unfriendly place for direct browsing. Users more happily find information in /sys/class/some-widget than under, say, /sys/devices/pci0000:00/0000:00:10.0/usb2/2-0:1.0.

The driver core exports two distinct interfaces for managing classes. The class_simple routines are designed to make it as easy as possible to add new classes to the system; their main purpose, usually, is to expose attributes containing device numbers to enable the automatic creation of device nodes. The regular class interface is more complex but offers more features as well. We start with the simple version.

14.5.1. The class_simple Interface

The class_simple interface was intended to be so easy to use that nobody would have any excuse for not exporting, at a minimum, an attribute containing a device's assigned number. Using this interface is simply a matter of a couple of function calls, with little of the usual boilerplate associated with the Linux device model.

The first step is to create the class itself. That is accomplished with a call to class_simple_create:

struct class_simple *class_simple_create(struct module *owner, char *name);

This function creates a class with the given name. The operation can fail, of course, so the return value should always be checked (using IS_ERR, described in the Section 1.8 in Chapter 11) before continuing.

A simple class can be destroyed with:

void class_simple_destroy(struct class_simple *cs);

The real purpose of creating a simple class is to add devices to it; that task is achieved with:

struct class_device *class_simple_device_add(struct class_simple *cs,
                                             dev_t devnum,
                                             struct device *device,
                                             const char *fmt, ...);

Here, cs is the previously created simple class, devnum is the assigned device number, device is the struct device representing this device, and the remaining parameters are a printk-style format string and arguments to create the device name. This call adds an entry to the class containing one attribute, dev, which holds the device number. If the device parameter is not NULL, a symbolic link (called device) points to the device's entry under /sys/devices.

It is possible to add other attributes to a device entry. It is just a matter of using class_device_create_file, which we discuss in the next section with the rest of the full class subsystem.

Classes generate hotplug events when devices come and go. If your driver needs to add variables to the environment for the user-space event handler, it can set up a hotplug callback with:

int class_simple_set_hotplug(struct class_simple *cs, 
                             int (*hotplug)(struct class_device *dev, 
                                            char **envp, int num_envp, 
                                            char *buffer, int buffer_size));

When your device goes away, the class entry should be removed with:

void class_simple_device_remove(dev_t dev);

Note that the class_device structure returned by class_simple_device_add is not needed here; the device number (which should certainly be unique) is sufficient.

14.5.2. The Full Class Interface

The class_simple interface suffices for many needs, but sometimes more flexibility is required. The following discussion describes how to use the full class mechanism, upon which class_simple is based. It is brief: the class functions and structures follow the same patterns as the rest of the device model, so there is little that is truly new here.

14.5.2.1 Managing classes

A class is defined by an instance of struct class:

struct class {
    char *name;
    struct class_attribute *class_attrs;
    struct class_device_attribute *class_dev_attrs;
    int (*hotplug)(struct class_device *dev, char **envp, 
                   int num_envp, char *buffer, int buffer_size);
    void (*release)(struct class_device *dev);
    void (*class_release)(struct class *class);
    /* Some fields omitted */
};

Each class needs a unique name, which is how this class appears under /sys/class. When the class is registered, all of the attributes listed in the (NULL-terminated) array pointed to by class_attrs is created. There is also a set of default attributes for every device added to the class; class_dev_attrs points to those. There is the usual hotplug function for adding variables to the environment when events are generated. There are also two release methods: release is called whenever a device is removed from the class, while class_release is called when the class itself is released.

The registration functions are:

int class_register(struct class *cls);
void class_unregister(struct class *cls);

The interface for working with attributes should not surprise anybody at this point:

struct class_attribute {
    struct attribute attr;
    ssize_t (*show)(struct class *cls, char *buf);
    ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};

CLASS_ATTR(name, mode, show, store);

int class_create_file(struct class *cls, 
                      const struct class_attribute *attr);
void class_remove_file(struct class *cls, 
                       const struct class_attribute *attr);

14.5.2.2 Class devices

The real purpose of a class is to serve as a container for the devices that are members of that class. A member is represented by struct class_device:

struct class_device {
    struct kobject kobj;
    struct class *class;
    struct device *dev;
    void *class_data;
    char class_id[BUS_ID_SIZE];
};

The class_id field holds the name of this device as it appears in sysfs. The class pointer should point to the class holding this device, and dev should point to the associated device structure. Setting dev is optional; if it is non-NULL, it is used to create a symbolic link from the class entry to the corresponding entry under /sys/devices, making it easy to find the device entry in user space. The class can use class_data to hold a private pointer.

The usual registration functions have been provided:

int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);

The class device interface also allows the renaming of an already registered entry:

int class_device_rename(struct class_device *cd, char *new_name);

Class device entries have attributes:

struct class_device_attribute {
   struct attribute attr;
   ssize_t (*show)(struct class_device *cls, char *buf);
   ssize_t (*store)(struct class_device *cls, const char *buf, 
                    size_t count);
};

CLASS_DEVICE_ATTR(name, mode, show, store);

int class_device_create_file(struct class_device *cls, 
                             const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, 
                              const struct class_device_attribute *attr);

A default set of attributes, in the class's class_dev_attrs field, is created when the class device is registered; class_device_create_file may be used to create additional attributes. Attributes may also be added to class devices created with the class_simple interface.

14.5.2.3 Class interfaces

The class subsystem has an additional concept not found in other parts of the Linux device model. This mechanism is called an interface, but it is, perhaps, best thought of as a sort of trigger mechanism that can be used to get notification when devices enter or leave the class.

An interface is represented by:

struct class_interface {
    struct class *class;
    int (*add) (struct class_device *cd);
    void (*remove) (struct class_device *cd);
};

Interfaces can be registered and unregistered with:

int class_interface_register(struct class_interface *intf);
void class_interface_unregister(struct class_interface *intf);

The functioning of an interface is straightforward. Whenever a class device is added to the class specified in the class_interface structure, the interface's add function is called. That function can perform any additional setup required for that device; this setup often takes the form of adding more attributes, but other applications are possible. When the device is removed from the class, the remove method is called to perform any required cleanup.

Multiple interfaces can be registered for a class.

    ⇦ prev ⇱ home next ⇨
    Poster of Linux kernelThe best gift for a Linux geek