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 返回的地址 之後就可以在內核空間讀寫

原理圖如下:
這裏寫圖片描述

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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章