ioremap和ioport_map

1390阅读 0评论2014-10-13 hnylcxq
分类:LINUX

IO寄存器(内存)有两种地址映射方式,即映射到IO地址空间(portio),映射到内存地址空间(mmio)。
在linux-2.6之前,前者使用inb/outb(......)访问,后者通过readb/writeb(......)来访问(访问前必须使用ioremap将IO物理地址映射到虚拟地址)。

ioremap函数作用如前所述,就是将IO物理地址映射成虚拟地址,这样readb/writeb可以访问映射后的地址。mmio映射的IO内存,其物理地址已经在4G地址空间内,与内存的编址方式相同。因此ioremap的作用实际上就是为这一段物理地址建立页表,返回驱动程序可以访问的虚拟地址。

ioport_map函数的目的是试图提供与ioremap一致的虚拟地址空间。这样不论portio还是mmio都可以使用统一的访问函数:ioread8/iowrite8(......)。
我们进入ioport_map和ioport_unmap函数:
  1. void __iomem *ioport_map(unsigned long port, unsigned int nr)
  2. {
  3.     if (port > PIO_MASK)
  4.         return NULL;
  5.     return (void __iomem *) (unsigned long) (port + PIO_OFFSET);
  6. }

  7. void ioport_unmap(void __iomem *addr)
  8. {
  9.     /* Nothing to do */
  10. }

其实函数很简单,ioport_map仅仅是将port加上PIO_OFFSET(64k),而ioport_unmap则什么都不做。这样portio的64k空间就被映射到虚拟地址的64k~128k之间,而ioremap返回的虚拟地址则肯定在3G之上。这样portio和mmio的虚拟地址就被统一起来。
再看ioread8的源码,其实现也就是对虚拟地址进行了判断,以区分portio和mmio,然后分别使用inb/outb,和readb/writeb来读写。
  1. unsigned int fastcall ioread8(void __iomem *addr)
  2. {
  3.     IO_COND(addr, return inb(port), return readb(addr));
  4. }

  5. #define VERIFY_PIO(port) BUG_ON((port & ~PIO_MASK) != PIO_OFFSET)
  6. #define IO_COND(addr, is_pio, is_mmio) do { \
  7.     unsigned long port = (unsigned long __force)addr; \
  8.         if (port < PIO_RESERVED) { \
  9.             VERIFY_PIO(port); \
  10.             port &= PIO_MASK; \
  11.             is_pio; \
  12.         } else { \
  13.             is_mmio; \
  14.         } \
  15. } while (0)

  16. 展开:
  17. unsigned int fastcall ioread8(void __iomem *addr)
  18. {
  19.     unsigned long port = (unsigned long __force)addr;
  20.     if( port < 0x40000UL ) {
  21.         BUG_ON( (port & ~PIO_MASK) != PIO_OFFSET );
  22.         port &= PIO_MASK;
  23.         return inb(port);
  24.     }else{
  25.         return readb(addr);
  26.     }
  27. }
上一篇:虚拟世界和现实世界(转)
下一篇:Linux驱动发开,usb设备的probe全过程