分类: 其他UNIX
2014-03-26 10:30:13
PCI配置空间
当VxWorks内核起来之后,PCI设备的第一次使用都是必须在调用sysHwInit2()例程之后。但是由于MMU内存映射的初始化和激活是在例程sysHwInit()和sysHwInit2()之间的,所以推荐的PCI初始化顺序为:
sysHwInit() 默认的MMU table entries相当于将本地事务映射为PCI事务的HOST-Bridge的访问侧
sysHwInit2() 在该例程中,程序员必须静态配置所有的PCI设备,或者调用动态配置routine pciAutoConfig()
每个PCI设备有3种物理空间:配置空间、存储器空间和I/O空间。配置空间是长度256字节的一段连续空间(16个32位寄存器)其中前64个字节为头标,其余192字节为设备相关信息。在64字节的头标中,前16字节的定义是确定的,后48字节的具体含义因设备而异。配置空间中的一个重要部分是基地址寄存器(BaseAddresssRegister),它的内容是PCI设备的地址空间映射到系统地址空间的起始物理地址。其中,bit0=1表示IO空间映射,bit0=0表示存储器空间映射。所有PCI设备必须实现存储器空间映射。通过向BAR写全1即可确定所需地址空间的大小。在VxWorks下要访问一个PCI设备,只需要知道该设备的厂商号和设备号。
基于VxWorks的PCi设备驱动程序开发流程,分为4个步骤:①创建设备;②根据PCI设备的配置参数,对PCI设备编写功能函数程序;
知道厂商号和设备号后,利用VxWorks提供的函数pciFindDevice()获得设备的总线号、设备编号和功能号。然后调用peiConfigInLong()获得多功能数据采集卡设备的配置空间、内存空间、I/O空间的地址;pciConfigInByte得到设备中断号。在读取设备的基地址时,结合设备相关的说明书进行。
STATUS pciFindDevice(int vendorId, int deviceId,int index,int *pBusNo,int *pDeviceNo,int *pFuncNo)
注意第三个参数 ‘index’该类PCI设备的索引号,这是在系统中有多个pci设备时我们需要从0开始查找,直到查找到我们想要驱动设备(第一个参数vendorId和第二个参数deviceId相同的设备)。
后面三个参数就是唯一标识该pci设备的资源参数,也是我们在查找pci设备的时候需要获得该设备空间中的pci设备资源。
由于使用的是PENTIUM系列的CPU来进行板卡驱动的开发,所以在边接中断向量的时候,中断号要加上0x20。
在系统配置PCI设备完毕后,必须首先访问PCI配置空间获取相关的信息并传递给Device Driver。因为BIOS在扫描PCI BUS的过程中,会自动给BUS编号。例如某个设备的编号为(2,5,0)可能会由于通过PCI-PCI桥来扩展或者移除PCI设备而变为(1,5,0)。在正常的设备操作过程中,不应该去访问PCI的配置空间。
查找到对应的设备之后,需要获取该设备配置空间的基本资源。
要访问pci设备空间有专门的访问控制函数,这里我们使用:
pciConfigInLong():从pci配置空间指定位置读取一个字长。
pciConfigInLong(int busNo,int deviceNo,int funcNo,int offset,UINT32 *pData)
注:最后一个参数用于存放返会的数据
通过pciConfigInLong()函数获取下面的几个信息:IO地址,PCI设备地址,中断号。并且需要映射内存,使能I/O
当将PCI设备空间的资源映射出来之后,PCI设备的基本框架就建立起来了。
首先注册驱动程序,调用:
int iosDrvInstall() / STATUS iosDevAdd()
Int iosDrvInstall(FUNCPTR pCreate, FUNCPTR pDelete,FUNCPTR pOpen, FUNCPTR pClose,FUNCPTR pRead, FUNCPTR pWrite,FUNCPTR pIoctl )除了最后一个参数之外,其它参数都可以为NULL,最后一个参数向系统注册一个应用程序与驱动通信的函数。
一般需要进行寄存器的写入或者读出,我们会用到下面的函数:
sysInLong(int port) 用于从寄存器读出数据,其中的参数为寄存器的地址。
sysOutLong(int port, long data)用于向寄存器port中写入数据data。
但是请注意:sysInLong (),sysOutLong()函数中的port参数虽然都代表寄存器地址,但不能直接使用芯片手册提供的地址,需要加一个设备基址“ioaddr”,这个地址是PCI设备空间的io基址。
调用驱动添加函数:
iosDevAdd(&pDrvCtrl ->pFCcardHdr, name, drvNum);
其中第一个参数是设备抽象中的第一个参数,也就是内核中的数据结构:DEV_HDR(pDrvCtrl参见前面的文章);第二个参数是我们为设备取的名字,也就是一个字符串;第三个参数为iosDrvInstall()函数的返回值。因此我们需要先调用iosDrvInstall()函数,再调用iosDevAdd函数。
pciConfigLib.c的标准库给用户访问PCI配置空间.
3、PCI Interrupt Handling
每个PCI设备都有4个可用的中断PIN,分别命名为A,B,C和D。每个单功能的PCI设备都被要求使用中断PIN A来产生中断,而对于多功能PCI设备,每个功能使用一个中断PIN,但是根据PCI规范,每个PCI设备最多可提供8个功能,这样就必须两个功能共用一个中断PIN。当产生PCI中断时,PCI中断处理系统需要调用多个中断服务程序,那么最简单的方法就是每个ISR都调用一遍,ISR必须有能力判断该次中断源是否是自己产生的,如果不是,则立即返回,并接着会调用下一个中断服务程序。pciIntLib.c提供一些routine来挂接多个ISR到一个中断LINE上,该库通过挂接一个特殊的ISR,该ISR会遍历一个中断链表,所有共用同一个中断的ISRs都被放在这个链表中。pciIntConnect()用于将设备的ISR挂接到中断链表上,而pciIntDisConnect()用于删除中断链表上的一个ISR。
例子:
pciInitConnect(Vector, ISR1, PARAM1);
pciInitConnect(Vector, ISR2, PARAM2);
pciInitConnect(Vector, ISR3, PARAM3);
上面3个语句把ISR1,ISR2,和ISR3分别挂接到中断向量为Vector的链表intList里面,那么当中断发生时,会执行下面一个函数:
void sISR(void)
{
while(intList->next !=NULL)
(*intList->INT_ISR)(PARAM);/*分别调用ISR1,ISR2,ISR3*/
}