<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Linux設(shè)備驅(qū)動(dòng)之I/O端口與I/O內(nèi)存

          Linux設(shè)備驅(qū)動(dòng)之I/O端口與I/O內(nèi)存

          作者: 時(shí)間:2016-10-10 來源:網(wǎng)絡(luò) 收藏

          3.操作IO(申請(qǐng),映射,訪問,釋放):

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

          I/O 內(nèi)存僅僅是一個(gè)類似于RAM 的區(qū)域,處理器通過總線訪問該區(qū)域,以實(shí)現(xiàn)對(duì)設(shè)備的訪問。同樣,讀寫這個(gè)區(qū)域是有邊際效應(yīng)。

          根據(jù)計(jì)算機(jī)體系和總線不同,I/O 內(nèi)存可分為可以或者不可以通過頁表來存取。若通過頁表存取,內(nèi)核必須先重新編排物理地址,使其對(duì)驅(qū)動(dòng)程序可見,這就意味著在進(jìn)行任何I/O操作之前,你必須調(diào)用ioremap;如果不需要頁表,I/O內(nèi)存區(qū)域就類似于I/O,你可以直接使用適當(dāng)?shù)腎/O函數(shù)讀寫它們。

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

          (1)申請(qǐng)I/O 內(nèi)存:

          I/O 內(nèi)存區(qū)在使用前必須先分配。分配內(nèi)存區(qū)的函數(shù)接口在定義中:

          /* request_mem_region分配一個(gè)開始于start,len字節(jié)的I/O內(nèi)存區(qū)。分配成功,返回一個(gè)非NULL指針;否則返回NULL。系統(tǒng)當(dāng)前所有I/O內(nèi)存分配信息都在/proc/iomem文件中列出,你分配失敗時(shí),可以看看該文件,看誰先占用了該內(nèi)存區(qū) */struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

          (2)映射:

          在訪問I/O內(nèi)存之前,分配I/O內(nèi)存并不是唯一要求的步驟,你還必須保證內(nèi)核可存取該I/O內(nèi)存。訪問I/O內(nèi)存并不只是簡(jiǎn)單解引用指針,在許多體系中,I/O 內(nèi)存無法以這種方式直接存取。因此,還必須通過ioremap 函數(shù)設(shè)置一個(gè)映射。

          /* ioremap用于將I/O內(nèi)存區(qū)映射到虛擬地址。參數(shù)phys_addr為要映射的I/O內(nèi)存起始地址,參數(shù)size為要映射的I/O內(nèi)存的大小,返回值為被映射到的虛擬地址 */void *ioremap(unsigned long phys_addr, unsigned long size);

          (3)訪問IO內(nèi)存:

          經(jīng)過 ioremap之后,就可以存取任何I/O內(nèi)存地址。注意,ioremap返回的地址不可以直接解引用;相反,應(yīng)當(dāng)使用內(nèi)核提供的訪問函數(shù)。訪問I/O內(nèi)存的正確方式是通過一系列專門用于實(shí)現(xiàn)此目的的函數(shù):

          #include /* I/O內(nèi)存讀函數(shù)。參數(shù)addr應(yīng)當(dāng)是從ioremap獲得的地址(可能包含一個(gè)整型偏移); 返回值是從給定I/O內(nèi)存讀取到的值 */unsigned int ioread8(void *addr);unsigned int ioread16(void *addr);unsigned int ioread32(void *addr);/* I/O內(nèi)存寫函數(shù)。參數(shù)addr同I/O內(nèi)存讀函數(shù),參數(shù)value為要寫的值 */void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);/* 以下這些函數(shù)讀和寫一系列值到一個(gè)給定的 I/O 內(nèi)存地址,從給定的buf讀或?qū)慶ount個(gè)值到給定的addr。參數(shù)count表示要讀寫的數(shù)據(jù)個(gè)數(shù),而不是字節(jié)大小 */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,,onst void *buf,,nsigned long count);/* 需要操作一塊I/O 地址時(shí),使用下列函數(shù)(這些函數(shù)的行為類似于它們的C庫類似函數(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);/* 舊的I/O內(nèi)存讀寫函數(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);

          (4)釋放IO內(nèi)存步驟:

          void iounmap(void * addr); /* iounmap用于釋放不再需要的映射 */void release_mem_region(unsigned long start, unsigned long len); /* iounmap用于釋放不再需要的映射 */

          4、像IO內(nèi)存一樣使用

          一些硬件有一個(gè)有趣的特性: 有些版本使用 I/O 端口;而有些版本則使用 I/O 內(nèi)存。不管是I/O 端口還是I/O 內(nèi)存,處理器見到的設(shè)備寄存器都是相同的,只是訪問方法不同。為了統(tǒng)一編程接口,使驅(qū)動(dòng)程序易于編寫,2.6 內(nèi)核提供了一個(gè)ioport_map函數(shù):

          /* ioport_map重新映射count個(gè)I/O端口,使它們看起來I/O內(nèi)存。此后,驅(qū)動(dòng)程序可以在ioport_map返回的地址上使用ioread8和同類函數(shù)。這樣,就可以在編程時(shí),消除了I/O 端口和I/O 內(nèi)存的區(qū)別 */void *ioport_map(unsigned long port, unsigned int count);void ioport_unmap(void *addr);/* ioport_unmap用于釋放不再需要的映射 */

          注意,I/O 端口在重新映射前必須使用request_region分配分配所需的I/O 端口。

          5、ARM體系的IO操作接口

          s3c24x0處理器使用的是I/O內(nèi)存,也就是說:s3c24x0處理器使用統(tǒng)一編址方式,I/O寄存器和內(nèi)存使用的是單一地址空間,并且讀寫I/O寄存器和讀寫內(nèi)存的指令是相同的。所以推薦使用I/O內(nèi)存的相關(guān)指令和函數(shù)。但這并不表示I/O端口的指令在s3c24x0中不可用。如果你注意過s3c24x0關(guān)于I/O方面的內(nèi)核源碼,你就會(huì)發(fā)現(xiàn):其實(shí)I/O端口的指令只是一個(gè)外殼,內(nèi)部還是使用和I/O內(nèi)存一樣的代碼。注意以下幾點(diǎn):

          1)所有的讀寫指令(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)核無法處理地址而出現(xiàn)oops。



          評(píng)論


          相關(guān)推薦

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

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();