对于PCI的学习,在文章《初识PCI》和《再识PCI:一个PCI驱动实例》中有介绍,文中使用大量代码进行演示。但总觉得有些认知不到位。于是就再写一文。
一、PCI驱动一般框架
先看一下PCI驱动一般框架的示例代码:
1 | #include <linux/pci.h> |
在这里指定PCI设备ID,PCI_VDEVICE会组装厂家ID和PCI设备ID,后面的是驱动私有数据,可以传递必要的信息。当然也可不写。
1 | static const struct pci_device_id misc_pci_tbl[] = { |
二、几点说明
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这两个目录。