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

最近需要完成一个linux系统下的PCI驱动程序,然而处理器是PowerPC架构,以为在linux用户态就可以实现,但是发现不行。

上一篇文章中通过I/O端口访问了PCI设备,但是x86家族之外的的处理器都不为端口提供独立的地址空间,因此PowerPC下无法直接通过地址用in/out方法来访问PCI设备。

于是我使用另外一种机制:将PCI设备的空间映射到内存中。


实现思路

  1. 完成PCI驱动代码,确保特定的PCI设备被linux识别
  2. 同时 还需要创建一个 字符设备驱动 可以让用户从用户空间传数据
  3. 先从 用户空间传数据到内核空间 然后 在内核空间操作PCI的内存
  4. 操作PCI内存的方式 是 读取 bar0的基地址 然后 ioremap 返回的地址 之后就可以在内核空间读写

原理图如下:
PowerPC架构下Linux系统读写PCI设备_第1张图片

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 
#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);

你可能感兴趣的:(Linux)