I O記憶體資源對映

2021-07-25 02:58:49 字數 4798 閱讀 8137

我們知道缺省外設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空間還是記...