




已閱讀5頁(yè),還剩6頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
I/O 端口和 I/O 內(nèi)存 1、I/O端口和I/O內(nèi)存每個(gè)外設(shè)都是通過(guò)讀寫(xiě)其寄存器來(lái)控制的。通常一個(gè)設(shè)備有幾個(gè)寄存器,它們位于內(nèi)存地址空間或者I/O地址空間,并且地址是連續(xù)的。在硬件層上,內(nèi)存區(qū)和I/O區(qū)域沒(méi)有概念上的區(qū)別:它們都是通過(guò)在地址總線和控制總線上發(fā)出電信號(hào)來(lái)存取(即,讀寫(xiě)信號(hào)),并且通過(guò)數(shù)據(jù)總線來(lái)讀寫(xiě)數(shù)據(jù)。在一些CPU制造商在其芯片上實(shí)現(xiàn)了一個(gè)單地址空間(統(tǒng)一編址)的同時(shí),其它的CPU制造商認(rèn)為外設(shè)不同于內(nèi)存,應(yīng)該有一個(gè)獨(dú)立的地址空間給外設(shè)(單獨(dú)編址),其生產(chǎn)處理器(特別是x86家族)的I/O端口有自己的讀寫(xiě)信號(hào)線和特殊的CPU指令來(lái)存取端口。因?yàn)橥庠O(shè)要與外設(shè)總線相匹配,并且大部分流行的I/O總線都是以個(gè)人計(jì)算機(jī)(主要是x86家族)作為模型,所以即便那些沒(méi)有單獨(dú)地址空間給I/O端口的處理器,也必須在訪問(wèn)外設(shè)時(shí)模擬成讀寫(xiě)端口。這通常通過(guò)外部芯片組(PC中的南北橋)或者在CPU核中附加額外電路來(lái)實(shí)現(xiàn)(基于嵌入式應(yīng)用的處理器)。關(guān)于編址方式、I/O端口和I/O內(nèi)存更詳細(xì)內(nèi)容見(jiàn)/u3/96613/showart_1926286.html由于同樣的理由,Linux在所有計(jì)算機(jī)平臺(tái)上都實(shí)現(xiàn)了I/O端口,甚至在那些單地址空間的CPU平臺(tái)上(模擬I/O端口)。但并不是所有的設(shè)備都會(huì)將其寄存器映射到I/O端口。雖然ISA設(shè)備普遍使用I/O端口,但大部分PCI設(shè)備將寄存器映射到某個(gè)內(nèi)存地址區(qū)。這種I/O內(nèi)存方法通常是首選的,因?yàn)樗鼰o(wú)需使用特殊的處理器指令,CPU存取內(nèi)存也更有效率,并且編譯器在存取內(nèi)存時(shí)在寄存器分配和尋址模式的選擇上有更多自由。1.1、I/O寄存器和常規(guī)內(nèi)存I/O寄存器和RAM的主要不同是I/O操作有邊際效應(yīng)(side effect),而內(nèi)存操作沒(méi)有:訪問(wèn)內(nèi)存只是在內(nèi)存某一位置存儲(chǔ)數(shù)值。因?yàn)閮?nèi)存存取速度嚴(yán)重影響CPU的性能,編譯器可能會(huì)對(duì)源碼進(jìn)行優(yōu)化,主要是:使用高速緩存和重排讀/寫(xiě)指令的順序。對(duì)于傳統(tǒng)內(nèi)存(至少在單處理器系統(tǒng))這些優(yōu)化是透明有益的,但是對(duì)于I/O寄存器,這可能是致命錯(cuò)誤,因?yàn)樗鼈兏蓴_了那些邊際效應(yīng)(驅(qū)動(dòng)程序存取I/O寄存器就是為了獲取邊際效應(yīng))。因此,驅(qū)動(dòng)程序必須確保在存取寄存器時(shí),不能使用高速緩存并且不能重新編排讀寫(xiě)指令的順序。side effect 是指:訪問(wèn)I/O寄存器時(shí),不僅僅會(huì)像訪問(wèn)普通內(nèi)存一樣影響存儲(chǔ)單元的值,更重要的是它可能改變CPU的I/O端口電平、輸出時(shí)序或CPU對(duì)I/O端口電平的反應(yīng)等等,從而實(shí)現(xiàn)CPU的控制功能。CPU在電路中的意義就是實(shí)現(xiàn)其side effect 。舉個(gè)例子,有些設(shè)備的中斷狀態(tài)寄存器只要一讀取,便自動(dòng)清零。硬件緩沖的問(wèn)題是最易解決的:只要將底層硬件配置(或者自動(dòng)地或者通過(guò)Linux初始化代碼)為當(dāng)存取I/O區(qū)時(shí),禁止任何硬件緩沖(不管是I/O內(nèi)存還是I/O端口)。編譯器優(yōu)化和硬件重編排讀寫(xiě)指令順序的解決方法是:在硬件或處理器必須以一個(gè)特定順序執(zhí)行的操作之間安放一個(gè)內(nèi)存屏障(memory barrier)。Linux提供以下幾個(gè)宏來(lái)實(shí)現(xiàn)這個(gè)功能:#include/* barrier(軟件內(nèi)存屏障)告知編譯器插入一個(gè)內(nèi)存屏障但是對(duì)硬件沒(méi)有影響。編譯后的代碼將當(dāng)前CPU寄存器所有修改過(guò)的值保存到內(nèi)存,并且在需要時(shí)重新讀取它們。barrier可阻止在屏障前后的編譯器優(yōu)化,但硬件能完成自己的重新排序 */voidbarrier(void)#include/* rmb(read memory barrier)保證任何出現(xiàn)于屏障前的讀在執(zhí)行任何后續(xù)讀之前完成 */voidrmb(void);/* read_barrier_depends是一種特殊的、弱些的讀屏障形式。rmb 阻止屏障前后的所有讀指令的重新排序,read_barrier_depends 只阻止依賴(lài)于其他讀指令返回的數(shù)據(jù)的讀指令的重新排序。區(qū)別微小, 并且不是所有體系都支持。除非你確切地理解它們的差別, 并確信完整的讀屏障會(huì)增加系統(tǒng)開(kāi)銷(xiāo),否則應(yīng)當(dāng)始終使用 rmb。*/voidread_barrier_depends(void);/* wmb(write memory barrier)保證任何出現(xiàn)于屏障前的寫(xiě)在執(zhí)行任何后續(xù)的寫(xiě)之前完成 */voidwmb(void);/* rmb(memory barrier)保證任何出現(xiàn)于屏障前的讀寫(xiě)操作在執(zhí)行任何后續(xù)的讀寫(xiě)操作之前完成 */voidmb(void);/* 以上這些宏都是是barrier的超集,它們插入硬件內(nèi)存屏障在編譯的指令流中,并且它們的實(shí)際實(shí)現(xiàn)是平臺(tái)相關(guān)的 */* 以下這些屏障宏僅當(dāng)SMP系統(tǒng)時(shí)插入硬件屏障;否則它們只是簡(jiǎn)單被替換成barrier宏 */voidsmp_rmb(void);voidsmp_read_barrier_depends(void);voidsmp_wmb(void);voidsmp_mb(void);典型例子:writel(dev-registers.addr,io_destination_address);writel(dev-registers.size,io_size);writel(dev-registers.operation,DEV_READ);wmb();/* 在開(kāi)始執(zhí)行操作(最后一個(gè)寫(xiě)操作)之前,先將各相關(guān)控制寄存器設(shè)置好(前三個(gè)寫(xiě)操作)。其實(shí)wmb就是一條分界線,它保證其之前的三個(gè)寫(xiě)操作執(zhí)行完了之后,才執(zhí)行其后的寫(xiě)操作,但是前面三個(gè)寫(xiě)操作順序則無(wú)法保證 */writel(dev-registers.control,DEV_GO);內(nèi)存屏障影響系統(tǒng)性能,所以只能在確實(shí)需要它們的地方使用。不同類(lèi)型的屏障也有不同的性能特性,因此,應(yīng)當(dāng)盡可能使用最合適的屏障類(lèi)型。值得注意的是大部分處理同步的內(nèi)核原語(yǔ),例如自旋鎖和atomic_t操作,也具有內(nèi)存屏障的功能。還有就是有些外設(shè)總線(如PCI總線)有它們自己的緩沖問(wèn)題;我們?cè)诤竺嬲鹿?jié)遇到時(shí)討論這些問(wèn)題。某些體系允許將賦值和內(nèi)存屏障組合在一起,以提高效率。它們?nèi)缦露x:#defineset_mb(var,value)dovar=value;mb();while0#defineset_wmb(var,,value)dovar=value;wmb();while0#defineset_rmb(var,,value)dovar=value;rmb();while0在合適的地方,為了更快的完成任務(wù),使用體系特定的(architecture-specific)指令來(lái)定義這些宏。注意,很少體系支持set_rmb。使用do.while結(jié)構(gòu)來(lái)定義宏是標(biāo)準(zhǔn)C的慣用方法(在內(nèi)核源碼中非常常見(jiàn)),它使被擴(kuò)展的宏可在所有上下文環(huán)境中作為一個(gè)正常的C語(yǔ)句被執(zhí)行。2、使用I/O端口I/O端口是驅(qū)動(dòng)用來(lái)和很多設(shè)備通訊的方法。2.1、分配I/O端口在驅(qū)動(dòng)還沒(méi)獨(dú)占設(shè)備之前,不應(yīng)對(duì)端口進(jìn)行操作。內(nèi)核提供了一個(gè)注冊(cè)接口,以允許驅(qū)動(dòng)聲明其需要的端口:#include/* request_region告訴內(nèi)核:要使用first開(kāi)始的n個(gè)端口。參數(shù)name為設(shè)備名。如果分配成功返回值是非NULL;否則無(wú)法使用需要的端口(/proc/ioports包含了系統(tǒng)當(dāng)前所有端口的分配信息,若request_region分配失敗時(shí),可以查看該文件,看誰(shuí)先用了你要的端口) */structresource*request_region(unsignedlongfirst,unsignedlongn,constchar*name);/* 用完I/O端口后(可能在模塊卸載時(shí)),應(yīng)當(dāng)調(diào)用release_region將I/O端口返還給系統(tǒng)。參數(shù)start和n應(yīng)與之前傳遞給request_region一致 */voidrelease_region(unsignedlongstart,unsignedlongn);/* check_region用于檢查一個(gè)給定的I/O端口集是否可用。如果給定的端口不可用,check_region返回一個(gè)錯(cuò)誤碼。不推薦使用該函數(shù),因?yàn)榧幢闼祷?(端口可用),它也不能保證后面的端口分配操作會(huì)成功,因?yàn)闄z查和后面的端口分配并不是一個(gè)原子操作。而request_region通過(guò)加鎖來(lái)保證操作的原子性,因此是安全的 */intcheck_region(unsignedlongfirst,unsignedlongn);2.2、操作I/O端口 在驅(qū)動(dòng)成功請(qǐng)求到I/O端口后,就可以讀寫(xiě)這些端口了。大部分硬件會(huì)將8位、16位和32位端口區(qū)分開(kāi),無(wú)法像訪問(wèn)內(nèi)存那樣混淆使用。驅(qū)動(dòng)程序必須調(diào)用不同的函數(shù)來(lái)訪問(wèn)不同大小的端口。如同前面所講的,僅支持單地址空間的計(jì)算機(jī)體系通過(guò)將I/O端口地址重新映射到內(nèi)存地址來(lái)偽裝端口I/O。為了提高移植性,內(nèi)核對(duì)驅(qū)動(dòng)隱藏了這些細(xì)節(jié)。Linux內(nèi)核頭文件(體系依賴(lài)的頭文件)定義了下列內(nèi)聯(lián)函數(shù)來(lái)存取I/O端口:/* inb/outb:讀/寫(xiě)字節(jié)端口(8位寬)。有些體系將port參數(shù)定義為unsigned long;而有些平臺(tái)則將它定義為unsigned short。inb的返回類(lèi)型也是依賴(lài)體系的 */unsignedinb(unsignedport);voidoutb(unsignedcharbyte,unsignedport);/* inw/outw:讀/寫(xiě)字端口(16位寬) */unsignedinw(unsignedport);voidoutw(unsignedshortword,unsignedport);/* inl/outl:讀/寫(xiě)32位端口。longword也是依賴(lài)體系的,有的體系為unsigned long;而有的為unsigned int */unsignedinl(unsignedport);voidoutl(unsignedlongword,unsignedport);從現(xiàn)在開(kāi)始,當(dāng)我們使用unsigned沒(méi)有進(jìn)一步指定類(lèi)型時(shí),表示是一個(gè)依賴(lài)體系的定義。注意,沒(méi)有64位的I/O端口操作函數(shù)。即便在64位體系中,端口地址空間使用一個(gè)32位(最大)的數(shù)據(jù)通路。2.3、從用戶空間訪問(wèn)I/O端口2.2節(jié)介紹的函數(shù)主要是提供給驅(qū)動(dòng)使用,但它們也可在用戶空間使用,至少在PC機(jī)上可以。GNUC庫(kù)在中定義它們。如果在用戶空間使用這些函數(shù),必須滿足下列條件:1)、程序必須使用-O選項(xiàng)編譯來(lái)強(qiáng)制擴(kuò)展內(nèi)聯(lián)函數(shù)2)、必須使用ioperm和iopl系統(tǒng)調(diào)用(#include ) 來(lái)獲得進(jìn)行操作I/O端口的權(quán)限。ioperm為獲取單個(gè)端口的操作許可,iopl為獲取整個(gè)I/O空間許可。這2個(gè)函數(shù)都是x86特有的3)、程序必須以root來(lái)調(diào)用ioperm或者iopl,或者其父進(jìn)程(祖先)必須以root獲得的端口操作權(quán)限如果平臺(tái)不支持ioperm和iopl系統(tǒng)調(diào)用,通過(guò)使用/dev/prot設(shè)備文件,用戶空間仍然可以存取I/O端口。但是要注意的是,這個(gè)文件的定義也是依賴(lài)平臺(tái)的。2.4、字串操作除了一次傳遞一個(gè)數(shù)據(jù)的I/O操作,某些處理器實(shí)現(xiàn)了一次傳遞一序列數(shù)據(jù)(單位可以是字節(jié)、字和雙字)的特殊指令。這些所謂的字串指令,它們完成任務(wù)比一個(gè)C語(yǔ)言循環(huán)更快。下列宏定義實(shí)現(xiàn)字串操作,在某些體系上,它們通過(guò)使用單個(gè)機(jī)器指令實(shí)現(xiàn);但如果目標(biāo)處理器沒(méi)有進(jìn)行字串I/O指令,則通過(guò)執(zhí)行一個(gè)緊湊的循環(huán)實(shí)現(xiàn)。字串函數(shù)的原型是:/* insb:從I/O端口port讀取count個(gè)數(shù)據(jù)(單位字節(jié))到以內(nèi)存地址addr為開(kāi)始的內(nèi)存空間 */voidinsb(unsignedport,void*addr,unsignedlongcount);/* outsb:將內(nèi)存地址addr開(kāi)始的count個(gè)數(shù)據(jù)(單位字節(jié))寫(xiě)到I/O端口port */voidoutsb(unsignedport,void*addr,unsignedlongcount);/* insw:從I/O端口port讀取count個(gè)數(shù)據(jù)(單位字)到以內(nèi)存地址addr為開(kāi)始的內(nèi)存空間 */voidinsw(unsignedport,void*addr,unsignedlongcount);/* outsw:將內(nèi)存地址addr開(kāi)始的count個(gè)數(shù)據(jù)(單位字)寫(xiě)到I/O端口port */voidoutsw(unsignedport,void*addr,unsignedlongcount);/* insl:從I/O端口port讀取count個(gè)數(shù)據(jù)(單位雙字)到以內(nèi)存地址addr為開(kāi)始的內(nèi)存空間 */voidinsl(unsignedport,void*addr,unsignedlongcount);/* outsl:將內(nèi)存地址addr開(kāi)始的count個(gè)數(shù)據(jù)(單位雙字)寫(xiě)到I/O端口port */voidoutsl(unsignedport,void*addr,unsignedlongcount);注意:使用字串函數(shù)時(shí),它們直接將字節(jié)流從端口中讀取或?qū)懭?。?dāng)端口和主機(jī)系統(tǒng)有不同的字節(jié)序時(shí),會(huì)導(dǎo)致不可預(yù)期的結(jié)果。 使用 inw 讀取端口應(yīng)在必要時(shí)自行轉(zhuǎn)換字節(jié)序,以匹配主機(jī)字節(jié)序。2.5、暫停式I/O操作函數(shù)由于處理器的速率可能與外設(shè)(尤其是低速設(shè)備)的并不匹配,當(dāng)處理器過(guò)快地傳送數(shù)據(jù)到或自總線時(shí),這時(shí)可能就會(huì)引起問(wèn)題。解決方法是:如果在I/O指令后面緊跟著另一個(gè)相似的I/O指令,就必須插入一個(gè)小的延時(shí)。為此,Linux提供了暫停式I/O操作函數(shù),這些函數(shù)的名子只是在非暫停式I/O操作函數(shù)(前面提到的那些I/O操作函數(shù)都是非暫停式的)名后加上_p,如inb_p、outb_p等。大部分體系都支持這些函數(shù),盡管它們常常被擴(kuò)展為與非暫停I/O同樣的代碼,因?yàn)槿绻w系使用一個(gè)合理的現(xiàn)代外設(shè)總線,沒(méi)有必要額外暫停。以下是ARM體系暫停式I/O宏的定義:#defineoutb_p(val,port)outb(val),(port)#defineoutw_p(val,port)outw(val),(port)#defineoutl_p(val,port)outl(val),(port)#defineinb_p(port)inb(port)#defineinw_p(port)inw(port)#defineinl_p(port)inl(port)#defineoutsb_p(port,from,len)outsb(port,from,len)#defineoutsw_p(port,from,len)outsw(port,from,len)#defineoutsl_p(port,from,len)outsl(port,from,len)#defineinsb_p(port,to,len)insb(port,to,len)#defineinsw_p(port,to,len)insw(port,to,len)#defineinsl_p(port,to,len)insl(port,to,len)因?yàn)锳RM使用內(nèi)部總線,就沒(méi)有必要額外暫停,所以暫停式的I/O函數(shù)被擴(kuò)展為與非暫停式I/O 同樣的代碼。2.6、平臺(tái)依賴(lài)性由于自身的特性,I/O指令高度依賴(lài)于處理器,非常難以隱藏各體系間的不同。因此,大部分的關(guān)于端口 I/O 的源碼是平臺(tái)依賴(lài)的。以下是x86和ARM所使用函數(shù)的總結(jié):IA-32(x86)x86_64這個(gè)體系支持本章介紹的所有函數(shù);port參數(shù)的類(lèi)型為unsignedshort。ARM端口映射到內(nèi)存,并且支持本章介紹的所有函數(shù);port參數(shù)的類(lèi)型為unsignedint;字串函數(shù)用C語(yǔ)言實(shí)現(xiàn)。3、使用I/O內(nèi)存盡管I/O端口在x86世界中非常流行,但是用來(lái)和設(shè)備通訊的主要機(jī)制是通過(guò)內(nèi)存映射的寄存器和設(shè)備內(nèi)存,兩者都稱(chēng)為I/O內(nèi)存,因?yàn)榧拇嫫骱蛢?nèi)存之間的區(qū)別對(duì)軟件是透明的。I/O內(nèi)存僅僅是一個(gè)類(lèi)似于RAM的區(qū)域,處理器通過(guò)總線訪問(wèn)該區(qū)域,以實(shí)現(xiàn)對(duì)設(shè)備的訪問(wèn)。同樣,讀寫(xiě)這個(gè)區(qū)域是有邊際效應(yīng)。根據(jù)計(jì)算機(jī)體系和總線不同,I/O內(nèi)存可分為可以或者不可以通過(guò)頁(yè)表來(lái)存取。若通過(guò)頁(yè)表存取,內(nèi)核必須先重新編排物理地址,使其對(duì)驅(qū)動(dòng)程序可見(jiàn),這就意味著在進(jìn)行任何I/O操作之前,你必須調(diào)用ioremap;如果不需要頁(yè)表,I/O內(nèi)存區(qū)域就類(lèi)似于I/O端口,你可以直接使用適當(dāng)?shù)腎/O函數(shù)讀寫(xiě)它們。由于邊際效應(yīng)的緣故,不管是否需要ioremap,都不鼓勵(lì)直接使用I/O內(nèi)存指針,而應(yīng)使用專(zhuān)門(mén)的I/O內(nèi)存操作函數(shù)。這些I/O內(nèi)存操作函數(shù)不僅在所有平臺(tái)上是安全,而且對(duì)直接使用指針操作 I/O 內(nèi)存的情況進(jìn)行了優(yōu)化。3.1、I/O內(nèi)存分配和映射I/O內(nèi)存區(qū)在使用前必須先分配。分配內(nèi)存區(qū)的函數(shù)接口在定義中:/* request_mem_region分配一個(gè)開(kāi)始于start,len字節(jié)的I/O內(nèi)存區(qū)。分配成功,返回一個(gè)非NULL指針;否則返回NULL。系統(tǒng)當(dāng)前所有I/O內(nèi)存分配信息都在/proc/iomem文件中列出,你分配失敗時(shí),可以看看該文件,看誰(shuí)先占用了該內(nèi)存區(qū) */structresource*request_mem_region(unsignedlongstart,unsignedlonglen,char*name);/* release_mem_region用于釋放不再需要的I/O內(nèi)存區(qū) */voidrelease_mem_region(unsignedlongstart,unsignedlonglen);/* check_mem_region用于檢查I/O內(nèi)存區(qū)的可用性。同樣,該函數(shù)不安全,不推薦使用 */intcheck_mem_region(unsignedlongstart,unsignedlonglen);在訪問(wèn)I/O內(nèi)存之前,分配I/O內(nèi)存并不是唯一要求的步驟,你還必須保證內(nèi)核可存取該I/O內(nèi)存。訪問(wèn)I/O內(nèi)存并不只是簡(jiǎn)單解引用指針,在許多體系中,I/O內(nèi)存無(wú)法以這種方式直接存取。因此,還必須通過(guò)ioremap函數(shù)(第8章第4節(jié)介紹過(guò))設(shè)置一個(gè)映射。#include/* ioremap用于將I/O內(nèi)存區(qū)映射到虛擬地址。參數(shù)phys_addr為要映射的I/O內(nèi)存起始地址,參數(shù)size為要映射的I/O內(nèi)存的大小,返回值為被映射到的虛擬地址 */void*ioremap(unsignedlongphys_addr,unsignedlongsize);/* ioremap_nocache為ioremap的無(wú)緩存版本。實(shí)際上,在大部分體系中,ioremap與ioremap_nocache的實(shí)現(xiàn)一樣的,因?yàn)樗?I/O 內(nèi)存都是在無(wú)緩存的內(nèi)存地址空間中 */void*ioremap_nocache(unsignedlongphys_addr,unsignedlongsize);/* iounmap用于釋放不再需要的映射 */voidiounmap(void*addr);經(jīng)過(guò)ioremap(和iounmap)之后,設(shè)備驅(qū)動(dòng)就可以存取任何I/O內(nèi)存地址。注意,ioremap返回的地址不可以直接解引用;相反,應(yīng)當(dāng)使用內(nèi)核提供的訪問(wèn)函數(shù)。3.2、訪問(wèn)I/O內(nèi)存訪問(wèn)I/O內(nèi)存的正確方式是通過(guò)一系列專(zhuān)門(mén)用于實(shí)現(xiàn)此目的的函數(shù):#include/* I/O內(nèi)存讀函數(shù)。參數(shù)addr應(yīng)當(dāng)是從ioremap獲得的地址(可能包含一個(gè)整型偏移); 返回值是從給定I/O內(nèi)存讀取到的值 */unsignedintioread8(void*addr);unsignedintioread16(void*addr);unsignedintioread32(void*addr);/* I/O內(nèi)存寫(xiě)函數(shù)。參數(shù)addr同I/O內(nèi)存讀函數(shù),參數(shù)value為要寫(xiě)的值 */voidiowrite8(u8 value,void*addr);voidiowrite16(u16 value,void*addr);voidiowrite32(u32 value,void*addr);/* 以下這些函數(shù)讀和寫(xiě)一系列值到一個(gè)給定的 I/O 內(nèi)存地址,從給定的buf讀或?qū)慶ount個(gè)值到給定的addr。參數(shù)count表示要讀寫(xiě)的數(shù)據(jù)個(gè)數(shù),而不是字節(jié)大小 */voidioread8_rep(void*addr,void*buf,unsignedlongcount);voidioread16_rep(void*addr,void*buf,unsignedlongcount);voidioread32_rep(void*addr,void*buf,unsignedlongcount);voidiowrite8_rep(void*addr,constvoid*buf,unsignedlongcount);voidiowrite16_rep(void*addr,constvoid*buf,unsignedlongcount);voidiowrite32_rep(void*addr,,onstvoid*buf,,nsignedlongcount);/* 需要操作一塊I/O 地址時(shí),使用下列函數(shù)(這些函數(shù)的行為類(lèi)似于它們的C庫(kù)類(lèi)似函數(shù)): */voidmemset_io(void*addr,u8 value,unsignedintcount);voidmemcpy_fromio(void*dest,void*source,unsignedintcount);voidmemcpy_toio(void*dest,void*source,unsignedintcount);/* 舊的I/O內(nèi)存讀寫(xiě)函數(shù),不推薦使用 */unsignedreadb(address);unsignedreadw(address);unsignedreadl(address);voidwriteb(unsignedvalue,address);voidwritew(unsignedvalue,address);voidwritel(unsignedvalue,address);3.3、像I/O內(nèi)存一樣使用端口一些硬件有一個(gè)有趣的特性:有些版本使用I/O端口;而有些版本則使用I/O內(nèi)存。不管是I/O端口還是I/O內(nèi)存,處理器見(jiàn)到的設(shè)備寄存器都是相同的,只是訪問(wèn)方法不同。為了統(tǒng)一編程接口,使驅(qū)動(dòng)程序易于編寫(xiě),2.6 內(nèi)核提供了一個(gè)ioport_map函數(shù):/* ioport_map重新映射count個(gè)I/O端口,使它們看起來(lái)I/O內(nèi)存。此后,驅(qū)動(dòng)程序可以在ioport_map返回的地址上使用ioread8和同類(lèi)函數(shù)。這樣,就可以在編程時(shí),消除了I/O 端口和I/O 內(nèi)存的區(qū)別 */void*ioport_map(unsignedlongport,unsignedintcount);/* ioport_unmap用于釋放不再需要的映射 */voidioport_unmap(void*addr);注意,I/O端口在重新映射前必須使用request_region分配分配所需的I/O端口。4、ARM體系的I/O操作接口s3c24x0處理器使用的是I/O內(nèi)存,也就是說(shuō):s3c24x0處理器使用統(tǒng)一編址方式,I/O寄存器和內(nèi)存使用的是單一地址空間,并且讀寫(xiě)I/O寄存器和讀寫(xiě)內(nèi)存的指令是相同的。所以推薦使用I/O內(nèi)存的相關(guān)指令和函數(shù)。但這并不表示I/O端口的指令在s3c24x0中不可用。如果你注意過(guò)s3c24x0關(guān)于I/O方面的內(nèi)核源碼,你就會(huì)發(fā)現(xiàn):其實(shí)I/O端口的指令只是一個(gè)外殼,內(nèi)部還是使用和I/O內(nèi)存一樣的代碼。下面是ARM體系原始的I/O操作函數(shù)。其實(shí)后面I/O端口和I/O內(nèi)存操作函數(shù),只是對(duì)這些函數(shù)進(jìn)行再封裝。從這里也可以看出為什么我們不推薦直接使用I/O端口和I/O內(nèi)存地址指針,而是要求使用專(zhuān)門(mén)的I/O操作函數(shù)專(zhuān)門(mén)的I/O操作函數(shù)會(huì)檢查地址指針是否有效是否為IO地址(通過(guò)_iomem或_chk_io_ptr)#include/* Generic IO read/write. These perform native-endian accesses. Note* that some architectures will want to re-define _raw_read,writew.*/externvoid_raw_writesb(void_iomem*addr,constvoid*data,intbytelen);externvoid_raw_writesw(void_iomem*addr,constvoid*data,intwordlen);externvoid_raw_writesl(void_iomem*addr,constvoid*data,intlonglen);externvoid_raw_readsb(constvoid_iomem*addr,void*data,intbytelen);externvoid_raw_readsw(constvoid_iomem*addr,void*data,intwordlen);externvoid_raw_readsl(constvoid_iomem*addr,void*data,intlonglen);#define_raw_writeb(v,a)(_chk_io_ptr(a),*(volatileunsignedchar_force*)(a)=(v)#define_raw_writew(v,a)(_chk_io_ptr(a),*(volatileunsignedshort_force*)(a)=(v)#define_raw_writel(v,a)(_chk_io_ptr(a),*(volatileunsignedint_force*)(a)=(v)#define_raw_readb(a)(_chk_io_ptr(a),*(volatileunsignedchar_force*)(a)#define_raw_readw(a)(_chk_io_ptr(a),*(volatileunsignedshort_force*)(a)#define_raw_readl(a)(_chk_io_ptr(a),*(volatileunsignedint_force*)(a)關(guān)于_force和_iomem#include/* _force表示所定義的變量類(lèi)型是可以做強(qiáng)制類(lèi)型轉(zhuǎn)換的 */#define_force _attribute_(force)/* _iomem是用來(lái)修飾一個(gè)變量的,這個(gè)變量必須是非解引用(no dereference)的,即這個(gè)變量地址必須是有效的,而且變量所在的地址空間必須是2,即設(shè)備地址映射空間。0表示normal space,即普通地址空間,對(duì)內(nèi)核代碼來(lái)說(shuō),當(dāng)然就是內(nèi)核空間地址了。1表示用戶地址空間,2表示是設(shè)備地址映射空間 */#define_iomem _attribute_(noderef,address_space(2)I/O端口#include#defineoutb(v,p)_raw_writeb(v,_io(p)#defineoutw(v,p)_raw_writew(_force _u16)cpu_to_le16(v),_io(p)#defineoutl(v,p)_raw_writel(_force _u32)cpu_to_le32(v),_io(p)#defineinb(p)(_u8 _v=_raw_readb(_io(p);_v;)#defineinw(p)(_u16 _v=le16_to_cpu(_force _le16)_raw_readw(_io(p);_v;)#defineinl(p)(_u32 _v=le32_to_cpu(_force _le32)_raw_readl(_io(p);_v;)#defineoutsb(p,d,l)_raw_writesb(_io(p),d,l)#defineoutsw(p,d,l)_raw_writesw(_io(p),d,l)#defineoutsl(p,d,l)_raw_writesl(_io(p),d,l)#defineinsb(p,d,l)_raw_readsb(_io(p),d
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 酒店安全知識(shí)教育
- 開(kāi)顱手術(shù)后的護(hù)理
- 股票技術(shù)分析培訓(xùn)
- 病理護(hù)理操作流程圖解
- 宮腔鏡診療配合及護(hù)理
- 引流管的護(hù)理診斷
- 企業(yè)數(shù)據(jù)資產(chǎn)實(shí)施路徑及規(guī)劃方案
- 能碳管理中心建設(shè)方案
- 2025年福建福州新區(qū)投資控股有限責(zé)任公司社會(huì)公考招聘考試筆試試題(含答案)
- 文庫(kù)發(fā)布:籃球課課件
- 中、小學(xué)文件材料分類(lèi)方案、歸檔范圍、保管期限表(三合一制度)
- 醫(yī)院管理案例:構(gòu)建網(wǎng)格化管道風(fēng)險(xiǎn)管理模式降低非計(jì)劃性拔管發(fā)生率
- 臨床成人ICU患者外周動(dòng)脈導(dǎo)管管理要點(diǎn)
- JJG 693-2011可燃?xì)怏w檢測(cè)報(bào)警器
- 中華民族共同體概論課件專(zhuān)家版9第九講 混一南北和中華民族大統(tǒng)合(元朝時(shí)期)
- 肩周炎的中醫(yī)治療課件
- 骨科手術(shù)后的康復(fù)用具與輔助器具
- 小學(xué)特色課程《口風(fēng)琴課程》校本教材
- 《如何寫(xiě)文獻(xiàn)綜述》課件
- 汽車(chē)美容店計(jì)劃書(shū)案例
- 信息機(jī)房火災(zāi)事故應(yīng)急處置方案
評(píng)論
0/150
提交評(píng)論