我的内核学习笔记6:PCI驱动probe的一点认知

    xiaoxiao2021-12-14  19

    对于PCI的学习,在文章《初识PCI》和《再识PCI:一个PCI驱动实例》中有介绍,文中使用大量代码进行演示。但总觉得有些认知不到位。于是就再写一文。

    一、PCI驱动一般框架

    先看一下PCI驱动一般框架的示例代码:

    #include <linux/pci.h> #include <linux/module.h> static int misc_pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { switch (dev->device) { case 0x0f1c: printk("found LPC pci device...\n"); break; case 0x0f12: printk("found SMBus pci device...\n"); // 此处可注册设备,如platform设备 // platform_device_register // 设置PCI驱动数据,在本驱动退出时,可获取到paltform设备从而卸载之 pci_set_drvdata(dev, pdev); break; default: printk("unknown pci device 0x%x...\n", dev->device); break; } return ret; } static void misc_pci_remove(struct pci_dev *dev) { // 得到platform设备,卸载 struct platform_device *pdev = pci_get_drvdata(dev); platform_device_unregister(pdev); } // 在这里指定PCI设备ID,PCI_VDEVICE会组装厂家ID和PCI设备ID,后面的是驱动私有数据,可以传递必要的信息。当然也可不写 static const struct pci_device_id misc_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x0f1c), 0 }, { PCI_VDEVICE(INTEL, 0x0f12), 1 }, // SMBus { }, }; MODULE_DEVICE_TABLE(pci, misc_pci_tbl); static struct pci_driver misc_pci_driver = { .name = "misc_pci", .id_table = misc_pci_tbl, .probe = misc_pci_probe, .remove = misc_pci_remove, }; // 注册PCI驱动 module_pci_driver(misc_pci_driver); MODULE_DESCRIPTION("PCI driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Late Lee<latelee@163.org>");

    二、几点说明

    1、内核启动时根据深度优先方式扫描PCI总线上的设备。比如,当前的PCI设备是PCI桥的话,就会继续扫描其下的设备,直到没有为止。

    2、在系统启动时,扫描PCI,找到PCI设备,如果该设备已经注册驱动,则调用驱动的probe函数。如果PCI设备已经有了对应的驱动,则不会再被调用。以上述代码为例,假如系统启动时已经有了ID为0x0f1和0x0f12的驱动,则上述的probe函数不会被调用到。可以认为当前已经有驱动占用着这2个设备。在《再识PCI:一个PCI驱动实例》一文中,还指定了E1000_DEV_ID_I211_COPPER这个ID,但没有识别出来。是因为其对应的IGB驱动被加载了。当然,如果当前不存在对应的驱动,就会调用到probe函数。

    3、有多个相同设备的也会继续调用probe。直到扫描结束。比如,有2个相同的网卡设备(即网卡芯片ID相同)处于不同的PCI总线情况时,它们对应pci_device_id只有一个ID。但它们在不同PCI总线上,当扫描到设备,会调用2次对应驱动的probe函数。

    三、bus(总线)、driver(驱动)、device(设备)

    顺着PCI驱动,再概述一下bus、driver、device这几个概念。

    一条bus上挂着许多device,而device需要driver才能工作。内核有注册device和注册driver的概念。注册device只是将deivce添加到内核中,该设备还不能工作。而注册driver,就是在对应的bus上找到device,从而调用driver的probe函数进行初始化,而后继续其它的事情。

    device和driver通过某些方式匹配,就能正常工作。比如platform设备根据name来匹配的,PCI设备根据ID匹配。所以看到很多内核代码在定义platform_device时指定name,而在定义platform_driver时指定driver成员的name,因为只有这样两者才能匹配。一般地,发行版本的Linux系统都带有很多驱动。如前段时间研究WIFI,将Intel无线网卡和atheros无线网卡插到安装有发行版本的系统的设备上,就可以正常工作。

    在/sys/bus/目录下有当前系统各种bus。比如i2c总线、PCI总线、SPI总线、platform总线。每种bus目录均有device和driver,在其中列出该总线上所有的设备和对应的驱动。比如platform设备、驱动分别在/sys/bus/platform/devices和/sys/bus/platform/drivers这两个目录。

    转载请注明原文地址: https://ju.6miu.com/read-963586.html

    最新回复(0)