<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://www.mywiki.cn/Hovercool/index.php?action=history&amp;feed=atom&amp;title=I%2FO%E7%AB%AF%E5%8F%A3%E7%9A%84%E8%AE%BF%E9%97%AE</id>
	<title>I/O端口的访问 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://www.mywiki.cn/Hovercool/index.php?action=history&amp;feed=atom&amp;title=I%2FO%E7%AB%AF%E5%8F%A3%E7%9A%84%E8%AE%BF%E9%97%AE"/>
	<link rel="alternate" type="text/html" href="https://www.mywiki.cn/Hovercool/index.php?title=I/O%E7%AB%AF%E5%8F%A3%E7%9A%84%E8%AE%BF%E9%97%AE&amp;action=history"/>
	<updated>2026-09-01T03:59:53Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://www.mywiki.cn/Hovercool/index.php?title=I/O%E7%AB%AF%E5%8F%A3%E7%9A%84%E8%AE%BF%E9%97%AE&amp;diff=789&amp;oldid=prev</id>
		<title>2015年5月6日 (三) 08:16 Hovercool</title>
		<link rel="alternate" type="text/html" href="https://www.mywiki.cn/Hovercool/index.php?title=I/O%E7%AB%AF%E5%8F%A3%E7%9A%84%E8%AE%BF%E9%97%AE&amp;diff=789&amp;oldid=prev"/>
		<updated>2015-05-06T08:16:49Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;=== &amp;#039;&amp;#039;&amp;#039;一、基础介绍&amp;#039;&amp;#039;&amp;#039; ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
IO寄存器(内存)有两种地址映射方式，即映射到IO地址空间（portio），映射到内存地址空间(mmio)。&lt;br /&gt;
在linux-2.6之前，前者使用inb/outb(......)访问，后者通过readb/writeb(......)来访问（访问前必须使用ioremap将IO物理地址映射到虚拟地址）。&lt;br /&gt;
&lt;br /&gt;
ioremap函数作用如前所述，就是将IO物理地址映射成虚拟地址，这样readb/writeb可以访问映射后的地址。mmio映射的IO内存，其物理地址已经在4G地址空间内，与内存的编址方式相同。因此ioremap的作用实际上就是为这一段物理地址建立页表，返回驱动程序可以访问的虚拟地址。&lt;br /&gt;
&lt;br /&gt;
ioport_map函数的目的是试图提供与ioremap一致的虚拟地址空间。这样不论portio还是mmio都可以使用统一的访问函数：ioread8/iowrite8(......)。&lt;br /&gt;
我们进入ioport_map和ioport_unmap函数：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
    void __iomem *ioport_map(unsigned long port, unsigned int nr)&lt;br /&gt;
    {&lt;br /&gt;
        if (port &amp;gt; PIO_MASK)&lt;br /&gt;
            return NULL;&lt;br /&gt;
        return (void __iomem *) (unsigned long) (port + PIO_OFFSET);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void ioport_unmap(void __iomem *addr)&lt;br /&gt;
    {&lt;br /&gt;
        /* Nothing to do */&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
其实函数很简单，ioport_map仅仅是将port加上PIO_OFFSET(64k)，而ioport_unmap则什么都不做。这样portio的64k空间就被映射到虚拟地址的64k~128k之间，而ioremap返回的虚拟地址则肯定在3G之上。这样portio和mmio的虚拟地址就被统一起来。&lt;br /&gt;
再看ioread8的源码，其实现也就是对虚拟地址进行了判断，以区分portio和mmio，然后分别使用inb/outb，和readb/writeb来读写。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
    unsigned int fastcall ioread8(void __iomem *addr)&lt;br /&gt;
    {&lt;br /&gt;
        IO_COND(addr, return inb(port), return readb(addr));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    #define VERIFY_PIO(port) BUG_ON((port &amp;amp; ~PIO_MASK) != PIO_OFFSET)&lt;br /&gt;
    #define IO_COND(addr, is_pio, is_mmio) do { \&lt;br /&gt;
        unsigned long port = (unsigned long __force)addr; \&lt;br /&gt;
            if (port &amp;lt; PIO_RESERVED) { \&lt;br /&gt;
                VERIFY_PIO(port); \&lt;br /&gt;
                port &amp;amp;= PIO_MASK; \&lt;br /&gt;
                is_pio; \&lt;br /&gt;
            } else { \&lt;br /&gt;
                is_mmio; \&lt;br /&gt;
            } \&lt;br /&gt;
    } while (0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
展开：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
    unsigned int fastcall ioread8(void __iomem *addr)&lt;br /&gt;
    {&lt;br /&gt;
        unsigned long port = (unsigned long __force)addr;&lt;br /&gt;
        if( port &amp;lt; 0x40000UL ) {&lt;br /&gt;
            BUG_ON( (port &amp;amp; ~PIO_MASK) != PIO_OFFSET );&lt;br /&gt;
            port &amp;amp;= PIO_MASK;&lt;br /&gt;
            return inb(port);&lt;br /&gt;
        }else{&lt;br /&gt;
            return readb(addr);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;#039;&amp;#039;&amp;#039;二、图文讲解&amp;#039;&amp;#039;&amp;#039; ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.IO端口访问 &lt;br /&gt;
&lt;br /&gt;
一种方法是：直接使用IO端口操作函数：在设备打开或驱动模块被加载时申请IO端口区域，之后使用inb(),outb()等进行端口访问，最后在设备关闭或驱动被卸载时释放IO端口范围。流程如下：&lt;br /&gt;
&lt;br /&gt;
[[Image:linux_portio_handle1.jpeg]]&lt;br /&gt;
&lt;br /&gt;
另外一种途径是:将IO端口映射为内存进行访问，在设备打开或驱动模块被加载时，申请IO端口区域并使用ioport_map()映射到内存，之后使用IO内存的函数进行端口访问，最后，在设备关闭或驱动模块被卸载时释放IO端口并释放映射，流程如下：&lt;br /&gt;
&lt;br /&gt;
[[Image:linux_portio_handle2.jpeg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.IO内存访问&lt;br /&gt;
&lt;br /&gt;
上边是IO端口的访问方法，至于IO内存的访问方法是：首先调用request_mem_region()申请资源，接着将寄存器地址通过ioremap()映射到内核空间的虚拟地址，之后就可以Linux设备访问编程接口访问这些寄存器了，访问完成后，使用ioremap()对申请的虚拟地址进行释放，并释放release_mem_region()申请的IO内存资源。&lt;br /&gt;
&lt;br /&gt;
流程如下：&lt;br /&gt;
&lt;br /&gt;
[[Image:linux_mmio_handle.jpeg]]&lt;br /&gt;
       &lt;br /&gt;
&lt;br /&gt;
参考文档：&lt;br /&gt;
&lt;br /&gt;
http://blog.csdn.net/hanchaoman/article/details/6967423&lt;br /&gt;
&lt;br /&gt;
http://blog.21ic.com/user1/6137/archives/2009/64709.html&lt;/div&gt;</summary>
		<author><name>Hovercool</name></author>
	</entry>
</feed>