Linux2.6PCI驱动加载原理
pci设备上电时,硬件保持未激活状态。设备不会有内存和I/O端口映射到计算机的地址空间。每个PCI主板上都配备有能够处理PCI的BIOS、NVRAM或PROM等固件。这些固件通过读写PCI控制器中的寄存器,提供了对设备配置地址空间的访问。系统引导时,固件在每个PCI设备上执行配置事务,以便为它提供的每个地址区域分配一个安全的位置。当驱动程序访问设备时,它的内存和 I/O区域已经被映射到了处理器的地址空间。
所有PCI设备都有至少256字节的地址空间。前64字节是标准化的,每种设备都有且意义相同,其余字节是设备相关的。
在内核中有三个主要的数据结构与PCI接口有关,在开发PCI设备驱动程序时要用到,分别是:
· pci_device_id,PCI设备类型的标识符。在include/linux/mod_devicetable.h头文件中定义。
struct pci_device_id{
__u32 vendor, device; /* Vendor and device ID orPCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
PCI设备的vendor、device和class的值都是预先定义好的,通过这些参数可以唯一确定设备厂商和设备类型。这些PCI设备的标准值在include/linux/pci_ids.h头文件中定义。
pci_device_id需要导出到用户空间,使模块装载系统在装载模块时知道什么模块对应什么硬件设备。宏MODULE_DEVICE_TABLE()完成该工作。
设备id一般用数组形式。如:
· staticstruct pci_device_id rtl8139_pci_tbl[] = {
{0x10ec, 0x8139, PCI_ANY_ID,PCI_ANY_ID, 0, 0, RTL8139 },
....
};
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
· static struct pci_device_id e1000_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1000) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1001) },
{0,}
};
MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
#define PCI_ANY_ID (~0)
#define PCI_DEVICE(vend,dev) \
.vendor = (vend), .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
这个pci_device_id结构体需要被导出到用户空间,使热插拔和模块装载系统知道什么模块针对什么硬件设备。宏MODULE_DEVICE_TABLE完成这个工作。例如上面的
MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
该语句创建一个名为__mod_pci_device_table的局部变量,只想struct pci_device_id数组。在稍后的内核构建过程中,depmod程序在所有的模块中搜索符号__mod_pci_device_table。如果找到了该符号,它把数据从该模块中抽出,添加到文件/lib/module/KERNEL_VERSION/module.pcimap中。当depmod结束之后,内核模块支持的所有PCI设备连同他们的模块名都在改文件中被列出。当内核告知热插拔系统一个新的PCI设备已经被发现时,热插拔系统使用module.pcimap文件来寻找要加载的适当的驱动程序。
使用这种方式编写的kernel modules,使用modinfo查看的结果如下:
root@dell-desktop:/lib/modules/2.6.24-16-generic#modinfo 8139too
filename: /lib/modules/2.6.24-16-generic/kernel/drivers/net/8139too.ko
version: 0.9.28
license: GPL
description: RealTek RTL-8139 FastEthernet driver
author: Jeff Garzik<[email protected]>
srcversion: 69C244F6E1FEDCB14385855
alias: pci:v*d00008139sv000013D1sd0000AB06bc*sc*i*
alias: pci:v*d00008139sv00001186sd00001300bc*sc*i*
alias: pci:v*d00008139sv000010ECsd00008139bc*sc*i*
alias: pci:v000010ECd00008129sv*sd*bc*sc*i*
alias: pci:v0000021Bd00008139sv*sd*bc*sc*i*
alias: pci:v00001743d00008139sv*sd*bc*sc*i*
alias: pci:v0000126Cd00001211sv*sd*bc*sc*i*
alias: pci:v0000018Ad00000106sv*sd*bc*sc*i*
alias: pci:v000002ACd00001012sv*sd*bc*sc*i*
alias: pci:v00001432d00009130sv*sd*bc*sc*i*
alias: pci:v000011DBd00001234sv*sd*bc*sc*i*
alias: pci:v000014EAd0000AB07sv*sd*bc*sc*i*
alias: pci:v000014EAd0000AB06sv*sd*bc*sc*i*
alias: pci:v00001259d0000A11Esv*sd*bc*sc*i*
alias: pci:v00001259d0000A117sv*sd*bc*sc*i*
alias: pci:v000013D1d0000AB06sv*sd*bc*sc*i*
alias: pci:v00001186d00001340sv*sd*bc*sc*i*
alias: pci:v00001186d00001300sv*sd*bc*sc*i*
alias: pci:v00004033d00001360sv*sd*bc*sc*i*
alias: pci:v00001500d00001360sv*sd*bc*sc*i*
alias: pci:v00001113d00001211sv*sd*bc*sc*i*
alias: pci:v000010ECd00008138sv*sd*bc*sc*i*
alias: pci:v000010ECd00008139sv*sd*bc*sc*i*
depends: mii
vermagic: 2.6.24-16-generic SMPmod_unload 586
parm: debug:8139too bitmappedmessage enable number (int)
parm: multicast_filter_limit:8139too maximum number of filtered multicastaddresses (int)
parm: media:8139too: Bits 4+9:force full duplex, bit 5: 100Mbps (array of int)
parm: full_duplex:8139too:Force full duplex for board(s) (1) (array of int)
/lib/modules/KERNEL_VERSION/modules.pcimap文件列出内核所支持的所有PCI设备和它们的模块名。
debian:/lib/modules/2.6.24-16-generic#cat modules.pcimap | grep 8139too
8139too 0x000010ec 0x00008139 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000010ec 0x00008138 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001113 0x00001211 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001500 0x00001360 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00004033 0x00001360 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001186 0x00001300 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001186 0x00001340 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000013d1 0x0000ab06 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001259 0x0000a117 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001259 0x0000a11e 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000014ea 0x0000ab06 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000014ea 0x0000ab07 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000011db 0x00001234 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001432 0x00009130 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000002ac 0x00001012 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x0000018a 0x00000106 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x0000126c 0x00001211 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x00001743 0x00008139 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x0000021b 0x00008139 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0x000010ec 0x00008129 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0xffffffff 0x00008139 0x000010ec 0x00008139 0x00000000 0x00000000 0x0
8139too 0xffffffff 0x00008139 0x00001186 0x00001300 0x00000000 0x00000000 0x0
8139too 0xffffffff 0x00008139 0x000013d1 0x0000ab06 0x00000000 0x00000000 0x0
目录 /lib/modules/`uname -r`/下的modules.*map文件都是由depmod命令生成的。depmod通过查找目录 /lib/modules/`uname -r`/下的核心模块中的数据建立的。里面包含的是各个核心模块和它所支持的具体硬件设备的对应关系。而这些对应关系是硬件驱动的作者在核心模块时候确定的。这种机制一般不会出现什么问题:没有人能够比驱动作者更清楚它要支持那些硬件。
利用这些映射文件,各种硬件管理程序就可以对硬件作出智能化的判断,即使他们本身对自己所处理的硬件设备并不熟悉。比如,kudzu通过探测系统总线,发现PCI设备,其vendorID和diviceID分别为1057:0003。kudzu通过查找/lib/modules /`uname -r`/modules.pcimap文件,根据如下行:
snd-mixart0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
就可以确定该设备的驱动为snd-mixart,然后就可以写modprobe.conf文件,加载核心模块 snd-mixart,而不需要对该设备有更多的了解。
/lib/modules/`uname -r`/modules.*map文件格式说明
下面是 modules.pcimap中的一部分,现在对其进行说明:
1. 以#开始的是注释,文件开头的注释说明了文件每一行各个域的意义,依次为:PCI模块名、生产商ID、设备ID、次生产商ID、次设备ID、类型编号、类型掩码、驱动数据。
2. ID使用8个十六进制数字表示(前面的0x表示是十六进制),而lspci显示的是4位十六进制数字。只要lspci结果与这些ID的低4位相同就可以认为它们相同。即:lspci的结果1057与modules.pcimap中的 0x00001057相同。
3. 如果subvendor和subdevice均为0xffffffff,如:
snd-mixart0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
那么我们可以认为模块nd-mixart支持所有vendor=0x00001057、device=0x00000003 的设备;如果vendor为0xffffffff,如:
8139too 0xffffffff 0x00008139 0x000010ec 0x000081390x00000000 0x00000000 0x0
那么我们可以认为模块8139too支持所有device=0x00008139、subvendor=0x000010ec、subdevice=0x00008139的设备。
4. 一个模块可以对应多个设备,说明这些设备均使用同一个驱动模块。
/lib/modules/`uname -r`/modules.pcimap文件的部分内容:
# pci module vendor device subvendor subdevice class class_mask driver_data
snd-mixart 0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x000000000x0
snd-ens1371 0x00001274 0x00001371 0xffffffff 0xffffffff 0x00000000 0x000000000x0
snd-ens1371 0x00001274 0x00005880 0xffffffff 0xffffffff 0x00000000 0x000000000x0
8139too 0xffffffff 0x00008139 0x000010ec 0x00008139 0x00000000 0x00000000 0x0
比较要注意的是 modules.pcimap,如果你详细看modules.pcimap这个文件的话,会发现一些相同的 vendor:device,但是却对应这两种不同的 modules。例如使用 10ec:8139 去寻找,会同时找到 8139cp 和 8139too。诸如这样的情况还有很多,在比较modules.pcimap文件时候应该遵循下面的原则:
比较的优先顺序应该是:
1.寻找vendor:device 和 subvendor:subdevice 相同的专用module
2.寻找device 和 subvendor:subdevice 相同的通用module
3.寻找device建议module
4.寻找 vendor:device 标准module
· pci_dev,标识具体的PCI设备实例,与net_device类似。内核通过该内核结构来访问具体的PCI设备。在include/linux/pci.h头文件中定义。
· pci_driver,设备驱动程序数据结构,它是驱动程序与PCI总线的接口,有大量的回调函数和指针,向PCI核心描述了PCI驱动程序。在include/linux/pci.h头文件中定义。
static structpci_driver rtl8139_pci_driver = {
.name = DRV_NAME, #设备名
.id_table = rtl8139_pci_tbl, #pci设备的id表组
.probe = rtl8139_init_one, #初始化函数
.remove =__devexit_p(rtl8139_remove_one), #退出函数
#ifdef CONFIG_PM #如果设备支持电源管理
.suspend = rtl8139_suspend, #休眠
.resume = rtl8139_resume, #从休眠恢复
#endif /* CONFIG_PM */
};
内核通过pci_register_driver和pci_unregister_driver函数来注册和注消PCI设备驱动程序。这两个函数在drivers/pci/pci.c源码中定义。pci_register_driver函数需要使用 pci_driver数据结构作为参数。通过注册,PCI设备就与PCI设备驱动程序关联起来了。
PCI设备最大的优点是可以自动探测每个设备所需的IRQ和其它资源。有两种探测方式,一种是静态探测,一种是动态探测。静态探测是通过设备驱动程序自动选择相关资源,动态探测是指支持热插拔设备的功能。
PCI设备通过pci_driver结构中的suspend和resume函数指针支持电源管理。可实现暂停和重新启动PCI设备的功能。
参考:
Linux设备驱动第12章 MODULE_DEVICE_TABLE小节
http://hi.baidu.com/xdebian/blog/item/c27cb503eb4304763812bbd9.html
http://fred-zone.blogspot.com/2007/11/linux-pci.html
http://203.208.39.99/search?q=cache:T7HcgoDIx9wJ:wiki.os-drive.com/bin/view/Main/LinuxDriver+modules.pcimap&hl=zh-CN&ct=clnk&cd=10&gl=cn&st_usg=ALhdy28wKf_dSuMGEoQOPYvbjvS6_oUReQ
http://www.ringkee.com/note/opensource/kernel/kernel.htm#id2810163