最近需要完成一个linux系统下的PCI驱动程序,然而处理器是PowerPC架构,以为在linux用户态就可以实现,但是发现不行。
上一篇文章中通过I/O端口访问了PCI设备,但是x86家族之外的的处理器都不为端口提供独立的地址空间,因此PowerPC下无法直接通过地址用in/out方法来访问PCI设备。
于是我使用另外一种机制:将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函数将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);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAJOR_NUM 250
struct pci_dev *g_pci_dev;
MODULE_LICENSE("GPL");
struct mycdev
{
int len;
unsigned char buffer[50];
struct cdev cdev;
};
static dev_t dev_num = {0};
struct mycdev *gcd;
struct class *cls;
unsigned long bar0_ss;
void * virt_addr;
bool request_mem;
unsigned long bar0_s;
unsigned long bar0_e;
unsigned long bar0_l;
unsigned long bar1_s;
unsigned long bar1_e;
unsigned long bar1_l;
#define PCI_VENDOR_ID_XILINX 0x10ee
#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,}
};
struct xilinx_card {
unsigned int magic;
struct xilinx_card *next;
};
static struct pci_driver xilinx_pci_driver = {
name: MODULE_NAME,
id_table: xilinx_pci_tbl,
probe: xilinx_probe,
remove: xilinx_release
};
static int xilinx_probe_pci(struct pci_dev *pci_dev)
{
int err;
void * virt_addr;
unsigned long read_result;
if (pci_enable_device(pci_dev))
return -EIO;
pci_set_master(pci_dev);
err = pci_set_dma_mask(pci_dev, 0xFFFFFFFFULL);
if (err) {
printk(KERN_ERR "xilinx: No usable DMA configuration, "
"aborting.\n");
}
printk("**************************************\n");
printk("**************************************\n");
printk("vendor:%0x\n", pci_dev->vendor);
printk("device:%0x\n", pci_dev->device);
printk("subvendor:%0x\n", pci_dev->subsystem_vendor);
printk("subdevice:%0x\n", pci_dev->subsystem_device);
printk("class:%0x\n", pci_dev->class);
printk("revision:%0x\n", pci_dev->revision);
printk("rom_base_reg:%0x\n", pci_dev->rom_base_reg);
printk("pin:%0x\n", pci_dev->pin);
printk("irq:%0x\n", pci_dev->irq);
printk("**************************************\n");
printk("------------------------------------------\n");
bar0_s = pci_resource_start(pci_dev, 0);
bar0_ss = pci_resource_start(pci_dev, 0);
bar0_e = pci_resource_end(pci_dev, 0);
bar0_l = pci_resource_len(pci_dev, 0);
bar1_s = pci_resource_start(pci_dev, 1);
bar1_e = pci_resource_end(pci_dev, 1);
bar1_l = pci_resource_len(pci_dev, 1);
printk("bar%d_s:0x%0x\n", 0, bar0_s);
printk("bar%d_e:0x%0x\n", 0, bar0_e);
printk("bar%d_l:0x%0x\n", 0, bar0_l);
printk("bar%d_s:0x%0x\n", 1, bar1_s);
printk("bar%d_e:0x%0x\n", 1, bar1_e);
printk("bar%d_l:0x%0x\n", 1, bar1_l);
int bar = bar0_l>>20;
printk("size of the bar0/20 is: (%d) \n", bar);
printk("------------------\n");
return 0;
}
static int __init xilinx_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
xilinx_probe_pci(pci_dev);
printk("do something pci xilinx module init \n");
if(bar0_l != 0x0)
{
printk("this memory is not being used ! \n");
if(true)//request_mem_region(bar0_s,sizeof(long),"dev_driver"))
{
request_mem = true;
printk("request this memory success \n");
virt_addr =ioremap(bar0_s,bar0_l);
printk("ioremap success \n");
}
else
{
request_mem = false;
printk("request this memory failed \n");
return -ENODEV;
}
}else
{
printk("bar0_l is 0 \n");
}
}
static int xilinx_release(struct inode *inode, struct file *file)
{
if(bar0_l != 0x0)
{
if(request_mem)
{
iounmap(bar0_s);
//release_mem_region(bar0_s, sizeof(long));
}
//iounmap(bar0_s);
printk("pci xilinx module release\n");
}
return 0;
}
///////////////////////////////////////////////////////////////////////
//open
static int dev_fifo_open(struct inode *inode, struct file *file)
{
printk("dev_fifo_open success!\n");
return 0;
}
static ssize_t dev_fifo_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
{
int n;
int ret;
unsigned long read_result;
if (bar0_l != 0x0)
{
if(request_mem)
{
read_result = ioread32(virt_addr);
printk("Value at (0x%0x): 0x%X\n", virt_addr, read_result);
printk("dev_fifo_read success!\n");
}else{
printk("can't use this region \n");
read_result =1;
}
ret = copy_to_user(ubuf,&read_result, sizeof(long));
if(ret != 0)
{
printk("dev_fifo_read failed!\n");
return -EFAULT;
}
}
return sizeof(long);
}
static ssize_t dev_fifo_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos)
{
int n;
int ret;
unsigned long writenum;
ret = copy_from_user(&writenum, ubuf, sizeof(long));
if(ret != 0)
return -EFAULT;
printk("user write values :%d \n",writenum);
unsigned long read_result;
//int writenum = ;
if (bar0_l != 0x0)
{
if (request_mem)
{
int i=0;
int size = sizeof(long);
read_result = ioread32(virt_addr+i);
printk("Value at (0x%0x): 0x%X ", virt_addr+i, read_result);
int bar = bar0_l>>15;
printk("size of the bar0/15 is: (%d) ", bar);
for(;i"0x%X ",read_result);
if(i %32 == 0) printk("\n");
i+=size;
}
// read 0
read_result = ioread32(virt_addr);
printk("Value at (0x%0x): 0x%X ", virt_addr, read_result);
iowrite32(writenum,virt_addr);
read_result = ioread32(virt_addr);
printk("Written 0x%X; readback %d\n", virt_addr, read_result);
printk("Written 0x%X; readback %0x\n", virt_addr, read_result);
//read +4
read_result = ioread32(virt_addr+4*size);
printk("Value at (0x%0x): 0x%X ", virt_addr+4*size, read_result);
iowrite32(writenum,virt_addr+4*size);
read_result = ioread32(virt_addr+4*size);
printk("Written 0x%X; readback %d\n", virt_addr+4*size, read_result);
printk("Written 0x%X; readback %0x\n", virt_addr+4*size, read_result);
printk("dev_fifo_write success!\n");
}else{
printk("this memeory space can't be used ! \n");
}
}
return sizeof(long);
}
static const struct file_operations fifo_operations = {
.owner = THIS_MODULE,
.open = dev_fifo_open,
.read = dev_fifo_read,
.write = dev_fifo_write,
};
int __init dev_fifo_init(void)
{
pci_register_driver(&xilinx_pci_driver);
int ret;
struct device *device;
gcd = kzalloc(sizeof(struct mycdev), GFP_KERNEL);
if(!gcd){
return -ENOMEM;
}
dev_num = MKDEV(MAJOR_NUM, 0);
ret = register_chrdev_region(dev_num,1,"dev_driver");
if(ret < 0){
ret = alloc_chrdev_region(&dev_num,0,1,"dev_driver");
if(ret < 0){
printk("Fail to register_chrdev_region\n");
goto err_register_chrdev_region;
}
}
cls = class_create(THIS_MODULE, "dev_driver");
if(IS_ERR(cls)){
ret = PTR_ERR(cls);
goto err_class_create;
}
cdev_init(&gcd->cdev,&fifo_operations);
ret = cdev_add(&gcd->cdev,dev_num,1);
if (ret < 0)
{
goto err_cdev_add;
}
device = device_create(cls,NULL,dev_num,NULL,"dev_driver");
if(IS_ERR(device)){
ret = PTR_ERR(device);
printk("Fail to device_create\n");
goto err_device_create;
}
printk("*******************************\n");
printk("Register dev_fifo to system,ok!\n");
return 0;
err_device_create:
cdev_del(&gcd->cdev);
err_cdev_add:
class_destroy(cls);
err_class_create:
unregister_chrdev_region(dev_num, 1);
err_register_chrdev_region:
return ret;
}
void __exit dev_fifo_exit(void)
{
pci_unregister_driver(&xilinx_pci_driver);
device_destroy(cls,dev_num );
class_destroy(cls);
cdev_del(&gcd->cdev);
unregister_chrdev_region(dev_num, 1);
printk("unregister this fifo module \n");
return;
}
module_init(dev_fifo_init);
module_exit(dev_fifo_exit);