Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1772541
  • 博文数量: 272
  • 博客积分: 1272
  • 博客等级: 少尉
  • 技术积分: 1866
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-09 15:51
文章分类

全部博文(272)

文章存档

2016年(16)

2015年(28)

2014年(97)

2013年(59)

2012年(25)

2011年(47)

分类: 其他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编号。例如某个设备的编号为(250)可能会由于通过PCI-PCI桥来扩展或者移除PCI设备而变为(150)。在正常的设备操作过程中,不应该去访问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*/

}

阅读(6159) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

fexe2014-09-01 13:00:46

非常感谢,博主的博客非常好!
整理的井井有条。