功能:注册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 源码 网络驱动,Linux网络驱动源码分析(一)相关推荐

  1. linux安装comfast网卡驱动,电脑如何通过usb共享手机网络 Linux安装wifi 无线网络 811AC usb网卡驱动...

    电脑如何通过usb共享手机网络 该方法是通过USB线将手机和电脑连接的方式来共享网络,所以不管是笔记本电脑还是台式机,不管电脑有无线网卡,都可以使用该方法. 准备工作:首先用数据线把手机连接到电脑上, ...

  2. 嵌入式Linux(5):驱动开发网络调试驱动设备的Linux系统移植

    驱动开发之网络调试驱动设备的Linux系统移植 1.Uboot移植到开发板 uboot移植 2.开发板网络通讯 nfs命令 tftp命令 3.Linux移植到开发板 4.BusyBox 构建根文件系统 ...

  3. Linux驱动开发(十八)---网络(网卡)驱动学习

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

  4. 电脑如何通过usb共享手机网络 Linux安装wifi 无线网络 811AC usb网卡驱动

    电脑如何通过usb共享手机网络 该方法是通过USB线将手机和电脑连接的方式来共享网络,所以不管是笔记本电脑还是台式机,不管电脑有无线网卡,都可以使用该方法. 准备工作:首先用数据线把手机连接到电脑上, ...

  5. linux配置无线网卡驱动,Linux无线网络配置——无线网卡驱动安装与WLAN802.11配置...

    Linux无线网络配置--无线网卡驱动安装与WLAN 802.11配置 WLAN (Wireless Local Area Network)类似于有线以太网,WLAN 的 802.11a 标准使用 5 ...

  6. Linux下imx6dl开发板从镜像的烧写、内核源码编译到第一个驱动运行的详细步骤

    文章目录 前言 一.对开发板烧写镜像 1.镜像烧写 2.串口测试 二.搭建交叉编译环境 1.Ubuntu下搭建交叉编译环境 2.WSL下搭建交叉编译环境 三.编译Linux内核源码 1.Ubuntu下 ...

  7. Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)

    在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层 ...

  8. linux nmcli源码,Linux上利用nmcli命令创建网络组(示例代码)

    网络组:是将多个网卡聚合在一起方法,从而实现冗错和提高吞吐量 网络组不同于旧版中bonding技术,提供更好的性能和扩展性 网络组由内核驱动和teamd守护进程实现. 下面我们以CentOS7系统为环 ...

  9. (转)Linux设备驱动之HID驱动 源码分析

    //Linux设备驱动之HID驱动 源码分析 http://blog.chinaunix.net/uid-20543183-id-1930836.html HID是Human Interface De ...

  10. linux设备驱动开发详解源码,linux设备驱动开发详解光盘源码.rar

    压缩包 : linux设备驱动开发详解光盘源码.rar 列表 19/busybox源代码/busybox-1.2.1.tar.bz2 19/MTD工具/mtd-utils-1.0.0.tar.gz 1 ...

最新文章

  1. 德国波恩大学于鹏组根系与微生物互惠方向招收博士研究生
  2. 网站优化中导致关键词排名不稳定的原因有哪些?
  3. xpath IE 7
  4. 芯片里的CPU、GPU、NPU是什么,它们是如何工作的
  5. pythondistutils安装_安装msi后的python distutils
  6. Study to Innovation 的一般步骤总结
  7. 指针和指针的指针_网络上的iPad指针
  8. 18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请问你是怎么调度的咩
  9. 虚拟机安装mac os x实战
  10. 新手入门:史上最全Web端即时通讯技术原理详解
  11. 5G套餐月资费感受下:最低325元 仅提供8GB数据流量
  12. 记录——《C Primer Plus (第五版)》第七章编程练习第四题
  13. MENTOR 安装过程
  14. 信息安全实验二、漏洞扫描与病毒防治
  15. 苹果ios8_一款苹果手机上目前体验还不错的免费小说软件,支持一键缓存
  16. 打开MPP格式文件的十种方法
  17. 设计一个算法,通过一趟遍历确定长度为n的但链表的中值的最大的节点
  18. 10G 网络变压器 10GBASE-T与1000Base-T区别
  19. 英语语法三大从句刷题总结
  20. (一)OSG初学者入门基础教程

热门文章

  1. 修改服务器时间需要重启吗,云服务器需要定期重启吗
  2. mysql stop salve_MySQL主从切换
  3. ORACLE 正值表达式
  4. java spring源码_spring源码分析-spring中的bean
  5. spring mvc和rest风格小例子
  6. ubuntu 安装php redis,ubuntu上安装php redis
  7. Ubuntu下安装Rabbitmq和golang环境
  8. codeforces 719A Vitya in the Countryside(序列判断趋势)
  9. [HTML5 Canvas学习]绘制矩形
  10. .net 4.0新特性-tuple