尊天命,尽人事
分类:
2011-08-23 19:48:48
原文地址:Linux2.6PCI驱动加载原理 作者:CUDev
所有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 or PCI_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一般用数组形式。如:
static struct pci_device_id rtl8139_pci_tbl[] = {
{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
....
};
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
MODULE_DEVICE_TABLE (pci, e1000_pci_tbl);
#define (~0)
#define (,dev) \
. = (), .device = (dev), \
. = , .subdevice =
这个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 Fast Ethernet driver
author: Jeff Garzik
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 SMP mod_unload 586
parm: debug:8139too bitmapped message enable number (int)
parm: multicast_filter_limit:8139too maximum number of filtered multicast addresses (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
目录 /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-mixart 0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0就可以确定该设备的驱动为snd-mixart,然后就可以写modprobe.conf文件,加载核心模块 snd-mixart,而不需要对该设备有更多的了解。
下面是 modules.pcimap中的一部分,现在对其进行说明:
snd-mixart 0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0那么我们可以认为模块nd-mixart支持所有vendor=0x00001057、device=0x00000003 的设备;如果vendor为0xffffffff,如:
8139too 0xffffffff 0x00008139 0x000010ec 0x00008139 0x00000000 0x00000000 0x0那么我们可以认为模块8139too支持所有device=0x00008139、subvendor=0x000010ec、subdevice=0x00008139的设备。
/lib/modules/`uname -r`/modules.pcimap文件的部分内容:
# pci module vendor device subvendor subdevice class class_mask driver_data
snd-mixart 0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
snd-ens1371 0x00001274 0x00001371 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
snd-ens1371 0x00001274 0x00005880 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
8139too 0xffffffff 0x00008139 0x000010ec 0x00008139 0x00000000 0x00000000 0x0
pci_dev,标识具体的PCI设备实例,与net_device类似。内核通过该内核结构来访问具体的PCI设备。在include/linux/pci.h头文件中定义。
pci_driver,设备驱动程序数据结构,它是驱动程序与PCI总线的接口,有大量的回调函数和指针,向PCI核心描述了PCI驱动程序。在include/linux/pci.h头文件中定义。
static struct pci_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设备的功能。
参考: