Overview
Linux的sysfs文件系统一般mount在/sys目录。本文主要介绍sysfs文件系统中设备驱动模型的建立过程,内核版本2.6.29。
设备驱动信息主要用来表示设备以及驱动的层次关系,以及处理热插拔等。/sys中与之相关的数据有:
class 代表一类设备,比如mtd、net、tty等等
bus 总线,比如PCI、USB、I2C等
device 代表一个设备
driver 代表一个驱动
以下是一些sysfs中的全局变量:
// /sys/class
struct kset * class_kset = kset_create_and_add("class", NULL, NULL);
// /sys/bus
struct kset * bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
// /sys/devices
struct kset * devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
struct pm_ops *pm;
struct class_private *p;
};
struct class_private {
struct kset class_subsys;
struct list_head class_devices;
struct list_head class_interfaces;
struct kset class_dirs;
struct mutex class_mutex;
struct class *class;
};
class在sysfs中的层次由struct class_private决定。struct class只是class_private的封装。
struct class_private::class_subsys.kobj.kset = class_kset; // 父目录为/sys/class
struct class_private::class_subsys.kobj->name代表这个class在/sys/class中显示的名字
struct class::dev_attrs为设备属性,往class中添加设备的时候,这些属性会自动添加到设备目录下。
新建class有两种方法:静态创建和动态创建。
l 静态创建
static struct class i2c_adapter_class = {
.owner = THIS_MODULE,
.name = "i2c-adapter",
.dev_attrs = i2c_adapter_attrs,
.class_attrs = ...
};
int retval = class_register(&i2c_adapter_class);
l 动态创建
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
class_create分配申请一块空间给class,然后对name、owner和class_release函数赋值,并最终调用class_register。
class_register
根据struct class的值,设置struct class_private
调用add_class_attrs在class中添加属性。
l class attrs
class的属性最终是在/sys/class/<new class>/目录下以文件的形式存在。用户程序可以直接对这些属性进行读写。如果要静态创建属性,可以在定义class时对.class_attrs域赋值,使其指向要添加的attr数组。如果要动态创建。可以通过函数class_create_file添加。
int class_create_file(struct class *cls, const struct class_attribute *attr);
如果是动态创建的属性,需要在模块卸载时调用class_remove_file释放。
如果是静态创建的属性,在调用class_unregister时会自动释放。
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct pm_ext_ops *pm;
struct bus_type_private *p;
};
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset;
struct kset *devices_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
与class类似,bus在sysfs中的显示由struct bus_type_private决定,struct bus_type只是一个封装。
struct bus_type_private::subsys.kobj代表/sys/bus/<bus>目录。
struct bus_type_private::subsys.kobj.kset = bus_kset; // 默认父目录为/sys/bus/
struct bus_type_private::subsys.kobj.ktype = &bus_ktype; // bus的属性操作
struct bus_type_private::subsys.kobj.name = <bus在/sys/bus/目录下显示的名字>;
在/sys/bus/目录,每创建成功一个<bus>,都会自动创建两个子目录drivers和devices,分别代表连到此<bus>的设备和驱动。在drivers和devices子目录下,每新建一个driver,会把struct bus_type中的drv_attrs属性赋给那个driver;每创建一个device,会把struct bus_type中dev_attrs赋给那个device。
struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
.bus_attr = …
};
int ret = bus_register(&i2c_bus_type);
int bus_register(struct bus_type *bus);
分配内存给struct bus_type_private;
根据struct bus_type的域设置bus_type_private;
根据.bus_attr设置bus的属性,这些属性在bus_unregister时会被自动释放。(bus属性也可通过bus_create_file动态添加,但所有动态添加的属性都要在卸载时通过bus_remove_file释放。)
struct device {
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
const char *init_name; /* initial name of the device */
struct device_type *type;
unsigned uevent_suppress:1;
struct semaphore sem; /* semaphore to synchronize calls to its driver. */
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this device */
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data, device core doesn't touch it */
struct dev_pm_info power;
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
struct list_head node;
struct class *class;
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
int device_register(struct device *dev) ;
此函数将device登记到sysfs中。在调用之前,需对struct device进行初始化。
struct device dev ;
dev. parent = <parent dev> ; // 父设备
dev.release = <func release> ; // 登记释放dev时调用的回调函数
dev.class = <class> ; // struct class
dev.bus = <bus> ; // 所属总线
然后调用device_register(&dev) ;
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
device_initialize
做一些初始化工作,dev->kobj.kset = devices_kset ; // 代表/sys/device目录
device_add
//设置dev->kobj.parent,即确定这个dev的父目录,详情见下节
setup_parent(dev, dev->parent);
// 将dev挂到dev->kobj.parent代表的目录,如果没有parent,父目录默认被设置成dev->kobj.kset代表的目录
kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
device_create_file(dev, &uevent_attr); // 给设备添加uevent属性
if (MAJOR(dev->devt))
device_create_file(dev, &devt_attr); // 给设备添加dev属性(打印主从设备号)
device_create_sys_dev_entry(dev); // 在设备的class下创建设备链接,比如/sys/char和/sys/block,链接名字为major:minor;如果设备没有class,默认为/sys/char目录
device_add_class_symlinks(dev);
// 在设备目录下建立subsystem链接,指向其所属的class
sysfs_create_link(&dev->kobj, &dev->class->p->class_subsys.kobj, "subsystem");
// 在设备所属class目录下建立指向设备的链接,以设备名命名
sysfs_create_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev->bus_id);
// 如果父设备存在,在设备目录下建立指向父设备的链接,以device命名
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
device_add_attrs(dev);
// 如果有class,把class中dev_attrs属性都加上
device_add_attributes(dev, class->dev_attrs);
// 如果有type,把type中dev_attrs属性都加上
device_add_groups(dev, type->groups);
// 把device中groups指向的属性都加上
device_add_groups(dev, dev->groups);
bus_add_device(dev); // 在设备有bus时有效
// 如果有bus,将bus中dev_attrs都加上
device_add_attrs(bus_get(dev->bus), dev);
// 在bus中建立指向设备的链接,以设备名命名
sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev->bus_id);
// 在设备中建立指向总线的链接,以”subsystem”命名
sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem");
dpm_sysfs_add(dev); // 建立power属性
device_pm_add(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_attach_device(dev);
/* 如果总线支持自动检测设备(drivers_autoprobe == 1; 默认都支持),调用device_attach(dev); device_attach中,如果发现dev已经有driver与之关联,作一些sysfs的操作;如果没有,对总线中每一个驱动调用__device_attach。
__device_attachàdriver_probe_device
driver_probe_device先调用bus的match函数,如果返回的是match,再调用really_probe。(bus的match函数在这里调用)
really_probe先看bus有没有probe函数,如果有,调用bus的probe。如果没有,调用driver的probe函数(这里就是我们驱动程序的probe函数被调用的地方)。*/
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
klist_add_tail(&dev->knode_parent, &parent->klist_children);
list_add_tail(&dev->node, &dev->class->p->class_devices);
list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
在sysfs的设备模型中,有四种设备类型:
物理设备 有parent设备,没有class
直接虚拟设备 有parent设备和class,parent没有class
间接虚拟设备 有parent设备和class,parent有class
纯虚拟设备 没有parent,有class(网络环回设备等)
以挂在PCI总线上的I2C适配器为例,首先需要创建一个设备,使其bus域指向PCI bus,这是一个物理设备;然后,以这个物理设备为父设备,创建一个class为I2C_adapter_class的子设备,这个设备是直接虚拟设备,描述I2C adapter的功能。I2C子系统对每一个I2C adapter,又进一步创建了一个字符设备,I2C dev,这个字符设备的class被设置为I2C_device_class,这里I2C dev就是一个间接虚拟设备。
除非是纯虚设备,否则任何一个虚拟设备向父设备追溯,一定能找到一个物理设备。
struct device中有两个域,bus和class,这两个域不能同时有值。如果bus域非空,说明这个struct device是挂在某个总线上,那么它必须是一个物理设备,class域必须是NULL;如果class域非空,说明这是一个属于某个类的虚拟设备,那么它的bus域就必须是NULL。所以,在上节提到的两个函数,device_add_class_symlinks(dev) 与bus_add_device(dev)中,虽然都创建了subsystem链接,但它们只有一个会起作用,否则系统会崩溃。
setup_parent函数
void setup_parent(struct device *dev, struct device *parent) ;
这个函数用来决定dev被加到sysfs的哪个目录下。代码逻辑为:
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
static struct kobject *get_device_parent(struct device *dev, struct device *parent) ;
这个函数会按照设备类型决定设备的父目录 :
l 如果是物理设备且有父设备 (dev->class == NULL && dev->parent)
父目录就是父设备代表的目录
l 如果是直接虚拟设备 (dev->class && dev->parent && dev->parent->class != NULL)
在父设备代表的目录下新建一个子目录,名字为dev->class->name。然后把这个新建的目录作为设备的父目录 :/sys/devices/<parent_name >/<class_name>。
l 如果是间接虚拟设备 (dev->class && dev->parent && dev->parent->class == NULL)
父目录就是父设备代表的目录
l 如果是纯虚拟设备 (dev->class && dev->parent == NULL)
父目录为/sys/devices/virtual/<class_name>
l 如果是物理设备且没有父设备 (dev->class == NULL && dev->parent == NULL)
本函数不设置父目录,返回NULL。但由于此函数返回后会继续调用kobject_add,所以父目录会设置成dev->kobj->kset代表的目录,也就是一开始device_initialize函数里设置的/sys/devices目录。可以看出这是物理root设备,比如platform (/sys/devices/platform)。
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct pm_ops *pm;
struct driver_private *p;
};
struct driver_private {
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module_kobject *mkobj;
struct device_driver *driver;
};
driver_register将driver注册到sysfs系统中,在注册之前需要对driver进行初始化
struct device_driver driver = {
.name = <driver name>;
.bus = <bus>;
.probe = <probe func>; // 探测设备
.remove = <remove func>; // 移除设备
.suspend = <suspend func>; // 挂起设备 进入低功耗状态)
.resume = <resume func>; // 运行设备(从低功耗状态恢复)
};
int driver_register(struct device_driver *drv) // 将driver登记到sysfs系统中
bus_add_driver(drv);
driver_add_groups(drv, drv->groups);
int bus_add_driver(struct device_driver *drv)
// 分配并初始化struct driver_private
。。。
priv->kobj.kset = bus->p->drivers_kset; // 父目录指向bus的drivers子目录
// 为driver新建一个kobj,父目录在上一行的kset中指定了
kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, “s%”, drv->name);
/* 如果drv的bus支持autoprobe(bus->p->drivers_autoprobe==1;默认都是1),调用driver_attach,driver_attach对bus中每一个device,调用__driver_attach。__driver_attach调用driver_probe_device, driver_probe_device的过程在上一章中的device_register函数中有描述。*/
if (drv->bus->p->drivers_autoprobe)
driver_attach(drv);
// 把driver加入到bus的drivers列表
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver (drv->owner, drv);
driver_create_file(drv, &driver_attr_uevent); // 加入uevent属性
driver_add_attrs(bus, drv); // 将bus中drv_attrs属性列表加入driver目录
add_bind_files(drv); // 加入bind、unbind属性(与热插拔有关)
kobject_uevent(&priv->kobj, KOBJ_ADD);
int driver_add_groups(struct device_driver *drv, struct attribute_group **groups)
对group中每一个group,调用sysfs_create_group在driver下创建一个子目录,并将group里的属性作为文件加入到子目录中。
前面提到的class、bus、drivers和devices在sysfs系统中都是以目录表示;它们的属性则由文件表示。所有的目录、文件都是通过sysfs模块提供的函数创建和维护。这些sysfs的函数主要包括:
// 在kobj代表的目录下新建一个文件
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
sysfs_add_file
sysfs_add_file_mode
// 在kobj代表的目录下创建链接,指向target,链接名为name
int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);
// 如果grp->name存在,在kobj代表的子目录下创建以grp->name命名的子目录
// 在kobj代表的目录或新建的子目录下添加属性文件
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
// 在sysfs中创建子目录
struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj);