转自http://blog.csdn.net/ustc_dylan/article/details/6329375
网络驱动是一种典型的PCI设备驱动,无论在嵌入式平台还是在PC领域,网络相关的项目开发有着比较广阔的前景,因此,分析当前Linux内核中网络设备的驱动,不但能了解网络相关的基本原理,而且可以借鉴Linux内核的先进的技术,将其应用到嵌入式或其他领域。本文以Linux内核中的rtl8139网络驱动为例,对网络驱动的源码进行了简单分析,并对其中涉及的相关概念和技术进行了简单的介绍。
一、PCI设备驱动模型
rtl8139是典型的PCI设备,Linux内核的PCI核心驱动为PCI驱动开发者提供了方便的系统接口,极大地方便了PCI设备驱动的开发。
1 pci设备驱动相关的数据结构
- struct pci_driver {
- struct list_head node; /*用于连接入pci驱动列表*/
- char *name; /*pci驱动的名称*/
-
const struct pci_device_id *id_table; /* must be non-NULL for probe to be called *//*该驱动支持的pci设备*/
-
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
- void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
-
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
-
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
-
int (*resume_early) (struct pci_dev *dev);
-
int (*resume) (struct pci_dev *dev); /* Device woken up */
- void (*shutdown) (struct pci_dev *dev);
- struct pci_error_handlers *err_handler;
- struct device_driver driver;
- struct pci_dynids dynids;
-
};
pci设备描述结构体
驱动开发者要想为某个PCI设备开发驱动就必须定义一个与当前PCI设备相对应的pci_driver数据结构,用来描述将要开发的pci驱动的相关信息,比如驱动的名称,当前驱动可以支持哪些设备,以及当前驱动支持的一些操作等,类似地,还需要有个结构体来表示PCI设备,描述PCI设备的硬件信息,如厂商ID,设备ID,以及各种资源等,详见注释。
二、PCI核心驱动API
Linux内核的PCI驱动为PCI设备驱动的开发提供了方便的结构,下面列举几个常用的接口:
pci_register_driver(struct pci_driver *drv)
功能:注册PCI驱动,参数为要注册的pci驱动的结构体。
下面来详细的分析以下这个函数,如此,才能更清楚的了解驱动和设备的匹配过程。
- pci_register_driver->driver_register(&drv->driver);->bus_add_driver->driver_attach->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
在这个过程中有涉及到一个更为抽象的结构体struct device_driver,它是pci_driver的更高级的抽象,即下层是pci_driver,其上是device_driver,这符合通常的程序设计逻辑,越往上层抽象级别越高,因为在操作系统看来,它并不需要知道具体是什么设备,所有的设备对操作系统来说都是相同的,即都用struct device_driver来表示。
在driver_register中先调用driver_find(drv->name, drv->bus),首先在相应的总线上查找drv->name的驱动是否已经被注册过,如果被注册过则返回,否则进行注册过程,即调用bus_add_driver(drv)。
int bus_add_driver(struct device_driver *drv)函数首先判断当前总线是否支持自动探测,如果执行则执行探测函数driver_attach(drv)。
- if (drv->bus->p->drivers_autoprobe) {
-
error = driver_attach(drv);
-
if (error)
- goto out_unregister;
-
}int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
这个函数对PCI总线的上所有已经连接的PCI设备与当前的PCI驱动进程一次匹配的过程,即对每一个PCI设备都调用匹配函数__driver_attach。
- static int __driver_attach(struct device *dev, void *data)
- {
- struct device_driver *drv = data;
-
/*
-
* Lock device and try to bind to it. We drop the error
-
* here and always return 0, because we need to keep trying
-
* to bind to devices and some drivers will return an error
-
* simply if it didn't support the device.
-
*
-
* driver_probe_device() will spit a warning if there
-
* is an error.
-
*/
-
if (!driver_match_device(drv, dev))
- return 0;
-
if (dev->parent) /* Needed for USB */
- device_lock(dev->parent);
- device_lock(dev);
-
if (!dev->driver)
- driver_probe_device(drv, dev);
- device_unlock(dev);
-
if (dev->parent)
- device_unlock(dev->parent);
- return 0;
- }
该函数首先判断总线提供的match函数是否为空,如果非空则执行总线提供的match函数,在rtl8139网络驱动中,match非空,参见代码:
- drv->driver.bus = &pci_bus_type;struct bus_type pci_bus_type = {
-
.name = "pci",
-
.match = pci_bus_match,
-
.uevent = pci_uevent,
-
.probe = pci_device_probe,
-
.remove = pci_device_remove,
-
.shutdown = pci_device_shutdown,
-
.dev_attrs = pci_dev_attrs,
-
.bus_attrs = pci_bus_attrs,
-
.pm = PCI_PM_OPS_PTR,
-
};
这里将match函数即pci_bus_match,最后该函数调用到
- static inline const struct pci_device_id *
- pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
- {
-
if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
-
(id->device == PCI_ANY_ID || id->device == dev->device) &&
-
(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
-
(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
-
!((id->class ^ dev->class) & id->class_mask))
- return id;
- return NULL;
- }
在这里进行了pci_driver和pci_dev的匹配,如果匹配成功,则返回pci_device_id。如果匹配不成功,而且当前设备还没有驱动,则调用driver_probe_device(drv,dev)。
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
-
int ret = 0;
-
if (!device_is_registered(dev))
- return -ENODEV;
- pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- pm_runtime_get_noresume(dev);
- pm_runtime_barrier(dev);
- ret = really_probe(dev, drv);
- pm_runtime_put_sync(dev);
- return ret;
- }
执行到这里说明,说明PCI总线没有提供match函数或者总线提供的match函数返回非空。还需要进行更深层次的探测,至少在总线提供的match函数中仅仅是进行了匹配,并没有将驱动和设备关联起来,这些操作就是在下面的函数中实现的。
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
-
int ret = 0;
-
if (!device_is_registered(dev))
- return -ENODEV;
- pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- pm_runtime_get_noresume(dev);
- pm_runtime_barrier(dev);
- ret = really_probe(dev, drv);
- pm_runtime_put_sync(dev);
- return ret;
- }
重点看really_probe函数:
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
-
int ret = 0;
- atomic_inc(&probe_count);
- pr_debug("bus: '%s': %s: probing driver %s with device %s/n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
- dev->driver = drv;
-
if (driver_sysfs_add(dev)) {
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
- __func__, dev_name(dev));
- goto probe_failed;
-
}
-
if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
-
if (ret)
- goto probe_failed;
-
} else if (drv->probe) {
- 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_name(dev), drv->name);
- goto done;
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
-
if (ret != -ENODEV && ret != -ENXIO) {
-
/* driver matched but the probe failed */
- printk(KERN_WARNING
-
"%s: probe of %s failed with error %d/n",
- drv->name, dev_name(dev), ret);
-
}
-
/*
-
* Ignore errors returned by ->probe so that the next driver can try
-
* its luck.
-
*/
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
在此函数中,首先将驱动和设备关联起来,即红色代码dev->driver = drv; 指明了当前设备的驱动。按照常规的程序设计思想,驱动和设备关联后是否还需要做一些其他工作才能是设备在相应的驱动下正常工作呢,这就是probe函数实现的功能了,很明显设备和驱动获取都需要做一些工作,因此这里分别留出设备和驱动的probe函数。其中设备的probe即设备所在总线的probe,这里暂且不去分析,因为与网络驱动关系不大,都是PCI总线相关的东西,重点来看驱动的probe,在前面提到的pci_driver结构体中,对于rtl8139驱动来说,其pci_driver结构体被初始化为:
- static struct pci_driver rtl8139_pci_driver = {
-
.name = DRV_NAME,
-
.id_table = rtl8139_pci_tbl,
-
.probe = rtl8139_init_one,
-
.remove = __devexit_p(rtl8139_remove_one),
- #ifdef CONFIG_PM
-
.suspend = rtl8139_suspend,
-
.resume = rtl8139_resume,
- #endif /* CONFIG_PM */
-
};
这里即调用rtl8139_init_one,经过上面的逐层分析,我们从pci核心驱动一步一步的走到了rtl8139网络设备的驱动,豁然开朗了,以后看网络驱动的时候就不会感到开始的地方有点迷糊了。代码分析重在代码之间的过渡,如果衔接不好,很多地方都会产生疑问。在下一篇文章中,将会详细分析rtl8139网络驱动的代码。
分享到:
相关推荐
Linux常见驱动源码分析(kernel hacker修炼之道)--李万鹏 李万鹏 IBM Linux Technology Center kernel team 驱动资料清单内容如下: Linux设备模型(中)之上层容器.pdf Linux设备模型(上)之底层模型.pdf Linux...
Linux下网卡驱动程序源码分析: Linux下网卡驱动程序源码分析很详细
dm9000网卡驱动分析 dm9000网卡驱动分析 dm9000网卡驱动分析 dm9000网卡驱动分析 dm9000网卡驱动分析
深入讲解了Linux下额e1000千兆网卡驱动的总体框架、工作原理及详细流程,对网卡驱动理解、改造及开发有极大的帮助。
本源码官网提供RTL8192系列网卡驱动源码。 经测试可正常使用。
第六讲 Linux操作系统全面分析 1. Linux常用命令 2. vi编辑器 3. gcc编译器 4. make工具使用,makefile编写 5. shell编程 Linux系统编程专题 第七讲 建立交叉编译环境 1. 编译原理,gcc的使用 2. 交叉编译原理 3. ...
1、让你的linux接入互联网,如果你是虚拟机安装,可以采用桥接本地网卡的方式上网(课堂上有介绍该方法)。如果你是独立安装linux请自行解决。配置好IP地址和网关,DNS设置两个(8.8.8.8和114.114.114.114)。 2...
3.7 网卡驱动部分.....................................................................................................................13 3.8 Nand Flash 驱动部分........................................
(无需积分)C语言实战105例源码 其他的都是要分值的,转来的不要积分 第1部分 基础篇 实例1 一个价值“三天”的BUG 2 实例2 灵活使用递增(递减)操作符 5 实例3 算术运算符计算器 7 实例4 逻辑运算符...
27 银行事件驱动模拟程序 28 模拟迷宫探路 29 实现高随机度随机序列 30 停车场管理系统 31 菜单实现 32 窗口制作 33 模拟屏幕保护程序 34 文件读写基本操作 35 格式化读写文件 36...
实例27 银行事件驱动模拟程序 84 实例28 模拟迷宫探路 87 实例29 实现高随机度随机序列 89 实例30 停车场管理系统 91 第3部分 文本屏幕与文件操作篇 实例31 菜单实现 96 实例32 窗口制作 97 实例33 ...