幾乎每一種外設都是通過讀寫設備上的寄存器來進行的,通常包括控制寄存器、狀態寄存器和數據寄存器三大類,外設的寄存器通常被連續地編址。根據CPU體系結構的不同,CPU對IO端口的編址方式有兩種:
(1)I/O映射方式(I/O-mapped)
典型地,如X86處理器為外設專門實現了一個單獨的地址空間,稱為"I/O地址空間"或者"I/O端口空間",CPU通過專門的I/O指令(如X86的IN和OUT指令)來訪問這一空間中的地址單元。
(2)內存映射方式(Memory-mapped)
RISC指令系統的CPU(如ARM、PowerPC等)通常只實現一個物理地址空間,外設I/O端口成為內存的一部分。此時,CPU可以象訪問一個內存單元那樣訪問外設I/O端口,而不需要設立專門的外設I/O指令。
但是,這兩者在硬件實現上的差異對於軟件來說是完全透明的,驅動程序開發人員可以將內存映射方式的I/O端口和外設內存統一看作是"I/O內存"資源。
設備I/O內存訪問流程
1. I/O 內存分配
I/O 內存區必須在使用前分配. 分配內存區的接口是( 在 <linux/ioport.h> 定義):
struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
這個函數分配一個 len 字節的內存區, 從 start 開始. 如果一切順利, 一個 非NULL 指針返回; 否則返回值是 NULL. 所有的 I/O 內存分配來 /proc/iomem 中列出.
內存區在不再需要時應當釋放:
void release_mem_region(unsigned long start, unsigned long len);
2、物理地址映射到虛擬內存地址
#include <asm/io.h> void *ioremap(unsigned long phys_addr, unsigned long size); void iounmap(void * addr);從 ioremap 返回的地址不應當直接解引用; 相反, 應當使用內核提供的存取函數.
3、 I/O 內存讀寫
使用 I/O 內存的正確方式是通過一系列為此而提供的函數(通過 <asm/io.h> 定義的).
從 I/O 內存讀, 使用下列之一:
unsigned int ioread8(void *addr); unsigned int ioread16(void *addr); unsigned int ioread32(void *addr);這裡, addr 應當是從 ioremap 獲得的地址(也許與一個整型偏移); 返回值是從給定 I/O 內存讀取的.
有類似的一系列函數來寫 I/O 內存:
void iowrite8(u8 value, void *addr); void iowrite16(u16 value, void *addr); void iowrite32(u32 value, void *addr);