Linux設備驅動開發(fā)-3.與硬件通信_第1頁
Linux設備驅動開發(fā)-3.與硬件通信_第2頁
Linux設備驅動開發(fā)-3.與硬件通信_第3頁
Linux設備驅動開發(fā)-3.與硬件通信_第4頁
Linux設備驅動開發(fā)-3.與硬件通信_第5頁
已閱讀5頁,還剩2頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、Linux設備驅動程序學習9與硬件通信  I/O 端口和 I/O 內存每種外設都是通過讀寫存放器來進行控制。 在硬件層,內存區(qū)和 I/O 區(qū)域沒有概念上的區(qū)別: 它們都是通過向在地址總線和控制總線發(fā)出電平信號來進行訪問,再通過數(shù)據(jù)總線讀寫數(shù)據(jù)。使用 I/O 端口I/O 端口是驅動用來和許多設備之間的通訊方式。I/O 端口分配在尚未取得端口的獨占訪問前,不應對端口進行操作。內核提供了一個注冊用的接口,允許驅動程序聲明它需要的端口:#include <linux/ioport.h>struct resource *request_region(unsigned long fi

2、rst, unsigned long n, const char *name);/*告訴內核:要使用從 first 開始的 n 個端口,name 參數(shù)為設備名。假設分配成功返回非 NULL,否那么將無法使用需要的端口。*/ /*所有的的端口分配顯示在 /proc/ioports 中。假設不能分配到需要的端口,那么可以到這里看看誰先用了。*/*當用完 I/O 端口集(可能在模塊卸載時), 應當將它們返回給系統(tǒng)*/void release_region(unsigned long start, unsigned long n); int check_region(unsigned long fir

3、st, unsigned long n); /*檢查一個給定的 I/O 端口集是否可用,假設不可用, 返回值是一個負錯誤碼。不推薦使用*/操作 I/O 端口在驅動程序注冊I/O 端口后,就可以讀/寫這些端口。大局部硬件會把8、16和32位端口區(qū)分開,不能像訪問系統(tǒng)內存那樣混淆使用。驅動必須調用不同的函數(shù)來存取不同大小的端口。Linux 內核頭文件(體系依賴的頭文件 <asm/io.h> ) 定義了以下內聯(lián)函數(shù)(有的體系是宏,有的不存在)來訪問 I/O 端口:unsigned inb(unsigned port); void outb(unsigned char byte, unsi

4、gned port); /*讀/寫字節(jié)端口( 8 位寬 )。port 參數(shù)某些平臺定義為 unsigned long ,有些為 unsigned short 。 inb 的返回類型也體系而不同。*/unsigned inw(unsigned port); void outw(unsigned short word, unsigned port); /*訪問 16位 端口( 一個字寬 )*/unsigned inl(unsigned port); void outl(unsigned longword, unsigned port); /*訪問 32位 端口。 longword 聲明有的平臺為

5、unsigned long ,有的為 unsigned int。*/在用戶空間訪問 I/O 端口以上函數(shù)主要提供應設備驅動使用,但它們也可在用戶空間使用,至少在 PC上可以。 GNU C 庫在 <sys/io.h> 中定義了它們。串操作除了一次傳輸一個數(shù)據(jù)的I/O操作,一些處理器實現(xiàn)了一次傳輸一個數(shù)據(jù)序列的特殊指令,序列中的數(shù)據(jù)單位可以是字節(jié)、字或雙字,這是所謂的串操作指令。它們完成任務比一個 C 語言循環(huán)更快。以下宏定義實現(xiàn)了串I/O,它們有的通過單個機器指令實現(xiàn);但如果目標處理器沒有進行串 I/O 的指令,那么通過執(zhí)行一個緊湊的循環(huán)實現(xiàn)。 有的體系的原型如下:void insb

6、(unsigned port, void *addr, unsigned long count); void outsb(unsigned port, void *addr, unsigned long count); void insw(unsigned port, void *addr, unsigned long count); void outsw(unsigned port, void *addr, unsigned long count); void insl(unsigned port, void *addr, unsigned long count); void outsl(u

7、nsigned port, void *addr, unsigned long count); 使用時注意: 它們直接將字節(jié)流從端口中讀取或寫入。當端口和主機系統(tǒng)有不同的字節(jié)序時,會導致不可預期的結果。 使用 inw 讀取端口應在必要時自行轉換字節(jié)序,以匹配主機字節(jié)序。暫停式 I/O為了匹配低速外設的速度,有時假設 I/O 指令后面還緊跟著另一個類似的I/O指令,就必須在 I/O 指令后面插入一個小延時。在這種情況下,可以使用暫停式的I/O函數(shù)代替通常的I/O函數(shù),它們的名字以 _p 結尾,如 inb_p、outb_p等等。 這些函數(shù)定義被大局部體系支持,盡管它們常常被擴展為與非暫停

8、式I/O 同樣的代碼。因為如果體系使用一個合理的現(xiàn)代外設總線,就沒有必要額外暫停。細節(jié)可參考平臺的 asm 子目錄的 io.h 文件。以下是includeasm-armio.h中的宏定義:#define outb_p(val,port)    outb(val),(port)#define outw_p(val,port)    outw(val),(port)#define outl_p(val,port)    outl(val),(port)#define inb_p(

9、port)        inb(port)#define inw_p(port)        inw(port)#define inl_p(port)        inl(port)#define outsb_p(port,from,len)    outsb(port,from,len)#define outs

10、w_p(port,from,len)    outsw(port,from,len)#define outsl_p(port,from,len)    outsl(port,from,len)#define insb_p(port,to,len)    insb(port,to,len)#define insw_p(port,to,len)    insw(port,to,len)#define insl_p(port,to,len)&

11、#160;   insl(port,to,len)由此可見,由于ARM使用內部總線,就沒有必要額外暫停,所以暫停式的I/O函數(shù)被擴展為與非暫停式I/O 同樣的代碼。平臺相關性由于自身的特性,I/O 指令與處理器密切相關的,非常難以隱藏系統(tǒng)間的不同。所以大局部的關于端口 I/O 的源碼是平臺依賴的。 ARM 端口映射到內存,支持所有函數(shù)。串操作 用C語言實現(xiàn)。端口是 unsigned int 類型。 使用 I/O 內存 除了 x86上普遍使用的I/O 端口外,和設備通訊另一種主要機制是通過使用映射到內存的存放器或設備內存,統(tǒng)稱為 I/O 內存。因為存放器和

12、內存之間的區(qū)別對軟件是透明的。I/O 內存僅僅是類似 RAM 的一個區(qū)域,處理器通過總線訪問這個區(qū)域,以實現(xiàn)設備的訪問。根據(jù)平臺和總線的不同,I/O 內存可以就是否通過頁表訪問分類。假設通過頁表訪問,內核必須首先安排物理地址使其對設備驅動程序可見,在進行任何 I/O 之前必須調用 ioremap。假設不通過頁表,I/O 內存區(qū)域就類似I/O 端口,可以使用適當形式的函數(shù)訪問它們。因為“side effect的影響,不管是否需要 ioremap ,都不鼓勵直接使用 I/O 內存的指針。而使用專用的 I/O 內存操作函數(shù),不僅在所有平臺上是平安,而且對直接使用指針操作 I/O 內存的情況

13、進行了優(yōu)化。 I/O 內存分配和映射I/O 內存區(qū)域使用前必須先分配,函數(shù)接口在 <linux/ioport.h> 定義:struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);/* 從 start 開始,分配一個 len 字節(jié)的內存區(qū)域。成功返回一個非NULL指針,否那么返回NULL。所有的 I/O 內存分配情況都 /proc/iomem 中列出。*/*I/O內存區(qū)域在不再需要時應當釋放*/void release_mem_region(unsigne

14、d long start, unsigned long len); /*一個舊的檢查 I/O 內存區(qū)可用性的函數(shù),不推薦使用*/int check_mem_region(unsigned long start, unsigned long len); 然后必須設置一個映射,由 ioremap 函數(shù)實現(xiàn),此函數(shù)專門用來為I/O 內存區(qū)域分配虛擬地址。經過ioremap 之后,設備驅動即可訪問任意的 I/O 內存地址。注意:ioremap 返回的地址不應當直接引用;應使用內核提供的 accessor 函數(shù)。以下為函數(shù)定義: #include <asm/io.h>void *i

15、oremap(unsigned long phys_addr, unsigned long size);void *ioremap_nocache(unsigned long phys_addr, unsigned long size);/*如果控制存放器也在該區(qū)域,應使用的非緩存版本,以實現(xiàn)side effect。*/void iounmap(void * addr);訪問I/O 內存訪問I/O 內存的正確方式是通過一系列專用于此目的的函數(shù)(在 <asm/io.h> 中定義的: /*I/O 內存讀函數(shù)*/unsigned int ioread8(void *addr);unsig

16、ned int ioread16(void *addr);unsigned int ioread32(void *addr);/*addr 是從 ioremap 獲得的地址(可能包含一個整型偏移量), 返回值是從給定 I/O 內存讀取的值*/*對應的I/O 內存寫函數(shù)*/void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);/*讀和寫一系列值到一個給定的 I/O 內存地址,從給定的 buf 讀或寫 count 個值到給定的 a

17、ddr */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);void iowrite8_rep(void *addr, const void *buf, unsigned long count);void iowrite16_rep(void *addr, const void

18、 *buf, unsigned long count);void iowrite32_rep(void *addr, const void *buf, unsigned long count);/*需要操作一塊 I/O 地址,使用一下函數(shù)*/void memset_io(void *addr, u8 value, unsigned int count);void memcpy_fromio(void *dest, void *source, unsigned int count);void memcpy_toio(void *dest, void *source, unsigned int c

19、ount);/*舊函數(shù)接口,仍可工作, 但不推薦。*/unsigned readb(address);unsigned readw(address);unsigned readl(address); void writeb(unsigned value, address);void writew(unsigned value, address);void writel(unsigned value, address); 像 I/O 內存一樣使用端口一些硬件有一個有趣的特性:一些版本使用 I/O 端口,而其他的使用 I/O 內存。為了統(tǒng)一編程接口,使驅動程序易于編寫,2.6 內核提供了

20、一個ioport_map函數(shù):void *ioport_map(unsigned long port, unsigned int count);/*重映射 count 個I/O 端口,使其看起來像 I/O 內存。,此后,驅動程序可以在返回的地址上使用 ioread8 和同類函數(shù)。其在編程時消除了I/O 端口和I/O 內存的區(qū)別。/*這個映射應當在它不再被使用時撤銷:*/void ioport_unmap(void *addr); /*注意:I/O 端口仍然必須在重映射前使用 request_region 分配I/O 端口。ARM9不支持這兩個函數(shù)!*/上面是基于?Linux設備驅動程序第3版?

21、的介紹,以下分析 ARM9的s3c2440A的linux驅動接口。ARM9的linux驅動接口s3c24x0處理器是使用I/O內存的,也就是說:他們的外設接口是通過讀寫相應的存放器實現(xiàn)的,這些存放器和內存是使用單一的地址空間,并使用和讀寫內存一樣的指令。所以推薦使用I/O內存的相關指令。但這并不表示I/O端口的指令在s3c24x0中不可用。但是只要你注意其源碼,你就會發(fā)現(xiàn):其實I/O端口的指令只是一個外殼,內部還是使用和I/O內存一樣的代碼。以以下出一些:I/O端口 #define outb(v,p)        

22、;_raw_writeb(v,_io(p)#define outw(v,p)        _raw_writew(_force _u16)                     cpu_to_le16(v),_io(p)#define outl(v,p)     &

23、#160;  _raw_writel(_force _u32)                     cpu_to_le32(v),_io(p)#define inb(p)    ( _u8 _v = _raw_readb(_io(p); _v; )#define inw(p)    ( _u16

24、 _v = le16_to_cpu(_force _le16)             _raw_readw(_io(p); _v; )#define inl(p)    ( _u32 _v = le32_to_cpu(_force _le32)             _raw_readl(_io(p); _v; )I/O內存 #define ioread8(p)    ( unsigned int _v = _raw_readb(p); _v; )#define ioread16(p)    ( unsigned int _v = le16_to_cpu(_raw_readw(p); _v; )#define ioread32(p)    ( unsigned int _v = le

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論