Linux内核:Pci设备驱动——pci设备驱动实例(realtek8168)

Realtek8168网卡时pci接口的网卡,其驱动程序就是一个PCI设备的驱动程序实例,我们一起看看其流程。

1. 首先,初始化模块调用static inline int pci_register_driver(struct pci_driver *driver)函数来注册设备驱动,这个函数的参数是struct pci_driver *driver,对应于r8168,就是

static struct pci_driver rtl8168_pci_driver = {

.name = MODULENAME,

.id_table = rtl8168_pci_tbl,

.probe = rtl8168_init_one,

.remove = __devexit_p(rtl8168_remove_one),

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)

.shutdown = rtl8168_shutdown,

#endif

#ifdef CONFIG_PM

.suspend = rtl8168_suspend,

.resume = rtl8168_resume,

#endif

};

这个结构体把这个设备驱动所支持的设备(rtl8168_pci_tbl),探测函数(rtl8168_init_one)等都定义好,后面我们将需要用到rtl8168_pci_tbl,rtl8168_init_one两部分内容来匹配,是否系统中的设备,看是否有设备可以跟这个驱动匹配

2. pci_register_driver 函数调用__pci_register_driver来完成任务,而__pci_register_driver则重新封装了要注册的驱动为PCI总线的,即

int __pci_register_driver(struct pci_driver *drv, struct module *owner)

{

……

drv->driver.bus = &pci_bus_type;

……

drv->driver.kobj.ktype = &pci_driver_kobj_type;

……

}

接下来就是调用设备驱动模型的函数,把我们要注册的驱动挂载到PCI总线的设备队列上,并扫描PCI总线的设备队列,查看是否有设备可以匹配这个驱动,这跟usb设备驱动的挂载是一致的,只是这里挂载的是PCI总线,usb挂载的是USB总线,大致的流程是

driver_register()---àbus_add_driver()----àdriver_attach()--à__driver_attach()--àdriver_probe_device()---àdev->bus->probe(),即最后还是调用了2.中的pci_bus_type结构体中的probe成员函数,即static int pci_device_probe(struct device * dev)

3. static int pci_device_probe(struct device * dev)函数的参数dev就是遍历了PCI总线上的设备链表,一一进行匹配来完成的,因为我们调用__driver_attach()的方式是bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

4. static int pci_device_probe(struct device * dev)通过两个宏转换to_pci_driver,to_pci_dev,获得需要匹配的设备和驱动,调用static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)函数进行匹配

5. __pci_device_probe函数首先通过设备驱动中的rtl8168_pci_tbl表,跟从设备获得vendorID,productID进行比较,看是否一致,如果一致,就返回这个表的地址;如果没有一致的,就表明,这个设备跟这个驱动不匹配,就不需要继续进行下面的操作了,直接退出

6. 如果第5步发现了一致的设备表,就表明有设备ID一致,需要进一步探测,接下来就要调用我们设备驱动程序中的探测函数,进行更具体的探测了,即pci_call_probe(drv, pci_dev, id)---à drv->probe(dev, id),到这里,就开始调用我们的设备驱动中的探测函数了。

7. static int __devinit rtl8168_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)函数是r8168的探测函数,其调用rtl8168_init_board(pdev, &dev, &ioaddr)来完成跟PCI设备驱动相关的探测。

8. static int __devinit rtl8168_init_board(struct pci_dev *pdev, struct net_device **dev_out, void __iomem **ioaddr_out)函数调用pci_enable_device函数来使能PCI设备,只有使能成功的PCI设备,才能正常使用。

9. 调用pci_set_mwi函数判断设备是否支持memory-write-invalidate 功能

10. 调用pci_find_capability函数来判断设备是否有电源管理功能.

11. 调用pci_resource_flags函数来判断PCI是内存映射模式,还是IO模式

12. 调用pci_resource_len函数来判断内存空间是否小于设备所需要的内存空间,如果小于,明显出错

13. 调用pci_request_regions函数通知内核,当前PCI将使用这些内存地址,其他设备不能再使用了

14. 调用pci_set_master(pdev)函数,设置设备具有获得总线的能力,即调用这个函数,使设备具备申请使用PCI总线的能力。

15. 调用ioremap函数把刚刚申请的物理内存,映射成虚拟内存,因为进程使用的都是虚拟内存地址,而不是物理内存地址。

16. 把ioremap映射的虚拟内存返回给调用函数。

17. 到此,跟PCI相关的初始化都完成了,设备即可正常工作了

原文作者:image eye

原文地址:Pci设备驱动1:pci设备驱动实例(realtek8168) - image eye - 博客园(版权归原文作者所有,侵权留言联系删除)

你可能感兴趣的:(linux,Linux内核,设备驱动)