免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2161 | 回复: 0
打印 上一主题 下一主题

LINUX设备驱动之设备模型三--device&driver&bus [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-12 16:47 |只看该作者 |倒序浏览

Author: Eric Fang  
Date:     2010-01-12
-----------------------------------------------------------------
本站分析linux内核源码,版本号为2.6.32.3
转载请注明出处:
http://ericfang.cublog.cn/
-----------------------------------------------------------------
在清楚了kobject之后,就可以继续分析device、driver、bus了,这三者是设备驱动程序的基本数据结构。


我们可以这样理解,内核用device来表示各种设备,然后用driver来表示它的驱动,而设备有很多种,也属于相同类型或不同类型,而其对应的驱动可能同时也是另外一个设备的驱动,为了管理这些设备和驱动,就引入了总线bus_type,总线上有两个集合(也可以理解为两条链,如上图中的bus),分别用来存放该总线类型的设备和驱动,当添加一个设备时就将设备添加到总线的设备集合(图中操作2),同时可能会到驱动集合去匹配适合它的驱动(图中操作3,在此之前device和driver没有挂钩),如何找到了就会将它们联系起来(图中操作6)。同样注册一个驱动,就会把它挂到相应总线类型的驱动集合里(图中操作1),同时去设备集合中找出它锁驱动的设备(图中操作4,在此之前device和driver没有挂钩),如果找到就把设备链接到它支持的设备链表上(图中操作6)。
下面我们进入到代码中去:
struct 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);
       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;
};
较之先前一些内核版本,bus_type把部分私有字段封装到bus_type_private类型结构里:
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;
};
字段klist_devices和klist_drivers分别表示挂在bus_type上的驱动和设备链表,bus_type的其他字段和函数指针将在分析过程中说明。
首先我们要为设备和驱动注册一个总线类型:
int bus_register(struct bus_type *bus)
{
       int retval;
       struct bus_type_private *priv;

       priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
       if (!priv)
              return -ENOMEM;

       priv->bus = bus;
       bus->p = priv;
分配私有区域的内存空间,并将其关联
       BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
初始化回调函数
       retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
       if (retval)
              goto out;

       priv->subsys.kobj.kset = bus_kset;
       priv->subsys.kobj.ktype = &bus_ktype;
       priv->drivers_autoprobe = 1;

       retval = kset_register(&priv->subsys);
       if (retval)
              goto out;
这里我们看到了subsys用来表示它的文件系统,可以回想上一节kset的注册。
这个bus_kset是系统启动是创建的,系统init进程kernel_init()中调用do_basic_setup(),其中调用driver_init(),其中调用的buses_init(),如下
int __init buses_init(void)
{
       bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
       if (!bus_kset)
              return -ENOMEM;
       return 0;
}
从而知道创建的文件系统目录在/sys/bus下。
static struct kset_uevent_ops bus_uevent_ops = {
       .filter = bus_uevent_filter,
};
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
       struct kobj_type *ktype = get_ktype(kobj);

       if (ktype == &bus_ktype)
              return 1;
       return 0;
}
继续bus_register()中的代码:
       retval = bus_create_file(bus, &bus_attr_uevent);
       if (retval)
              goto bus_uevent_fail;
bus_create_file()如下:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
       int error;
       if (bus_get(bus)) {
              error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
              bus_put(bus);
       } else
              error = -EINVAL;
       return error;
}
用bus_attr_uevent创建了bus->p->subsys.kobj的属性文件,由上面的赋值知道其读写操作在bus_ktype的sysfs_ops,其定义如下:
static struct kobj_type bus_ktype = {
       .sysfs_ops      = &bus_sysfs_ops,
};
static struct sysfs_ops bus_sysfs_ops = {
       .show     = bus_attr_show,
       .store      = bus_attr_store,
};
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
                          char *buf)
{
       struct bus_attribute *bus_attr = to_bus_attr(attr);
       struct bus_type_private *bus_priv = to_bus(kobj);
       ssize_t ret = 0;

       if (bus_attr->show)
              ret = bus_attr->show(bus_priv->bus, buf);
       return ret;
}

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
                           const char *buf, size_t count)
{
       struct bus_attribute *bus_attr = to_bus_attr(attr);
       struct bus_type_private *bus_priv = to_bus(kobj);
       ssize_t ret = 0;

       if (bus_attr->store)
              ret = bus_attr->store(bus_priv->bus, buf, count);
       return ret;
}
由上面的程序可以看出文件的读写操作最终会回到struct bus_attribute &bus_attr_uevent的show和store方法。
bus_attr_uevent是通过宏定义的:
#define __ATTR(_name,_mode,_show,_store) { \
       .attr = {.name = __stringify(_name), .mode = _mode },     \
       .show     = _show,                             \
       .store      = _store,                             \
}

#define BUS_ATTR(_name, _mode, _show, _store)    \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
其show方法为NULL,说明不可读。
static ssize_t bus_uevent_store(struct bus_type *bus,
                            const char *buf, size_t count)
{
       enum kobject_action action;

       if (kobject_action_type(buf, count, &action) == 0)
              kobject_uevent(&bus->p->subsys.kobj, action);
       return count;
}
在用户空间可以控制事件的发生,如echo add > event将产生一个add的事件。
接着继续bus_register()中的代码:
       priv->devices_kset = kset_create_and_add("devices", NULL,
                                           &priv->subsys.kobj);
       if (!priv->devices_kset) {
              retval = -ENOMEM;
              goto bus_devices_fail;
       }

       priv->drivers_kset = kset_create_and_add("drivers", NULL,
                                           &priv->subsys.kobj);
       if (!priv->drivers_kset) {
              retval = -ENOMEM;
              goto bus_drivers_fail;
       }
创建两个kset,其内嵌object的parent都指向priv->subsys.kobj,说明其文件系统在bus所在目录下。由上回分析中知道kset_create_and_add()时其内嵌kobj的ktype指向kset_ktype,而这里没有输入参数的uevent_ops为NULL,则会以priv->subsys.kobj->kset->uevent_ops来产生事件,我们上面分析中知道这个uevent_ops为bus_uevent_ops,其filter会比较kobj的ktype是不是&bus_ktype,而这里是&kset_ktype,所以这里是忽略了事件。
       klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
       klist_init(&priv->klist_drivers, NULL, NULL);
初始化总线上设备和驱动的链表。
       retval = add_probe_files(bus);
       if (retval)
              goto bus_probe_files_fail;
add_probe_files()函数如下:
static int add_probe_files(struct bus_type *bus)
{
       int retval;

       retval = bus_create_file(bus, &bus_attr_drivers_probe);
       if (retval)
              goto out;

       retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
       if (retval)
              bus_remove_file(bus, &bus_attr_drivers_probe);
out:
       return retval;
}
同上面创建bus_attr_uevent属性一样创建bus_attr_drivers_probe和bus_attr_drivers_autoprobe属性文件。粘出属性的代码:
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
              show_drivers_autoprobe, store_drivers_autoprobe);
bus_attr_drivers_autoprobe的show指向NULL,说明其改文件不可写。
static ssize_t store_drivers_probe(struct bus_type *bus,
                               const char *buf, size_t count)
{
       struct device *dev;

       dev = bus_find_device_by_name(bus, NULL, buf);
       if (!dev)
              return -ENODEV;
       if (bus_rescan_devices_helper(dev, NULL) != 0)
              return -EINVAL;
       return count;
}
将用户输(在用户空间)和的设备名称对应的设备与驱动匹配一次。
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
       return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
在用户空间可以打印drivers_autoprobe的值,如cat drivers_autoprobe
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
                                   const char *buf, size_t count)
{
       if (buf[0] == '0')
              bus->p->drivers_autoprobe = 0;
       else
              bus->p->drivers_autoprobe = 1;
       return count;
}
在用户空间可以改变drivers_autoprobe的值,如echo 1 > drivers_autoprobe
继续分析bus_register()中的代码:
       retval = bus_add_attrs(bus);
       if (retval)
              goto bus_attrs_fail;
bus_add_attrs()如下:
static int bus_add_attrs(struct bus_type *bus)
{
       int error = 0;
       int i;

       if (bus->bus_attrs) {
              for (i = 0; attr_name(bus->bus_attrs); i++) {
                     error = bus_create_file(bus, &bus->bus_attrs);
                     if (error)
                            goto err;
              }
       }
done:
       return error;
err:
       while (--i >= 0)
              bus_remove_file(bus, &bus->bus_attrs);
       goto done;
}
如果bus->bus_attrs存在,则同样为其创建属性文件。
       pr_debug("bus: '%s': registered\n", bus->name);
       return 0;

bus_attrs_fail:
       remove_probe_files(bus);
bus_probe_files_fail:
       kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
       kset_unregister(bus->p->devices_kset);
bus_devices_fail:
       bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
       kset_unregister(&bus->p->subsys);
       kfree(bus->p);
out:
       bus->p = NULL;
       return retval;
}  
bus_register()分析完了,总结一下,它注册了一个总线类型,创建对应的文件系统(包括目录和属性),初始化总线上的驱动和设备,这样我们就可以通过内核提供的函数往总线上注册设备和驱动了。

在往总线注册注册设备前要先创建device,我们可以静态的定义device结构变量,然后调用device_register()将其注册,或者通过内核提供的device_create()接口函数创建和注册device。先看看device的数据结构定义:
struct device {
       struct device          *parent;

       struct device_private      *p;

       struct kobject kobj;
       const char             *init_name; /* initial name of the device */
       struct device_type  *type;

       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        *platform_data;      /* Platform specific data, device
                                      core doesn't touch it */
       struct dev_pm_info       power;

#ifdef CONFIG_NUMA
       int           numa_node;    /* NUMA node this device is close to */
#endif
       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;

       dev_t                    devt;       /* dev_t, creates the sysfs "dev" */

       spinlock_t              devres_lock;
       struct list_head       devres_head;

       struct klist_node     knode_class;
       struct class            *class;
       const struct attribute_group **groups; /* optional groups */

       void (*release)(struct device *dev);
};
和总线类型一样,device也把部分私有数据封装到device_private结构中:
struct device_private {
       struct klist klist_children;
       struct klist_node knode_parent;
       struct klist_node knode_driver;
       struct klist_node knode_bus;
       void *driver_data;
       struct device *device;
};
创建device的函数device_create()如下:
struct device *device_create(struct class *class, struct device *parent,
                          dev_t devt, void *drvdata, const char *fmt, ...)
{
       va_list vargs;
       struct device *dev;

       va_start(vargs, fmt);
       dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
       va_end(vargs);
       return dev;
}
调用device_create_vargs():
struct device *device_create_vargs(struct class *class, struct device *parent,
                               dev_t devt, void *drvdata, const char *fmt,
                               va_list args)
{
       struct device *dev = NULL;
       int retval = -ENODEV;

       if (class == NULL || IS_ERR(class))
              goto error;

       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
       if (!dev) {
              retval = -ENOMEM;
              goto error;
       }

       dev->devt = devt;
       dev->class = class;
       dev->parent = parent;
       dev->release = device_create_release;
       dev_set_drvdata(dev, drvdata);

       retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
       if (retval)
              goto error;

       retval = device_register(dev);
       if (retval)
              goto error;

       return dev;

error:
       put_device(dev);
       return ERR_PTR(retval);
}
该函数为创建的device分配内存空间,根据输入参数和一些默认值对其初始化,然后调用device_register将其注册到相应的总线上。
int device_register(struct device *dev)
{
       device_initialize(dev);
       return device_add(dev);
}                        
device_initialize()先对dev初始化:
void device_initialize(struct device *dev)
{
       dev-> kobj.kset = devices_kset;
为其内嵌kobj的kset赋值,类似bus_kset,devices_kset也是系统启动是创建的:
int __init devices_init(void)
{
       devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
       if (!devices_kset)
              return -ENOMEM;
       dev_kobj = kobject_create_and_add("dev", NULL);
       if (!dev_kobj)
              goto dev_kobj_err;
       sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
       if (!sysfs_dev_block_kobj)
              goto block_kobj_err;
       sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
       if (!sysfs_dev_char_kobj)
              goto char_kobj_err;

       return 0;

char_kobj_err:
       kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
       kobject_put(dev_kobj);
dev_kobj_err:
       kset_unregister(devices_kset);
       return -ENOMEM;
}
对应sysfs中的/sys/devices、/sys/block、/sys/block。
接着device_initialize()中的代码:
       kobject_init(&dev-> kobj, &device_ktype);
初始化dev内嵌的kobj
       INIT_LIST_HEAD(&dev->dma_pools);
       init_MUTEX(&dev->sem);
       spin_lock_init(&dev->devres_lock);
       INIT_LIST_HEAD(&dev->devres_head);
初始化一些其它的字段。
       device_init_wakeup(dev, 0);
       device_pm_init(dev);
电源管理的一些初始化。
       set_dev_node(dev, -1);
设置numa。
}   
初始化dev后调用device_add() 往相应总线上添加设备。分段分析这个函数:
int device_add(struct device *dev)
{
       struct device *parent = NULL;
       struct class_interface *class_intf;
       int error = -EINVAL;

       dev = get_device(dev);
       if (!dev)
              goto done;

       if (!dev->p) {
              error = device_private_init(dev);
              if (error)
                     goto done;
       }
增加dev的计数,初始化其私有结构,实际上面初始化调用的dev_set_drvdata(dev, drvdata)里边已经包含了device_private_init(),但如果是直接调用device_register()而不是device_create()时,必须在这里再初始化一次,显然第一个调用device_private_init()是不必要id。所以在这里才分析device_private_init(),函数如下:
int device_private_init(struct device *dev)
{
       dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
       if (!dev->p)
              return -ENOMEM;
为其分配内存。
       dev->p->device = dev;
关联到dev。
       klist_init(&dev->p-> klist_children, klist_children_get,
                 klist_children_put);
初始化klist_children
       return 0;
}
接着device_add()函数。
       /*
        * for statically allocated devices, which should all be converted
        * some day, we need to initialize the name. We prevent reading back
        * the name, and force the use of dev_name()
        */
       if (dev->init_name) {
              dev_set_name(dev, "%s", dev->init_name);
              dev->init_name = NULL;
       }

       if (!dev_name(dev))
              goto name_error;

       pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
用dev->init_name设置dev->kobj.name,而后dev->init_name指向NULL,如果dev->kobj.name则跳到name_error。
       parent = get_device(dev->parent);
增加dev->parent-> kobj的计数。
       setup_parent(dev, parent);
看下这个函数:
static void setup_parent(struct device *dev, struct device *parent)
{
       struct kobject *kobj;
       kobj = get_device_parent(dev, parent);
       if (kobj)
              dev->kobj.parent = kobj;
}
static struct kobject *get_device_parent(struct device *dev,
                                    struct device *parent)
{
       int retval;

       if (dev->class) {
              struct kobject *kobj = NULL;
              struct kobject *parent_kobj;
              struct kobject *k;

              /*
               * If we have no parent, we live in "virtual".
               * Class-devices with a non class-device as parent, live
               * in a "glue" directory to prevent namespace collisions.
               */
              if (parent == NULL)
                     parent_kobj = virtual_device_parent(dev);
              else if (parent->class)
                     return &parent->kobj;
              else
                     parent_kobj = &parent->kobj;

              /* find our class-directory at the parent and reference it */
              spin_lock(&dev->class->p->class_dirs.list_lock);
              list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
                     if (k->parent == parent_kobj) {
                            kobj = kobject_get(k);
                            break;
                     }
              spin_unlock(&dev->class->p->class_dirs.list_lock);
              if (kobj)
                     return kobj;

              /* or create a new class-directory at the parent device */
              k = kobject_create();
              if (!k)
                     return NULL;
              k->kset = &dev->class->p->class_dirs;
              retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
              if (retval
                     kobject_put(k);
                     return NULL;
              }
              /* do not emit an uevent for this simple "glue" directory */
              return k;
       }

       if (parent)
              return &parent->kobj;
       return NULL;
}
dev-> kobj.parent的设置如下:如果dev->parent,则将dev->parent->kobj赋给它,否则如果device_create() 的class参数不为为NULL时,则通过virtual_device_parent(dev)获得其parent,否则指向NULL,在调用kobject_add()时使其kset内嵌的object。


待续。。。
   








本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/92745/showart_2146768.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP