日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

模块计算机型x86yu,ldd3学习之九:与硬件通信

發(fā)布時(shí)間:2025/3/19 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 模块计算机型x86yu,ldd3学习之九:与硬件通信 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

操作 I/O 端口

在驅(qū)動(dòng)程序注冊(cè)I/O 端口后,就可以讀/寫(xiě)這些端口。大部分硬件會(huì)把8、16和32位端口區(qū)分開(kāi),不能像訪問(wèn)系統(tǒng)內(nèi)存那樣混淆使用。驅(qū)動(dòng)必須調(diào)用不同的函數(shù)來(lái)存取不同大小的端口。

只支持內(nèi)存映射的 I/O 寄存器的計(jì)算機(jī)體系通過(guò)重新映射I/O端口到內(nèi)存地址來(lái)偽裝端口I/O。為了提高移植性,內(nèi)核向驅(qū)動(dòng)隱藏了這些細(xì)節(jié)。Linux 內(nèi)核頭文件(體系依賴(lài)的頭文件??) 定義了下列內(nèi)聯(lián)函數(shù)(有的體系是宏,有的不存在)來(lái)訪問(wèn) I/O 端口:unsigned?inb(unsigned?port);

void?outb(unsigned?char?byte,?unsigned?port);

/*讀/寫(xiě)字節(jié)端口( 8 位寬 )。port 參數(shù)某些平臺(tái)定義為 unsigned long ,有些為 unsigned short 。 inb 的返回類(lèi)型也體系而不同。*/

unsigned?inw(unsigned?port);

void?outw(unsigned?short?word,?unsigned?port);

/*訪問(wèn) 16位 端口( 一個(gè)字寬 )*/

unsigned?inl(unsigned?port);

void?outl(unsigned?longword,?unsigned?port);

/*訪問(wèn) 32位 端口。 longword 聲明有的平臺(tái)為 unsigned long ,有的為 unsigned int。*/

在用戶空間訪問(wèn) I/O 端口(x86平臺(tái)用法)

以上函數(shù)主要提供給設(shè)備驅(qū)動(dòng)使用,但它們也可在用戶空間使用,至少在 PC上可以。 GNU C 庫(kù)在 中定義了它們。如果在用戶空間代碼中使用必須滿足以下條件:

(1)程序必須使用 -O 選項(xiàng)編譯來(lái)強(qiáng)制擴(kuò)展內(nèi)聯(lián)函數(shù)。

(2)必須用ioperm 和 iopl 系統(tǒng)調(diào)用(#include )?來(lái)獲得對(duì)端口 I/O 操作的權(quán)限。ioperm 為獲取單獨(dú)端口操作權(quán)限,而 iopl 為整個(gè) I/O 空間的操作權(quán)限。(x86 特有的)

(3)程序以 root 來(lái)調(diào)用 ioperm?和 iopl,或是其父進(jìn)程必須以 root 獲得端口操作權(quán)限。(x86 特有的)

若平臺(tái)沒(méi)有 ioperm 和 iopl 系統(tǒng)調(diào)用,用戶空間可以仍然通過(guò)使用 /dev/prot 設(shè)備文件訪問(wèn) I/O 端口。注意:這個(gè)文件的定義是體系相關(guān)的,并且I/O 端口必須先被注冊(cè)。

串操作

除了一次傳輸一個(gè)數(shù)據(jù)的I/O操作,一些處理器實(shí)現(xiàn)了一次傳輸一個(gè)數(shù)據(jù)序列的特殊指令,序列中的數(shù)據(jù)單位可以是字節(jié)、字或雙字,這是所謂的串操作指令。它們完成任務(wù)比一個(gè) C 語(yǔ)言循環(huán)更快。下列宏定義實(shí)現(xiàn)了串I/O,它們有的通過(guò)單個(gè)機(jī)器指令實(shí)現(xiàn);但如果目標(biāo)處理器沒(méi)有進(jìn)行串 I/O 的指令,則通過(guò)執(zhí)行一個(gè)緊湊的循環(huán)實(shí)現(xiàn)。 有的體系的原型如下:void?insb(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(unsigned?port,?void?*addr,?unsigned?long?count);

使用時(shí)注意: 它們直接將字節(jié)流從端口中讀取或?qū)懭搿.?dāng)端口和主機(jī)系統(tǒng)有不同的字節(jié)序時(shí),會(huì)導(dǎo)致不可預(yù)期的結(jié)果。 使用 inw 讀取端口應(yīng)在必要時(shí)自行轉(zhuǎn)換字節(jié)序,以匹配主機(jī)字節(jié)序。

暫停式?I/O

為了匹配低速外設(shè)的速度,有時(shí)若 I/O 指令后面還緊跟著另一個(gè)類(lèi)似的I/O指令,就必須在 I/O 指令后面插入一個(gè)小延時(shí)。在這種情況下,可以使用暫停式的I/O函數(shù)代替通常的I/O函數(shù),它們的名字以 _p 結(jié)尾,如 inb_p、outb_p等等。 這些函數(shù)定義被大部分體系支持,盡管它們常常被擴(kuò)展為與非暫停式I/O 同樣的代碼。因?yàn)槿绻w系使用一個(gè)合理的現(xiàn)代外設(shè)總線,就沒(méi)有必要額外暫停。細(xì)節(jié)可參考平臺(tái)的 asm 子目錄的 io.h 文件。以下是include\asm-arm\io.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(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?outsw_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)????insl(port,to,len)

由此可見(jiàn),由于ARM使用內(nèi)部總線,就沒(méi)有必要額外暫停,所以暫停式的I/O函數(shù)被擴(kuò)展為與非暫停式I/O 同樣的代碼。

平臺(tái)相關(guān)性

由于自身的特性,I/O 指令與處理器密切相關(guān)的,非常難以隱藏系統(tǒng)間的不同。所以大部分的關(guān)于端口 I/O 的源碼是平臺(tái)依賴(lài)的。IA-32 (x86)x86_64這個(gè)體系支持所有的以上描述的函數(shù),端口號(hào)是 unsigned short 類(lèi)型。ARM端口映射到內(nèi)存,支持所有函數(shù)。串操作 用C語(yǔ)言實(shí)現(xiàn)。端口是 unsigned int 類(lèi)型。

4.使用 I/O 內(nèi)存

除了?x86上普遍使用的I/O 端口外,和設(shè)備通訊另一種主要機(jī)制是通過(guò)使用映射到內(nèi)存的寄存器或設(shè)備內(nèi)存,統(tǒng)稱(chēng)為 I/O 內(nèi)存。因?yàn)榧拇嫫骱蛢?nèi)存之間的區(qū)別對(duì)軟件是透明的。I/O 內(nèi)存僅僅是類(lèi)似 RAM 的一個(gè)區(qū)域,處理器通過(guò)總線訪問(wèn)這個(gè)區(qū)域,以實(shí)現(xiàn)設(shè)備的訪問(wèn)。

根據(jù)平臺(tái)和總線的不同,I/O 內(nèi)存可以就是否通過(guò)頁(yè)表訪問(wèn)分類(lèi)。若通過(guò)頁(yè)表訪問(wèn),內(nèi)核必須首先安排物理地址使其對(duì)設(shè)備驅(qū)動(dòng)程序可見(jiàn),在進(jìn)行任何 I/O 之前必須調(diào)用 ioremap。若不通過(guò)頁(yè)表,I/O 內(nèi)存區(qū)域就類(lèi)似I/O 端口,可以使用適當(dāng)形式的函數(shù)訪問(wèn)它們。因?yàn)椤皊ide effect”的影響,不管是否需要 ioremap?,都不鼓勵(lì)直接使用 I/O 內(nèi)存的指針。而使用專(zhuān)用的 I/O 內(nèi)存操作函數(shù),不僅在所有平臺(tái)上是安全,而且對(duì)直接使用指針操作 I/O 內(nèi)存的情況進(jìn)行了優(yōu)化。

I/O 內(nèi)存分配和映射

I/O 內(nèi)存區(qū)域使用前必須先分配,函數(shù)接口在??定義:struct?resource?*request_mem_region(unsigned?long?start,?unsigned?long?len,?char*name);/* 從 start 開(kāi)始,分配一個(gè) len 字節(jié)的內(nèi)存區(qū)域。成功返回一個(gè)非NULL指針,否則返回NULL。所有的 I/O 內(nèi)存分配情況都 /proc/iomem 中列出。*/

/*I/O內(nèi)存區(qū)域在不再需要時(shí)應(yīng)當(dāng)釋放*/

void?release_mem_region(unsigned?long?start,?unsigned?long?len);

/*一個(gè)舊的檢查 I/O 內(nèi)存區(qū)可用性的函數(shù),不推薦使用*/

int?check_mem_region(unsigned?long?start,?unsigned?long?len);

然后必須設(shè)置一個(gè)映射,由 ioremap 函數(shù)實(shí)現(xiàn),此函數(shù)專(zhuān)門(mén)用來(lái)為I/O 內(nèi)存區(qū)域分配虛擬地址。經(jīng)過(guò)ioremap?之后,設(shè)備驅(qū)動(dòng)即可訪問(wèn)任意的 I/O 內(nèi)存地址。注意:ioremap 返回的地址不應(yīng)當(dāng)直接引用;應(yīng)使用內(nèi)核提供的 accessor 函數(shù)。以下為函數(shù)定義:#include?

void?*ioremap(unsigned?long?phys_addr,?unsigned?long?size);

void?*ioremap_nocache(unsigned?long?phys_addr,?unsigned?long?size);/*如果控制寄存器也在該區(qū)域,應(yīng)使用的非緩存版本,以實(shí)現(xiàn)side effect。*/

void?iounmap(void?*?addr);

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

訪問(wèn)I/O 內(nèi)存的正確方式是通過(guò)一系列專(zhuān)用于此目的的函數(shù)(在??中定義的):/*I/O 內(nèi)存讀函數(shù)*/

unsigned?int?ioread8(void?*addr);

unsigned?int?ioread16(void?*addr);

unsigned?int?ioread32(void?*addr);

/*addr 是從 ioremap 獲得的地址(可能包含一個(gè)整型偏移量), 返回值是從給定 I/O 內(nèi)存讀取的值*/

/*對(duì)應(yīng)的I/O 內(nèi)存寫(xiě)函數(shù)*/

void?iowrite8(u8 value,?void?*addr);

void?iowrite16(u16 value,?void?*addr);

void?iowrite32(u32 value,?void?*addr);

/*讀和寫(xiě)一系列值到一個(gè)給定的 I/O 內(nèi)存地址,從給定的 buf 讀或?qū)?count 個(gè)值到給定的 addr */

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?*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?count);

/*舊函數(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 內(nèi)存一樣使用端口

一些硬件有一個(gè)有趣的特性:一些版本使用 I/O 端口,而其他的使用 I/O 內(nèi)存。為了統(tǒng)一編程接口,使驅(qū)動(dòng)程序易于編寫(xiě),2.6 內(nèi)核提供了一個(gè)ioport_map函數(shù):void?*ioport_map(unsigned?long?port,?unsigned?int?count);/*重映射 count 個(gè)I/O 端口,使其看起來(lái)像 I/O 內(nèi)存。,此后,驅(qū)動(dòng)程序可以在返回的地址上使用 ioread8 和同類(lèi)函數(shù)。其在編程時(shí)消除了I/O 端口和I/O 內(nèi)存的區(qū)別。

/*這個(gè)映射應(yīng)當(dāng)在它不再被使用時(shí)撤銷(xiāo):*/

void?ioport_unmap(void?*addr);

/*注意:I/O 端口仍然必須在重映射前使用 request_region 分配I/O 端口。ARM9不支持這兩個(gè)函數(shù)!*/

ARM9的linux驅(qū)動(dòng)接口

s3c24x0處理器是使用I/O內(nèi)存的,也就是說(shuō):他們的外設(shè)接口是通過(guò)讀寫(xiě)相應(yīng)的寄存器實(shí)現(xiàn)的,這些寄存器和內(nèi)存是使用單一的地址空間,并使用和讀寫(xiě)內(nèi)存一樣的指令。所以推薦使用I/O內(nèi)存的相關(guān)指令。

但這并不表示I/O端口的指令在s3c24x0中不可用。但是只要你注意其源碼,你就會(huì)發(fā)現(xiàn):其實(shí)I/O端口的指令只是一個(gè)外殼,內(nèi)部還是使用和I/O內(nèi)存一樣的代碼。以下列出一些:

I/O端口#define?outb(v,p)????????__raw_writeb(v,__io(p))

#define?outw(v,p)????????__raw_writew((__force __u16)?\

cpu_to_le16(v),__io(p))

#define?outl(v,p)????????__raw_writel((__force __u32)?\

cpu_to_le32(v),__io(p))

#define?inb(p)????({?__u8 __v?=?__raw_readb(__io(p));?__v;?})

#define?inw(p)????({?__u16 __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內(nèi)存#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?=?le32_to_cpu(__raw_readl(p));?__v;?})

#define?iowrite8(v,p)????__raw_writeb(v,?p)

#define?iowrite16(v,p)????__raw_writew(cpu_to_le16(v),?p)

#define?iowrite32(v,p)????__raw_writel(cpu_to_le32(v),?p)

在這里值得注意的有4點(diǎn):

(1)所有的讀寫(xiě)指令所賦的地址必須都是虛擬地址,你有兩種選擇:使用內(nèi)核已經(jīng)定義好的地址,如 S3C2440_GPJCON等等,這些都是內(nèi)核定義好的虛擬地址,有興趣的可以看源碼。還有一種方法就是使用自己用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ì)制止。

(3)在使用I/O指令時(shí),所賦的地址數(shù)據(jù)有時(shí)必須通過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換為?unsigned?long,雖然你的程序可能也可以使用,但是最好還是不要有警告為妙。

(4)在include\asm-arm\arch-s3c2410\hardware.h中定義了很多io口的操作函數(shù),有需要可以在驅(qū)動(dòng)中直接使用,很方便。

總結(jié)

以上是生活随笔為你收集整理的模块计算机型x86yu,ldd3学习之九:与硬件通信的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。