Chinaunix首页 | 论坛 | 博客
  • 博客访问: 250376
  • 博文数量: 101
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 95
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-12 12:35
文章分类

全部博文(101)

文章存档

2016年(5)

2015年(16)

2014年(37)

2013年(32)

2012年(8)

2011年(3)

我的朋友

分类: LINUX

2013-12-03 17:38:54

------------------------------------------------
#个人浅见,如有问题敬请谅解!
#kernel version: 2.6.26
#Author: andy wang
-------------------------------------------------
   和bus一样devices也是一个ket ,devices目录下有序的组织着注册到内核中的设备
还是看看注册一个device到内核中后,sysfs下目录结构:
图1-11是注册一个pci设备以后sysfs的目录结构.
首先来看看devices目录是怎么建立起来的.
 
devices初始化
1072 int __init devices_init(void)
1073 {
1074         devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
1075         if (!devices_kset)
1076                 return -ENOMEM;
1077         return 0;
1078 }
这个函数就是注册一个devices_kset 如图1-11会建立一个devices目录.
 
devices注册
如果向内核注册一个设备就会在devices目录下或者在class目录下建立一个目录.
在讨论注册流程之前,先来看看struct device的定义:
struct device {
         struct klist           klist_children;
         struct klist_node knode_parent;    
         struct klist_node knode_driver;
         struct klist_node knode_bus;
         struct device                 *parent;
         struct kobject      kobj;
         char   bus_id[BUS_ID_SIZE];       
         struct device_type       *type;
         unsigned              uevent_suppress:1;
         struct semaphore          sem; 
         struct bus_type  *bus;         
         struct device_driver *driver; 
         void            *driver_data;      
         void            *platform_data; 
         struct dev_pm_info      power;
#ifdef CONFIG_NUMA
         int              numa_node;        
#endif
         u64             *dma_mask;       
         u64             coherent_dma_mask;                     
         struct device_dma_parameters *dma_parms;
         struct list_head   dma_pools;         /* dma pools (if dma'ble) */
         struct dma_coherent_mem    *dma_mem;
         struct dev_archdata      archdata;
         spinlock_t           devres_lock;
         struct list_head   devres_head;
         struct list_head   node;
         struct class          *class;
         dev_t                            devt; /* dev_t, creates the sysfs "dev" */
         struct attribute_group  **groups;   /* optional groups */
         void  (*release)(struct device *dev);
};
 struct device结构包含了一个kobject结构, 所以我们可以猜想一下,注册一个device其实就是向内核注册一个kobj. 先来理理设备注册的流程:
device_register()->device_initialize()->device_add()->setup_parent()->kobject_add()->device_create_file()->device_add_attrs()->bus_add_device()->kobject_uevent()->bus_attach_device()
调用函数device_register()向内核注册一个设备,传入的参数是struct device *dev, 这个dev往往是被包含在另一个更大的结构中的, 一般不单独使用了.
注册之前先是初始化device结构, device_initialize()这个函数设置了dev包含kobj的.kset 和ktype,( dev->kobj.kset = devices_kset;kobject_init(&dev->kobj, &device_ktype))
好了下面看下注册设备的核心函数dev_add():
789 int device_add(struct device *dev)
 790 {
 791         struct device *parent = NULL;
 792         struct class_interface *class_intf;
 793         int error;
 794
 795         dev = get_device(dev);
 796         if (!dev || !strlen(dev->bus_id)) {
 797                 error = -EINVAL;
 798                 goto Done;
 799         }
 800
 801         pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
 802
 803         parent = get_device(dev->parent);
 804         setup_parent(dev, parent);
 805
 806         /* use parent numa_node */
 807         if (parent)
 808                 set_dev_node(dev, dev_to_node(parent));
 809
 810         /* first, register with generic layer. */
 811         error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
 812         if (error)
 813                 goto Error;
 814
 815         /* notify platform of device entry */
 816         if (platform_notify)
 817                 platform_notify(dev);
 818
 819         /* notify clients of device entry (new way) */
 820         if (dev->bus)
 821                 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 822                                              BUS_NOTIFY_ADD_DEVICE, dev);
 823
 824         error = device_create_file(dev, &uevent_attr);
 825         if (error)
 826                 goto attrError;
 827
 828         if (MAJOR(dev->devt)) {
 829                 error = device_create_file(dev, &devt_attr);
 830                 if (error)
 831                         goto ueventattrError;
 832         }
 833
 834         error = device_add_class_symlinks(dev);
 835         if (error)
 836                 goto SymlinkError;
 837         error = device_add_attrs(dev);
 838         if (error)
 839                 goto AttrsError;
 840         error = bus_add_device(dev);
 841         if (error)
 842                 goto BusError;
 843         error = device_pm_add(dev);
 844         if (error)
 845                 goto PMError;
 846         kobject_uevent(&dev->kobj, KOBJ_ADD);
 847         bus_attach_device(dev);
 848         if (parent)
 849                 klist_add_tail(&dev->knode_parent, &parent->klist_children);
 850
 851         if (dev->class) {
 852                 down(&dev->class->sem);
 853                 /* tie the class to the device */
 854                 list_add_tail(&dev->node, &dev->class->devices);
 855
 856                 /* notify any interfaces that the device is here */
 857                 list_for_each_entry(class_intf, &dev->class->interfaces, node)
 858                         if (class_intf->add_dev)
 859                                 class_intf->add_dev(dev, class_intf);
 860                 up(&dev->class->sem);
 861         }
 862  Done:
                            ……………..
}
        
第795行,增加dev->kobj引用计数.
第803行,取出dev的父对象, 并增加其引用计数/
第804行, 安装dev->kobj的父对象,看看这段代码:
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;
}
如果定义了dev->class将会取出dev->class->subsys.kobj对象作为dev->kobj的父对象 ,否则把parent->kobj对象作为dev->kobj的父对象.
第811行, 注册一个kobject, 这个函数在以前已经分析过了,这里就不看了. 这时设备目录就建立起来了.
下面的代码就是需要建立一些设备属性文件了.
第824行,在刚建立起来的设备目录下创建一个uevent设备属性文件.
第828-829行, 如果定义了设备号dev->devt, 就会创建一个名叫dev的设备属性文件 ,我们可以通过这个文件查看设备号.
第840行代码, 在dev关联的总线(bus/device)目录下创建符号链接文件,
第846行, 向udev发送一个KOBJ_ADD事件的消息.
第847行代码, bus_attach_device(dev) 与注册在总线上的驱动进行匹配 .我们来详细分析一下这个函数:
 
匹配设备驱动
匹配总线上驱动的软件流程如下:
bus_attach_device()->device_attach()->__device_attach()->driver_probe_device()->really_probe()->driver_bound
首先看看device_attach()代码:
int device_attach(struct device *dev)
{
         int ret = 0;
 
         down(&dev->sem);
         if (dev->driver) {   //如果设备以定义了驱动
                   ret = device_bind_driver(dev);  //绑定驱动
                   if (ret == 0)
                            ret = 1;
                   else {
                            dev->driver = NULL;
                            ret = 0;
                   }
         } else {
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);   //遍历总线上的驱动并与设备匹配
         }
         up(&dev->sem);
         return ret;
}
首先要判断设备是否已经指定了驱动,如果已经指定了驱动就只需要绑定驱动就可以了, 否则就需要遍历总线上的驱动然后进行匹配.
来看看绑定驱动都做了些什么操作:
device_bind_driver()->driver_sysfs_add()
static int driver_sysfs_add(struct device *dev)
{
         int ret;
 
         ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
                              kobject_name(&dev->kobj));  //在bus/driver目录下创建一个名字为设备名的符号链接
         if (ret == 0) {
                   ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
                                               "driver");      //在device目录下创建一个名字为driver的符号链接文件
                   if (ret)
                            sysfs_remove_link(&dev->driver->p->kobj,
                                               kobject_name(&dev->kobj));
         }
         return ret;
}
好了指定驱动的情况分析完了,,下面接着分析设备没有指定驱动的情况 .
下面看看bus_for_each_drv()代码片段:
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
                        void *data, int (*fn)(struct device_driver *, void *))
{
         struct klist_iter i;
         struct device_driver *drv;
         int error = 0;
 
         if (!bus)
                   return -EINVAL;
 
         klist_iter_init_node(&bus->p->klist_drivers, &i,
                                 start ? &start->p->knode_bus : NULL);
         while ((drv = next_driver(&i)) && !error)
                   error = fn(drv, data);
         klist_iter_exit(&i);
         return error;
}
这个函数就是遍历bus->p->klist_drivers下面挂的驱动,然后调用一个回调函数fn(drv, data)进行匹配工作.
在这里这个回调函数就是__device_attach() ,这个函数会调用函数driver_probe_device();来跟踪一下它:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
         int ret = 0;
 
         if (!device_is_registered(dev))   //如果设备已经注册,返回错误
                   return -ENODEV;
         if (drv->bus->match && !drv->bus->match(dev, drv)) //调用总线定义的match方法进行匹配
                   goto done;
 
         pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                    drv->bus->name, __func__, dev->bus_id, drv->name);
 
         ret = really_probe(dev, drv);
 
done:
         return ret;
}
如果总线定义了match匹配方法的话,就要调用这个回调函数进行初级匹配.
接下来做进一步匹配工作,由really_probe()函数实现:
static int really_probe(struct device *dev, struct device_driver *drv)
{
         int ret = 0;
 
         ………………
 
         dev->driver = drv;    
         if (driver_sysfs_add(dev)) {      //建立相关符号链接文件,,前面以分析过.
                   printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
                            __func__, dev->bus_id);
                   goto probe_failed;
         }
 
         if (dev->bus->probe) {   //首先调用总线定义的prob回调函数
                   ret = dev->bus->probe(dev);
                   if (ret)
                            goto probe_failed;
         } else if (drv->probe) {   //然后调用驱动定义的prob回调函数
                   ret = drv->probe(dev);
                   if (ret)
                            goto probe_failed;
         }
 
         driver_bound(dev); //绑定设备和驱动
         ret = 1;
         pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
                    drv->bus->name, __func__, dev->bus_id, drv->name);
         goto done;
         …………….
}
这个函数中有两个重要的回调函数 dev->bus->probe(dev)和drv->probe(dev); 在以后研究具体设备驱动时会看到它的用处. 设备驱动的匹配就分析完了.
 
好了,在内核注册一个设备的流程就是这样子了.
 
阅读(356) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~