Linux驱动学习
来源:互联网 发布:mac全屏游戏怎么切换 编辑:程序博客网 时间:2024/06/03 04:10
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 the12 * 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 converted19 * some day, we need to initialize the name. We prevent reading back20 * 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()的分析就到这里,后面会以平台设备和平台驱动来说说是怎么利用这些东西让它们“沟通”起来的,这也就是编写驱动的人比较关心的内容。
- linux驱动--i2c驱动学习
- linux驱动学习
- linux驱动学习
- Linux 驱动学习
- 高效学习linux驱动
- linux驱动学习
- linux驱动开发学习
- Linux 驱动学习笔记
- linux驱动开发学习
- Linux网卡驱动学习
- linux 驱动学习
- Linux驱动学习
- linux驱动学习路线
- linux 驱动学习
- linux驱动学习
- linux驱动学习
- Linux设备驱动学习
- Linux驱动学习书籍
- nyoj 104 最大和
- framebuffer显示原理、图像RGB数据处理
- 简易图片浏览器!
- 高端MCU战火烧到M4:X86仍猛攻,ARM不懈怠
- Discuz和jQuery冲突的解决办法
- Linux驱动学习
- Income Tax Hazard (II) (数学,难)
- 程序猿的一周
- 用ABAP编程破解世界上最难数独游戏
- CentOS+Nginx+PHP+MySQL标准生产环境
- 数据结构-03 数组 -冒泡排序
- 建立嵌入式 Linux 开发环境
- 使用JDOM读取XML文件,并输出其中的数据
- 精确获取时间(QueryPerformanceCounter)