2016-02-28 302 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 *()