[转]PCI IO操作

1. 转载之转载(未找到原出处):https://blog.csdn.net/cupidove/article/details/52402257

2. 本人注:这篇文章对我最大的价值,就是终于明确看到,pci配置寄存器中的BAR,当有memory地址和io地址两种方式时,只是提供了两种访问pci寄存器的不同方法,本文以内存访问和io访问两种方式,最终都读到的是网卡芯片最开始6个寄存器中保存的6字节mac地址。


以rtl8139d以太网卡驱动为例子可以很好的来学习、分析这个问题:
        8139芯片中开始六个寄存器中存放的是网卡的mac地址,有多种方法可以得到。pci设备有三个空间分别为配置空间、io空间、内存空间,配置空间在开机时由bootloader设置、程序中一般用系统函数将一些配置信息读出来就可以了,很方便,没有必要直接操作。io空间和内存空间这两种模式都可以帮助我们得到mac的值,利用io空间的话,需要用CPU的特殊io读写指令例如inb outb等,x86是有这个特性的,一般arm好像都是memory mapped所以在arm上一般采用映射到内存空间来访问。
         在标准的8139too.c中,他提供了两种方法即内存空间和io空间。
 pio_start = pci_resource_start (pdev, 0);
 pio_end = pci_resource_end (pdev, 0);
 pio_flags = pci_resource_flags (pdev, 0);
 pio_len = pci_resource_len (pdev, 0);

 mmio_start = pci_resource_start (pdev, 1);
 mmio_end = pci_resource_end (pdev, 1);
 mmio_flags = pci_resource_flags (pdev, 1);
 mmio_len = pci_resource_len (pdev, 1);
         这样看来0号bar就是提供的io映射,1号bar提供内存映射。所以我想如果自己写一个以太网驱动的话,关于这块的代码就可以精简一下,只使用内存映射的方法就可以了。下面是网上有人写的几种不同方式测试代码,他这些代码应该是在x86上试的,我没有试过,先分析,周末搞到arm上去跑一下。
[第一种]
unsigned long mmio_start, addr1, addr2;
void __iomem *ioaddr;
mmio_start = pci_resource_start( pdev, 1);
ioaddr = pci_iomap(pdev, 1, 0);
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO "mmio start: %lX\n", mmio_start);
printk(KERN_INFO "ioaddr: %p\n", ioaddr);
printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lX\n",
(addr1) & 0xFF,
(addr1 >> 8) & 0xFF,
(addr1 >> 16 ) & 0xFF,
(addr1 >> 24 ) & 0xFF,
(addr2) & 0xFF,
(addr2 >> 8) & 0xFF );
运行结果:
Mar 10 22:34:56 localhost kernel: mmio start: E0000800
Mar 10 22:34:56 localhost kernel: ioaddr: f8aa6800
Mar 10 22:34:56 localhost kernel: 00.02.3F.AC.41.9D
------------------------------------------------------------------------------------------------
这种方法采用内存映射的方法,得到bar1的物理地址,mmio_start然后由pci_ioremap函数把bar1的这段地址映射到内核虚拟地址空间。然后用ioread32()函数读出里面的值。这个ioread32函数不知道是怎么定义的,要查一查。
---------------------------------------------------------------------------------------------------
[第二种]
unsigned long pio_start, pio_len, addr1, addr2;
void __iomem *ioaddr;
pio_start = pci_resource_start( pdev, 0);
pio_len = pci_resource_len (pdev, 0);
ioaddr = ioport_map(pio_start, pio_len);
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO "pio start: %lX\n", pio_start);
printk(KERN_INFO "ioaddr: %p\n", ioaddr);
printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lX\n",
(addr1) & 0xFF,
(addr1 >> 8) & 0xFF,
(addr1 >> 16 ) & 0xFF,
(addr1 >> 24 ) & 0xFF,
(addr2) & 0xFF,
(addr2 >> 8) & 0xFF );
运行结果:
Mar 10 22:30:52 localhost kernel: pio start: 3400
Mar 10 22:30:52 localhost kernel: ioaddr: 00013400
Mar 10 22:30:52 localhost kernel: 00.02.3F.AC.41.9D
-------------------------------------------------------------------------------------------------------
这种方式是使用io端口映射,首先在bar0得到io端口的物理首地址和其长度,然后使用ioport_map将物理地址映射到内核虚拟空间。然后使用的读写函数仍然是ioread32
-------------------------------------------------------------------------------------------------------
[第三种]
unsigned long pio_start, addr1, addr2;
pio_start = pci_resource_start( pdev, 0 );
addr1 = inl( pio_start );
addr2 = inl( pio_start + 4 );
printk(KERN_INFO "port io start: %lX\n", pio_start);
printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lX\n",
(addr1) & 0xFF,
(addr1 >> 8) & 0xFF,
(addr1 >> 16) & 0xFF,
(addr1 >> 24) & 0xFF,
(addr2) & 0xFF,
(addr2 >> 8) & 0xFF );
运行结果:
Mar 10 22:36:18 localhost kernel: port io start: 3400
Mar 10 22:36:18 localhost kernel: 00.02.3F.AC.41.9D
---------------------------------------------------------------------------------------------------------
这里也是使用io映射,估计这里的inl函数有phy->virt转化的功能,不知道他引用的哪里的。
----------------------------------------------------------------------------------------------------------
[第四种]
unsigned long pio_start;
u8 addr1, addr2, addr3, addr4, addr5, addr6;
pio_start = pci_resource_start( pdev, 0 );
addr1 = inb( pio_start );
addr2 = inb( pio_start + 1 );
addr3 = inb( pio_start + 2 );
addr4 = inb( pio_start + 3 );
addr5 = inb( pio_start + 4 );
addr6 = inb( pio_start + 5 );
printk(KERN_INFO "port io start: %lX\n", pio_start);
printk(KERN_INFO "%02X.%02X.%02X.%02X.%02X.%02X\n",
addr1, addr2, addr3, addr4, addr5, addr6 );
运行结果:
Mar 10 22:37:19 localhost kernel: port io start: 3400
Mar 10 22:37:19 localhost kernel: 00.02.3F.AC.41.9D
----------------------------------------------------------------------------------------------------------
这里也是使用的io映射,但是在读的时候一个字节一个字节的读,不知道这种方法行不行,8139的datasheet上好像说这个地方是要4个字节一读的。回去试一下才知道。
------------------------------------------------------------------------------------------------------------


发布了14 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/wxchbhd/article/details/79820813
PCI