linux设备驱动程序注冊过程具体解释
Linux的驱动程序注冊过程,大致分为两个步骤:
- 模块初始化
- 驱动程序注冊
以下以内核提供的演示样例代码pci-skeleton.c,具体说明一个pci设备驱动程序的注冊过程。其它设备的驱动代码注冊过程基本同样,大家可自行查看。使用的内核代码版本号是2.6.38。
1. 模块初始化
1.1 驱动程序入口
1922 module_init(netdrv_init_module);
1923 module_exit(netdrv_cleanup_module);
module_init/module_exit是两个宏。module_init是该驱动程序的入口,载入驱动模块时,驱动程序就从netdrv_init_module函数開始运行。而当该驱动程序相应的设备被删除了,则会运行netdrv_cleanup_module这个函数。
1.2 模块初始化
1906 static int __init netdrv_init_module(void)
1907 {
1908 /* when a module, this is printed whether or not devices are found in probe */
1909 #ifdef MODULE
1910 printk(version);
1911 #endif
1912 return pci_register_driver(&netdrv_pci_driver);
1913 }
能够看到,初始化函数非常easy,仅仅运行了一个pci_register_driver函数就返回了。
2. 驱动程序注冊
2.1 linux总线设备驱动模型
50 struct bus_type {
51 const char *name;
52 struct bus_attribute *bus_attrs;
53 struct device_attribute *dev_attrs;
54 struct driver_attribute *drv_attrs;
55
56 int (*match)(struct device *dev, struct device_driver *drv);
。。。
68 };
设备:
406 struct device {
407 struct device *parent;
408
409 struct device_private *p;
410
411 struct kobject kobj;
412 const char *init_name; /* initial name of the device */
413 struct device_type *type;
414
415 struct mutex mutex; /* mutex to synchronize calls to
416 * its driver.
417 */
418
419 struct bus_type *bus; /* type of bus device is on */
420 struct device_driver *driver; /* which driver has allocated this
421 device */
。。。
456
457 void (*release)(struct device *dev);
458 };
驱动:
122 struct device_driver {
123 const char *name;
124 struct bus_type *bus;
125
126 struct module *owner;
127 const char *mod_name; /* used for built-in modules */
128
129 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
130
131 #if defined(CONFIG_OF)
132 const struct of_device_id *of_match_table;
133 #endif
134
135 int (*probe) (struct device *dev);
136 int (*remove) (struct device *dev);
。。。
145 };
2.2 注冊函数具体解释
2.2.1 驱动的描写叙述
896 #define pci_register_driver(driver) \
897 __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) 1103 int __pci_register_driver(struct pci_driver *drv, struct module *owner,
1104 const char *mod_name)
真正的注冊函数式__pci_register_driver(),它的第一个參数是struct pci_driver类型的,再看看这个结构的定义:
542 struct pci_driver {
543 struct list_head node;
544 const char *name;
545 const struct pci_device_id *id_table; /* must be non-NULL for probe to be calle d */
546 int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New de vice inserted */
547 void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
548 int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended * /
549 int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
550 int (*resume_early) (struct pci_dev *dev);
551 int (*resume) (struct pci_dev *dev); /* Device woken up */
552 void (*shutdown) (struct pci_dev *dev);
553 struct pci_error_handlers *err_handler;
554 struct device_driver driver;
555 struct pci_dynids dynids;
556 };
细致看这个结构,发现其中一个成员是我们上面的总线设备模型中的driver的结构体。事实上在linux内核中,全部设备的驱动的定义,都是以struct device_driver为基类,进行继承与扩展的。你没有看错,内核其中使用了非常多OO的思想。再看看网卡I2C设备的的驱动描写叙述:
143 struct i2c_driver {
144 unsigned int class;
145
。。。
174 struct device_driver driver;
175 const struct i2c_device_id *id_table;
176
177 /* Device detection callback for automatic device creation */
178 int (*detect)(struct i2c_client *, struct i2c_board_info *);
179 const unsigned short *address_list;
180 struct list_head clients;
181 };
如今我们知道了pci设备的驱动程序的描写叙述方法。可是问题又来了:这么复杂的一个结构体,我们怎么用呢?
1894 static struct pci_driver netdrv_pci_driver = {
1895 .name = MODNAME,
1896 .id_table = netdrv_pci_tbl,
1897 .probe = netdrv_init_one,
1898 .remove = __devexit_p(netdrv_remove_one),
1899 #ifdef CONFIG_PM
1900 .suspend = netdrv_suspend,
1901 .resume = netdrv_resume,
1902 #endif /* CONFIG_PM */
1903 };
我们能够看出来,并非这个结构体的全部成员我们都要操作,我们仅仅管当中最关键的几个即可了。
221 static DEFINE_PCI_DEVICE_TABLE(netdrv_pci_tbl) = {
222 {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
223 {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NETDRV_CB },
224 {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX },
225 /* {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/
226 {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 },
227 {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },
228 {0,}
229 };
230 MODULE_DEVICE_TABLE(pci, netdrv_pci_tbl);
这里表示的就是本驱动程序支持的设备类型。
2.2.2 驱动-设备的匹配
1103 int __pci_register_driver(struct pci_driver *drv, struct module *owner,
1104 const char *mod_name)
1105 {
1106 int error;
1107
1108 /* initialize common driver fields */
1109 drv->driver.name = drv->name;
1110 drv->driver.bus = &pci_bus_type;
1111 drv->driver.owner = owner;
1112 drv->driver.mod_name = mod_name;
1113
1114 spin_lock_init(&drv->dynids.lock);
1115 INIT_LIST_HEAD(&drv->dynids.list);
1116
1117 /* register with core */
1118 error = driver_register(&drv->driver);
1119 if (error)
1120 goto out;
1121
。。。。。。。。
1137 }
222 int driver_register(struct device_driver *drv)
223 {
224 int ret;
225 struct device_driver *other;
226
227 BUG_ON(!drv->bus->p);
228
229 if ((drv->bus->probe && drv->probe) ||
230 (drv->bus->remove && drv->remove) ||
231 (drv->bus->shutdown && drv->shutdown))
232 printk(KERN_WARNING "Driver '%s' needs updating - please use "
233 "bus_type methods\n", drv->name);
234
235 other = driver_find(drv->name, drv->bus);
236 if (other) {
237 put_driver(other);
238 printk(KERN_ERR "Error: Driver '%s' is already registered, "
239 "aborting...\n", drv->name);
240 return -EBUSY;
241 }
242
243 ret = bus_add_driver(drv);
244 if (ret)
245 return ret;
246 ret = driver_add_groups(drv, drv->groups);
247 if (ret)
248 bus_remove_driver(drv);
249 return ret;
250 }
driver_register中调用bus_add_driver,将设备驱动加入�到总线上。
625 int bus_add_driver(struct device_driver *drv)
626 {
。。。。。。。。。。。。。
642 klist_init(&priv->klist_devices, NULL, NULL);
643 priv->driver = drv;
644 drv->p = priv;
645 priv->kobj.kset = bus->p->drivers_kset;
646 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
647 "%s", drv->name);
648 if (error)
649 goto out_unregister;
650
651 if (drv->bus->p->drivers_autoprobe) {
652 error = driver_attach(drv);
653 if (error)
654 goto out_unregister;
655 }
。。。。。。。。。。。。。。。
690 }
303 int driver_attach(struct device_driver *drv)
304 {
305 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
306 }
能够看出来,上面讲驱动程序追加到总线上之后,如今開始将设备与驱动进行匹配了。
285 int bus_for_each_dev(struct bus_type *bus, struct device *start,
286 void *data, int (*fn)(struct device *, void *))
287 {
。。。。。。。。。。。。
295 klist_iter_init_node(&bus->p->klist_devices, &i,
296 (start ? &start->p->knode_bus : NULL));
297 while ((dev = next_device(&i)) && !error)
298 <span style="color:#ff0000;">error = fn(dev, data);</span>
299 klist_iter_exit(&i);
300 return error;
301 }
这里的fn就是__driver_attach函数,我们来看一下它干了什么:
265 static int __driver_attach(struct device *dev, void *data)
266 {
267 struct device_driver *drv = data;
。。。。。。。。。。。。。。。
279 if (!driver_match_device(drv, dev))
280 return 0;
281
282 if (dev->parent) /* Needed for USB */
283 device_lock(dev->parent);
284 device_lock(dev);
285 if (!dev->driver)
286 driver_probe_device(drv, dev);
287 device_unlock(dev);
288 if (dev->parent)
289 device_unlock(dev->parent);
290
291 return 0;
292 }
279行的driver_match_device函数就是用来为driver匹配device的
108 static inline int driver_match_device(struct device_driver *drv,
109 struct device *dev)
110 {
111 return drv->bus->match ? drv->bus->match(dev, drv) : 1;
112 }
这里開始调用device_driver中注冊的match函数来进行匹配了,匹配的详细过程就不看了。再回到上面的__driver_attach函数的driver_probe_device中。
200 int driver_probe_device(struct device_driver *drv, struct device *dev)
201 {
202 int ret = 0;
203
204 if (!device_is_registered(dev))
205 return -ENODEV;
206
207 pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
208 drv->bus->name, __func__, dev_name(dev), drv->name);
209
210 pm_runtime_get_noresume(dev);
211 pm_runtime_barrier(dev);
212 ret = really_probe(dev, drv);
213 pm_runtime_put_sync(dev);
214
215 return ret;
216 }
108 static int really_probe(struct device *dev, struct device_driver *drv)
109 {
110 int ret = 0;
111
112 atomic_inc(&probe_count);
113 pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
114 drv->bus->name, __func__, drv->name, dev_name(dev));
115 WARN_ON(!list_empty(&dev->devres_head));
116
117 dev->driver = drv;
118 if (driver_sysfs_add(dev)) {
119 printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
120 __func__, dev_name(dev));
121 goto probe_failed;
122 }
123
124 if (dev->bus->probe) {
125 ret = dev->bus->probe(dev);
126 if (ret)
127 goto probe_failed;
128 } else if (drv->probe) {
129 ret = drv->probe(dev);
130 if (ret)
131 goto probe_failed;
132 }
133
。。。。。。。。。。。。。。
160 }
到这里,最终看到drv->probe函数了。驱动程序的probe函数開始运行了,驱动程序的注冊工作也就大功告成了。
3. 总结
linux设备驱动程序注冊过程具体解释的更多相关文章
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注冊流程具体解释
视频下载地址: 驱动注冊:http://pan.baidu.com/s/1i34HcDB 设备注冊:http://pan.baidu.com/s/1kTlGkcR 总线_设备_驱动注冊流程具体解释 • ...
- LINUX设备驱动程序笔记(三)字符设备驱动程序
<一>.主设备号和次设备号 对字符设备的訪问时通过文件系统内的设备名称进行的.那些设备名称简单称之为文件系统树的节点,它们通常位于/dev文件夹. 字符设备驱动程 ...
- Linux设备驱动程序学习之分配内存
内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...
- 【转】linux设备驱动程序中的阻塞机制
原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...
- Linux设备驱动程序 第三版 读书笔记(一)
Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...
- Linux设备驱动程序学习----2.内核模块与应用程序的对比
内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...
- Linux设备驱动程序学习----3.模块的编译和装载
模块的编译和装载 更多内容请参考Linux设备驱动程序学习----目录 1. 设置测试系统 第1步,要先从kernel.org的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...
- linux设备驱动程序-设备树(1)-dtb转换成device_node
linux设备驱动程序-设备树(1)-dtb转换成device_node 本设备树解析基于arm平台 从start_kernel开始 linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂 ...
- linux设备驱动程序-i2c(2)-adapter和设备树的解析
linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 在本系列linux内核i2c框架的前两篇,分别讲 ...
随机推荐
- javaku快捷键
Eclipse 的编辑功能非常强大,掌握了 Eclipse 快捷键功能,能够大大提高开发效率.Eclipse 中有如下一些和编辑相关的快捷键. 1. [ALT+/] 此快捷键为用户编辑的好帮手,能为用 ...
- ThinkPHP - 连贯操作 - 【实现机制】
<?php //模型类 class Model { //数据库连接 private $_conn = NULL; //where语句 private $_where = NULL; //表名称 ...
- D - 二叉树遍历(推荐)
二叉树遍历问题 Description Tree Recovery Little Valentine liked playing with binary trees very much. Her ...
- Ubuntu设置为命令行登录
root@ubuntu:~# vi /etc/default/grub 改: #GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMD ...
- linux下编码和vim编码问题解决
Linux下编码问题 在Linux环境下经常会出现文件乱码的问题,这实际上就是因为文件编码,以Ubuntu为例,默认的字符编码为UTF-8,并且没有默认安装gbk和gb2312,所以需要我们进行安装和 ...
- Android 开发 AirPlay Server
安卓上开发 AirPlay Server 主要是参考了和修改了 DroidAirPlay项目 , 和Airplay 协议 1, 将DroidAirPlay 下载下来 2, Eclipse 新建一个 ...
- nginx+apache 404错误页面
公司新系统 随风做的 给客户演示出错不想让客户看到 自动返回上一页面. 刚开始按照网上说的 在nginx 处理: # 定义错误提示页面 error_page 500 502 503 504 / ...
- 基于 JVMTI 实现 Java 线程的监控(转)
随着多核 CPU 的日益普及,越来越多的 Java 应用程序使用多线程并行计算来充分发挥整个系统的性能.多线程的使用也给应用程序开发人员带来了巨大的挑战,不正确地使用多线程可能造成线程死锁或资源竞争, ...
- linux mount挂载设备(u盘,光盘,iso等 )使用说明
对于新手学习,mount 命令,一定会有很多疑问.其实我想疑问来源更多的是对linux系统本身特殊性了解问题. linux是基于文件系统,所有的设备都会对应于:/dev/下面的设备.如: [cheng ...
- k路归并(败者树,记录败者)
败者树在外排序中用到,每加入一个数字时,调整树需要o(lgk),比较快.外排序过程主要分为两个阶段:(1)初始化各归并段写入硬盘,初识化的方法,可利用内排序方法还可以一种叫置换选择排序的方 ...