为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

《Linux设备驱动开发详解》第21章、PCI设备驱动

2011-06-29 34页 pdf 601KB 55阅读

用户头像

is_152320

暂无简介

举报
《Linux设备驱动开发详解》第21章、PCI设备驱动 嵌入式学院—华清远见旗下品牌:www.embedu.org 嵌入式学院—华清远见旗下品牌:www.embedu.org 《Linux设备驱动开发详解》——第 21章、PCI设备驱动 嵌入式学院—华清远见旗下品牌:www.embedu.org ...
《Linux设备驱动开发详解》第21章、PCI设备驱动
嵌入式学院—华清远见旗下品牌: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
/
本文档为【《Linux设备驱动开发详解》第21章、PCI设备驱动】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索