《精通linux设备驱动程序开发》第10章PCI 学习笔记
主要内容:
- PCI系列
- 寻址和识别
- 访问PCI
1.PCI系列
PCI(Peripheral Component Interconnect,外围组件互联)是一种CPU和外围设备通信的高速传输总线,串行通信比并行通信速度更快,更便宜。
PCI系列具有设备自动配置系统的优势,PCI驱动程序不需要实现复杂的检测逻辑。启动时,BIOS类的启动固件会遍历总线并分配资源,设备驱动程序查找叫做PCI配置空间的内存来找到资源分配情况。
2.寻址和识别
PCI设备的地址由总线号、设备号和功能号组成,分别称为厂家ID、设备ID、和设备类代码。配置空间中包含10字节用于表示设备类型的编码。表示PCI桥设备类型编码的第1字节是0x06,网络设备类编码的第1个字节是0x02,通信设备类编码的第1个字节是0x07,类编码定义在include/linux/pci_ids.h。
3.访问PCI
PCI设备包括3个可寻址空间:配置空间、I/O端口和设备内存。
3.1配置区
要得到分配给某卡功能的中断号,操作如下:
unsigned char irq; pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq);
读取PCI状态寄存器,操作如下:
unsigned short status; pci_read_config_word(pdev, PCI_STATUS, &status);
3.2 I/O
PCI卡有6个I/O或内存区域,I/O区域包含寄存器,内存区域存放数据。
如PCI视频卡设备控制寄存器,驱动程序要完成如下事情:
(1)从配置区相应基址寄存器里得到I/O区域的基址:
unsigned long io_base = pci_resource_start(pdev, bar);
(2)用内核的request_region()常规机制获得这个I/O区域,并标明它对对应的设备:
request_region(io_base, length, "my_driver");
(3)用寄存器手册上的偏移地址加上第(1)步得到的基址,然后用inb()和 outb()函数访问这些寄存器:
/* Read */ register_data = inl(io_base + REGISTER_OFFSET); /* Use */ /* ... */ /* Write*/ outl(register_data, io_base + REGISTER_OFFSET);
3.3内存
为了能操作PCI设备IDE内存区域,如前面PCI视频卡的帧缓冲,按下面步骤进行:
(1)获得基址、内存区域长度即内存相关的标志:
unsigned long mmio_base = pci_resource_start(pdev, bar); unsigned long mmio_length = pci_resource_length(pdev, bar); unsigned long mmio_flags = pci_resource_flags(pdev, bar);
(2)用内核的request_mem_region()常规机制标记这片内存区的拥有者
request_mem_region(mmio_base, nmio_length, "my_driver");
(3)让CPU访问第(1)步获得的设备内存。