PCI是CPU和外围设备通讯的高速传输总线。普通PCI总线带宽通常为132MB/s(在32bit/33Mhz下)或则264MB/s(在32bit/66Mhz下)。
PCI总线体系结构是一种层次式的体系结构,PCI桥设备抢占着重要的地位,它将父总线与子总线联接在一起,进而使整个系统看上去像一颗倒置的树状结构。
PCI桥包括以下几种:
Host/PCI桥:用于联接CPU与PCI根总线,在PC中,显存控制器也一般被集成到Host/PCI桥设备芯片,Host/PCI桥一般被称为“北桥芯片组”。
PCI/ISA桥:用于联接旧的ISA总线。PCI/ISA桥也被称为“南桥芯片组”。
PCI-to-PCI桥:用于联接PCI主总线与次总线。
二、PCI配置空间访问
PCI有3种粮址空间:PCI/IO空间、PCI显存地址空间和PCI配置空间。
PCI配置空间(共为256字节):
制造商标识:由PCI组织分配给厂家。设备标示:按产品分类给本卡编号。分类码:本卡功能分类码。申请储存器空间:PCI卡里有储存器或以储存器编址的寄存器和I/O空间,为使驱动程序和应用程序能访问它们。配置空间的基地址寄存器用于此目的。申请I/O空间:配置空间基地址寄存器拿来进行系统I/O空间申请。中断资源申请:向系统申请中断资源。
内核为驱动提供的函数访问配置空间:
pci_read_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value);
pci_write_config_byte/word/dword(struct pci_dev *pdev, int offset, int *value);
PCI的I/O和显存空间:
获取I/O或显存资源:
pci_resource_start(struct pci_dev *dev, int bar);/*Bar值的范围为0-5*/
该函数返回六个PCII/O区域之一的第一个地址(显存地址或I/O端口编号)。
pci_resource_end(struct pci_dev *dev, int bar) /* Bar值的范围为0-5*/
该函数返回第bar个I/O区域的后一个地址。注意这是最后一个可用的地址,而不是该区域以后的第一个地址。
pci_resource_flags(struct pci_dev *dev, int bar) /* Bar值的范围为0-5 */
该函数返回资源关联的标志。资源标志拿来定义单个资源的个别特点。对与PCII/O区域关联的PCI资源,该信息从基地址寄存器中获得linux主机,但对其它与PCI设备无关的资源,它可能来自任何地方。所有的资源标志定义在中linux usb驱动程序开发,下边列举其中最重要的几个:
IORESOURCE_IO
IORESOURCE_MEM
假如对应的I/O区域存在,将设置里面标志中的一个深度linux,并且只有一个。
IORESOURCE_PREFETCH
IORESOURCE_READONLY
上述标志定义显存区域是可预取的,或则是写保护的。对PCI资源来讲,从来不会设置前面的那种标志。通过使用pci_resource_flags函数,设备驱动程序可完全忽视底层的PCI寄存器linux usb驱动程序开发,由于系统早已使用这种寄存器建立了资源信息。
申请/释放I/O或显存资源:
int pci_request_regions(struct pci_dev *pdev, const char *res_name);
void pci_release_regions(struct pci_dev *pdev);
获取/设置驱动私有数据:
void *pci_get_drvdata(struct pci_dev *pdev);
void pci_set_drvdata((struct pci_dev *pdev, void *data);
使能/严禁PCI设备:
int pci_enable_device(struct pci_dev *pdev);
int pci_disable_device(struct pci_dev *pdev);
设置主总线为DMA模式:
void pci_set_master(struct pci_dev *pdev);
找寻指定总线指定槽位的PCI设备:
struct pci_dev *pci_find_slot (unsigned int bus,unsigned int devfn);
设置PCI能量管理状态:
int pci_set_power_state(struct pci_dev *pdev, pci_power_t state);
在设备的能力中表中找出指定能力:
int pci_find_capability(struct pci_dev *pdev, int cap);
启用设备显存写无效事务:
int pci_set_mwi(struct pci_dev *pdev);
禁用设备显存写无效事务:
void pci_clear_mwi(struct pci_dev *pdev);
三、PCI设备驱动组成
PCI本质上就是一种总线,具体的PCI设备可以是字符设备、网络设备、USB等,所以PCI设备驱动应当包含两部份:
PCI驱动依据需求的设备驱动
依据需求的设备驱动是最终目的,PCI驱动只是手段帮助需求设备驱动达到最终目的而已。换句话说PCI设备驱动除了要实现PCI驱动还要实现依据需求的设备驱动。
PCI驱动注册与注销:
int pci_register_driver(struct pci_driver * driver);
int pci_unregister_driver(struct pci_driver * driver);
pci_driver结构体:
struct pci_driver {
struct list_head node;
char *name;/* 描述该驱动程序的名字*/
struct moudule *owner;
const struct pci_device_id *id_table;/* 指向设备驱动程序感兴趣的设备ID的一个列表,包括:厂商ID、设备ID、子厂商ID、子设备ID、类别、类别掩码、私有数据*/
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);/* 指向一个函数对于每一个与id_table中的项匹配的且未被其他驱动程序处理的设备在执行pci_register_driver时候调用此函数或者如果是以后插入的一个新设备的话,只要满足上述条件也调用此函数*/
void (*remove) (struct pci_dev *dev);/* 指向一个函数当该驱动程序卸载或者被该驱动程序管理的设备被卸下的时候将调用此函数*/
int (*save_state) (struct pci_dev *dev, u32 state);/* 用于在设备被挂起之前保存设备的相关状态*/
int (*suspend)(struct pci_dev *dev, u32 state);/* 挂起设备使之处于节能状态*/
int (*resume) (struct pci_dev *dev);/* 唤醒处于挂起态的设备*/
int (*enable_wake) (struct pci_dev *dev, u32 state, int enable);/* 使设备能够从挂起态产生唤醒事件*/
};
四、小结
PCI总线是目前应用广泛的计算机总线标准,并且是一种兼容性最强、功能最全的计算机总线。而Linux作为一种新的操作系统,同时也为PCI总线与各类新型设备互连成为可能。因为Linux源码开放,因而给联接到PCI总线上的任何设备编撰驱动程序显得相对容易。本文介绍怎么编撰Linux下的PCI驱动程序的关键函数插口。