A. 幾乎每一種外設都是通過讀寫裝置上的寄存器來進行的。外設寄存器也稱為“I/O端口”, 通常包括:控制寄存器、狀态寄存器和資料寄存器三大類,而且一個外設的寄存器通常被連續地編址。 CPU對外設IO端口 實體位址 的編址方式有兩種:一種是 I/O映射方式(I/O-mapped),另一種是記憶體映射方式(Memory-mapped)。而具體采用哪一種則取決于CPU的體系結構。
有些體系結構的CPU(如,PowerPC、m68k等)通常隻實作一個實體位址空間(RAM)。在這種情況下,外設I/O端口的實體位址就被映射到CPU的單一實體位址空間中,而成為記憶體的一部分。此時,CPU可以象通路一個記憶體單元那樣通路外設I/O端口,而不需要設立專門的外設I/O指令。這就是所謂的“記憶體映射方式”(Memory-mapped)。
而另外一些體系結構的CPU(典型地如X86)則為外設專門實作了一個單獨地位址空間,稱為“I/O位址空間”或者“I/O端口空間”。這是一個與CPU地RAM實體位址空間不同的位址空間,所有外設的I/O端口均在這一空間中進行編址。CPU通過設立專門的I/O指令(如X86的IN和OUT指令)來通路這一空間中的位址單元(也即I/O端口)。這就是所謂的“I/O映射方式”(I/O-mapped)。與RAM實體位址空間相比,I/O位址空間通常都比較小,如x86 CPU的I/O空間就隻有64KB(0-0xffff)。這是“I/O映射方式”的一個主要缺點。
Linux将基于I/O映射方式的或記憶體映射方式的I/O端口通稱為“I/O區域”(I/O region)。在讨論對I/O區域的管理之前,我們首先來分析一下Linux是如何實作“I/O資源”這一抽象概念的.
B.
在驅動程式編寫過程中,很少會注意到IO Port和IO Mem的差別。雖然使用一些不符合規範的代碼可以達到最終目的,這是極其不推薦使用的。
結合下圖,我們徹底講述IO端口和IO記憶體以及記憶體之間的關系。主存16M位元組的SDRAM,外設是個視訊采集卡,上面有16M位元組的SDRAM作為緩沖區。
1. CPU是i386架構的情況
在i386系列的進行中,記憶體和外部IO是獨立編址,也是獨立尋址的。MEM的記憶體空間是32位可以尋址到4G,IO空間是16位可以尋址到64K。
在Linux核心中,通路外設上的IO Port必須通過IO Port的尋址方式。而通路IO Mem就比較羅嗦,外部MEM不能和主存一樣通路,雖然大小上不相上下,可是外部MEM是沒有在系統中注冊的。通路外部IO MEM必須通過remap映射到核心的MEM空間後才能通路。
為了達到接口的同一性,核心提供了IO Port到IO Mem的映射函數。映射後IO Port就可以看作是IO Mem,按照IO Mem的通路方式即可。
2. CPU是ARM 或PPC架構的情況
在這一類的嵌入式處理器中,IO Port的尋址方式是采用記憶體映射,也就是IO bus就是Mem bus。系統的尋址能力如果是32位,IO Port+Mem(包括IO Mem)可以達到4G。
通路這類IO Port時,我們也可以用IO Port專用尋址方式。至于在對IO Port尋址時,核心是具體如何完成的,這個在核心移植時就已經完成。在這種架構的處理器中,仍然保持對IO Port的支援,完全是i386架構遺留下來的問題,在此不多讨論。而通路IO Mem的方式和i386一緻。
注意:linux核心給我提供了完全對IO Port和IO Mem的支援,然而具體去看看driver目錄下的驅動程式,很少按照這個規範去組織IO Port和IO Mem資源。對這二者通路最關鍵問題就是位址的定位,在C語言中,使用volatile 就可以實作。很多的代碼通路IO Port中的寄存器時,就使用volatile關鍵字,雖然功能可以實作,我們還是不推薦使用。就像最簡單的延時莫過于while,可是在多任務的系統中是堅決避免的!