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
- name;
- subsys, a bus type is a subsystem in the sysfs hierarchy;
- two kset, one for drivers and one for devices;
- match(), the bus type matches devices against drivers to
make them work together;
- add() ...;
- hotplug() ...;
- suspend() and resume(), for power management.
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 descriptive name;
- a pointer to the bus_type, bus, it belongs to;
- a semaphore, to protect unloading;
- a kobj, for the metadata;
- the list of devices;
- and the methods probe(), remove(), shutdown(),
suspend(), and resume().
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
- a name;
- a subsys, which shows up in sysfs;
- the list of children devices;
- the list of interfaces;
- and the methods hotplug(), release(), and
class_release().
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
- a kobject kobj for the metadata;
- the link node for the list of siblings in the class'
children;
- a pointer to the class of this class_device;
- a pointer, dev, to the device;
- a pointer to class-specific data, class_data, accessible through
the functions class_get_devdata() and class_set_devdata();
- a class_id, a short string.
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
- a link node for the list of interfaces of a class;
- a pointer to its class;
- and the functions add() and remove().
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
- a link node for the list of siblings (under the parent's
children);
- a link bus_list for the list of devices in a bus;
- a link driver_list for the list of devices attached to a
driver;
- a pointer to the parent device in the hierarchy;
- a list head for the children devices;
- a kobject kobj;
- a bus_id, ascii representation of this device address
on the bus;
- a pointer bus to the bus_type;
- a pointer to the device driver;
- a private driver_data; there are functions to get/set
this data
- a platform_data;
- power management info power, and power_state;
- dma data: dma_mask and dma_pools;
- a release() method.
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)
- device_bind_driver(): ...
- device_release_driver(): ...
- driver_attach(): ...
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
- keeping track of the usage reference count;
- managing complex relative relationships (containment, usage, etc.);
- managing the show up of a device;
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
- populate(), if the kobject has a ktype, and this has
default attributes, for each of them this function creates a file
in the directory of the kobject in sysfs.
- create_dir(), creates a directory for the kobject in sysfs,
and populate() it.
- kobject_set_name() assign a name to the kobject.
- kobject_rename() change the kobject name.
- kobject_init() set the refcount, initializes the list
entry, and "get" the kobject's kset (increase its refcount).
This is the first function that must be called on a kobject.
- kobject_add() insert a kobject into the hierarchy:
Increases the kobject's refcount, sets its k_name.
Increases the parent's refcount if the kobject has its parent.
If the kobject has a kset, link the kobject entry in the
list() of the kset, and use the kset's kobject as parent
if the kobject does not have a parent.
Create the directory for the kobject and call the hotplug "add".
- kobject_register() is a shorthand for init and add.
- kobject_del() removes the kobject from the hierarchy:
call the hotplug "remove", remove the directory from the sysfs,
and unlink the kobject from the kset.
- kobject_unregister() is a shorthand for del and put.
- kobject_get() increases the refcount.
- kobject_put() decreases the refcount.
If this reaches 0 do a cleanup.
- kobject_cleanup() frees the kobject resources:
If the kobject's ktype has a release function this is invoked.
If the kobject belongs to a kset, put the kset.
If the kobject has a parent, put it.
The functions of kset include
- kset_init() initializes the embedded kobject and the
list.
- kset_add() add a kset object to the hierarchy.
It adds the kobject embedded in the kset
- kset_register() is a shorthand for init and add.
- kset_unregister() unregister the embedded kobject.
- kset_find_obj() find a kobject by name inside the kset.
The functions of a subsystem include
- subsystem_init() initializes the embedded kset, and the
semaphore.
- subsystem_register() shorthand for init and add the
embedded kset.
- subsystem_unregister() unregister the embedded kset.
- subsys_create_file() creates a file in sysfs for the
kset's kobject, and increases the refcount of the subsystem.
- subsys_remove_file() remove the sysfs file, and decreases the
refcount.
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,
- name, the name with which they appear in sysfs;
- owner, usually THIS_MODULE;
- mode, the file mode.
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