1. 程式人生 > >Linux 字符設備驅動—— ioremap() 函數解析

Linux 字符設備驅動—— ioremap() 函數解析

地址 實現 統一 map函數 差異 ons 頭文件 字節 inline

一、 ioremap() 函數基礎概念

幾乎每一種外設都是通過讀寫設備上的相關寄存器來進行的,通常包括控制寄存器、狀態寄存器和數據寄存器三大類,外設的寄存器通常被連續地編址。根據CPU體系結構的不同,CPU對IO端口的編址方式有兩種:

a -- I/O 映射方式(I/O-mapped)

典型地,如X86處理器為外設專門實現了一個單獨的地址空間,稱為"I/O地址空間"或者"I/O端口空間",CPU通過專門的I/O指令(如X86的IN和OUT指令)來訪問這一空間中的地址單元。

b -- 內存映射方式(Memory-mapped)

RISC指令系統的CPU(如ARM、PowerPC等)通常只實現一個物理地址空間,外設I/O端口成為內存的一部分。此時,CPU可以象訪問一個內存單元那樣訪問外設I/O端口,而不需要設立專門的外設I/O指令。

但是,這兩者在硬件實現上的差異對於軟件來說是完全透明的,驅動程序開發人員可以將內存映射方式的I/O端口和外設內存統一看作是"I/O內存"資源。

一般來說,在系統運行時,外設的I/O內存資源的物理地址是已知的,由硬件的設計決定。但是CPU通常並沒有為這些已知的外設I/O內存資源的物理地址預定義虛擬地址範圍,驅動程序並不能直接通過物理地址訪問I/O內存資源,

而必須將它們映射到核心虛地址空間內(通過頁表),然後才能根據映射所得到的核心虛地址範圍,通過訪內指令訪問這些I/O內存資源。

Linux在io.h頭文件中聲明了函數ioremap(),用來將I/O內存資源的物理地址映射到核心虛地址空間(3GB-4GB)中(這裏是內核空間),原型如下:

1、ioremap函數

ioremap宏定義在asm/io.h內:

#define ioremap(cookie,size) __ioremap(cookie,size,0)

__ioremap函數原型為(arm/mm/ioremap.c):

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);

參數:

phys_addr:要映射的起始的IO地址

size:要映射的空間的大小

flags:要映射的IO空間和權限有關的標誌

該函數返回映射後的內核虛擬地址(3G-4G). 接著便可以通過讀寫該返回的內核虛擬地址去訪問之這段I/O內存資源。

2、iounmap函數

iounmap函數用於取消ioremap()所做的映射,原型如下:

void iounmap(void * addr);

二、 ioremap() 相關函數解析

在將I/O內存資源的物理地址映射成核心虛地址後,理論上講我們就可以象讀寫RAM那樣直接讀寫I/O內存資源了。為了保證驅動程序的跨平臺的可移植性,我們應該使用Linux中特定的函數來訪問I/O內存資源,而不應該通過指向核心虛地址的指針來訪問

讀寫I/O的函數如下所示:

a -- writel()

writel()往內存映射的 I/O 空間上寫數據,wirtel() I/O 上寫入 32 位數據 (4字節)。

原型:void writel (unsigned char data , unsigned int addr )

b -- readl()

readl() 從內存映射的 I/O 空間上讀數據,readl 從 I/O 讀取 32 位數據 ( 4 字節 )。
原型:unsigned char readl (unsigned int addr )

具體定義如下:

 1 #define readb __raw_readb
 2 #define readw(addr) __le16_to_cpu(__raw_readw(addr))
 3 #define readl(addr) __le32_to_cpu(__raw_readl(addr))
 4 #ifndef __raw_readb
 5 static inline u8 __raw_readb(const volatile void __iomem *addr)
 6 {
 7     return *(const volatile u8 __force *) addr;
 8 }
 9 #endif
10  
11 #ifndef __raw_readw
12 static inline u16 __raw_readw(const volatile void __iomem *addr)
13 {
14     return *(const volatile u16 __force *) addr;
15 }
16 #endif
17  
18 #ifndef __raw_readl
19 static inline u32 __raw_readl(const volatile void __iomem *addr)
20 {
21     return *(const volatile u32 __force *) addr;
22 }
23 #endif
24  
25 #define writeb __raw_writeb
26 #define writew(b,addr) __raw_writew(__cpu_to_le16(b),addr)
27 #define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr)

Linux 字符設備驅動—— ioremap() 函數解析