struct bus_type 结构体
来源:互联网 发布:太阳帆轨迹优化 编辑:程序博客网 时间:2024/06/09 19:45
这个结构体的定义在include/Linux/device.h中。本节先简单介绍结构体中的成员,再对每个成员作详细描述。
- 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 (*resume)(struct device *dev);
- const struct dev_pm_ops *pm;
- struct bus_type_private *p;
- };
1. 成员简单介绍
- 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 (*resume)(struct device *dev);
- 恢复时调用。
- const struct dev_pm_ops *pm;
- 设备电源管理。
- struct bus_type_private *p;
- 私有数据。完全由驱动核心初始化并使用。
2. 成员详细描述
2.0 预备知识
在开始之前,我们先简单介绍一个结构体struct attribute。这个结构体作为属性的基本结构,嵌入在struct bus_attribute等结构体中。如下:
- /* FIXME
- * The *owner field is no longer used.
- * x86 tree has been cleaned up. The owner
- * attribute is still left for other arches.
- */
- struct attribute {
- const char *name;
- struct module *owner;
- mode_t mode;
- #ifdef CONFIG_DEBUG_LOCK_ALLOC
- struct lock_class_key *key;
- struct lock_class_key skey;
- #endif
- };
- // 注:在2.6.37中,owner已经被移除了。
我们只关心name和mode成员。name作为属性文件名称,会出现在sysfs伪系统系统的特定目录下(具体目录和struct attribute所在的驱动相关),而mode是name文件的权限。
作为文件名称,name不能含有”/”,同时最好不含有空格,因为 shell分隔符IFS一般包含空格,空格对shell应用处理等会带来不便和错误。name值必须具有持久属性,例如静态字符数组或者字符串字面值(不能是栈内字符数组)。
mode和用户空间中文件的属性一致,可以针对所有者、同组、其他用户分别设置读、写、可执行权限等,需要注意的是,即使设置可执行权限,属性文件也是不允许执行的,最起码在当前的内核版本下是不允许执行的。
2.1 const char *name;
总线名称,体现在sysfs文件系统的/sys/bus下,不能和别的总线名称有冲突。作为文件名称,它不能含有”/”;同时最好不含有空格,值必须有持久性。例如:
struct bus_type ycbus_type = { .name = “ycbus” };
2.2 struct bus_attribute *bus_attrs;
总线属性。表现为/sys/bus/<name>/文件夹下的文件。这个结构体的具体定义如下:
- struct bus_attribute {
- struct attribute attr;
- ssize_t (*show)(struct bus_type *bus, char *buf);
- ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
- };
其中struct attribute attr的如2.0所述。show和store方法的第一个参数都是struct bus_type指针,用来指示属性所在总线。
show方法实现数据读取。当用户空间读取属性值时,核心调用该方法实现编码,结果存放在形参buf中,注意大小不能超过过PAGE_SIZE。
store方法实现数据保存。当用户控件设置属性值时,核心调用该方法实现解码,使用buf传递的数据解码,count指示buf传递的数据长度。注意buf信息来自用户空间,因此在解码前应当检测数据合法性,如果数据格式或者数值和期望的不符,应该返回一个负的错误码,而不是采取不可预期或者无法恢复的动作。
另外一个需要注意的是,对于store方法,不能返回0,否则会产生死循环。因为如果store返回小于形参count,驱动核心会认为解码未完成,并以本次解码剩余的缓冲区继续调用store。我们假设一个最多一次只能解码4个字符的store函数,见如下代码调用
const char *p=”1234567890”;
attrs->store(bus,p,10);
第一次返回5,驱动核心会接着调用:
attrs->store(bus,p+4,10-4);
attrs->store(bus,p+8,10-8);
…
持续直到count为0。因此一个返回0的store会导致永久循环。
下文的设备属性和驱动属性的store有同样的限制。
2.3 struct device_attribute *dev_attrs;
默认设备属性。对于每个将要注册到该总线上的设备,在设备注册时,默认添加dev_attrs数组指定的属性。这个结构体定义如下:
- struct device_attribute {
- struct attribute attr;
- ssize_t (*show)(struct device *dev, struct device_attribute *attr,
- char *buf);
- ssize_t (*store)(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count);
- };
这个结构体和struct bus_attribute的成员相类似。成员attr保存名称和权限,show和store方法分别在用户空间获取/设置属性值时调用。值得注意的是,此处show和store的函数接口,与struct bus_attribute中的完全不同。
2.4 struct driver_attribute *drv_attrs;
默认驱动属性。对于每个将要注册到该总线上的驱动,在驱动注册时,默认添加drv_attrs数组指定的属性。这个结构体定义如下:
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *driver, const char *buf,
size_t count);
};
现在观看这个结构体,会觉得十分熟悉。如同struct bus_attribute或struct device_attribute,attr设定属性的名称和权限,show和store方法指定用户空间数据获取/设置的编解码算法。需要注意show和store有不同的形参列表。
2.5 int (*match)(struct device *dev, struct device_driver *drv);
匹配函数。
判定设备和驱动是否匹配,是总线体系相关的。驱动核心通过match和probe两个函数来完成匹配。其中match就是此处讨论的函数,而probe在下文提及。
每当有设备添加到总线时,驱动核心遍历总线上的驱动链表(执行流程device_register->device_add->bus_probe_device->device_attach->bus_for_each_drv->match/probe)查找设备驱动;每当有驱动添加到总线时,驱动核心遍历总线上的设备链表(执行流程driver_register->bus_add_driver->driver_attach->bus_for_each_dev->match/probe)查找驱动可操控的设备。当前,match一般只执行总线特定的匹配处理,而在probe中,通过回调设备驱动probe,完成设备特定的匹配、设备初始化等。举例来说,对于PCI总线,match判断驱动支持的ID列表是否包含设备ID,如果包含则匹配,否则失配;probe会再次执行ID匹配判断,并回调驱动提供的probe函数。
match匹配成功则返回1,失配返回0。match函数指针为NULL,驱动核心返回1。
对于一次遍历匹配而言,如果match和probe均成功,则结束匹配过程;如果match成功而probe失配,继续遍历查找匹配;如果遍历结束而没有找到成功的匹配,对于驱动而言表示没有可操控设备,对于设备而言表示没有适当的驱动。
2.6 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
在发送热插拔事件消息到用户空间之前添加环境变量。
在设备注册、移除,或者状态更改时,内核负责发送通知事件到用户空间。通过man udev和man udevd可获取相关帮助。uevent在事件发送到用户空间之前调用,用来给事件添加总线特定的环境变量。
在Linux-2.6.13以后,采用udev机制动态创建设备文件,本文暂时不讨论这个细节。
2.7 int (*probe)(struct device *dev);
探测函数。
如在match中所述,probe执行设备相关的匹配探测、设备初始化、资源分配等。
需要注意,在probe调用时,dev->driver已经被设置为match成功匹配的驱动指针了,因此不再需要struct device_driver指针。
2.8 int (*remove)(struct device *dev);
移除设备。
设备移除时,调用该方法,完成部分清理工作。如删除设备驱动中,设备链表下的该设备。
2.9 void (*shutdown)(struct device *dev);
系统关机。
系统关机时,调用该方法关闭设备。
2.10 int (*suspend)(struct device *dev, pm_message_t state);
设备休眠(挂起)。
设备休眠(挂起)时调用该方法。一般在该方法中设置设备为低耗电状态。
2.11 int (*resume)(struct device *dev);
设备恢复。
设备从休眠中恢复时调用该方法。
2.12 const struct dev_pm_ops *pm;
电源管理。
一些设备有电源状态转换。结构体内部提供很多方法实现这个过程。暂时忽略这个结构体。
2.13 struct bus_type_private *p;
总线私有数据。
驱动核心设置并使用,总线驱动不必关心这个成员,并且一般不要去修改它。
3. 示例代码
我们实现一个示例驱动,以ycbus为基础,添加一个设备ycbus-dev0和一个驱动ycbus-drv0。设置总线属性、默认设备属性和默认驱动属性,各自包含一个只读version属性和一个读写rw-test属性,因为共享驱动数据缓冲,其中一个设置rw-test另一个也更改。如下代码保存为ycbus.c
- /*
- * ycbus: a software bus driver (virtual bus driver)
- *
- * a trivial ycbus driver
- */
- #include <linux/device.h>
- #include <linux/module.h>
- /*
- * attributes helper
- */
- static const char *attrs_rw_test(bool bset, const char *value, size_t l)
- {
- static char buf[64] = "rw-test-default";
- if (bset)
- {
- if (value)
- {
- /* Despite of c-library snprintf, kernel snprintf will appended '/0' automatically */
- if (l > 63) l = 63;
- memcpy(buf, value, l);
- buf[l] = '/0';
- }
- else
- {
- buf[0] = '/0';
- }
- }
- return buf;
- }
- static inline ssize_t attrs_version_show(const char *prefix, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%s: version 1.0.0/n", prefix);
- }
- static inline ssize_t attrs_rw_test_show(const char *prefix, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%s: %s/n", prefix, attrs_rw_test(false, NULL, 0));
- }
- static inline ssize_t attrs_rw_test_store(const char *prefix, const char *buf, size_t count)
- {
- /* it will not be failed */
- attrs_rw_test(true, buf, count);
- return count;
- }
- /*
- * bus attributes methods
- */
- ssize_t bus_attrs_version_show(struct bus_type *bus, char *buf)
- {
- return attrs_version_show("ycbus", buf);
- }
- ssize_t bus_attrs_rw_test_show(struct bus_type *bus, char *buf)
- {
- return attrs_rw_test_show("ycbus", buf);
- }
- ssize_t bus_attrs_rw_test_store(struct bus_type *bus, const char *buf, size_t count)
- {
- return attrs_rw_test_store("ycbus", buf, count);
- }
- /*
- * device attribute methods
- */
- ssize_t dev_attrs_version_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- return attrs_version_show("ycbus-dev0", buf);
- }
- ssize_t dev_attrs_rw_test_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- return attrs_rw_test_show("ycbus-dev0", buf);
- }
- ssize_t dev_attrs_rw_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
- {
- return attrs_rw_test_store("ycbus-dev0", buf, count);
- }
- /*
- * driver attribute
- */
- ssize_t drv_attrs_version_show(struct device_driver *driver, char *buf)
- {
- return attrs_version_show("ycbus-drv0", buf);
- }
- ssize_t drv_attrs_rw_test_show(struct device_driver *drv, char *buf)
- {
- return attrs_rw_test_show("ycbus-drv0", buf);
- }
- ssize_t drv_attrs_rw_test_store(struct device_driver *drv, const char *buf, size_t count)
- {
- return attrs_rw_test_store("ycbus-drv0", buf, count);
- }
- void dev_release(struct device *dev)
- {
- }
- static struct bus_attribute bus_attrs[] = {
- __ATTR(version, S_IRUGO, bus_attrs_version_show, NULL),
- __ATTR(rw-test, (S_IRUGO|S_IWUGO), bus_attrs_rw_test_show, bus_attrs_rw_test_store),
- __ATTR_NULL,
- };
- static struct device_attribute dev_attrs[] = {
- __ATTR(version, S_IRUGO, dev_attrs_version_show, NULL),
- __ATTR(rw-test, (S_IRUGO|S_IWUGO), dev_attrs_rw_test_show, dev_attrs_rw_test_store),
- __ATTR_NULL,
- };
- struct driver_attribute drv_attrs[] = {
- __ATTR(version, S_IRUGO, drv_attrs_version_show, NULL),
- __ATTR(rw-test, (S_IRUGO|S_IWUGO), drv_attrs_rw_test_show, drv_attrs_rw_test_store),
- __ATTR_NULL,
- };
- static struct bus_type ycbus_type = {
- .name = "ycbus",
- .bus_attrs = bus_attrs,
- .dev_attrs = dev_attrs,
- .drv_attrs = drv_attrs,
- };
- static struct device ycbus_dev = {
- .init_name = "ycbus-dev0",
- .bus = &ycbus_type,
- };
- static struct device_driver ycbus_drv = {
- .name = "ycbus-drv0",
- .bus = &ycbus_type,
- };
- static int __init ycbus_driver_init(void)
- {
- int ret;
- printk(KERN_DEBUG "ycbus_driver_init/n");
- ret = bus_register(&ycbus_type);
- if (ret) goto bus_fail;
- ret = device_register(&ycbus_dev);
- if (ret) goto dev_fail;
- ret = driver_register(&ycbus_drv);
- if (ret) goto drv_fail;
- return ret;
- drv_fail:
- device_unregister(&ycbus_dev);
- dev_fail:
- bus_unregister(&ycbus_type);
- bus_fail:
- return ret;
- }
- static void __exit ycbus_driver_exit(void)
- {
- printk(KERN_DEBUG "ycbus_driver_exit/n");
- driver_unregister(&ycbus_drv);
- device_unregister(&ycbus_dev);
- bus_unregister(&ycbus_type);
- }
- MODULE_AUTHOR("yc <cppgp@qq.com>");
- MODULE_DESCRIPTION("yc pseudo-bus driver");
- MODULE_LICENSE("GPL");
- module_init(ycbus_driver_init);
- module_exit(ycbus_driver_exit);
提供Makefile如下。保存为Makefile,注意大写首字母M。
- # A trivial bus driver Makefile. Saved as “Makefile” exactly
- ifneq ($(KERNELRELEASE),)
- obj-m := ycbus.o
- else
- KERNDIR ?= /lib/modules/$(shell uname -r)/build
- PWD := $(shell pwd)
- default:
- $(MAKE) -C ${KERNDIR} M=${PWD} modules
- endif
- clean:
- rm -rf modules.order Module.symvers .tmp_versions ycbus.ko .ycbus.ko.cmd ycbus.mod.c ycbus.mod.o .ycbus.mod.o.cmd ycbus.o .ycbus.o.cmd
注意 $(MAKE)和rm两行前是TAB而不是空格。
编译、加载、测试:
- ~$ make
- ~$ sudo insmod ycbus.ko
- ~$ tree /sys/bus/ycbus/
- /sys/bus/ycbus/
- ├── devices
- │ └── ycbus-dev0 -> ../../../devices/ycbus-dev0
- ├── drivers
- │ └── ycbus-drv0
- │ ├── bind
- │ ├── rw-test
- │ ├── uevent
- │ ├── unbind
- │ ├── version
- │ └── ycbus-dev0 -> ../../../../devices/ycbus-dev0
- ├── drivers_autoprobe
- ├── drivers_probe
- ├── rw-test
- ├── uevent
- └── version
- 5 directories, 10 files
- ~$ tree /sys/devices/ycbus-dev0/
- /sys/devices/ycbus-dev0/
- ├── driver -> ../../bus/ycbus/drivers/ycbus-drv0
- ├── power
- │ ├── control
- │ ├── runtime_active_time
- │ ├── runtime_status
- │ ├── runtime_suspended_time
- │ └── wakeup
- ├── rw-test
- ├── subsystem -> ../../bus/ycbus
- ├── uevent
- └── version
- 3 directories, 8 files
- $ tree /sys/bus/ycbus/drivers/ycbus-drv0/
- /sys/bus/ycbus/drivers/ycbus-drv0/
- ├── bind
- ├── rw-test
- ├── uevent
- ├── unbind
- ├── version
- └── ycbus-dev0 -> ../../../../devices/ycbus-dev0
- 1 directory, 5 files
- ~$ ls /sys/bus/ycbus/ -l
- total 0
- drwxr-xr-x 2 root root 0 2011-04-19 11:39 devices
- drwxr-xr-x 3 root root 0 2011-04-19 11:38 drivers
- -rw-r--r-- 1 root root 4096 2011-04-19 11:39 drivers_autoprobe
- --w------- 1 root root 4096 2011-04-19 11:39 drivers_probe
- -rw-rw-rw- 1 root root 4096 2011-04-19 11:39 rw-test
- --w------- 1 root root 4096 2011-04-19 11:39 uevent
- -r--r--r-- 1 root root 4096 2011-04-19 11:39 version
- ~$ cat /sys/bus/ycbus/rw-test
- ycbus: rw-test-default
- ~$ cat /sys/bus/ycbus/devices/ycbus-dev0/rw-test
- ycbus-dev0: rw-test-default
- ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/rw-test
- ycbus-drv0: rw-test-default
- ~$ echo -n "set ycbus new value" > /sys/bus/ycbus/rw-test
- ~$ cat /sys/bus/ycbus/rw-test
- ycbus: set ycbus new value
- ~$ cat /sys/bus/ycbus/devices/ycbus-dev0/rw-test
- ycbus-dev0: set ycbus new value
- ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/rw-test
- ycbus-drv0: set ycbus new value
- ~$ echo -n "set ycbus-dev0 new value" > /sys/bus/ycbus/devices/ycbus-dev0/rw-test
- ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/rw-test
- ycbus-drv0: set ycbus-dev0 new value
- :~$ cat /sys/bus/ycbus/version
- ycbus: version 1.0.0
- ~$ cat /sys/bus/ycbus/drivers/ycbus-drv0/version
- ycbus-drv0: version 1.0.0
- ~$ cat /proc/kallsyms | grep ycbus
- e0aae280 t ycbus_driver_exit [ycbus]
- e0aae3c0 d ycbus_drv [ycbus]
- e0aae400 d ycbus_dev [ycbus]
- e0aae540 d ycbus_type [ycbus]
- e0aae580 d buf.15065 [ycbus]
- e0aae5c0 d bus_attrs [ycbus]
- e0aae600 d dev_attrs [ycbus]
- e0aae640 d __this_module [ycbus]
- e0aae1b0 t bus_attrs_rw_test_show [ycbus]
- e0aae280 t cleanup_module [ycbus]
- e0aae010 t drv_attrs_rw_test_store [ycbus]
- e0aae0d0 t bus_attrs_rw_test_store [ycbus]
- e0aae000 t dev_release [ycbus]
- e0aae220 t dev_attrs_version_show [ycbus]
- e0aae070 t dev_attrs_rw_test_store [ycbus]
- e0aae380 d drv_attrs [ycbus]
- e0aae1f0 t drv_attrs_version_show [ycbus]
- e0aae250 t bus_attrs_version_show [ycbus]
- e0aae170 t dev_attrs_rw_test_show [ycbus]
- e0aae130 t drv_attrs_rw_test_show [ycbus]
代码很好理解,只是增加了一个虚拟设备和一个驱动到总线上,因为我们并未提供match和probe,而核心的默认处理是认为设备和驱动是匹配的(对于match和probe为空,核心直接返回1),因此它们是匹配的。可以在ycbus-dev0设备下看到驱动,也可以在ycbus-drv0下看到设备。另外,一个需要注意的问题是,注册一个设备必须提供release方法,否则核心会发出抱怨,并打印Call Trace信息。
- struct bus_type 结构体
- Linux总线驱动-02: struct bus_type 结构体
- Linux总线驱动-02: struct bus_type 结构体
- struct bus_type
- struct bus_type
- 介绍struct bus_type非常好的
- 介绍struct bus_type非常好的
- 介绍struct bus_type非常好的
- bus_type
- Linux Kernel设备驱动模型之 struct bus_type
- 结构体(struct) 用法
- 结构体struct module
- 什么结构体 struct
- struct结构体
- struct hostent结构体
- struct hostent结构体
- struct termios结构体
- struct hostent结构体
- Unity3D-模拟物理完全弹性碰撞反弹
- 百度地图个人demo
- poj 1185 炮兵布阵 最详细题解(状压DP经典)
- 使用Flask-Mail发送邮件
- ContentProvider及ContentResolver示例
- struct bus_type 结构体
- python3 install
- Maven学习笔记
- Canvas使用 -- 在canvas上绘制圆角矩形并添加文字
- 八数码问题 【隐式图bfs】
- redis持久化RDB和AOF
- 《新生-7年就是一辈子》
- Redis持久化存储(AOF与RDB两种模式)
- HTML 入门