嵌入式学院—华清远见旗下品牌:www.embedu.org
嵌入式学院—华清远见旗下品牌:www.embedu.org
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
第 21章 PCI设备驱动
PCI(及 cPCI)总线在一般的小型手持设备中不太可能用到,但是在工控和通信
设备及其 PC中却引领着潮流。在 Linux系统中,PCI设备驱动和 USB设备驱动有共
性,那就是其驱动都由总线相关部分和自身设备类型驱动两部分组成。
21.1节讲解了 PCI总线及其配置空间,给出了 PCI总线在 Linux内核中的数据结
构。
PCI 设备驱动的 PCI 相关部分围绕着 pci_driver 结构体的成员函数展开,21.2 节
讲解了 pci_driver结构体及其成员函数的含义,并分析了 PCI设备驱动的框架结构。
21.3节以 Intel 810主板集成声卡为实例进一步讲解 PCI设备驱动的编写方法。
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
PCI总线与配置空间
21.1.1 PCI总线的 Linux描述
从本书第 2章的图 2.16可以看出,PCI总线体系结构是一种层次式的体系结构。
在这种层次式体系结构中,PCI 桥设备占据着重要的地位,它将父总线与子总线连
接在一起,从而使整个系统看起来像一颗倒置的树型结构。树的顶端是系统的 CPU,
它通过一个较为特殊的 PCI桥设备——Host/PCI桥设备与根 PCI总线连接起来。
作为一种特殊的 PCI设备,PCI桥包括以下几种。
l Host/PCI 桥:用于连接 CPU 与 PCI 根总线,第 1 个根总线的编号为 0。在
PC中,内存控制器也通常被集成到 Host/PCI桥设备芯片中,因此,Host/PCI
桥通常也被称为“北桥芯片组(North Bridge Chipset)”。
l PCI/ISA桥:用于连接旧的 ISA总线。通常,PCI中的类似 i8359A中断控制
器这样的设备也会被集成到 PCI/ISA 桥设备中,因此,PCI/ISA 桥通常也被
称为“南桥芯片组(South Bridge Chipset)”。
l PCI-to-PCI桥:用于连接 PCI主总线(primary bus)与次总线(secondary bus)。
PCI桥所处的 PCI总线称为“主总线”(即次总线的父总线),桥设备所连接的 PCI总
线称为“次总线”(即主总线的子总线)。
在 Linux系统中,PCI总线用 pci_bus来描述,这个结构体记录了本 PCI总线的信
息以及本 PCI总线的父总线、子总线、桥设备信息,这个结构体的定义如代码清单 21.1
所示。
代码清单 21.1 pci_bus结构体
1 struct pci_bus
2 {
3 struct list_head node; /* 链表元素 node */
4 struct pci_bus *parent; /*指向该 PCI总线的父总线,即 PCI桥所在的总线 */
5 struct list_head children; /* 描述了这条 PCI总线的子总线链表的表头 */
6 struct list_head devices; /* 描述了这条 PCI总线的逻辑设备链表的表头 */
7 struct pci_dev *self; /* 指向引出这条 PCI总线的桥设备的 pci_dev结构 */
8 struct resource *resource[PCI_BUS_NUM_RESOURCES];
9 /* 指向应路由到这条 PCI总线的地址空间资源 */
10
11 struct pci_ops *ops; /* 这条 PCI总线所使用的配置空间访问函数 */
12 void *sysdata; /* 指向系统特定的扩展数据 */
13 struct proc_dir_entry *procdir;/*该 PCI总线在/proc/bus/pci中对应的
目录项*/
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
14
15 unsigned char number; /* 这条 PCI总线的总线编号 */
16 unsigned char primary; /* 桥设备的主总线 */
17 unsigned char secondary; /* PCI总线的桥设备的次总线号 */
18 unsigned char subordinate;/*PCI总线的下属 PCI总线的总线编号最大值*/
19
20 char name[48];
21
22 unsigned short bridge_ctl;
23 unsigned short pad2;
24 struct device *bridge;
25 struct class_device class_dev;
26 struct bin_attribute *legacy_io;
27 struct bin_attribute *legacy_mem;
28 };
假定一个如图 21.1所示的 PCI总线系统,根总线 0上有一个 PCI桥,它引出子总
线 Bus 1,Bus 1上又有一个 PCI桥引出 Bus 2。
图 21.1 示例 PCI总线系统
在上图中,Bus 0总线的 pci_bus结构体中的 number、primary、secondary都应该
为 0,因为它是通过 Host/PCI桥引出的根总线;Bus 1总线的 pci_bus结构体中的 number
和 secondary都为 1,但是它的 primary应该为 0;Bus 2总线的 pci_bus结构体中的 number
和 secondary都应该为 2,而其 primary则应该等于 1。这 3条总线的 subordinate值都
应该等于 2。
系统中当前存在的所有根总线都通过其 pci_bus结构体中的 node成员链接成一条
全局的根总线链表,其表头由 list类型的全局变量 pci_root_buses来描述。而根总线下
面的所有下级总线则都通过其 pci_bus 结构体中的 node 成员链接到其父总线的
children链表中。这样,通过这两种 PCI总线链表,Linux内核就将所有的 pci_bus结
构体以一种倒置树的方式组织起来。假定对于如图21.2所示的多根PCI总线体系结构,
它所对应的总线链表结构将如图 21.3所示。
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
图 21.2 多根 PCI总线体系结构
图 21.3 PCI总线链表
21.1.2 PCI设备的 Linux描述
在 Linux系统中,所有种类的 PCI设备都可以用 pci_dev结构体来描述,由于一
个 PCI接口卡上可能包含多个功能模块,每个功能被当作一个独立的逻辑设备,因此,
每一个 PCI功能,即 PCI逻辑设备都唯一地对应一个 pci_dev设备描述符,该结构体
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
的定义如代码清单 21.2所示。
代码清单 21.2 pci_dev结构体
1 struct pci_dev
2 {
3 struct list_head global_list; /* 全局链表元素 */
4 struct list_head bus_list; /* 总线设备链表元素 */
5 struct pci_bus *bus; /* 这个 PCI设备所在的 PCI总线的 pci_bus结构 */
6 struct pci_bus *subordinate; /* 指向这个 PCI设备所桥接的下级总线 */
7
8 void *sysdata; /* 指向一片特定于系统的扩展数据 */
9 struct proc_dir_entry *procent; /* 该 PCI设备在/proc/bus/pci中对应
的目录项 */
10
11 unsigned int devfn; /* 这个 PCI设备的设备功能号 */
12 unsigned short vendor; /* PCI设备的厂商 ID*/
13 unsigned short device; /* PCI设备的设备 ID */
14 unsigned short subsystem_vendor; /* PCI设备的子系统厂商 ID */
15 unsigned short subsystem_device; /* PCI设备的子系统设备 ID */
16 unsigned int class ; /* 32位的无符号整数,表示该 PCI设备的类别,
17 bit[7∶0]为编程接口,bit[15∶8]为子类别代码,bit[23∶16]
18 为基类别代码,bit[31∶24]无意义 */
19 u8 hdr_type; /* PCI配置空间头部的类型 */
20 u8 rom_base_reg; /* 表示 PCI配置空间中的 ROM基地址寄存器在 PCI配置空间
中的位置 */
21
22 struct pci_driver *driver; /* 指向这个 PCI设备所对应的驱动 pci_driver
结构 */
23 u64 dma_mask; /* 该设备支持的总线地址位掩码,通常是 0xffffffff */
24
25 pci_power_t current_state; /* 当前的操作状态 */
26
27 struct device dev; /* 通用的设备接口 */
28
29 /* 定义这个 PCI设备与哪些设备相兼容 */
30 unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
31 unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
32
33 int cfg_size; /* 配置空间大小 */
34
35 unsigned int irq;
36 struct resource resource[DEVICE_COUNT_RESOURCE];
37 /*表示该设备可能用到的资源,包括:
38 I/O端口区域、设备内存地址区域以及扩展 ROM地址区域 */
39
40 unsigned int transparent: 1; /* 透明 PCI桥 */
41 unsigned int multifunction: 1; /* 多功能设备 */
42 /* keep track of device state */
43 unsigned int is_enabled: 1; /* pci_enable_device已经被调用? */
44 unsigned int is_busmaster: 1; /* 设备是主设备? */
45 unsigned int no_msi: 1; /* 设备可不使用 msi? */
46
47 u32 saved_config_space[16]; /* 挂起事保存的配置空间 */
48 struct bin_attribute *rom_attr; /* sysfs ROM入口的属性描述 */
49 int rom_attr_enabled;
50 struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE];/*资源的
sysfs文件*/
51 };
在 Linux系统中,所有的 PCI设备都通过其 pci_dev结构体中的 global_list成员链
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
接一条全局 PCI设备链表 pci_devices。另外,同属一条 PCI总线上的所有 PCI设备也
通过其 pci_dev 结构体中的 bus_list 成员链接成一个属于这条 PCI 总线的总线设备链
表,表头则由该 PCI总线的 pci_bus结构中的 devices成员所定义。图 21.4所示为 PCI
设备链表的典型例子。
图 21.4 PCI设备链表
21.1.3 PCI配置空间访问
PCI有 3种地址空间:PCI I/O空间、PCI内存地址空间和 PCI配置空间。CPU可
以访问所有的地址空间,其中 PCI I/O空间和 PCI内存地址空间由设备驱动程序使用,
而 PCI配置空间由 Linux内核中的 PCI初始化代码使用,这些代码用于配置 PCI设备,
比如中断号以及 I/O或内存基地址。
PCI规范定义了 3种类型的 PCI配置空间头部,其中 type 0用于
的 PCI设备,
type 1用于 PCI桥,type 2用于 PCI CardBus桥。如图 2.17所示,不管是哪一种类型
的配置空间头部,其前 16个字节的
都是相同的,/include/linux/pci_regs.h文件中
定义了 PCI配置空间头部,如代码清单 21.3所示。
代码清单 21.3 PCI配置空间头部寄存器定义
1 #define PCI_VENDOR_ID 0x00 /* 16位厂商 ID */
2 #define PCI_DEVICE_ID 0x02 /* 16位设备 ID */
3
4 /* PCI命令寄存器 */
5 #define PCI_COMMAND 0x04 /* 16位 */
6 #define PCI_COMMAND_IO 0x1 /* 使能设备响应对 I/O 空间的访问
*/
7 #define PCI_COMMAND_MEMORY 0x2 /* 使能设备响应对存储空间的访
问 */
8 #define PCI_COMMAND_MASTER 0x4 /* 使能总线主模式 */
9 #define PCI_COMMAND_SPECIAL 0x8 /* 使能设备响应特殊周期 */
10 #define PCI_COMMAND_INVALIDATE 0x10 /*使用 PCI内存写无效事务 */
11 #define PCI_COMMAND_VGA_PALETTE 0x20 /* 使能 VGA调色板侦测 */
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
12 #define PCI_COMMAND_PARITY 0x40 /* 使能奇偶校验 */
13 #define PCI_COMMAND_WAIT 0x80 /* 使能地址/数据步进 */
14 #define PCI_COMMAND_SERR 0x100 /* 使能 SERR */
15 #define PCI_COMMAND_FAST_BACK 0x200 /* 使能背靠背写 */
16 #define PCI_COMMAND_INTX_DISABLE 0x400 /* 禁止中断竞争*/
17
18 /* PCI状态寄存器 */
19 #define PCI_STATUS 0x06 /* 16位 */
20 #define PCI_STATUS_CAP_LIST 0x10 /* 支持的能力列表 */
21 #define PCI_STATUS_66MHZ 0x20 /* 支持 PCI 2.1 66MHz */
22 #define PCI_STATUS_UDF 0x40 /* 支持用户定义的特征 */
23 #define PCI_STATUS_FAST_BACK 0x80 /* 快速背靠背操作 */
24 #define PCI_STATUS_PARITY 0x100 /* 侦测到奇偶校验错 */
25 #define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL定时 */
26 #define PCI_STATUS_DEVSEL_FAST 0x000
27 #define PCI_STATUS_DEVSEL_MEDIUM 0x200
28 #define PCI_STATUS_DEVSEL_SLOW 0x400
29 #define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* 目标设备异常 */
30 #define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* 主设备确认 */
31 #define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* 主设备异常 */
32 #define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* 驱动了 SERR */
33 #define PCI_STATUS_DETECTED_PARITY 0x8000 /* 奇偶校验错 */
34
35 /* 类代码寄存器和修订版本寄存器 */
36 #define PCI_CLASS_REVISION 0x08 /* 高 24位为类码,低 8位为修订版本 */
37 #define PCI_REVISION_ID 0x08 /* 修订号 */
38 #define PCI_CLASS_PROG 0x09 /* 编程接口 */
39 #define PCI_CLASS_DEVICE 0x0a /* 设备类 */
40 #define PCI_CACHE_LINE_SIZE 0x0c /* 8位 */
41 #define PCI_LATENCY_TIMER 0x0d /* 8位 */
42
43 /* PCI头类型 */
44 #define PCI_HEADER_TYPE 0x0e /* 8位头类型 */
45 #define PCI_HEADER_TYPE_NORMAL 0
46 #define PCI_HEADER_TYPE_BRIDGE 1
47 #define PCI_HEADER_TYPE_CARDBUS 2
48
49 /* 表示配置空间头部中的 Built-In Self-Test寄存器在配置空间中的字节地址索引
*/
50 #define PCI_BIST 0x0f /* 8 位 */
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
51 #define PCI_BIST_CODE_MASK 0x0f /* 完成代码 */
52 #define PCI_BIST_START 0x40 /* 用于启动 BIST*/
53 #define PCI_BIST_CAPABLE 0x80 /* 设备是否支持 BIST? */
紧接着前 16 个字节的寄存器为基地址寄存器 0~基地址寄存器 5,其中,
PCI_BASE_ADDRESS_ 2~5 仅对标准 PCI 设备的 0 类型配置空间头部有意义,而
PCI_BASE_ADDRESS_0~1则适用于 0类型和 1类型配置空间头部。
基地址寄存器中的 bit[0]的值决定了这个基地址寄存器所指定的地址范围是在
I/O 空间还是在内存映射空间内进行译码。当基地址寄存器所指定的地址范围位于内
存映射空间中时,其 bit[2∶1]表示内存地址的类型,bit[3]表示内存范围是否为
可预取(Prefetchable)的内存。
1类型配置空间头部适用于 PCI-PCI桥设备,其基地址寄存器 0 与基地址寄存器
1可以用来指定桥设备本身可能要用到的地址范围,而后 40个字节(0x18~0x39)则
被用来配置桥设备的主、次编号以及地址过滤窗口等信息。
pci_bus结构体中的 pci_ops类型成员指针 ops指向该 PCI总线所使用的配置空间
访问操作的具体实现,pci_ops 结构体的定义如代码清单 21.4所示。
代码清单 21.4 pci_ops结构体
1 struct pci_ops
2 {
3 int(*read)(struct pci_bus *bus, unsigned int devfn, int where, int
size, u32
4 *val);//读配置空间
5 int(*write)(struct pci_bus *bus, unsigned int devfn, int where, int
size, u32
6 val); //写配置空间
7 };
read()和 write()成员函数中的 size 表示访问的是字节、2 字节还是 4 字节,对于
write()而言,val是要写入的值;对于 read()而言,val是要返回的读取到的值的指针。
通过 bus参数的成员以及 devfn可以定位相应 PCI总线上相应 PCI逻辑设备的配置空
间。在 Linux设备驱动中,可用如下一组函数来访问配置空间:
inline int pci_read_config_byte(struct pci_dev *dev, int where, u8
*val);
inline int pci_read_config_word(struct pci_dev *dev, int where, u16
*val);
inline int pci_read_config_dword(struct pci_dev *dev, int where, u32
*val);
inline int pci_write_config_byte(struct pci_dev *dev, int where, u8
val);
inline int pci_write_config_word(struct pci_dev *dev, int where, u16
val);
inline int pci_write_config_dword(struct pci_dev *dev, int where, u32
val);
上述函数只是对如下函数进行调用:
int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn,
int where, u8 *val); //读字节
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn,
int where, u16 *val); //读字
int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn,
int where, u32 *val); //读双字
int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn,
int where, u8 val); //写字节
int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn,
int where, u16 val); //写字
int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn,
int where, u32 val); //写双字
最后,我们来看一下 PCI总线、设备与驱动在/proc和/sysfs文件系统中的描述。
首先,通过查看/proc/bus/pci 中的文件,可以获得系统连接的 PCI 设备的基本信息描
述。在 VmWare虚拟机 Linux上的/proc/bus/pci目录下的树型结构如下:
|-- 00
| |-- 00.0
| |-- 01.0
| |-- 07.0
| |-- 07.1
| |-- 07.2
| |-- 07.3
| |-- 0f.0
| |-- 10.0
| |-- 11.0
| '-- 12.0
'-- devices
sysfs文件系统/sys/bus/pci目录中也给出了系统中总线上挂接的设备及驱动信息,
该目录下的树型结构如下:
|-- devices
| |-- 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
| |-- 0000:00:01.0 -> ../../../devices/pci0000:00/0000:00:01.0
| |-- 0000:00:07.0 -> ../../../devices/pci0000:00/0000:00:07.0
| |-- 0000:00:07.1 -> ../../../devices/pci0000:00/0000:00:07.1
| |-- 0000:00:07.2 -> ../../../devices/pci0000:00/0000:00:07.2
| |-- 0000:00:07.3 -> ../../../devices/pci0000:00/0000:00:07.3
| |-- 0000:00:0f.0 -> ../../../devices/pci0000:00/0000:00:0f.0
| |-- 0000:00:10.0 -> ../../../devices/pci0000:00/0000:00:10.0
| |-- 0000:00:11.0 -> ../../../devices/pci0000:00/0000:00:11.0
| '-- 0000:00:12.0 -> ../../../devices/pci0000:00/0000:00:12.0
'-- drivers
| ...
|-- PIIX_IDE
| |-- 0000:00:07.1
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
-> ../../../../devices/pci0000:00/0000:00:07.1
| |-- bind
| |-- new_id
| '-- unbind
| ...
'-- pcnet32
|-- 0000:00:11.0
-> ../../../../devices/pci0000:00/0000:00:11.0
|-- bind
|-- new_id
'-- unbind
此外,pciutils(PCI 工具)中的 lspci 工具会分析/proc/bus/pci 中的文件,从而可
被用户用于查看系统中 PCI设备的描述信息,例如笔者在 VmWare虚拟机 Linux上运
行 lspci的结果为:
00:00.0 Host bridge: Intel Corp. 440BX/ZX/DX - 82443BX/ZX/DX Host bridge
(rev 01)
00:01.0 PCI bridge: Intel Corp. 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge
(rev 01)
00:07.0 ISA bridge: Intel Corp. 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corp. 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.2 USB Controller: Intel Corp. 82371AB/EB/MB PIIX4 USB
00:07.3 Bridge: Intel Corp. 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:0f.0 VGA compatible controller: VMWare Inc: Unknown device 0405
00:10.0 SCSI storage controller: BusLogic BT-946C (BA80C30)
[MultiMaster 10] (rev 01)
00:11.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32
LANCE] (rev 10)
00:12.0 Multimedia audio controller: Ensoniq ES1371 [AudioPCI-97] (rev
02)
PCI设备驱动结构
21.2.1 pci_driver结构体
从本质上讲 PCI只是一种总线,具体的 PCI设备可以是字符设备、网络设备、USB
主机控制器等,因此,一个通过 PCI总线与系统连接的设备的驱动至少包含以下两部
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
分内容。
l PCI设备驱动。
l 设备本身的驱动。
PCI 驱动只是为了辅助设备本身的驱动,它不是目的,只是手段,PCI 设备本身
含有双重以上的身份。例如对于通过 PCI总线与系统连接的字符设备,则驱动中除了
要实现 PCI 驱动部分外,其主体仍然是设备作为字符设备本身的驱动,即实现
file_operations 成员函数并注册 cdev。分析 Linux 内核可知,在 /drivers/block/、
/drivers/atm/、/drivers/char/、/drivers/i2c/、/drivers/ieee1394/、/drivers/media/、/drivers/mtd/、
/drivers/net/、/drivers/serial/、/drivers/video/、/sound/等目录中均广泛分布着 PCI 设备
驱动。
在 Linux 内核中,用 pci_driver 结构体来定义 PCI 驱动,该结构体中包含了 PCI
设备的探测/移除、挂起/恢复等函数,其定义如代码清单 21.5所示。
代码清单 21.5 pci_driver结构体
1 struct pci_driver
2 {
3 struct list_head node;
4 char *name;
5 struct module *owner;
6 const struct pci_device_id *id_table; /*不能为 NULL,以便 probe函数
调用*/
7 /* 新设备添加 */
8 int(*probe)(struct pci_dev *dev, const struct pci_device_id *id);
9 void(*remove)(struct pci_dev *dev); /* 设备移出 */
10 int(*suspend)(struct pci_dev *dev, pm_message_t state); /* 设备挂
起 */
11 int(*resume)(struct pci_dev *dev); /* 设备唤醒 */
12 /* 使能唤醒事件 */
13 int(*enable_wake)(struct pci_dev *dev, pci_power_t state, int
enable);
14 void(*shutdown)(struct pci_dev *dev);
15
16 struct device_driver driver;
17 struct pci_dynids dynids;
18 };
对 pci_driver的注册和注销通过如下函数来实现:
int pci_register_driver(struct pci_driver *driver);
void pci_unregister_driver(struct pci_driver *drv);
有一个对应于 pci_register_driver()的已经过时的宏定义:
#define pci_module_init pci_register_driver
在 PCI设备驱动中其他常用的函数(或宏)如下所示。
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
l 获取 I/O或内存资源。
#define pci_resource_start(dev,bar) ((dev)->resource[(bar)].start)
#define pci_resource_end(dev,bar) ((dev)->resource[(bar)].end)
#define pci_resource_flags(dev,bar) ((dev)->resource[(bar)].flags)
#define pci_resource_len(dev,bar) \
((pci_resource_start((dev),(bar)) == 0 && \
pci_resource_end((dev),(bar)) == \
pci_resource_start((dev),(bar))) ? 0 : \
\
(pci_resource_end((dev),(bar)) - \
pci_resource_start((dev),(bar)) + 1))
l 申请/释放 I/O或内存资源。
int pci_request_regions(struct pci_dev *pdev, const char *res_name);
void pci_release_regions(struct pci_dev *pdev);
l 获取/设置驱动私有数据。
void *pci_get_drvdata (struct pci_dev *pdev);
void pci_set_drvdata (struct pci_dev *pdev, void *data);
l 使能/禁止 PCI设备。
int pci_enable_device(struct pci_dev *dev);
void pci_disable_device(struct pci_dev *dev);
l 设置为总线主 DMA。
void pci_set_master(struct pci_dev *dev);
l 寻找指定总线指定槽位的 PCI设备。
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
l 设置 PCI能量管理状态(0=D0 ... 3=D3)。
int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
l 在设备的能力表中找出指定的能力。
int pci_find_capability (struct pci_dev *dev, int cap);
l 启用设备内存写无效事务。
int pci_set_mwi(struct pci_dev *dev);
l 禁用设备内存写无效事务。
void pci_clear_mwi(struct pci_dev *dev);
pci_driver的 probe()函数要完成 PCI设备的初始化及其设备本身身份(字符、TTY、
网络等)驱动的注册。当 Linux内核启动并完成对所有 PCI设备进行扫描、登录和分
配资源等初始化操作的同时,会建立起系统中所有 PCI设备的拓扑结构,probe()函数
将负责硬件的探测工作并保存配置信息。
drivers/net/pci-skeleton.c 给出了一个 PCI 接口网络设备驱动程序的“骨架”,其
pci_driver 中 probe()成员函数 netdrv_init_one()及其调用的 netdrv_init_board()完成了
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
PCI设备初始化及对应的网络设备注册工作,代码清单 21.6所示为这两个函数的实现。
代码清单 21.6 PCI设备驱动的 probe()函数
1 static int __devinit netdrv_init_one (struct pci_dev *pdev,
2 const struct pci_device_id *ent)
3 {
4 struct net_device *dev = NULL;
5 struct netdrv_private *tp;
6 int i, addr_len, option;
7 void *ioaddr = NULL;
8 static int board_idx = -1;
9
10 ...
11
12 board_idx++;
13 i = netdrv_init_board (pdev, &dev, &ioaddr);
14 if (i < 0) {
15 return i;
16 }
17
18 ...
19 /* 赋值 net_device的成员函数 */
20 dev->open = netdrv_open;
21 dev->hard_start_xmit = netdrv_start_xmit;
22 dev->stop = netdrv_close;
23 dev->get_stats = netdrv_get_stats;
24 dev->set_multicast_list = netdrv_set_rx_mode;
25 dev->do_ioctl = netdrv_ioctl;
26 dev->tx_timeout = netdrv_tx_timeout;
27 dev->watchdog_timeo = TX_TIMEOUT;
28
29 dev->irq = pdev->irq;
30 dev->base_addr = (unsigned long) ioaddr;
31 ...
32 pci_set_drvdata(pdev, dev);//设置 PCI私有数据
33 ...
34
35 return 0;
36 }
37
38 static int _ _devinit netdrv_init_board(struct pci_dev *pdev, struct
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
net_device
39 **dev_out, void **ioaddr_out)
40 {
41 void *ioaddr = NULL;
42 struct net_device *dev;
43 struct netdrv_private *tp;
44 int rc, i;
45 u32 pio_start, pio_end, pio_flags, pio_len;
46 unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
47 u32 tmp;
48
49 *ioaddr_out = NULL;
50 *dev_out = NULL;
51
52 /* 分配 ethernet设备 */
53 dev = alloc_etherdev(sizeof(*tp));
54 if (dev == NULL)
55 {
56 printk(KERN_ERR PFX "unable to alloc new ethernet\n");
57 DPRINTK("EXIT, returning -ENOMEM\n");
58 return - ENOMEM;
59 }
60 SET_MODULE_OWNER(dev);
61 SET_NETDEV_DEV(dev, &pdev->dev);
62 tp = dev->priv;
63
64 /* 使能设备 */
65 rc = pci_enable_device(pdev);
66 if (rc)
67 goto err_out;
68
69 /* 获取 I/O和内存基地址 */
70 pio_start = pci_resource_start(pdev, 0);
71 pio_end = pci_resource_end(pdev, 0);
72 pio_flags = pci_resource_flags(pdev, 0);
73 pio_len = pci_resource_len(pdev, 0);
74
75 mmio_start = pci_resource_start(pdev, 1);
《Linux设备驱动开发详解》——第 21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌:www.embedu.org
76 mmio_end = pci_resource_end(pdev, 1);
77 mmio_flags = pci_resource_flags(pdev, 1);
78 mmio_len = pci_resource_l