linux pci驱动 - linux2web工作室

Download Report

Transcript linux pci驱动 - linux2web工作室

PCI驱动
讲师:金鑫
www.linux2web.net
pci总线结构介绍
pci配置寄存器及读写
IO端口和内存
pci设备初始化
pci驱动注册以及匹配
PCI总线简介
PCI是外围设备互连(Peripheral Component Interconnect)的简
称,作为一种通用的总线接口标准,它在目前的计算机系统中得到了非
常广泛的应用。PCI提供了一组完整的总线接口规范,其 目的是描述如
何将计算机系统中的外围设备以一种结构化和可控化的方式连接在一起
,同时它还刻画了外围设备在连接时的电气特性和行为规约,并且详细
定义了计 算机系统中的各个不同部件之间应该如何正确地进行交互。
pci总线在x86体系中比较常见,在arm体系的cpu里基本没有pci总线
PCI寻址(物理)
pci域:(16位)
总线号:(8位)
设备号:(5位)
功能号:(3位)
总线号、设备号和功能号共同组成pci外设的16位硬件地址。
但是由于256个总线对许多大系统是不够的, Linux 现在支持
PCI 域。每个 PCI 域可以占用多达 256 个总线. 每个总线占
用 32 个设备, 每个设备可以是一个多功能卡(例如一个声音
设备, 带有一个附加的 CD-ROM 驱动)有最多 8 个功能
这样,linux系统可以支持更多的pci设备
PCI配置寄存器
又叫”配置空间“
(一)pci配置寄存器
每个pci设备都有一个私有的至少256字节的地址空间,前64字节是标准
的(每个pci设备都有),后面的空间依赖设备来配置。
配置寄存器里包含了如下信息:
1.此pci设备的设备信息,如厂商id,设备id等
2.此设备工作时需要的io地址和mem地址起始地址以及长度
3.设备的irq号等
(二)配置寄存器的作用
1.linux内核启动时会从pci设备的配置寄存器里读取内存/IO起始地址
以及irq,并把这些信息赋值给struct pci_dev的相应成员;
2.pci驱动也会读写配置寄存器获得/保存设备相关的信息。
(三)配置寄存器的初始化
系统启动时,BIOS会为每个pci设备分配内存、IO空间以及irq号,并写
入相应pci设备的配置寄存器里去。
标准pci配置寄存器
PCI配置寄存器的读写
因为微处理器无法直接存取配置空间, 计算机供应商不得不
提供一个方法来完成它. 为存取配置空间, CPU 必须写和读
PCI 配置寄存器中的内容, 但是确切的实现是依赖于供应商
的。Linux提供了一个标准接口来存取配置空间.
PCI配置寄存器的读写
从由 dev 所标识出的pci设备的配置寄存器里读 1 个, 2 个或
者 4 个字节. where 参数是从配置寄存器开始的字节偏移. 从
配置空间取得的值通过 val 指针返回。
取得设备的io或mem
此pci设备用到了几个BAR?应该从哪个bar里读取所需的io地址或内存地
址?
这些信息是硬件相关的,需要查阅pci设备的datasheet才能得到以上信息
读取内存或io地址的函数:
unsigned long pci_resource_start(struct pci_dev *dev, int bar);
unsigned long pci_resource_end(struct pci_dev *dev, int bar);
unsigned long pci_resource_len(struct pci_dev *dev, int bar);
bar的取值为0~5
unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
这个函数返回和这个资源相关联的标识.
IORESOURCE_IO:io端口
IORESOURCE_MEM :内存
io内存和io端口
我们知道,外设都是通过读写设备上的寄存器来进行的,外设寄存器也
称为“I/O端口”。而IO端口有两种编址方式:独立编址和统一编制。
而具体采用哪一种则取决于CPU的体系结构。 如,PowerPC、m68k等
采用
统一编址;而X86等则采用独立编址。
对于某一既定的系统,它要么是独立编址,也即“I/O端口”方式,外设
寄存器位于“I/O空间”;
要么是统一编制,也即“I/O内存”方式,外设寄存器位于“内存空间”
。
linxu对io内存和io端口的访问
对于Linux内核而言,它可能用于不同的CPU,所以它必须都要考虑这两种访问方式:
1.linux访问IO内存的流程是:
request_mem_region() -> ioremap()
-> ioread8()/iowrite8()
-> iounmap() -> release_mem_region() 。
2.访问IO端口的方式:
(1)Linux内核提供了如下一些访问I/O端口的内联函数(这种方法比较常用)
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
(2)linux访问io端口的新机制
ioport_map()
-> ioread8()/iowrite8()
->ioport_unmap()
struct pci_dev {
struct list_head global_list;
struct list_head bus_list;
struct pci_bus *bus;
struct pci_bus *subordinate;
void
*sysdata;
struct proc_dir_entry *procent;
unsigned int devfn;//功能号
unsigned short vendor;//厂商号
unsigned short device;//设备号
unsigned short subsystem_vendor;//子厂商号
unsigned short subsystem_device;//子设备号
};
struct resource resource[DEVICE_COUNT_RESOURCE];
struct resource dma_resource[DEVICE_COUNT_DMA];
struct resource irq_resource[DEVICE_COUNT_IRQ];
struct pci_device_id用来表示驱动支持的设备类型
linux中pci设备的初始化
1.bios启动时,会为每个pci设备分配地址和irq等信息,并写
入各个pci设备的配置寄存器中。
2.linux系统启动时,会探测系统中的所有pci设备,并为探测
到的每个pci设备做如下操作:
(1)分配一个struct pci_dev结构体,这个结构体表示一个
pci设备
(2)为这个结构体填充设备vendor id、device id、
subvendor id、subdevice id以及地址和irq信息(通过读
取pci配置寄存器得到)
(3)然后把这个struct pci_dev结构体添加到pci总线上
驱动和设备的匹配
当调用pci_register_driver(struct pci_driver *drv)时,系统会
遍历pci总线上的所有的pci设备,并拿每个pci设备来和驱动进行匹配:
pci_bus_match->pci_match_device->pci_match_one_device
阅读pci视频卡的驱动源码testvideo.c,要求:
1.pci驱动的id_table里包括哪些选项?
2.此设备用到哪几个bar,是io端口还是mem?
3.中断号怎么得到?
4.视频驱动的注册代码在哪里?