最近需要完成一个linux系统下的PCI驱动程序,然而处理器是PowerPC架构,以为在linux用户态就可以实现,但是发现不行。
上一篇文章中通过I/O端口访问了PCI设备,但是x86家族之外的的处理器都不为端口提供独立的地址空间,因此PowerPC下无法直接通过地址用in/out方法来访问PCI设备。
于是我使用另外一种机制:将PCI设备的空间映射到内存中。
实现思路
- 完成PCI驱动代码,确保特定的PCI设备被linux识别
- 同时 还需要创建一个 字符设备驱动 可以让用户从用户空间传数据
- 先从 用户空间传数据到内核空间 然后 在内核空间操作PCI的内存
- 操作PCI内存的方式 是 读取 bar0的基地址 然后 ioremap 返回的地址 之后就可以在内核空间读写
原理图如下:
PCI驱动
pci驱动代码很简单,我们提供设备ID和厂商ID,并指定数据结构pci_driver中的id_table以及初始化函数probe和移除函数remove。
同时在内核模块加载时调用pci_register_driver 和卸载时调用pci_unregister_driver 函数。一个简单的模板代码如下:
#define PCI_VENDOR_ID_XILINX 0x10ee //change to your pci device
#define PCI_DEVICE_ID_XILINX 0x7021
#define MODULE_NAME "xilinx_7021"
static int __init xilinx_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id);
static int xilinx_release(struct inode *inode, struct file *file);
static struct pci_device_id xilinx_pci_tbl [] __initdata = {
{PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX, PCI_ANY_ID, PCI_ANY_ID},
{
0,}
};
static struct pci_driver xilinx_pci_driver = {
name: MODULE_NAME,
id_table: xilinx_pci_tbl,
probe: xilinx_probe,
remove: xilinx_release
};
字符驱动
字符驱动提供了操作设备内存的方法,包括读写,从而用户打开设备后可以方便的读写设备内存。本文不谈及实现linux字符驱动实现的细节,详情可以参考博客:
字符驱动
ioremap
ioremap函数将PCI内存空间转换到内核空间,从而驱动程序可以访问任意的PCI内存地址。因此当我们加载了PCI设备时,我们可以将PCI配置空间的bar0地址保存,之后读写的时候将bar0地址用ioremap函数转换,然后用ioread/iowrite函数进行读写,如下例:
virt_addr =ioremap(bar0_s,bar0_l);
read_result = ioread32(virt_addr+4*size);
iowrite32(writenum,virt_addr+4*size);
用户空间代码
用户空间代码相对简单,只要就行代开对应大的字符设备驱动,然后读写即可。代码如下:
int main(int argc, const char *argv[])
{
int fd ;
int n;
fd = open("/dev/dev_driver",O_RDWR);//对应的字符设备名称,由自己定义
if(fd < 0){
perror("Fail ot open");
return -1;
}
printf("open successful ,fd = %d\n",fd);
unsigned long writenum=2112;
n = write(fd,&writenum,sizeof(long));
if(n < 0){
perror("Fail to write");
return -1;
}
printf("write %d bytes!\n",n);
unsigned long read_result;
read(fd,&read_result,sizeof(long));
printf("the my_driver is %d\n",read_result);