struct bus_type使用方法

本文测试系统为:Ubuntu 10.10 x86_64 2.6.35-24-generic

 我们还看到了一个和总线驱动相关的结构体bus_type。这个结构体的定义在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, structdevice_driver *drv);   

    int (*uevent)(struct device *dev, structkobj_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);   

    conststruct dev_pm_ops *pm;   

    struct bus_type_private *p;   

}; 

 

 

1. 成员简单介绍

const char*name;   

  总线名称。   

structbus_attribute *bus_attrs;   

  总线属性。   

structdevice_attribute    *dev_attrs;   

  该总线上所有设备的默认属性。   

structdriver_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;   

  设备电源管理。   

structbus_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 otherarches. 

 */ 

struct attribute{  

    const char      *name;  

    struct module       *owner;  

    mode_t          mode;  

#ifdefCONFIG_DEBUG_LOCK_ALLOC  

    struct lock_class_key   *key;  

    struct lock_class_key   skey;  

#endif  

};  

// 注:在2.6.37中,owner已经被移除了。 

 

 

我们只关心namemode成员。name作为属性文件名称,会出现在sysfs伪系统系统的特定目录下(具体目录和structattribute所在的驱动相关),而modename文件的权限。

作为文件名称,name不能含有”/”,同时最好不含有空格,因为 shell分隔符IFS一般包含空格,空格对shell应用处理等会带来不便和错误。name值必须具有持久属性,例如静态字符数组或者字符串字面值(不能是栈内字符数组)。

mode和用户空间中文件的属性一致,可以针对所有者、同组、其他用户分别设置读、写、可执行权限等,需要注意的是,即使设置可执行权限,属性文件也是不允许执行的,最起码在当前的内核版本下是不允许执行的。

 

2.1 const char*name;

总线名称,体现在sysfs文件系统的/sys/bus下,不能和别的总线名称有冲突。作为文件名称,它不能含有”/”;同时最好不含有空格,值必须有持久性。例如:

struct bus_typeycbus_type = { .name = “ycbus” };

 

2.2 structbus_attribute *bus_attrs;

总线属性。表现为/sys/bus/<name>/文件夹下的文件。这个结构体的具体定义如下:

structbus_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所述。showstore方法的第一个参数都是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);

 

持续直到count0。因此一个返回0store会导致永久循环。

下文的设备属性和驱动属性的store有同样的限制。

 

 

2.3 structdevice_attribute *dev_attrs;

默认设备属性。对于每个将要注册到该总线上的设备,在设备注册时,默认添加dev_attrs数组指定的属性。这个结构体定义如下:

structdevice_attribute {  

    struct attribute    attr;  

    ssize_t (*show)(struct device *dev, structdevice_attribute *attr,  

            char *buf);  

    ssize_t (*store)(struct device *dev, structdevice_attribute *attr,  

             const char *buf, size_tcount);  

}; 

 

这个结构体和struct bus_attribute的成员相类似。成员attr保存名称和权限,showstore方法分别在用户空间获取/设置属性值时调用。值得注意的是,此处showstore的函数接口,与struct bus_attribute中的完全不同。

 

 

2.4 structdriver_attribute *drv_attrs;

默认驱动属性。对于每个将要注册到该总线上的驱动,在驱动注册时,默认添加drv_attrs数组指定的属性。这个结构体定义如下:

structdriver_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_attributestruct device_attributeattr设定属性的名称和权限,showstore方法指定用户空间数据获取/设置的编解码算法。需要注意showstore有不同的形参列表。

 

2.5 int(*match)(struct device *dev, struct device_driver *drv);

匹配函数。

判定设备和驱动是否匹配,是总线体系相关的。驱动核心通过matchprobe两个函数来完成匹配。其中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,失配返回0match函数指针为NULL,驱动核心返回1

对于一次遍历匹配而言,如果matchprobe均成功,则结束匹配过程;如果match成功而probe失配,继续遍历查找匹配;如果遍历结束而没有找到成功的匹配,对于驱动而言表示没有可操控设备,对于设备而言表示没有适当的驱动。

 

2.6 int(*uevent)(struct device *dev, struct kobj_uevent_env *env);

在发送热插拔事件消息到用户空间之前添加环境变量。

在设备注册、移除,或者状态更改时,内核负责发送通知事件到用户空间。通过man udevman 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 conststruct dev_pm_ops *pm;

电源管理。

一些设备有电源状态转换。结构体内部提供很多方法实现这个过程。暂时忽略这个结构体。

 

2.13 structbus_type_private *p;

总线私有数据。

驱动核心设置并使用,总线驱动不必关心这个成员,并且一般不要去修改它。

3. 示例代码

 

我们实现一个示例驱动,以ycbus为基础,添加一个设备ycbus-dev0和一个驱动ycbus-drv0。设置总线属性、默认设备属性和默认驱动属性,各自包含一个只读version属性和一个读写rw-test属性,因为共享驱动数据缓冲,其中一个设置rw-test另一个也更改。如下代码保存为ycbus.c

/* 

 * ycbus: a software bus driver (virtual busdriver) 

 * 

 * a trivial ycbus driver 

 */ 

#include<linux/device.h>  

#include<linux/module.h>  

/* 

 * attributes helper 

 */ 

static constchar *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, kernelsnprintf will appended '\0' automatically */ 

      if (l > 63) l = 63;  

      memcpy(buf, value, l);  

      buf[l] = '\0';  

    }  

    else 

    {  

      buf[0] = '\0';  

    }  

  }  

  return buf;  

}  

static inlinessize_t attrs_version_show(const char *prefix, char *buf)  

{  

  return snprintf(buf, PAGE_SIZE, "%s:version 1.0.0\n", prefix);  

}  

static inlinessize_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 inlinessize_t attrs_rw_test_store(const char *prefix, const char *buf, size_tcount)  

{  

  /* it will not be failed */ 

  attrs_rw_test(true, buf, count);  

  return count;  

}  

/* 

 * bus attributes methods 

 */ 

ssize_tbus_attrs_version_show(struct bus_type *bus, char *buf)  

{  

  return attrs_version_show("ycbus",buf);  

}  

ssize_tbus_attrs_rw_test_show(struct bus_type *bus, char *buf)  

{  

  return attrs_rw_test_show("ycbus",buf);  

}  

ssize_t bus_attrs_rw_test_store(structbus_type *bus, const char *buf, size_t count)  

{  

  return attrs_rw_test_store("ycbus",buf, count);  

}  

/* 

 * device attribute methods 

 */ 

ssize_tdev_attrs_version_show(struct device *dev, struct device_attribute *attr, char*buf)  

{  

  returnattrs_version_show("ycbus-dev0", buf);  

}  

ssize_tdev_attrs_rw_test_show(struct device *dev, struct device_attribute *attr, char*buf)  

{  

  returnattrs_rw_test_show("ycbus-dev0", buf);  

}  

ssize_tdev_attrs_rw_test_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)  

{  

  returnattrs_rw_test_store("ycbus-dev0", buf, count);  

}  

/* 

 * driver attribute 

 */ 

ssize_tdrv_attrs_version_show(struct device_driver *driver, char *buf)  

{  

  returnattrs_version_show("ycbus-drv0", buf);  

}  

ssize_tdrv_attrs_rw_test_show(struct device_driver *drv, char *buf)  

{  

  returnattrs_rw_test_show("ycbus-drv0", buf);  

}  

ssize_tdrv_attrs_rw_test_store(struct device_driver *drv, const char *buf, size_tcount)  

{  

  returnattrs_rw_test_store("ycbus-drv0", buf, count);  

}  

voiddev_release(struct device *dev)  

{  

}  

static structbus_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 structdevice_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,  

};  

structdriver_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 structbus_type ycbus_type = {  

  .name     = "ycbus",  

  .bus_attrs = bus_attrs,  

  .dev_attrs = dev_attrs,  

  .drv_attrs = drv_attrs,  

};  

static structdevice ycbus_dev = {  

  .init_name = "ycbus-dev0",  

  .bus      = &ycbus_type,  

};   

static structdevice_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("ycpseudo-bus driver");  

MODULE_LICENSE("GPL");  

module_init(ycbus_driver_init);  

module_exit(ycbus_driver_exit); 

 

 

提供Makefile如下。保存为Makefile,注意大写首字母M

# A trivial busdriver 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.cmdycbus.o .ycbus.o.cmd 

 

 

注意 $(MAKE)rm两行前是TAB而不是空格。

编译、加载、测试:

~$ make  

~$ sudo insmodycbus.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, 8files  

$ 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, 5files  

~$ ls/sys/bus/ycbus/ -l  

total 0  

drwxr-xr-x 2root root    0 2011-04-19 11:39devices  

drwxr-xr-x 3root root    0 2011-04-19 11:38drivers  

-rw-r--r-- 1root root 4096 2011-04-19 11:39 drivers_autoprobe  

--w------- 1root root 4096 2011-04-19 11:39 drivers_probe  

-rw-rw-rw- 1root root 4096 2011-04-19 11:39 rw-test  

--w------- 1 rootroot 4096 2011-04-19 11:39 uevent  

-r--r--r-- 1root 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 ycbusnew value  

~$ cat/sys/bus/ycbus/devices/ycbus-dev0/rw-test   

ycbus-dev0: setycbus new value  

~$ cat/sys/bus/ycbus/drivers/ycbus-drv0/rw-test   

ycbus-drv0: setycbus 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: setycbus-dev0 new value  

:~$ cat/sys/bus/ycbus/version   

ycbus: version1.0.0  

~$ cat/sys/bus/ycbus/drivers/ycbus-drv0/version   

ycbus-drv0:version 1.0.0  

~$ cat/proc/kallsyms | grep ycbus  

e0aae280 tycbus_driver_exit    [ycbus]  

e0aae3c0 dycbus_drv    [ycbus]  

e0aae400 dycbus_dev    [ycbus]  

e0aae540 dycbus_type   [ycbus]  

e0aae580 dbuf.15065    [ycbus]  

e0aae5c0 dbus_attrs    [ycbus]  

e0aae600 ddev_attrs    [ycbus]  

e0aae640 d__this_module        [ycbus]  

e0aae1b0 tbus_attrs_rw_test_show       [ycbus]  

e0aae280 tcleanup_module       [ycbus]  

e0aae010 tdrv_attrs_rw_test_store      [ycbus]  

e0aae0d0 tbus_attrs_rw_test_store      [ycbus]  

e0aae000 tdev_release  [ycbus]  

e0aae220 tdev_attrs_version_show       [ycbus]  

e0aae070 tdev_attrs_rw_test_store      [ycbus]  

e0aae380 ddrv_attrs    [ycbus]  

e0aae1f0 tdrv_attrs_version_show       [ycbus]  

e0aae250 tbus_attrs_version_show       [ycbus]  

e0aae170 tdev_attrs_rw_test_show       [ycbus]  

e0aae130 tdrv_attrs_rw_test_show       [ycbus] 

 

 

代码很好理解,只是增加了一个虚拟设备和一个驱动到总线上,因为我们并未提供matchprobe,而核心的默认处理是认为设备和驱动是匹配的(对于matchprobe为空,核心直接返回1),因此它们是匹配的。可以在ycbus-dev0设备下看到驱动,也可以在ycbus-drv0下看到设备。另外,一个需要注意的问题是,注册一个设备必须提供release方法,否则核心会发出抱怨,并打印Call Trace信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值