Linux2.6PCI驱动加载原理

Linux2.6PCI驱动加载原理

pci
设备上电时,硬件保持未激活状态。设备不会有内存和I/O端口映射到计算机的地址空间。每个PCI主板上都配备有能够处理PCIBIOSNVRAMPROM等固件。这些固件通过读写PCI控制器中的寄存器,提供了对设备配置地址空间的访问。系统引导时,固件在每个PCI设备上执行配置事务,以便为它提供的每个地址区域分配一个安全的位置。当驱动程序访问设备时,它的内存和 I/O区域已经被映射到了处理器的地址空间。

所有PCI设备都有至少256字节的地址空间。前64字节是标准化的,每种设备都有且意义相同,其余字节是设备相关的。

在内核中有三个主要的数据结构与PCI接口有关,在开发PCI设备驱动程序时要用到,分别是:

·        pci_device_idPCI设备类型的标识符。在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设备的vendordeviceclass的值都是预先定义好的,通过这些参数可以唯一确定设备厂商和设备类型。这些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<jgarzik@pobox.com>
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设备,其vendorIDdiviceID分别为1057:0003kudzu通过查找/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的结果1057modules.pcimap中的 0x00001057相同

3.     如果subvendorsubdevice均为0xffffffff,如:

snd-mixart0x00001057 0x00000003 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

那么我们可以认为模块nd-mixart支持所有vendor=0x00001057device=0x00000003 的设备;如果vendor0xffffffff,如:

8139too    0xffffffff 0x00008139 0x000010ec 0x000081390x00000000 0x00000000 0x0

那么我们可以认为模块8139too支持所有device=0x00008139subvendor=0x000010ecsubdevice=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_driverpci_unregister_driver函数来注册和注消PCI设备驱动程序。这两个函数在drivers/pci/pci.c源码中定义。pci_register_driver函数需要使用 pci_driver数据结构作为参数。通过注册,PCI设备就与PCI设备驱动程序关联起来了。

PCI设备最大的优点是可以自动探测每个设备所需的IRQ和其它资源。有两种探测方式,一种是静态探测,一种是动态探测。静态探测是通过设备驱动程序自动选择相关资源,动态探测是指支持热插拔设备的功能。

PCI设备通过pci_driver结构中的suspendresume函数指针支持电源管理。可实现暂停和重新启动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

<think>嗯,用户想了解Excel中的VLOOKUP函数的用法和参数含义。首先,我需要回忆一下VLOOKUP的基本结构。VLOOKUP是垂直查找函数,用于在表格的首列查找指定的值,然后返回该行中指定列的数据。它的四个参数分别是查找值、表格范围、列索引号和匹配模式。 接下来,我得确认每个参数的具体作用。第一个参数是lookup_value,也就是用户要查找的值,比如产品ID或者姓名。第二个参数是table_array,即查找的范围,这里要注意的是,查找值必须位于这个范围的第一列,否则函数无法正确工作。第三个参数是col_index_num,也就是用户想要返回的数据在表格范围中的第几列,这个列数是从表格范围的第一列开始算起的,不是整个工作表的列。第四个参数是range_lookup,决定是精确匹配还是近似匹配,通常用FALSE或0来指定精确匹配,避免错误。 然后,用户可能需要一个具体的例子来理解如何使用。比如,假设有一个产品价格表,用户想根据产品ID查找对应的价格。这时候,VLOOKUP就可以在ID列查找,然后返回价格列的数据。同时,要提醒用户注意表格范围最好使用绝对引用,比如$A$2:$C$100,这样在拖动公式时范围不会变化。 可能遇到的问题也需要考虑。比如,如果查找值不在表格的第一列,或者列索引号超过了表格的列数,函数会返回错误。此外,数据格式不一致也可能导致问题,比如查找值是数字,而表格中是文本格式的数字,这时候需要统一格式。还有,当出现#N/A错误时,可能是查找值不存在,或者匹配模式设置不正确,需要检查参数是否正确。 用户可能还会问到如何避免常见的错误,比如部分数据匹配出错的情况。这时候需要检查是否有多余的空格,或者数据格式不一致,或者表格范围没有锁定导致拖动时偏移。此外,XLOOKUP作为VLOOKUP的升级版,可能在某些情况下更方便,比如可以向左查找或者返回多列数据,这也是可以提及的内容。 最后,整理步骤,从基础语法到参数说明,再到具体案例,逐步引导用户理解如何使用VLOOKUP函数。同时,提供常见错误和解决方法,帮助用户在实际应用中避免问题。如果有必要,还可以比较VLOOKUP和其他查找函数的区别,比如HLOOKUP或XLOOKUP,但根据用户的问题,主要集中在VLOOKUP本身即可。</think>### Excel VLOOKUP函数详解 #### 一、基础语法与参数说明 VLOOKUP函数的语法为: $$=VLOOKUP(lookup\_value, table\_array, col\_index\_num, [range\_lookup])$$ 包含4个参数: 1. **lookup_value**(必填):要查找的值(如单元格引用或具体值) 2. **table_array**(必填):包含数据的表格范围(必须包含查找列和返回列) 3. **col_index_num**(必填):返回值所在列的序号(从table_array第一列开始计数) 4. **range_lookup**(可选):匹配类型 - `TRUE`/`1`:近似匹配(默认值,需数据升序排列) - `FALSE`/`0`:精确匹配(常用选项) [^1][^2] #### 二、使用步骤演示(工资表查询案例) 假设需要根据员工编号查询工资: 1. 建立查询单元格(如`B12`) 2. 输入公式: ```excel =VLOOKUP(A12, $A$2:$D$100, 4, 0) ``` - `A12`:待查询的员工编号 - `$A$2:$D$100`:锁定数据区域(绝对引用) - `4`:返回第4列(工资列) - `0`:精确匹配 [^2][^3] #### 三、常见错误与解决方法 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | #N/A | 查找值不存在 | 检查数据源或改用`IFERROR`容错 | | #REF! | 列序号超出范围 | 确认col_index_num ≤ 表格列数 | | 部分匹配失败 | 数据格式不一致 | 统一数值/文本格式 | | 结果错位 | 表格未锁定 | 使用`$`符号固定区域引用 | [^3][^4] #### 四、进阶技巧 1. **多条件查询**: 使用辅助列合并多个条件字段 ```excel =VLOOKUP(A2&B2, $D$2:$F$100, 3, 0) ``` 2. **通配符匹配**: `"*"`匹配任意字符,`"?"`匹配单个字符 ```excel =VLOOKUP("张*", $A$2:$C$100, 3, 0) ``` 3. **跨表查询**: 引用其他工作表数据 ```excel =VLOOKUP(A2, Sheet2!$A$2:$D$100, 4, 0) ``` [^1][^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值