Linux驱动学习

23 篇文章 2 订阅

Linux驱动学习(一)

注:基于linux-2.6.38

        在linux源码的根目录下有一个叫drivers的目录,可以发现linux源码根目录下也就那么十来二十个目录,linux把drivers作为根目录下的一个独立的目录,足见drivers在linux里占有的分量有多重。

       打开drivers目录一看,有一种晕呼呼的感觉,好几十个目录就这样“活生生”地摆在眼前,不知该如何下手。任何东西如果多了,但没有秩序去维持,肯定会变得很混乱,显然linux不会让这种情况出现。一般来说,里面的每一个目录可以说代表一类驱动,但linux整个driver的初始化操作在哪里?根据前人的探索和经验可知,没错,就在/drivers/base/init.c里,打开它发现里面就一个driver_init()函数,有必要把它全部贴出来:

复制代码
 1 void __init driver_init(void)
 2 {       
 3         /* These are the core pieces */
 4         devtmpfs_init();
 5         devices_init();
 6         buses_init();
 7         classes_init();
 8         firmware_init();
 9         hypervisor_init();
10 
11         /* These are also core pieces, but must come after the
12          * core core pieces.
13          */
14         platform_bus_init();
15         system_bus_init();
16         cpu_dev_init();
17         memory_dev_init();
18 }
复制代码

哟,原来是“禾草盖珍珠”,该函数内部全都是函数调用,其实这种现象在linux里多的是。在这里我主要想沿着一条主线“走下去”,而不是走着走着就“跑到”老远去,然后再回来。对于学习这件事情,我更偏向于先看到结果然后再努力去搞懂其内在的原理。好吧,接下就去寻找我想要的结果。

第4行调用devtmpfs_init()函数,从它的名字去理解它,就是/dev文件系统的初始化。第5行调用devices_init()函数,在drivers/base/core.c里定义,看看它怎么实现的:

复制代码
 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 }
复制代码

 还是比较简单的,第3行在/sys目录下创建了devices目录,第6行在/sys下创建了dev目录,第9,12行分别在/sys/dev下创建了block和char这两个目录。

接下来看在drivers/base/bus.c里定义的buses_init()函数:

复制代码
1 int __init buses_init(void)
2 {
3         bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
4         if (!bus_kset)
5                 return -ENOMEM;
6         return 0;
7 }
复制代码

第3行在/sys下创建了bus目录。

drivers/base/class.c里定义的classes_init()函数:

复制代码
1 int __init classes_init(void)
2 {
3         class_kset = kset_create_and_add("class", NULL, NULL);
4         if (!class_kset)
5                 return -ENOMEM;
6         return 0;
7 }
复制代码

第3行在/sys下创建了class目录。

对于firmware_init()和hypervisor_init这两个函数暂时掠过。

在drivers/base/platform.c里定义的platform_bus_init()函数:

复制代码
 1 int __init platform_bus_init(void)
 2 {
 3         int error;
 4         
 5         early_platform_cleanup();
 6 
 7         error = device_register(&platform_bus);
 8         if (error)
 9                 return error;
10         
11         error =  bus_register(&platform_bus_type);
12         if (error)
13                 device_unregister(&platform_bus);
14         return error;
15 }
复制代码

第5行,在early_platform_cleanup()函数里通过遍历链表清除之前的平台代码。第7行,设备注册,不要被它的参数的名字骗了,先看参数platform_bus的定义:

1 struct device platform_bus = {
2         .init_name      = "platform",
3 };

只定义了设备的名字为platform。再看device_register()函数:

1 int device_register(struct device *dev)
2 {       
3         device_initialize(dev);
4         return device_add(dev);
5 } 

 只有两个函数调用,先看device_initialize()函数:

复制代码
 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 }
复制代码

第3行的devices_kset由之前的devices_init()里被赋值,作为所有设备的顶层kset。第4行初始化当前设备的kobject。第10行是该设备电源管理的初始化。第11行,看一下:

1 static inline void set_dev_node(struct device *dev, int node)
2 {       
3         dev->numa_node = node;
4 }

没什么,就给dev里的成员numa_node赋值为node(这里是-1)。

回到device_register()里的device_add()函数,这个函数比较长,涉及的内容也很多,不过还是得看,暂时将它分为两部分吧,先看第一部分:

复制代码
 1 int device_add(struct device *dev)
 2 {
 3         struct device *parent = NULL;
 4         struct class_interface *class_intf;
 5         int error = -EINVAL;
 6 
 7         dev = get_device(dev);
 8         if (!dev)
 9                 goto done;
10 
11         if (!dev->p) {
12                 error = device_private_init(dev);
13                 if (error)
14                         goto done;
15         }
16 
17         /*
18          * for statically allocated devices, which should all be converted
19          * some day, we need to initialize the name. We prevent reading back
20          * the name, and force the use of dev_name()
21          */
22         if (dev->init_name) {
23                 dev_set_name(dev, "%s", dev->init_name);
24                 dev->init_name = NULL;
25         }
26 
27         if (!dev_name(dev)) {
28                 error = -EINVAL;
29                 goto name_error;
30         }
31 
32         pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
33 
34         parent = get_device(dev->parent);
35         setup_parent(dev, parent);
36 
37         /* use parent numa_node */
38         if (parent)
39                 set_dev_node(dev, dev_to_node(parent));
40 
41         /* first, register with generic layer. */
42         /* we require the name to be set before, and pass NULL */
43         error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
44         if (error)
45                 goto Error;
46 
47         /* notify platform of device entry */
48         if (platform_notify)
49                 platform_notify(dev);
50 
51         error = device_create_file(dev, &uevent_attr);
52         if (error)
53                 goto attrError;
54 
55         if (MAJOR(dev->devt)) {
56                 error = device_create_file(dev, &devt_attr);
57                 if (error)
58                         goto ueventattrError;
59 
60                 error = device_create_sys_dev_entry(dev);
61                 if (error)
62                         goto devtattrError;
63                 
64                 devtmpfs_create_node(dev);
65         }
66         error = device_add_class_symlinks(dev);
67         if (error)
68                 goto SymlinkError;
69         error = device_add_attrs(dev);
70         if (error)
71                 goto AttrsError;
72         error = bus_add_device(dev);
73         if (error)
74                 goto BusError;
75         error = dpm_sysfs_add(dev);
76         if (error)
77                 goto DPMError;
78         device_pm_add(dev);
.....................
复制代码

第7行,增加该设备的引用计数;第12行,主要为dev->p成员分配内存,然后对p里面的一些成员作初始化;第22~30行是关与dev->name的一些操作;第35行,设置当前设备的父设备;第39行,看注释就知道是将父设备numa_node成员的值赋给当前设备;第43行,调用kobject_add(),这个函数的内部调用关系挺复杂的,主要功能是建立当前设备与父设备的kobject对象关系和在/sys相应的目录下建立一个目录;第51行,创建设备的属性文件;第55行,如果该设备定义了主设备号的话就再生成一个设备文件,还有就是通过devtmpfs_create_node()函数在/dev下动态创建设备节点。第66~77行主要涉及sysfs文件系统的操作,在此暂时掠过;第78行是与电源管理相关的。

接下来看device_add()函数的第2部分:

复制代码
 1         if (dev->bus)
 2                 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 3                                              BUS_NOTIFY_ADD_DEVICE, dev);
 4         
 5         kobject_uevent(&dev->kobj, KOBJ_ADD);
 6         bus_probe_device(dev);
 7         if (parent)
 8                 klist_add_tail(&dev->p->knode_parent,
 9                                &parent->p->klist_children);
10 
11         if (dev->class) {
12                 mutex_lock(&dev->class->p->class_mutex);
13                 /* tie the class to the device */
14                 klist_add_tail(&dev->knode_class,
15                                &dev->class->p->klist_devices);
16 
17                 /* notify any interfaces that the device is here */
18                 list_for_each_entry(class_intf,
19                                     &dev->class->p->class_interfaces, node)
20                         if (class_intf->add_dev)
21                                 class_intf->add_dev(dev, class_intf);
22                 mutex_unlock(&dev->class->p->class_mutex);
23         }
24 done:
25         put_device(dev);
26         return error;
27  DPMError:
28         bus_remove_device(dev);
29  BusError:
30         device_remove_attrs(dev);
31  AttrsError:
32         device_remove_class_symlinks(dev);
33  SymlinkError:
34         if (MAJOR(dev->devt))
35                 devtmpfs_delete_node(dev);
36         if (MAJOR(dev->devt))
37                 device_remove_sys_dev_entry(dev);
38  devtattrError:
39         if (MAJOR(dev->devt))
40                 device_remove_file(dev, &devt_attr);
41  ueventattrError:
42         device_remove_file(dev, &uevent_attr);
43  attrError:
44         kobject_uevent(&dev->kobj, KOBJ_REMOVE);
45         kobject_del(&dev->kobj);
46  Error:
47         cleanup_device_parent(dev);
48         if (parent)
49                 put_device(parent);
50 name_error:
51         kfree(dev->p);
52         dev->p = NULL;
53         goto done;
54 }
复制代码

第5行kobject_uevent()这个函数的实现不是一般的复杂,主要是向用户空间发送消息,实现热插拔,暂时用不到,先掠过;第6行,bus_probe_device()这个函数非常重要,因此尽可能详细地分析一下,看它在drivers/base/bus.c里定义:

复制代码
 1 void bus_probe_device(struct device *dev)
 2 {
 3         struct bus_type *bus = dev->bus;
 4         int ret;
 5 
 6         if (bus && bus->p->drivers_autoprobe) {
 7                 ret = device_attach(dev);
 8                 WARN_ON(ret < 0);
 9         }
10 }
复制代码

别看它那么短,其实没那么简单。if的条件很显然,直接看第7行的device_attach()函数,在drivers/base/dd.c里定义为:

复制代码
 1 int device_attach(struct device *dev)
 2 {
 3         int ret = 0;
 4         
 5         device_lock(dev);
 6         if (dev->driver) {
 7                 ret = device_bind_driver(dev);
 8                 if (ret == 0) 
 9                         ret = 1;
10                 else {                
11                         dev->driver = NULL;
12                         ret = 0;
13                 }
14         } else {
15                 pm_runtime_get_noresume(dev);
16                 ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
17                 pm_runtime_put_sync(dev);
18         }
19         device_unlock(dev);
20         return ret;
21 }
复制代码

第6行,如果当前设备已经绑定了相应的驱动程序,那么就调用device_bind_driver()。在这里有个疑问:先有设备还是先有驱动?一般来说是先有设备再有驱动,但对于热插拔设备来说的话则相反。不管怎样,去看看它是怎么定义的:

复制代码
1 int device_bind_driver(struct device *dev)
2 {
3         int ret;
4 
5         ret = driver_sysfs_add(dev);
6         if (!ret)
7                 driver_bound(dev);
8         return ret;
9 }
复制代码

第5行是与sysfs有关的,直接看第7行的driver_bound()函数:

复制代码
 1 static void driver_bound(struct device *dev)
 2 {
 3         if (klist_node_attached(&dev->p->knode_driver)) {
 4                 printk(KERN_WARNING "%s: device %s already bound\n",
 5                         __func__, kobject_name(&dev->kobj));
 6                 return;
 7         }
 8 
 9         pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
10                  __func__, dev->driver->name);
11 
12         klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
13 
14         if (dev->bus)
15                 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
16                                              BUS_NOTIFY_BOUND_DRIVER, dev);
17 }
复制代码

咋一看,都是与链表操作相关的,关键是第12行,实现将驱动程序和设备联系起来。

回到device_attach()函数的第16行,调用bus_for_each_drv()函数遍历设备所在总线上所有已经挂载了的驱动,每遍历一个就调用一次__device_attach()函数,直接看__device_attach()的定义:

复制代码
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 }
复制代码

第5行的函数是在drivers/base/base.h头文件中定义的,只有一行:

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 }

如果驱动所在的总线上定义了match函数,那么就调用它,否则返回1。

如果driver_match_device()成功,接下来就调用driver_probe_device():

复制代码
 1 int driver_probe_device(struct device_driver *drv, struct device *dev)
 2 {
 3         int ret = 0;
 4 
 5         if (!device_is_registered(dev))
 6                 return -ENODEV;
 7 
 8         pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 9                  drv->bus->name, __func__, dev_name(dev), drv->name);
10 
11         pm_runtime_get_noresume(dev);
12         pm_runtime_barrier(dev);
13         ret = really_probe(dev, drv);
14         pm_runtime_put_sync(dev);
15 
16         return ret;
17 } 
复制代码

主要是第13行的really_probe(),该函数有点长,主要看它的核心部分:

复制代码
 1 static int really_probe(struct device *dev, struct device_driver *drv)
 2 {
 3 ..................................
 4         dev->driver = drv;
 5         if (driver_sysfs_add(dev)) {
 6                 printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
 7                         __func__, dev_name(dev));
 8                 goto probe_failed;
 9         }
10 
11         
12         if (dev->bus->probe) {
13                 ret = dev->bus->probe(dev);
14                 if (ret)
15                         goto probe_failed;
16         } else if (drv->probe) {
17                 ret = drv->probe(dev);
18                 if (ret)
19                         goto probe_failed;
20         }
21 
22         driver_bound(dev);
23 ..............................
复制代码

第4行,不用说;第12行,如果设备所在的总线定义了probe()函数则调用它,否则如果设备对应的驱动定义了probe()函数则调用它,在这里可以说第16行的条件一般会满足,至少对于平台设备来说是这样的(研究过平台设备和平台驱动的同学应该懂我的意思)。第22行的driver_bound()函数在上面已经说过了。

已经“跑”得很远了,回到device_add()函数,发现后面的内容基本上就是一些相应的出错处理。好了,device_register()的分析到这里,回到platform_bus_init()第11行调用的bus_register(),这个函数还是很长,不贴出代码了,主要是涉及kobject,kset和klist等一些操作。

好了,回到最初的函数driver_init(),第15行调用system_bus_init():

复制代码
1 int __init system_bus_init(void)
2 {
3         system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
4         if (!system_kset)
5                 return -ENOMEM;
6         return 0;
7 }
复制代码

第3行,在一个/sys/devices下创建system目录。

driver_init()中最后2个函数是cpu和内存初始化相关的,暂掠过。

driver_init()的分析就到这里,后面会以平台设备和平台驱动来说说是怎么利用这些东西让它们“沟通”起来的,这也就是编写驱动的人比较关心的内容。

 


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值