[LDD3速记]_PCI驱动程序

总线由电气接口和编程接口构成

 PCI(Peripheral Component Interconnect,外围设备互联)

探测方法:“模块参数”、“自动检测IRQ编号”、PCI规范提供的方法

查看PCI:lspci、/proc/bus/pci/devices


一、PCI接口

1. 目标

        获得在计算机和外设之间传输数据时更好的性能(高时钟频率),尽可能的平台无关,简化往系统中添加和删除外设的工作


2. 特性        

        每个PCI外设由一个总线编号、一个设备编号和一个功能编号来标识

        在单个系统中插入多个总线,通过桥(bridge)来完成

        IRQ线共享

        PCI字节序:小头


3. 寻址

        显示硬件地址:两个值(8位总线编号和8位设备及功能编号)、三个值(总线、设备、功能)、四个值(16位的域,8位的总线,5位的设备和3位的功能),以16进制显示

        每个外设板的硬件电路对三种地址空间的查询进行应答:内存位置、I/O端口和配置寄存器。前两种地址空间由同一PCI总线上的所有设备共享,驱动程序以惯常的方式(inb、readb等)进行访问;而配置空间利用了物理寻址,配置事物是通过调用特定的内核函数访问配置寄存器来执行

        I/O空间使用32位地址总线,内存空间使用32位或64位地址来访问,初始化PCI硬件时把每个内存和I/O地址区域映射到不同地址,可从配置空间读取这些地址(故不需探测就能访问)

        配置空间中每个设备功能256字节(PCI快速设备除外,有4KB),含一个功能ID,驱动程序通过查询外设特定的ID来识别设备

        与ISA相比:有配置地址空间


4. 引导阶段

        PCI设备上电时硬件保持未激活状态,设备只响应配置事务。

        PCI主板有能处理PCI的固件(BIOS、NVRAM、PROM)。固件通过读写PCI控制器中的寄存器,提供配置地址空间的访问。

        系统引导时,固件(或内核)执行配置事务,为各个地址区域分配安全的位置(映射内存、I/O区域)


5. 配置寄存器和初始化

        256字节,前64字节标准化,其余与设备相关

        必需寄存器、可选寄存器

        通常用三个(或五个)寄存器标志一个设备:vendorID、deviceID、class;进一步区分:subsystem vendorID、subsystem deviceID

        struct pci_device_id:定义驱动程序支持的不同类型的PCI设备列表


6. MODULE_DEVICE_TABLE

        pci_device_id需要导出到用户空间,便于被热插拔和模块装载系统识别

MODULE_DEVICE_TABLE(pci, i810_ids);
/* 
建立指向pci_device_id的局部变量__mod_pci_device_table 
在内核构建过程时,depmod程序在所有模块中搜索该符号
若找到则把模块的数据添加到 /lib/modules/KERNEL_VERSION/modules.pcimap
当内核告知热插拔系统一个新的PCI设备被发现时,热插拔系统使用modules.pcimap来寻找要装载的驱动
*/

7. 注册PCI驱动程序

        struct pci_drivers结构体必须注意的字段:

        const char *name;

        const struct pci_device_id *id_table;

        int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); 指向PCI驱动程序中探测函数的指针

        void (*remove) (struct pci_dev *dev); 指向一个移除函数的指针

        int (*suspend) (struct pci_dev *dev, u32 state); 可选,指向一个挂起函数的指针

        int (*resume) (struct pci_dev *dev); 可选,指向一个恢复函数的指针

static struct pci_driver pci_driver = {
    .name = "pci_skel",
    .id_table = ids,
    .probe = probe,
    .remove = remove,
};
/* 初始化pci_driver结构体 */

static int __init pci_skel_init(void)
{
    return pci_register_driver(&pci_driver);
}
/* 注册 */

static void __exit pci_skel_exit(void)
{
    pci_unregister_driver(&pci_driver);
}
/* 卸载 */

8. 访问配置空间

int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);
/*
 往设备dev的配置空间读写,where是配置空间起始位置的偏移量
读时,从配置空间获得的值通过val指针返回
写时,要写入的值通过val传递
word和dword会转换字节序
*/

int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int
  where, u8 *val);
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_dev时,用上述函数代替 */

9.访问I/O和内存空间

        一个PCI设备可实现多达6个I/O地址区域,每个区域可以是内存也可以是I/O地址

        标记为“内存可预取”的内存能进行各种优化

        获取区域信息的接口:

unsigned long pci_resource_start(struct pci_dev *dev, int bar);
/* 
返回六个PCI I/O区域之一的首地址(内存地址或I/O端口号)
该区域由bar(base address regisiter,基地址寄存器)指定,取值为0-5
*/

unsigned long pci_resource_end(struct pci_dev *dev, int bar);
/* 返回第bar个I/O区域的尾地址 */

unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
/* 返回和该资源相关联的标志 */

        其中资源标志有几个为:

        IORESOURCE_IO、IORESOURCE_MEM

        IORESOURCE_PREFETCH:可预取的

        IORESOURCE_READONLY:写保护(只读)


10. PCI中断

        中断号保存在配置寄存器60(PCI_INTERRUPT_LINE),允许多达256条中断线(实际受限于CPU)

        处理中断的PCI特定代码只需要读取配置字节,以获取中断号

result = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &myirq);
if (result) {
    /* deal with error */
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值