一、什么是sysfs?
Sysfs 是Linux 2.6所提供的一种虚拟文件系统。这个文件系统不仅可以把设备(devices)和驱动程序(drivers)的信息从内核输出到用户空间,也可以用来对设备和驱动程序做设置。
简单的说,sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。
sysfs 给应用程序提供了统一访问设备的接口,但可以看到, sysfs 仅仅是提供了一个可以统一访问设备的框架,但究竟是否支持 sysfs 还需要各设备驱动程序的编程支持;在 2.6 内核 诞 生 5年以来的发展中,很多子系统、设备驱动程序逐渐转向了 sysfs 作为与用户空间友好的接口,但仍然也存在大量的代码还在使用旧的 proc 或虚拟字符设备的 ioctl 方式;如果仅从最终用户的角度来说, sysfs 与 proc 都是在提供相同或类似的功能,对于旧的 proc 代码,没有绝对的必要去做 proc 至 sysfs 的升级;
二、注册流程
/sys/bus:该目录包含linux下的总线设备,每个子目录下主要包含两个目录:device和driver.
Linux 设备总线驱动模型
简单来说,bus 负责维护 注册进来的devcie 与 driver ,每注册进来一个device 或者 driver 都会调用 Bus->match 函数 将device 与 driver 进行配对,并将它们加入链表,如果配对成功,调用Bus->probe或者driver->probe函数, 调用 kobject_uevent 函数设置环境变量,mdev进行创建设备节点等操作。后面,我们从 Bus driver 到 device三个部分进行详细的分析。
这张图也可以直观看出,系统启动到再到驱动的注册,也很直观
三、bus_register
kzalloc ----> subsys. private空间
kobjetset name ----> 设置bus名到kobjet
kset register ----> 在/5y/bus下创建kobjet目录及ttribute文件
bus_ create. file ----> 创建ueven文件
kset reate. and add ----> 创建5y/s/ox/device8录
kset _create and add ----> 创建5/s/0o/driver目录
add probe files ----> 创建drivers. probe及drivers _autoprobe文件
bus. add groups ----> 创建bus->bus. group属性文件
那么bus_register 函数处理的工作有哪些呢,博主在网上找了一下解释,写的挺好,如下:
1、将 Bus 与 priv 相互建立联系
2、BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
3、设置 bus->priv->subsys(kset).kobj 的名字为 bus->name
4、设置 bus->priv->subsys(kset).kobj.kset 指向 bus_kset
5、设置 bus->priv->subsys(kset).kobj.ktype 为 bus_ktype ,提供 show store 函数
6、设置 bus->priv->drivers_autoprobe = 1;
7、注册 bus->priv->subsys(kset) 对应于图中④与⑥的关系
由于4,且没有指定bus->priv->subsys(kset).kobj.Parent,会将 bus_kest.kobj 设置为 bus->priv->subsys(kset).kobj.Parent 因此,会将bus->priv->subsys(kset).kobj.entry 加入 bus_kest 链表,且会在/sys/bus目录下创建相应的总线目录/sys/bus/ ( b u s − > n a m e ) ,例如 / s y s / b u s / p l a t f o r m 8 、创建 b u s a t t r u e v e n t − > a t t r 属性文件 9 、创建并注册 d e v i c e s k s e t , d e v i c e s k s e t . k o b j . p a r e n t = b u s − > p r i v − > s u b s y s . k o b j , 名字为 d e v i c e ,因此会创建 / s y s / b u s / (bus->name),例如 /sys/bus/platform 8、创建 bus_attr_uevent->attr 属性文件 9、创建并注册 devices_kset ,devices_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 device ,因此会创建 /sys/bus/ (bus−>name),例如/sys/bus/platform8、创建bus
a
ttr
u
event−>attr属性文件9、创建并注册devices
k
set,devices
k
set.kobj.parent=bus−>priv−>subsys.kobj,名字为device,因此会创建/sys/bus/(bus->name)/devices
10、创建并注册 drivers_kset ,drivers_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 drivers ,因此会创建 /sys/bus/$(bus->name)/drivers
11、初始化 bus->priv->klist_devices 链表
12、初始化 bus->priv->klist_drivers 链表
13、创建 bus->bus_attrs 属性文件
原码如下
int bus_register(struct bus_type *bus) { int retval; struct bus_type_private *priv; priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); /* 1. bus 与 prv 相互建立联系 */ // 私有数据 .bus -> bus 本身 priv->bus = bus; // bus->p 指向 priv bus->p = priv; // 内核通知链 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); /* 设置 bus->prv->subsys->kobj */ // 设置 priv->subsys.kobj.name = bus->name 对应于/sys/ 目录下的目录名 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); // 所有的 priv->subsys.kobj.kset 指向 bus_kse 对应于图中④与六的关系 priv->subsys.kobj.kset = bus_kset; // 所有的priv->subsys.kobj.ktype 等于 bus_ktype priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; /* 注册 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */ // 注册 priv->subsys ,由于 priv->subsys.kobj.kset = bus_kset,所以会在 /sys/bus/目录下创建 目录 如/sys/bus/plateform retval = kset_register(&priv->subsys); // sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr); retval = bus_create_file(bus, &bus_attr_uevent); // 由于 priv->subsys.kobj.kset = bus_kset ,因此会创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/devices priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); // 同理 创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/drivers priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); // 初始化 klist_devices 并设置get put 函数 初始化 klist_drivers 不知为何没有get put ? klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); retval = add_probe_files(bus); // static inline int add_probe_files(struct bus_type *bus) { return 0; } // 添加 bus->attrs 属性文件 retval = bus_add_attrs(bus); return 0; }
四、注册以致用(probe)
通过上文的注册
通过这张图可以看出,驱动和设备需要进行match进行匹配,如果没有成功匹配到,则适用所有设备