我們知道缺省外設i/o資源是不在linux核心空間中的(如sram或硬體介面暫存器等),若需要訪問該外設i/o資源,必須先將其位址對映到核心空間中來,然後才能在核心空間中訪問它。
linux核心訪問外設i/o記憶體資源的方式有兩種:動態對映(ioremap)和靜態對映(map_desc)。
一、動態對映(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);
size:要對映的空間的大小
flags:要對映的io空間和許可權有關的標誌
該函式返回對映後的核心虛擬位址(3g-4g). 接著便可以通過讀寫該返回的核心虛擬位址去訪問之這段i/o記憶體資源。
舉乙個簡單的例子: (取自s3c2410的iis音訊驅動)
比如我們要訪問s3c2410平台上的i2s暫存器, 檢視datasheet 知道iis實體地址為0x55000000,
我們把它定義為巨集s3c2410_pa_iis,如下:
#define s3c2410_pa_iis
(0x55000000)
若要在核心空間(iis驅動)中訪問這段i/o暫存器(iis)資源需要先建立到核心位址空間的對映:
our_card->regs = ioremap(s3c2410_pa_iis, 0x100);
if (our_card->regs == null)
展開後等價於:
static struct map_desc s3c2410_iodesc __initdata = {
.virtual
= (unsigned long)s3c24xx_va_ lcd),
.pfn
= __phys_to_pfn(s3c24xx_pa_ lcd),
.length
= s3c24xx_sz_ lcd,
.type
= mt_device
s3c24xx_pa_ lcd和s3c24xx_va_ lcd為定義在map.h內的lcd暫存器的實體地址和虛擬位址。在這裡map_desc 結構體的virtual成員被初始化為s3c24xx_va_ lcd,pfn成員值通過__phys_to_pfn核心函式計算,只需要傳遞給它該i/o資源的實體地址就行。length為對映資源的大小。 mt_device為i/o型別,通常定義為mt_device。
這裡最重要的即virtual 成員的值s3c24xx_va_ lcd,這個值即該i/o資源對映後的核心虛擬位址,建立對映表成功後,便可以在核心或驅動中直接通過該虛擬位址訪問這個i/o資源。
s3c24xx_va_ lcd以及s3c24xx_pa_ lcd定義如下:
#define s3c24xx_va_lcd
s3c2410_addr(0x00600000)
//lcd對映後的虛擬位址
#define s3c2410_pa_lcd
(0x4d000000)
//lcd暫存器實體地址
#define s3c24xx_sz_lcd
sz_1m
//lcd暫存器大小
s3c2410_addr 定義如下:
#define s3c2410_addr(x)
((void __iomem *)0xf0000000 + (x))
這裡就是一種線性偏移關係,即s3c2410建立的i/o靜態對映表會被對映到0xf0000000之後。(這個線性偏移值可以改,也可以你自己在virtual成員裡手動定義乙個值,只要不和其他io資源對映位址衝突,但最好是在0xf0000000之後。)
io_address巨集定義如下:
#define io_address(x)
(((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) +
0xf0000000) )
s3c2410_iodesc這個對映表建立成功後,我們在核心中便可以直接通過s3c24xx_va_ lcd訪問lcd的暫存器資源。
如:s3c2410 lcd驅動的probe函式內
info->regs.lcdcon1 &= ~s3c2410_lcdcon1_envid;
lcdcon1 = readl(s3c2410_lcdcon1); //read對映後的暫存器虛擬位址
writel(lcdcon1 & ~s3c2410_lcdcon1_envid, s3c2410_lcdcon1); //write對映後的虛擬位址
#define s3c2410_lcdreg(x) ((x) + s3c24xx_va_lcd)
#define s3c2410_lcdcon1
s3c2410_lcdreg(0x00)
到此,我們知道了通過map_desc結構體建立i/o記憶體資源靜態對映表的原理了。總結一下發現其實過程很簡單,一通過定義map_desc結構體建立靜態對映表,二在核心中通過建立對映後虛擬位址訪問該io資源。
三、i/o靜態對映方式應用例項
i/o靜態對映方式通常是用在暫存器資源的對映上,這樣在編寫核心**或驅動時就不需要再進行ioremap,直接使用對映後的核心虛擬位址訪問。同樣的io資源只需要在核心初始化過程中對映一次,以後就可以一直使用。
暫存器資源對映的例子上面講原理時已經介紹得很清楚了,這裡我舉乙個sram的例項介紹如何應用這種i/o靜態對映方式。當然原理和操作過程同暫存器資源是一樣的,可以把sram看成是大號的i/o暫存器資源。
比如我的板子在0x30000000位置有一塊64kb大小的sram。我們現在需要通過靜態對映的方式去訪問該sram。我們要做的事內容包 括修改kernel**,新增sram資源相應的map_desc結構,建立sram到核心位址空間的靜態對映表。寫乙個sram module,在sram module 內直接通過靜態對映後的核心虛擬位址訪問該sram。
第一步:建立sram靜態對映表
在我板子的map_des結構體陣列(***_io_desc)內新增sram資源相應的map_desc。如下:
static struct map_desc ***_io_desc __initdata = {
.virtual
= io_address(*** _uart2_base),
.pfn
= __phys_to_pfn(*** _uart2_base),
.length
= sz_4k,
.type
= mt_device
.virtual
= io_address(***_sram_base),
.pfn
= __phys_to_pfn(***_sram_base),
.length
= sz_4k,
.type
= mt_device
巨集***_sram_base為我板子上sram的實體地址,定義為0x30000000。我的kernel是通過io_address的方式 計算核心虛擬位址的,這點和之前介紹的s3c2410有點不一樣。不過原理都是相同的,為乙個線性偏移, 範圍在0xf0000000之後。
sram module**如下:
static void sram_test(void)
void * sram_p;
char str = "hello,sram!\n"
sram_p = (void *)io_address (***_sram_base);
memcpy(sram_p, str, sizeof(str));
//將 str字元陣列拷貝到sram內
printk(sram_p);
printk("\n");
static int __init sram_init(void)
struct resource * ret;
printk("request sram mem region ............\n");
ret = request_mem_region(sram_base, sram_size, "sram region");
if (ret ==null) {
printk("request sram mem region failed!\n");
return -1;
sram_test();
return 0;
static void __exit sram_exit(void)
release_mem_region(sram_base, sram_size);
printk("release sram mem region success!\n");
printk("sram is closed\n");
module_init(sram_init);
module_exit(sram_exit);
在開發板上執行結果如下:
/ # insmod bin/sram.ko
request sram mem region ............
hello,sram!
ß 這句即列印的sram內的字串
/ # rmmod sram
release sram mem region success!
sram is close
實驗發現可以通過對映後的位址正常訪問sram。
磁碟IO 快取IO 直接IO 記憶體對映
磁碟io的幾種訪問方式如下 快取io 快取i o又被稱作標準i o,大多數檔案系統的預設i o操作都是快取i o。在linux的快取i o機制中,資料先從磁碟複製到核心空間的緩衝區,然後從核心空間緩衝區複製到應用程式的位址空間。讀操作 作業系統檢查核心的緩衝區有沒有需要的資料,如果已經快取了,那麼就...
Linux的記憶體I O對映
記憶體的i o對映是將檔案某區域的內容對映到程序的虛擬空間的技術 通過對檔案的記憶體io對映,可使用使用者對檔案的操作轉換為對記憶體的操作,這樣不僅使用方便而且提高了儲存速度。void mmap void addr,size t length,int prot,int flags,int fd,of...
記憶體對映I O與埠對映I O的區別
cpu與裝置的控制暫存器和資料緩衝區進行通訊,控制器中暫存器的兩種編址方案 兩種方法工作原理 當cpu想要讀入乙個字的時候,不論是從記憶體中讀入還是從io埠中讀入,它都要將需要的位址放到匯流排的位址線上,然後在匯流排的一條控制線上置起乙個read訊號。還要用到第二條訊號線來表明需要的是io空間還是記...