嵌入式Linux中文站

Linux设备驱动模型-Device


前言

Linux将所有的设备统一抽象为struct device结构, 同时将所有的驱动统一抽象为struct device_driver结构。这样设计之后就方便驱动开发工程师编写驱动,只需要将具体的设备包含struct device结构,具体的驱动包含struct device_driver结构。最终会调用device_register和driver_register将驱动和设备注册到系统,表现出来就是在sys目录的device和driver目录下。本小节先分析device结构,以及相关API,以及如何注册到系统中,以及提供给上层的sys接口。
 

数据结构

Linux将所有的设备统一抽象为struct device结构。定义在<linux/device.h>
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. struct device {  
  2.     struct device       *parent;  
  3.   
  4.     struct device_private   *p;  
  5.   
  6.     struct kobject kobj;  
  7.     const char      *init_name; /* initial name of the device */  
  8.     const struct device_type *type;  
  9.   
  10.     struct mutex        mutex;  /* mutex to synchronize calls to 
  11.                      * its driver. 
  12.                      */  
  13.   
  14.     struct bus_type *bus;       /* type of bus device is on */  
  15.     struct device_driver *driver;   /* which driver has allocated this 
  16.                        device */  
  17.     void        *platform_data; /* Platform specific data, device 
  18.                        core doesn't touch it */  
  19.     void        *driver_data;   /* Driver data, set and get with 
  20.                        dev_set/get_drvdata */  
  21.     struct dev_pm_info  power;  
  22.     struct dev_pm_domain    *pm_domain;  
  23.   
  24. #ifdef CONFIG_PINCTRL  
  25.     struct dev_pin_info *pins;  
  26. #endif  
  27.   
  28. #ifdef CONFIG_NUMA  
  29.     int     numa_node;  /* NUMA node this device is close to */  
  30. #endif  
  31.     u64     *dma_mask;  /* dma mask (if dma'able device) */  
  32.     u64     coherent_dma_mask;/* Like dma_mask, but for 
  33.                          alloc_coherent mappings as 
  34.                          not all hardware supports 
  35.                          64 bit addresses for consistent 
  36.                          allocations such descriptors. */  
  37.     unsigned long   dma_pfn_offset;  
  38.   
  39.     struct device_dma_parameters *dma_parms;  
  40.   
  41.     struct list_head    dma_pools;  /* dma pools (if dma'ble) */  
  42.   
  43.     struct dma_coherent_mem *dma_mem; /* internal for coherent mem 
  44.                          override */  
  45. #ifdef CONFIG_DMA_CMA  
  46.     struct cma *cma_area;       /* contiguous memory area for dma 
  47.                        allocations */  
  48. #endif  
  49.     /* arch specific additions */  
  50.     struct dev_archdata archdata;  
  51.   
  52.     struct device_node  *of_node; /* associated device tree node */  
  53.     struct acpi_dev_node    acpi_node; /* associated ACPI device node */  
  54.   
  55.     dev_t           devt;   /* dev_t, creates the sysfs "dev" */  
  56.     u32         id; /* device instance */  
  57.   
  58.     spinlock_t      devres_lock;  
  59.     struct list_head    devres_head;  
  60.   
  61.     struct klist_node   knode_class;  
  62.     struct class        *class;  
  63.     const struct attribute_group **groups;  /* optional groups */  
  64.   
  65.     void    (*release)(struct device *dev);  
  66.     struct iommu_group  *iommu_group;  
  67.   
  68.     bool            offline_disabled:1;  
  69.     bool            offline:1;  
  70. };  
device结构体比较复杂,同时其中还包含了dma, numa,  pintrl, pm相关的知识,本小节不做过多谈论。不过该结构体的注释是相当的详细,可以参考。
 
parent:       代表设备的parent节点,通常parent是bus或者controller。 如果此parent为NULL,则此设备就是顶层设备。
p:                代表device的私有数据的指针,指针类型为device_private。
kobj:            kobject结构,用于层级关系。
init_name:   设备对象的名称,出现在sys目录下。
type:            指向device_type结构,代表了设备的特殊的信息。
mutex:          同步操作。
bus:              设备所属的总线。
driver:          设备所对应的驱动。
platfrorm_data:  设备所对应的平台数据。
driver_data:     保存设备所对于驱动的数据,一般使用get/set函数。
power/pm_domain:  电源管理相关的内容。
of_node:        该设备对应的设备树结构。
devt:               设备号,由主设备号和次设备号组成。
id:                   设备索引号。
devres_head: 设备资源管理的链表。用于将设备使用的资源用链表管理。
class:             设备所属的class。
group:            设备默认的属性。
      

设备相关函数

  • devices_init
此函数主要是初始化devices_kset,以及初始化所有的dev_kset
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int __init devices_init(void)  
  2. {  
  3.     devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);     
  4.     if (!devices_kset)  
  5.         return -ENOMEM;  
  6.     dev_kobj = kobject_create_and_add("dev", NULL);  
  7.     if (!dev_kobj)  
  8.         goto dev_kobj_err;  
  9.     sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);  
  10.     if (!sysfs_dev_block_kobj)  
  11.         goto block_kobj_err;  
  12.     sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);  
  13.     if (!sysfs_dev_char_kobj)  
  14.         goto char_kobj_err;  
  15.   
  16.     return 0;  
  17.   
  18.  char_kobj_err:  
  19.     kobject_put(sysfs_dev_block_kobj);  
  20.  block_kobj_err:  
  21.     kobject_put(dev_kobj);  
  22.  dev_kobj_err:  
  23.     kset_unregister(devices_kset);  
  24.     return -ENOMEM;  
  25. }  
内核中每一个设备都是struct device结构,同时内核将所有的设备使用devices_kset管理。同时为了统一方便管理又将设备分为block和char设备,生成的内核对象分别为sysfs_dev_block_kobj和sysfs_dev_char_kobj。当然这两个内核模块都是在dev内核模块之下的。
这个函数的操作实现最后表现到sys文件目录下,分别为/sys/devices,  /sys/dev,  /sys/dev/char, /sys/dev/block。
  • device_initialize(用于初始化一个device)
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. void device_initialize(struct device *dev)  
  2. {  
  3.     dev->kobj.kset = devices_kset;  
  4.     kobject_init(&dev->kobj, &device_ktype);  
  5.     INIT_LIST_HEAD(&dev->dma_pools);  
  6.     mutex_init(&dev->mutex);  
  7.     lockdep_set_novalidate_class(&dev->mutex);  
  8.     spin_lock_init(&dev->devres_lock);  
  9.     INIT_LIST_HEAD(&dev->devres_head);  
  10.     device_pm_init(dev);  
  11.     set_dev_node(dev, -1);  
  12. }  
主要是设置设备所属的kset,设置设备所属kset的ktype,初始化设备资源管理的链表,以及设备电源管理初始化。
  • device_add(添加一个设备到系统中)
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int device_add(struct device *dev)  
  2. {  
  3.     struct device *parent = NULL;  
  4.     struct kobject *kobj;  
  5.     struct class_interface *class_intf;  
  6.     int error = -EINVAL;  
  7.   
  8.     dev = get_device(dev);                                  //增加此设备的引用计数  
  9.     if (!dev)  
  10.         goto done;  
  11.   
  12.     if (!dev->p) {  
  13.         error = device_private_init(dev);            ---------------A  
  14.         if (error)  
  15.             goto done;  
  16.     }  
  17.   
  18.     /* 
  19.      * for statically allocated devices, which should all be converted 
  20.      * some day, we need to initialize the name. We prevent reading back 
  21.      * the name, and force the use of dev_name() 
  22.      */  
  23.     if (dev->init_name) {  
  24.         dev_set_name(dev, "%s", dev->init_name);  
  25.         dev->init_name = NULL;  
  26.     }  
  27.   
  28.     /* subsystems can specify simple device enumeration */  
  29.     if (!dev_name(dev) && dev->bus && dev->bus->dev_name)  
  30.         dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);  
  31.   
  32.     if (!dev_name(dev)) {                                                  //设置device的name域,如果设置失败退出。  
  33.         error = -EINVAL;                                                     
  34.         goto name_error;  
  35.     }                                                
  36.   
  37.     pr_debug("device: '%s': %s\n", dev_name(dev), __func__);  
  38.   
  39.     parent = get_device(dev->parent);                                      //增加设备parent的引用计数  
  40.     kobj = get_device_parent(dev, parent);                                 //设置设备parent的内核对象,建立设备在sys下的层次结构。  
  41.     if (kobj)  
  42.         dev->kobj.parent = kobj;  
  43.   
  44.     /* use parent numa_node */  
  45.     if (parent)  
  46.         set_dev_node(dev, dev_to_node(parent));  
  47.   
  48.     /* first, register with generic layer. */  
  49.     /* we require the name to be set before, and pass NULL */  
  50.     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);             //调用kobject_add将设备添加到sys中  
  51.     if (error)  
  52.         goto Error;  
  53.   
  54.     /* notify platform of device entry */  
  55.     if (platform_notify)                                                   
  56.         platform_notify(dev);   
  57.   
  58.     error = device_create_file(dev, &dev_attr_uevent);                 //创建设备的uevent属性  
  59.     if (error)  
  60.         goto attrError;  
  61.   
  62.     if (MAJOR(dev->devt)) {  
  63.         error = device_create_file(dev, &dev_attr_dev);             //如果主设备号存在,创建dev属性  
  64.         if (error)  
  65.             goto ueventattrError;  
  66.   
  67.         error = device_create_sys_dev_entry(dev);  
  68.         if (error)  
  69.             goto devtattrError;  
  70.   
  71.         devtmpfs_create_node(dev);                                  //调用devtmpfs,自动创建设备节点  
  72.     }  
  73.   
  74.     error = device_add_class_symlinks(dev);                             //创建链接文件  
  75.     if (error)  
  76.         goto SymlinkError;  
  77.     error = device_add_attrs(dev);                                      //添加设备的属性  
  78.     if (error)  
  79.         goto AttrsError;  
  80.     error = bus_add_device(dev);                                        //将此设备添加到一条总线上  
  81.     if (error)  
  82.         goto BusError;  
  83.     error = dpm_sysfs_add(dev);  
  84.     if (error)  
  85.         goto DPMError;  
  86.     device_pm_add(dev);                                                  //将设备添加到pm core链表中,会在suspend中使用到  
  87.   
  88.     /* Notify clients of device addition.  This call must come 
  89.      * after dpm_sysfs_add() and before kobject_uevent(). 
  90.      */  
  91.     if (dev->bus)  
  92.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,    //使用通知链同时有新设备添加到bus中  
  93.                          BUS_NOTIFY_ADD_DEVICE, dev);  
  94.   
  95.     kobject_uevent(&dev->kobj, KOBJ_ADD);                     //使用uevent通知上层,这时候就会使用到device_uevent_ops中的函数  
  96.     bus_probe_device(dev);                                    //匹配bus下的设备与驱动  
  97.     if (parent)  
  98.         klist_add_tail(&dev->p->knode_parent,             //如果parent存在,将dev添加到parent链表中  
  99.                    &parent->p->klist_children);  
  100.   
  101.     if (dev->class) {                                         //如果class存在,则将dev添加到class链表中  
  102.         mutex_lock(&dev->class->p->mutex);  
  103.         /* tie the class to the device */  
  104.         klist_add_tail(&dev->knode_class,  
  105.                    &dev->class->p->klist_devices);  
  106.   
  107.         /* notify any interfaces that the device is here */  
  108.         list_for_each_entry(class_intf,  
  109.                     &dev->class->p->interfaces, node)  
  110.             if (class_intf->add_dev)  
  111.                 class_intf->add_dev(dev, class_intf);  
  112.         mutex_unlock(&dev->class->p->mutex);  
  113.     }  
  114. }  
A:  如果设备的device_private不存在,就重现分配一个
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int device_private_init(struct device *dev)  
  2. {  
  3.     dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);                         //分配一个device_private结构  
  4.     if (!dev->p)  
  5.         return -ENOMEM;  
  6.     dev->p->device = dev;                                                  //设置device_private的device结构  
  7.     klist_init(&dev->p->klist_children, klist_children_get,           
  8.            klist_children_put);                                       //初始化设备下所有children链表  
  9.     INIT_LIST_HEAD(&dev->p->deferred_probe);  
  10.     return 0;  
  11. }  
B:  主要分析下如何将设备添加到一条总线上,这是核心。
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int bus_add_device(struct device *dev)  
  2. {  
  3.     struct bus_type *bus = bus_get(dev->bus);  
  4.     int error = 0;  
  5.   
  6.     if (bus) {  
  7.         pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));  
  8.         error = device_add_attrs(bus, dev);  
  9.         if (error)  
  10.             goto out_put;  
  11.         error = device_add_groups(dev, bus->dev_groups);  
  12.         if (error)  
  13.             goto out_groups;  
  14.         error = sysfs_create_link(&bus->p->devices_kset->kobj,  
  15.                         &dev->kobj, dev_name(dev));  
  16.         if (error)  
  17.             goto out_id;  
  18.         error = sysfs_create_link(&dev->kobj,  
  19.                 &dev->bus->p->subsys.kobj, "subsystem");  
  20.         if (error)  
  21.             goto out_subsys;  
  22.         klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);  
  23.     }  
  24.     return 0;  
  25. }  
如果此设备不属于任何总线,直接就返回,否则添加设备属性,创建链接文件,将此bus下的设备添加到klist_devices链表中。
 
C:  关于总线先设备与驱动的匹配是设备驱动模型的核心
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. void bus_probe_device(struct device *dev)  
  2. {  
  3.     struct bus_type *bus = dev->bus;  
  4.     struct subsys_interface *sif;  
  5.     int ret;  
  6.   
  7.     if (!bus)                               //如果不存在总线,直接返回  
  8.         return;  
  9.   
  10.     if (bus->p->drivers_autoprobe) {       //是否总线支持自动匹配  
  11.         ret = device_attach(dev);  
  12.         WARN_ON(ret < 0);  
  13.     }  
  14.   
  15.     mutex_lock(&bus->p->mutex);             
  16.     list_for_each_entry(sif, &bus->p->interfaces, node)  
  17.         if (sif->add_dev)  
  18.             sif->add_dev(dev, sif);              
  19.     mutex_unlock(&bus->p->mutex);  
  20. }  
一般总线都会支持自动匹配,当然可以修改driver_autoprobe的值,然后调用device_attach进行匹配。
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. int device_attach(struct device *dev)  
  2. {  
  3.     int ret = 0;  
  4.   
  5.     device_lock(dev);                      //因为此操作只能运行一个dev进行,需要互斥保护。  
  6.     if (dev->driver) {                     //如果设备存在对应的driver,说明已经进行了匹配,只需要调用device_bind_driver在sys中建立关系。  
  7.         if (klist_node_attached(&dev->p->knode_driver)) {  
  8.             ret = 1;  
  9.             goto out_unlock;  
  10.         }  
  11.         ret = device_bind_driver(dev);  
  12.         if (ret == 0)  
  13.             ret = 1;  
  14.         else {  
  15.             dev->driver = NULL;  
  16.             ret = 0;  
  17.         }  
  18.     } else {  
  19.         ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);      //如果driver不存在,则需要遍历dev所在bus下的所有driver  
  20.         pm_request_idle(dev);  
  21.     }  
  22. out_unlock:  
  23.     device_unlock(dev);  
  24.     return ret;  
  25. }  
当遍历driver下过程中,最终会调用到__device_attach函数。
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static int __device_attach(struct device_driver *drv, void *data)  
  2. {  
  3.     struct device *dev = data;  
  4.   
  5.     if (!driver_match_device(drv, dev))  
  6.         return 0;  
  7.   
  8.     return driver_probe_device(drv, dev);  
  9. }  
函数match最终使用的匹配是
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static inline int driver_match_device(struct device_driver *drv,  
  2.                       struct device *dev)  
  3. {  
  4.     return drv->bus->match ? drv->bus->match(dev, drv) : 1;  
  5. }  
根据bus是否存在match函数,如果存在调用bus的match函数,如果不存在,直接返回1。如果匹配成功之后就会调用driver_probe_device进行绑定,最终会调用really_probe函数。
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static int really_probe(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     int ret = 0;  
  4.     int local_trigger_count = atomic_read(&deferred_trigger_count);  
  5.   
  6.     atomic_inc(&probe_count);  
  7.     pr_debug("bus: '%s': %s: probing driver %s with device %s\n",  
  8.          drv->bus->name, __func__, drv->name, dev_name(dev));  
  9.     WARN_ON(!list_empty(&dev->devres_head));  
  10.   
  11.     dev->driver = drv;                                                 //设置设备的driver变量  
  12.   
  13.     /* If using pinctrl, bind pins now before probing */  
  14.     ret = pinctrl_bind_pins(dev);  
  15.     if (ret)  
  16.         goto probe_failed;  
  17.   
  18.     if (driver_sysfs_add(dev)) {                                         
  19.         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",  
  20.             __func__, dev_name(dev));  
  21.         goto probe_failed;  
  22.     }  
  23.   
  24.     if (dev->bus->probe) {  
  25.         ret = dev->bus->probe(dev);                    //如果bus的probe存在,则调用bus的probe函数  
  26.         if (ret)  
  27.             goto probe_failed;  
  28.     } else if (drv->probe) {                               //否则调用驱动的probe,这下知道驱动的probe函数是如何调用的。  
  29.         ret = drv->probe(dev);  
  30.         if (ret)  
  31.             goto probe_failed;  
  32.     }  
  33.   
  34.     driver_bound(dev);                                     //将设备所属的驱动加入到驱动链表中,使用通知链发出BOUND信息。  
  35.     ret = 1;  
  36.     pr_debug("bus: '%s': %s: bound device %s to driver %s\n",  
  37.          drv->bus->name, __func__, dev_name(dev), drv->name);  
  38.     goto done;  
  39.   
  40. probe_failed:               //probe失败之后,就会清空设备资源,设备driver设置为NULL,从sys中移除dev建立的driver链接。  
  41.     devres_release_all(dev);  
  42.     driver_sysfs_remove(dev);  
  43.     dev->driver = NULL;  
  44.     dev_set_drvdata(dev, NULL);  
  45.   
  46.     if (ret == -EPROBE_DEFER) {  
  47.         /* Driver requested deferred probing */  
  48.         dev_info(dev, "Driver %s requests probe deferral\n", drv->name);  
  49.         driver_deferred_probe_add(dev);  
  50.         /* Did a trigger occur while probing? Need to re-trigger if yes */  
  51.         if (local_trigger_count != atomic_read(&deferred_trigger_count))  
  52.             driver_deferred_probe_trigger();  
  53.     } else if (ret != -ENODEV && ret != -ENXIO) {  
  54.         /* driver matched but the probe failed */  
  55.         printk(KERN_WARNING  
  56.                "%s: probe of %s failed with error %d\n",  
  57.                drv->name, dev_name(dev), ret);  
  58.     } else {  
  59.         pr_debug("%s: probe of %s rejects match %d\n",  
  60.                drv->name, dev_name(dev), ret);  
  61.     }  
  62.     /* 
  63.      * Ignore errors returned by ->probe so that the next driver can try 
  64.      * its luck. 
  65.      */  
  66.     ret = 0;  
  67. }  
  • device_register(注册一个设备到系统中是上述两个函数的结合体)
  • device_unregister(将一个设备从系统中注销)
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. void device_unregister(struct device *dev)  
  2. {  
  3.     pr_debug("device: '%s': %s\n", dev_name(dev), __func__);  
  4.     device_del(dev);  
  5.     put_device(dev);  
  6. }  
此函数分为两步走,第一步从系统中移除此设备,第二步将设备的引用计数减去1。主要的注销操作在device_del中实现,也就是register的反操作。
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. void device_del(struct device *dev)  
  2. {  
  3.     struct device *parent = dev->parent;  
  4.     struct class_interface *class_intf;  
  5.   
  6.     /* Notify clients of device removal.  This call must come 
  7.      * before dpm_sysfs_remove(). 
  8.      */  
  9.     if (dev->bus)      //如果bus总线存在,调用通知链删除设备  
  10.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,  
  11.                          BUS_NOTIFY_DEL_DEVICE, dev);  
  12.     dpm_sysfs_remove(dev); //电源管理函数,关掉设备电源  
  13.     if (parent)            //如果有父设备,将当前设备从父设备所属链表删除  
  14.         klist_del(&dev->p->knode_parent);  
  15.     if (MAJOR(dev->devt)) {     //如果主设备号存在, 动态删除设备节点,移除设备的属性  
  16.         devtmpfs_delete_node(dev);  
  17.         device_remove_sys_dev_entry(dev);  
  18.         device_remove_file(dev, &dev_attr_dev);  
  19.     }  
  20.     if (dev->class) {   //如果设备所属的class存在,移除设备的链接,调用remove_dev做移除,从class链表中删除该设备  
  21.         device_remove_class_symlinks(dev);  
  22.   
  23.         mutex_lock(&dev->class->p->mutex);  
  24.         /* notify any interfaces that the device is now gone */  
  25.         list_for_each_entry(class_intf,  
  26.                     &dev->class->p->interfaces, node)  
  27.             if (class_intf->remove_dev)  
  28.                 class_intf->remove_dev(dev, class_intf);  
  29.         /* remove the device from the class list */  
  30.         klist_del(&dev->knode_class);  
  31.         mutex_unlock(&dev->class->p->mutex);  
  32.     }  
  33.     device_remove_file(dev, &dev_attr_uevent);    //移除设备的uevent属性  
  34.     device_remove_attrs(dev);              
  35.     bus_remove_device(dev);                       //从总线中移除设备,以及电源管理等。  
  36.     device_pm_remove(dev);  
  37.     driver_deferred_probe_del(dev);  
  38.   
  39.     /* Notify the platform of the removal, in case they 
  40.      * need to do anything... 
  41.      */  
  42.     if (platform_notify_remove)  
  43.         platform_notify_remove(dev);  
  44.     if (dev->bus)  
  45.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,  
  46.                          BUS_NOTIFY_REMOVED_DEVICE, dev);  
  47.     kobject_uevent(&dev->kobj, KOBJ_REMOVE);  
  48.     cleanup_device_parent(dev);  
  49.     kobject_del(&dev->kobj);              //删除设备的内核模块  
  50.     put_device(parent);  
  51. }  

设备属性

linux中使用device_attribute结构体表示一个设备的属性
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. struct device_attribute {  
  2.     struct attribute    attr;  
  3.     ssize_t (*show)(struct device *dev, struct device_attribute *attr,  
  4.             char *buf);  
  5.     ssize_t (*store)(struct device *dev, struct device_attribute *attr,  
  6.              const char *buf, size_t count);  
  7. };  
通常使用to_dev_attr宏定义得到device_attribute对象
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)  
同时为了方便定义设备的属性,内核提供了一系列相关的宏定义,用于初始化设备的属性。
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #define DEVICE_ATTR(_name, _mode, _show, _store) \  
  2.     struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)  
  3. #define DEVICE_ATTR_RW(_name) \  
  4.     struct device_attribute dev_attr_##_name = __ATTR_RW(_name)  
  5. #define DEVICE_ATTR_RO(_name) \  
  6.     struct device_attribute dev_attr_##_name = __ATTR_RO(_name)  
  7. #define DEVICE_ATTR_WO(_name) \  
  8.     struct device_attribute dev_attr_##_name = __ATTR_WO(_name)  
关于设备属性的调用过程,最终会调用到设备的show和store函数中,具体的流程分析可见Linux设备驱动模型-Ktype
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,  
  2.                  char *buf)  
  3. {  
  4.     struct device_attribute *dev_attr = to_dev_attr(attr);  
  5.     struct device *dev = kobj_to_dev(kobj);  
  6.     ssize_t ret = -EIO;  
  7.   
  8.     if (dev_attr->show)  
  9.         ret = dev_attr->show(dev, dev_attr, buf);  
  10.     if (ret >= (ssize_t)PAGE_SIZE) {  
  11.         print_symbol("dev_attr_show: %s returned bad count\n",  
  12.                 (unsigned long)dev_attr->show);  
  13.     }  
  14.     return ret;  
  15. }  
  16.   
  17. static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,  
  18.                   const char *buf, size_t count)  
  19. {  
  20.     struct device_attribute *dev_attr = to_dev_attr(attr);  
  21.     struct device *dev = kobj_to_dev(kobj);  
  22.     ssize_t ret = -EIO;  
  23.   
  24.     if (dev_attr->store)  
  25.         ret = dev_attr->store(dev, dev_attr, buf, count);  
  26.     return ret;  
  27. }  
  28.   
  29. static const struct sysfs_ops dev_sysfs_ops = {  
  30.     .show   = dev_attr_show,  
  31.     .store  = dev_attr_store,  
  32. };  

设备类型

在include/linux/device.h文件存在这样的结构体:
[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. struct device_type {  
  2.     const char *name;  
  3.     const struct attribute_group **groups;  
  4.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
  5.     char *(*devnode)(struct device *dev, umode_t *mode,  
  6.              kuid_t *uid, kgid_t *gid);  
  7.     void (*release)(struct device *dev);  
  8.   
  9.     const struct dev_pm_ops *pm;  
  10. };  
此结构代表设备类型。通常一个Bus下会存在各种设备的,比如:disks,  mouse,  event等。而此结构就表明此设备是何种类型的设备。
 
name:     代表设备的名称,在上报event的时候,会通过DEVTYPE设置设备的类型名称。
groups:   代码设备的属性,在添加设备的属性的时候,如果存在设备类型,也会添加设备类型的属性。
uevent:   在新增一个设备时,通过该函数上报设备类型的uevent。
devnode: 在创建一个设备节点的时候会使用到,获取设备的信息。
release:   在设备释放时候,如果存在设备类型会调用到。

本文永久更新链接:http://embeddedlinux.org.cn/emb-linux/kernel-driver/201703/10-6246.html



分享:

评论