您的位置:首页 > 运维架构 > 网站架构

PowerPC架构下Linux系统读写PCI设备

2016-01-21 10:50 826 查看
最近需要完成一个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);
return 0;
}


完整的代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <linux/ioport.h>

#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<bar;)
{
read_result = ioread32(virt_addr+i);
printk("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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息