2016-02-28 175 views
1

我正在尋找一種訪問PCI設備(顯式BAR2和BAR3)的內存空間而不使用DMA和IO映射的方式。我已經閱讀了很多文檔,但是我從來沒有看到流程圖或一步一步的如何。所以我所有的嘗試都不成功。如何通過內存映射從Linux內核空間訪問PCI內存(內核3.14)

這些都是內部pci_probe步驟其實我嘗試:

  1. data = kzalloc(sizeof(*data) , GFP_KERNEL);
  2. pci_set_drvdata(pdev, data);
  3. pci_enable_device(pdev);

現在是什麼的問題是訪問正確的地址BAR2+offset使用writebreadb?還是有另一個功能來讀取/寫入這個空間?


PS:關於iomap一個類似的問題被張貼here

+0

你不能訪問任何東西而不映射它。你試圖解決的實際問題是什麼? –

+0

目標是訪問PCI設備上的存儲空間以實現快速數據交換。我知道我需要映射地址。但我讀到有兩種不同類型的映射。 IO映射達到IO空間和內存映射達到內存空間。我知道通過io映射到達內存空間的方式,但現在我正在尋找通過內存映射來實現它的更快方法。 – Alex44

+0

在x86(-64)上,我只能通過內存映射通過I/O映射和內存BAR映射I/O BAR,據我所知。其他體系結構不一定有區別,其實現可以使用I/O範圍的內存映射(例如PPC)。 – pmdj

回答

1

經過大量的研究,我找到了一種方法,讀寫PCI BAR2。看來,ioremap,pci_ioremap_barmemremap()(內核4.3+)允許CPU緩存在PCI設備和內核空間內存之間傳輸的數據。這會導致數據損壞。但我不知道它終於來自哪裏。

解決此問題的方法使用的ioremap_nocache。以下代碼顯示了PCI探針功能。

static int 
_pci_probe (struct pci_dev *pdev, 
      const struct pci_device_id *ent) 
{ 
    int ret = 0; 
    int i; 
    unsigned long *pbas2addr; 
    u8 buf8; 
    u8 *mem8; 

    buf8 = 0xF0; 
    // put mem8 to the heap and initialize them with zeros 
    mem8 = kcalloc((0x020000),sizeof(u8), GFP_KERNEL); 

    // enabling the device 
    ret = pci_enable_device(pdev); 
    if(ret) 
    { 
    printk(KERN_ERR "Failed to enable PCI device.\n"); 
    goto no_enable; 
    } 

    // take ownership of pci related regions 
    pci_request_regions(pdev, "expdev"); 

    // checking if PCI-device reachable by checking that BAR0 is defined and 
    // memory mapped 
    if(!(pci_resource_flags(pdev,0) & IORESOURCE_MEM)) 
    { 
    printk(KERN_ERR "Incorrect BAR configuration.\n"); 
    ret = -ENODEV; 
    goto bad_bar; 
    } 

    // remap BAR2 avoiding the use of CPU cache 
    pbas2addr = ioremap_nocache(pci_resource_start(pdev,2), 
           pci_resource_len(pdev,2)); 

    printk(KERN_INFO "BAR2 Addr: %p\n",pbas2addr); 
    printk(KERN_INFO "BAR2 len: %x\n",(int)pci_resource_len(pdev,2)); 

    // write something to BAR2 
    buf8 = 0xF0; 
    for (i = 0x000000; i<0x020000; i++) 
    { 
    *((u8*)pbas2addr+i) = buf8; // it's important to cast the pointer 
    } 

    // read back 
    buf8 = 0; 
    for (i = 0x000000; i<0x020000; i++) 
    { 
    mem8[i] = *((u8*)pbas2addr+i); 
    } 

    return 0; 

bad_bar: 
    pci_disable_device(pdev); 
no_enable: 

    return ret; 
} 

此外:

  1. 使用iowrite不起作用穩定映射內存的訪問。有時會在PCI BAR2內存中找到人造廢話。也許有這個命令的保留序列。我不知道。
  2. 在我的情況下,BAR2內存的一些地址範圍需要寫入兩次。我認爲這是該設備的特殊行爲。
  3. 在我的情況下,並不是所有的地址範圍都可以通過32位訪問達到。我認爲這也是這種設備的特殊行爲。
+0

)我建議看看[mei:通過devres函數簡化錯誤處理。](https://git.kernel.org/pub/scm/linux /kernel/git/torvalds/linux.git/commit/?id=f8a096059fc5f719301d314e5d7451f1bab5032a) – 0andriy

0

便攜式方法是使用pci_iomap()函數。它會自動確定BAR的類型,並與MMIO和PIO一起使用。而不是writeb()/ readb()你應該使用ioread *()/ iowrite *()