pci驱动与硬件通信
接上一篇博文,当将PCI设备空间的资源映射出来之后。只需要做两件事情,PCI设备的基本框架就建立起来了。
首先注册驱动程序,调用如下函数:
int iosDrvInstall() / STATUS iosDevAdd()
先来讲int iosDrvInstall()函数:
Int iosDrvInstall
(
FUNCPTR pCreate, FUNCPTR pDelete,
FUNCPTR pOpen, FUNCPTR pClose,
FUNCPTR pRead, FUNCPTR pWrite,
FUNCPTR pIoctl /*pointer to driver ioctl function*/
)
前面的文章已经提到过。除了最后一个参数之外,其它参数都可以为NULL,最后一个参数向系统注册一个应用程序与驱动通信的函数。应用程序通过调用
ioctl
(
int fd,
int request,
void *data
);
三个参数分别代表文件描述符,请求操作码和需要传往驱动的数据。
假设我们在函数iosDrvInstall()最后注册的IOCtl函数为:pci_Ioctl()。
pci_Ioctl(int fd, int request, void *data)
注意:这里这个函数也需要包含三个参数,与ioctl的参数对应,当在应用层调用ioctl()函数的时候,ioctl()中的第三个参数就会根据文件描述符来识别出正确的设备并将与驱动通信的数据写入到驱动中的IOCTL函数中,这里是:pci_Ioctl()。
pci_Ioctl()函数的大体框架如下:
LOCAL STATUS pci_Ioctl(int DevID,int request, void *arg)
{
…
Switch(request)
{
case(X1):
…
break;
case(X2):
…
break;
default:
…
}
return 1;
}
在每个分支针对传进来的操作码进行操作。操作码一般是定义的宏,不同的操作我们定义一个不同的值来做标识。
在case语句中一般需要进行寄存器的写入或者读出,我们会用到下面的函数:
sysInLong(int port)
sysOutLong(int port, long data)
其中sysInLong()函数用于从寄存器读出数据,其中的参数为寄存器的地址。
sysOutLong()函数用于向寄存器port中写入数据data。
还记得上面的pci_Ioctl()函数吗,这个函数中的第三个参数就是用于读出或者写入数据。为了实现读出或者写入并使的这些数据能轻松在应用程序和驱动之间传递,这个参数最好入上面一样申明为void *。
但是请注意:sysInLong (),sysOutLong()函数中的port参数虽然都代表寄存器地址,但是不能直接使用芯片手册或者是FPGA设计者提供的地址,需要加一个设备基址,也就是上一篇文章中提到的“ioaddr”,这个地址是PCI设备空间的io基址。因此这两个读入和写入函数一般会进行封装,方便操作。
可以这样做:
#define xxxDevice_Write(reg, value) sysOutLong(ioaddr+(reg), value)
#define xxxDevice_Read(reg) ((unsigned int) sysInLong(ioaddr+(reg)))
之后就可以直接调用这两个宏,用于写入或者读出了。
现在可以介绍
iosDevAdd(DEV_HDR *pDevHdr, char *name, int drvnum)
了,我们可以这样来调用该函数:
iosDevAdd(&pDrvCtrl ->pFCcardHdr, name, drvNum);
其中第一个参数是设备抽象中的第一个参数,也就是内核中的数据结构:DEV_HDR(pDrvCtrl参见前面的文章);第二个参数是我们为设备取的名字,也就是一个字符串;第三个参数为iosDrvInstall()函数的返回值。因此我们需要先调用iosDrvInstall()函数,再调用iosDevAdd函数。
经过上面的介绍,驱动的大体框架就建立好了。具体的操作就根据具体情况来进行了。
本博客还会继续介绍Windows下基于WDM的PCI设备驱动程序开发和Linux下的PCI驱动程序开发。如果有可能还会介绍Windows下WDF的PCI驱动程序的开发。