1. 程式人生 > >linux裝置驅動開發學習--記憶體和IO訪問

linux裝置驅動開發學習--記憶體和IO訪問

一 I/O 埠

1. 讀寫位元組埠(8 位寬)
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);

2. 讀寫字埠(16 位寬)
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);

3. 讀寫長字埠(32 位寬)
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);

4. 讀寫一串位元組。
void insb(unsigned port, void *addr, unsigned long count);  //insb()從埠 port 開始讀 count 個位元組埠,並將讀取結果寫入 addr 指向的記憶體
void outsb(unsigned port, void *addr, unsigned long count);  //outsb()將 addr 指向的記憶體的 count 個位元組連續地寫入 port 開始的埠。

5. 讀寫一串字。
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);

6. 讀寫一串長字。
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);

二 I/O 記憶體
在核心中訪問 I/O 記憶體之前,需首先使用 ioremap()函式將裝置所處的實體地址對映到虛擬地址
void *ioremap(unsigned long offset, unsigned long size);
ioremap()返回一個特殊的虛擬地址,該地址可用來存取特定的實體地址範圍。
通過 ioremap()獲得的虛擬地址應該被 iounmap()函式釋放,
void iounmap(void * addr);
在裝置的實體地址被對映到虛擬地址之後,儘管可以直接通過指標訪問這些地址,
但是可以使用 Linux 核心的如下一組函式來完成裝置記憶體對映的虛擬地址的讀寫.

1. 讀 I/O 記憶體。
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
與上述函式對應的較早版本的函式為(這些函式在 Linux 2.6 中仍然被支援)
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);

2. 寫 I/O 記憶體。
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
與上述函式對應的較早版本的函式為(這些函式在 Linux 2.6 中仍然被支援)
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);

3. 讀一串 I/O 記憶體。
void ioread8_rep(void *addr, void *buf, unsigned long count);
void ioread16_rep(void *addr, void *buf, unsigned long count);
void ioread32_rep(void *addr, void *buf, unsigned long count);

4. 寫一串 I/O 記憶體
void iowrite8_rep(void *addr, const void *buf, unsigned long count);
void iowrite16_rep(void *addr, const void *buf, unsigned long count);
void iowrite32_rep(void *addr, const void *buf, unsigned long count);

5. 複製 I/O 記憶體。
void memcpy_fromio(void *dest, void *source, unsigned int count);
void memcpy_toio(void *dest, void *source, unsigned int count);

6. 設定 I/O 記憶體。
void memset_io(void *addr, u8 value, unsigned int count);

三 把 I/O 埠對映到記憶體空間

void *ioport_map(unsigned long port, unsigned int count);
可以把 port 開始的 count 個連續的 I/O 埠重對映為一段“記憶體空間”
void ioport_unmap(void *addr);
當不再需要這種對映時,需要呼叫上面的函式來撤銷

四 I/O 埠申請

Linux 核心提供了一組函式用於申請和釋放 I/O 埠。
struct resource *request_region(unsigned long first, unsigned long n,const char *name);
這個函式向核心申請 n 個埠,這些埠從 first 開始,name 引數為裝置的名稱。如果分配成功返回值是非 NULL,如果返回 NULL,則意味著申請埠失敗。
void release_region(unsigned long start, unsigned long n);
當用 request_region()申請的 I/O 埠使用完成後,應當使用 release_region()函式將它們歸還給系統

五 I/O 記憶體申請
同樣,Linux 核心也提供了一組函式用於申請和釋放 I/O 記憶體的範圍。
struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
這個函式向核心申請 n 個記憶體地址,這些地址從 first 開始,name 引數為裝置的名稱
void release_mem_region(unsigned long start, unsigned long len);
當用request_mem_region()申請的I/O記憶體使用完成後,應當使用release_mem_region()函式將它們歸還給系統

基於 DMA 的硬體使用匯流排地址而非實體地址,匯流排地址是從裝置角度上看到的記憶體地址,
實體地址則是從 CPU 角度上看到的未經轉換的記憶體地址(經過轉換的為虛擬地址)

字元裝置與塊裝置 I/O 操作的不同如下。
(1)塊裝置只能以塊為單位接受輸入和返回輸出,而字元裝置則以位元組為單位。
大多數裝置是字元裝置,因為它們不需要緩衝而且不以固定塊大小進行操作。
(2)塊裝置對於 I/O 請求有對應的緩衝區,因此它們可以選擇以什麼順序進行響應,
字元裝置無須緩衝且被直接讀寫。對於儲存裝置而言調整讀寫的順序作用巨大,
因為在讀寫連續的扇區比分離的扇區更快。
(3)字元裝置只能被順序讀寫,而塊裝置可以隨機訪問。雖然塊裝置可隨機訪問,
但是對於磁碟這類機械裝置而言,順序地組織塊裝置的訪問可以提高效能。