Linux驱动之设备模型(4)

来源:互联网 发布:即时通讯软件 开源 编辑:程序博客网 时间:2024/05/24 03:24

5.总线

5.1  overview

前面我们介绍了设备模型的底层部分,下面我们来看下设备模型的高层部分,总线、设备和驱动

 

5.2 总线

l  总线,是处理器与一个或者多个设备之间的通道。在Linux设备模型中,用bus_type结构来表示

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

 

         struct subsys_private *p;     /* 私有数据 */

};

Match函数  注册驱动或设备时被用来完成匹配

Uevent函数 用于热插拔,添加环境变量

Probe函数  匹配成功时调用,完成相应工作

P          私有数据,包含内嵌的kset,设备kset和链表,驱动kset和链表等

 

5.3  总线属性

l  总线属性用bus_attribute结构来表示

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

};

BUS_ATTR(_name,_mode, _show, _store)

 

l  属性操作

n  创建属性

int bus_create_file(structbus_type *, struct bus_attribute *);

n  删除属性

voidbus_remove_file(struct bus_type *, struct bus_attribute *);

 

5.4  总线基本操作

l  总线注册和注销函数

n  int bus_register(struct bus_type *bus);

n  void bus_unregister(struct bus_type *bus);

 

l  遍历总线

n  遍历总线上每个驱动,并执行fn函数

intbus_for_each_drv(struct bus_type *bus, struct device_driver *start,

                        void *data, int (*fn)(struct device_driver*, void *));

n  遍历总线上每个设备,并执行fn函数

intbus_for_each_dev(struct bus_type *bus, struct device *start, void *data,

int (*fn)(struct device *dev, void *data));

 

l  总线注册分析

intbus_register(struct bus_type *bus)

{

         retval =kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

 

         priv->subsys.kobj.kset = bus_kset;

         priv->subsys.kobj.ktype =&bus_ktype;

         priv->drivers_autoprobe = 1;

 

         /* 注册bus kset,在bus/下产生bus->name目录 */

         retval = kset_register(&priv->subsys);

 

         retval = bus_create_file(bus,&bus_attr_uevent);

 

         /* 产生device目录 */

         priv->devices_kset =kset_create_and_add("devices", NULL,

                                                         &priv->subsys.kobj);

 

         /* 产生driver目录 */

         priv->drivers_kset =kset_create_and_add("drivers", NULL,

                                                         &priv->subsys.kobj);

 

         /* 初始化设备链和驱动链 */

         klist_init(&priv->klist_devices,klist_devices_get, klist_devices_put);

         klist_init(&priv->klist_drivers,NULL, NULL);

 

         retval = add_probe_files(bus);

 

         retval = bus_add_attrs(bus);

}

Klist_drivers和klist_devices这两条链非常重要,用来遍历drivers_kset和devices_kset,drivers_kset和devices_kset分别是总线上所有driver和device的集合。

 

5.5  实例分析

创建一条scbus总线,并添加版本属性。实际中我们并不需要自己创建总线,此试验仅用来学习

/*

* for learn bus

*/

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/device.h>

 

static char *Version = "revision 1.0,scbus";

 

/* 匹配函数,通过设备名和驱动名来匹配 */

static int scbus_match(struct device *dev,struct device_driver *driver)

{

         printk("\n%s,%s\n", dev_name(dev), driver->name);

         return!strncmp(dev_name(dev), driver->name, strlen(driver->name));

}

 

static void scbus_release(struct device*dev)

{

         printk("scbusrelease\n");

}

 

struct bus_type scbus_type = {

         .name       = "scbus",

         .match   = scbus_match,

};

EXPORT_SYMBOL_GPL(scbus_type);

 

struct device scbus = {

         .init_name        = "scbus0",

         .release   = scbus_release,

};

EXPORT_SYMBOL_GPL(scbus);

 

/*

* export bus attribute

*/

static ssize_t show_bus_version(structbus_type *bus, char *buf)

{

         returnsnprintf(buf, PAGE_SIZE, "%s\n", Version);

}

static BUS_ATTR(version, S_IRUGO,show_bus_version, NULL);

 

static int __init scbus_init(void)

{

         intret;

 

         ret= bus_register(&scbus_type);

         if(ret)

                   returnret;

 

         ret= bus_create_file(&scbus_type, &bus_attr_version);

         if(ret)

                   gotocreate_error;

 

         ret= device_register(&scbus);

         if(ret)

                   gotodevice_error;

 

         printk("Createa scbus\n");

         return0;

 

device_error:

         bus_remove_file(&scbus_type,&bus_attr_version);

create_error:

         bus_unregister(&scbus_type);

         returnret;

}

 

static void __exit scbus_exit(void)

{

         device_unregister(&scbus);

         bus_remove_file(&scbus_type,&bus_attr_version);

         bus_unregister(&scbus_type);

         printk("Removea scbus\n");

}

 

module_init(scbus_init);

module_exit(scbus_exit);

 

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");

 

试验结果:

原创粉丝点击