2012-08-28 203 views
9

我正在爲Linux 2.6.36編程一個PCI驅動程序。Linux驅動程序開發:PCI和PCIe驅動程序之間的區別?

這是我的代碼。我的問題是,如果我想將此驅動程序用於PCIe設備,是否必須進行一些修改?

#include <linux/fs.h> 
#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/pci.h> 
#include <linux/interrupt.h> 
#include <asm-generic/signal.h> 
#undef debug 


// ATTENTION copied from /uboot_for_mpc/arch/powerpc/include/asm/signal.h 
// Maybe it don't work with that 
//____________________________________________________________ 
#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ 
#define SA_SHIRQ  0x04000000 
//____________________________________________________________ 

#define pci_module_init pci_register_driver // function is obsoleted 

// Hardware specific part 
#define MY_VENDOR_ID 0x5333 
#define MY_DEVICE_ID 0x8e40 
#define MAJOR_NR  240 
#define DRIVER_NAME "PCI-Driver" 

static unsigned long ioport=0L, iolen=0L, memstart=0L, memlen=0L,flag0,flag1,flag2,temp=0L; 

// private_data 
struct _instance_data { 

    int counter; // just as a example (5-27) 

    // other instance specific data 
}; 

// Interrupt Service Routine 
static irqreturn_t pci_isr(int irq, void *dev_id, struct pt_regs *regs) 
{ 
    return IRQ_HANDLED; 
} 


// Check if this driver is for the new device 
static int device_init(struct pci_dev *dev, 
     const struct pci_device_id *id) 
{ 
    int err=0; // temp variable 

    #ifdef debug 

    flag0=pci_resource_flags(dev, 0); 
    flag1=pci_resource_flags(dev, 1); 
    flag2=pci_resource_flags(dev, 2); 
    printk("DEBUG: FLAGS0 = %u\n",flag0); 
    printk("DEBUG: FLAGS1 = %u\n",flag1); 
    printk("DEBUG: FLAGS2 = %u\n",flag2); 

    /* 
    * The following sequence checks if the resource is in the 
    * IO/Storage/Interrupt/DMA address space 
    * and prints the result in the dmesg log 
    */ 
    if(pci_resource_flags(dev,0) & IORESOURCE_IO) 
    { 
     // Ressource is in the IO address space 
     printk("DEBUG: IORESOURCE_IO\n"); 
    } 
    else if (pci_resource_flags(dev,0) & IORESOURCE_MEM) 
    { 
     // Resource is in the Storage address space 
     printk("DEBUG: IORESOURCE_MEM\n"); 
    } 
    else if (pci_resource_flags(dev,0) & IORESOURCE_IRQ) 
    { 
     // Resource is in the IRQ address space 
     printk("DEBUG: IORESOURCE_IRQ\n"); 
    } 
    else if (pci_resource_flags(dev,0) & IORESOURCE_DMA) 
    { 
     // Resource is in the DMA address space 
     printk("DEBUG: IORESOURCE_DMA\n"); 
    } 
    else 
    { 
     printk("DEBUG: NOTHING\n"); 
    } 

    #endif /* debug */ 

    // allocate memory_region 
    memstart = pci_resource_start(dev, 0); 
    memlen = pci_resource_len(dev, 0); 
    if(request_mem_region(memstart, memlen, dev->dev.kobj.name)==NULL) { 
     printk(KERN_ERR "Memory address conflict for device \"%s\"\n", 
       dev->dev.kobj.name); 
     return -EIO; 
    } 
    // allocate a interrupt 
    if(request_irq(dev->irq,pci_isr,SA_INTERRUPT|SA_SHIRQ, 
      "pci_drv",dev)) { 
     printk(KERN_ERR "pci_drv: IRQ %d not free.\n", dev->irq); 
    } 
    else 
    { 
     err=pci_enable_device(dev); 
     if(err==0)  // enable device successful 
     { 
      return 0; 
     } 
     else  // enable device not successful 
     { 
      return err; 
     } 

    } 
    // cleanup_mem 
    release_mem_region(memstart, memlen); 
    return -EIO; 
} 
// Function for deinitialization of the device 
static void device_deinit(struct pci_dev *pdev) 
{ 
    free_irq(pdev->irq, pdev); 
    if(memstart) 
     release_mem_region(memstart, memlen); 
} 

static struct file_operations pci_fops; 

static struct pci_device_id pci_drv_tbl[] __devinitdata = { 
    {  MY_VENDOR_ID,   // manufacturer identifier 
     MY_DEVICE_ID,   // device identifier 
     PCI_ANY_ID,    // subsystem manufacturer identifier 
     PCI_ANY_ID,    // subsystem device identifier 
     0,      // device class 
     0,      // mask for device class 
     0 },     // driver specific data 
     { 0, } 
}; 

static int driver_open(struct inode *geraetedatei, struct file *instance) 
{ 
    struct _instance_data *iptr; 

    iptr = (struct _instance_data *)kmalloc(sizeof(struct _instance_data), 
      GFP_KERNEL); 
    if(iptr==0) { 
     printk("not enough kernel mem\n"); 
     return -ENOMEM; 
    } 
    /* replace the following line with your instructions */ 
    iptr->counter= strlen("Hello World\n")+1; // just as a example (5-27) 

    instance->private_data = (void *)iptr; 
    return 0; 
} 

static void driver_close(struct file *instance) 
{ 
    if(instance->private_data) 
     kfree(instance->private_data); 
} 


static struct pci_driver pci_drv = { 
    .name= "pci_drv", 
      .id_table= pci_drv_tbl, 
      .probe= device_init, 
      .remove= device_deinit, 
}; 

static int __init pci_drv_init(void) 
{ // register the driver by the OS 
    if(register_chrdev(MAJOR_NR, DRIVER_NAME, &pci_fops)==0) { 
     if(pci_module_init(&pci_drv) == 0) // register by the subsystem 
      return 0; 
     unregister_chrdev(MAJOR_NR,DRIVER_NAME); // unregister if no subsystem support 
    } 
    return -EIO; 
} 

static void __exit pci_drv_exit(void) 
{ 
    pci_unregister_driver(&pci_drv); 
    unregister_chrdev(MAJOR_NR,DRIVER_NAME); 
} 

module_init(pci_drv_init); 
module_exit(pci_drv_exit); 

MODULE_LICENSE("GPL"); 
+0

在調用'pci_enable_device'之後調用'release_mem_region'是否合適?就目前而言,即使此通話失敗,內存區仍將被分配。也許使用'check_mem_region'也可能是一種理智的行爲? – HonkyTonk

回答

2

從軟件角度看,PCI和PCI Express設備基本相同。 PCIe設備具有相同的配置空間,BAR和(通常)支持相同的PCI INTx中斷。

示例1:Windows XP對PCIe沒有特殊的瞭解,但在PCIe系統上運行良好。示例#2:我的公司提供PCI和PCIe版本的外設板,並且它們使用相同的Windows/Linux驅動程序包。司機不會「知道」兩板之間的區別。

但是:PCIe設備頻繁使用「高級」功能,如MSI,熱插拔,擴展配置空間等等。許多這些功能都存在於傳統PCI上,但未被使用。如果這是您正在設計的設備,無論您是否實施這些高級功能,都取決於您。

+0

不要忘記,PCIe傳統設備(PCIe設備出於兼容性原因就像它們是PCI設備一樣)實現了許多不再推薦的功能。想到的主要特徵是I/O讀寫,在實踐中被發現效率非常低。 – Joshua

3

據我記得,對於我寫的設備驅動程序,PCI設備和PCIe設備沒有區別。我的設備驅動程序使用與您的相同種類的呼叫:chrdev,pci_resource,irqmem_region

+0

感謝您的回答! – Peter

+1

嗨@Peter,據我記憶,PCIE設備也支持MSI中斷。因此您可以請求MSI中斷來節省CPU週期。謝謝 –

0

PCIe是一款具有更高速度和更高性能的高級版本。 所有標準的基本功能保持不變。 驅動程序註冊和提供處理程序是同一個,因爲所有PCI驅動程序都註冊到相同的Linux PCI子系統。