新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > IO端口和IO內(nèi)存的區(qū)別及分別使用的函數(shù)接口

IO端口和IO內(nèi)存的區(qū)別及分別使用的函數(shù)接口

作者: 時(shí)間:2016-11-23 來(lái)源:網(wǎng)絡(luò) 收藏
IO端口和IO內(nèi)存的區(qū)別及分別使用的函數(shù)接口

每個(gè)外設(shè)都是通過(guò)讀寫(xiě)其寄存器來(lái)控制的。外設(shè)寄存器也稱為I/O端口,通常包括:控制寄存器、狀態(tài)寄存器和數(shù)據(jù)寄存器三大類。根據(jù)訪問(wèn)外設(shè)寄存器的不同方式,可以把CPU分成兩大類。一類CPU(如M68K,Power PC等)把這些寄存器看作內(nèi)存的一部分,寄存器參與內(nèi)存統(tǒng)一編址,訪問(wèn)寄存器就通過(guò)訪問(wèn)一般的內(nèi)存指令進(jìn)行,所以,這種CPU沒(méi)有專門(mén)用于設(shè)備I/O的指令。這就是所謂的“I/O內(nèi)存”方式。另一類CPU(典型的如X86),將外設(shè)的寄存器看成一個(gè)獨(dú)立的地址空間,所以訪問(wèn)內(nèi)存的指令不能用來(lái)訪問(wèn)這些寄存器,而要為對(duì)外設(shè)寄存器的讀/寫(xiě)設(shè)置專用指令,如IN和OUT指令。這就是所謂的“I/O端口”方式。但是,用于I/O指令的“地址空間”相對(duì)來(lái)說(shuō)是很小的,如x86 CPU的I/O空間就只有64KB(0-0xffff)。

本文引用地址:http://2s4d.com/article/201611/320405.htm

結(jié)合下圖,我們徹底講述IO端口和IO內(nèi)存以及內(nèi)存之間的關(guān)系。主存16M字節(jié)的SDRAM,外設(shè)是個(gè)視頻采集卡,上面有16M字節(jié)的SDRAM作為緩沖區(qū)。

1.CPU是i386架構(gòu)的情況

在i386系列的處理中,內(nèi)存和外部IO是獨(dú)立編址,也是獨(dú)立尋址的。MEM的內(nèi)存空間是32位可以尋址到4G,IO空間是16位可以尋址到64K。

在Linux內(nèi)核中,訪問(wèn)外設(shè)上的IO Port必須通過(guò)IO Port的尋址方式。而訪問(wèn)IO Mem就比較羅嗦,外部MEM不能和主存一樣訪問(wèn),雖然大小上不相上下,可是外部MEM是沒(méi)有在系統(tǒng)中注冊(cè)的。訪問(wèn)外部IO MEM必須通過(guò)remap映射到內(nèi)核的MEM空間后才能訪問(wèn)。為了達(dá)到接口的同一性,內(nèi)核提供了IO Port到IO Mem的映射函數(shù)。映射后IO Port就可以看作是IO Mem,按照IO Mem的訪問(wèn)方式即可。

3. CPU是ARM或PPC架構(gòu)的情況

在這一類的嵌入式處理器中,IO Port的尋址方式是采用內(nèi)存映射,也就是IO bus就是Mem bus。系統(tǒng)的尋址能力如果是32位,IO Port+Mem(包括IO Mem)可以達(dá)到4G。

1.使用I/O端口

I/O端口是驅(qū)動(dòng)用來(lái)和很多設(shè)備通訊的方法。

1.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);


1.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)核頭文件(體系依賴的頭文件)定義了下列內(nèi)聯(lián)函數(shù)來(lái)存取I/O端口:

/*inb/outb:讀/寫(xiě)字節(jié)端口(8位寬)。有些體系將port參數(shù)定義為unsigned long;而有些平臺(tái)則將它定義為unsigned short。inb的返回類型也是依賴體系的 */
unsignedinb(unsignedport);
voidoutb(unsignedcharbyte,unsignedport);
/*inw/outw:讀/寫(xiě)字端口(16位寬)*/
unsignedinw(unsignedport);
voidoutw(unsignedshortword,unsignedport);
/*inl/outl:讀/寫(xiě)32位端口。longword也是依賴體系的,有的體系為unsigned long;而有的為unsigned int */
unsignedinl(unsignedport);
voidoutl(unsignedlongword,unsignedport);

從現(xiàn)在開(kāi)始,當(dāng)我們使用unsigned沒(méi)有進(jìn)一步指定類型時(shí),表示是一個(gè)依賴體系的定義。

注意,沒(méi)有64位的I/O端口操作函數(shù)。即便在64位體系中,端口地址空間使用一個(gè)32位(最大)的數(shù)據(jù)通路。

1.3、從用戶空間訪問(wèn)I/O端口

1.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è)文件的定義也是依賴平臺(tái)的。

1.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é)序。

1.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同樣的代碼。

1.6、平臺(tái)依賴性

由于自身的特性,I/O指令高度依賴于處理器,非常難以隱藏各體系間的不同。因此,大部分的關(guān)于端口I/O的源碼是平臺(tái)依賴的。以下是x86和ARM所使用函數(shù)的總結(jié):

IA-32(x86)

x86_64

這個(gè)體系支持本章介紹的所有函數(shù);port參數(shù)的類型為unsignedshort。

ARM

端口映射到內(nèi)存,并且支持本章介紹的所有函數(shù);port參數(shù)的類型為unsignedint;字串函數(shù)用C語(yǔ)言實(shí)現(xiàn)。

2、使用I/O內(nèi)存

盡管I/O端口在x86世界中非常流行,但是用來(lái)和設(shè)備通訊的主要機(jī)制是通過(guò)內(nèi)存映射的寄存器和設(shè)備內(nèi)存,兩者都稱為I/O內(nèi)存,因?yàn)榧拇嫫骱蛢?nèi)存之間的區(qū)別對(duì)軟件是透明的。

I/O內(nèi)存僅僅是一個(gè)類似于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ū)域就類似于I/O端口,你可以直接使用適當(dāng)?shù)腎/O函數(shù)讀寫(xiě)它們。

由于邊際效應(yīng)的緣故,不管是否需要ioremap,都不鼓勵(lì)直接使用I/O內(nèi)存指針,而應(yīng)使用專門(mén)的I/O內(nèi)存操作函數(shù)。這些I/O內(nèi)存操作函數(shù)不僅在所有平臺(tái)上是安全,而且對(duì)直接使用指針操作I/O內(nèi)存的情況進(jìn)行了優(yōu)化。

2.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ù)設(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ù)。

2.2、訪問(wèn)I/O內(nèi)存

訪問(wèn)I/O內(nèi)存的正確方式是通過(guò)一系列專門(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ù)的行為類似于它們的C庫(kù)類似函數(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);

2.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和同類函數(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端口。


3、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)存地址指針,而是要求使用專門(mén)的I/O操作函數(shù)——專門(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,write}w.
*/
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表示所定義的變量類型是可以做強(qiáng)制類型轉(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,l)
#defineinsw(p,d,l)__raw_readsw(__io(p),d,l)
#defineinsl(p,d,l)__raw_readsl(__io(p),d,l)

I/O內(nèi)存

#include

#defineioread8(p)({unsignedint__v=__raw_readb(p);__v;})
#defineioread16(p)({unsignedint__v=le16_to_cpu((__force __le16)__raw_readw(p));__v;})
#defineioread32(p)({unsignedint__v=le32_to_cpu((__force __le32)__raw_readl(p));__v;})
#defineiowrite8(v,p)__raw_writeb(v,p)
#defineiowrite16(v,p)__raw_writew((__force __u16)cpu_to_le16(v),p)
#defineiowrite32(v,p)__raw_writel((__force __u32)cpu_to_le32(v),p)
#defineioread8_rep(p,d,c)__raw_readsb(p,d,c)
#defineioread16_rep(p,d,c)__raw_readsw(p,d,c)
#defineioread32_rep(p,d,c)__raw_readsl(p,d,c)
#defineiowrite8_rep(p,s,c)__raw_writesb(p,s,c)
#defineiowrite16_rep(p,s,c)__raw_writesw(p,s,c)
#defineiowrite32_rep(p,s,c)__raw_writesl(p,s,c)

注意:

1)、所有的讀寫(xiě)指令(I/O操作函數(shù))所賦的地址必須都是虛擬地址,你有兩種選擇:使用內(nèi)核已經(jīng)定義好的地址,如在include/asm-arm/arch-s3c2410/regs-xxx.h中定義了s3c2410處理器各外設(shè)寄存器地址(其他處理器芯片也可在類似路徑找到內(nèi)核定義好的外設(shè)寄存器的虛擬地址;另一種方法就是使用自己用ioremap映射的虛擬地址。絕對(duì)不能使用實(shí)際的物理地址,否則會(huì)因?yàn)閮?nèi)核無(wú)法處理地址而出現(xiàn)oops。

2)、在使用I/O指令時(shí),可以不使用request_region和request_mem_region,而直接使用outb、ioread等指令。因?yàn)閞equest的功能只是告訴內(nèi)核端口被誰(shuí)占用了,如再次request,內(nèi)核會(huì)制止(資源busy)。但是不推薦這么做,這樣的代碼也不規(guī)范,可能會(huì)引起并發(fā)問(wèn)題(很多時(shí)候我們都需要獨(dú)占設(shè)備)。

3)、在使用I/O指令時(shí),所賦的地址數(shù)據(jù)有時(shí)必須通過(guò)強(qiáng)制類型轉(zhuǎn)換為unsigned long,不然會(huì)有警告。

4)、在includeasm-armarch-s3c2410hardware.h中定義了很多io口的操作函數(shù),有需要可以在驅(qū)動(dòng)中直接使用,很方便。


上一頁(yè) 1 2 下一頁(yè)

評(píng)論


技術(shù)專區(qū)

關(guān)閉