Linux驅(qū)動(dòng)技術(shù)(二) _訪問(wèn)I/O內(nèi)存
ARM是對(duì)內(nèi)存空間和IO空間統(tǒng)一編址的,所以,通過(guò)讀寫SFR來(lái)控制硬件也就變成了通過(guò)讀寫相應(yīng)的SFR地址來(lái)控制硬件。這部分地址也被稱為I/O內(nèi)存。x86中對(duì)I/O地址和內(nèi)存地址是分開(kāi)編址的,這樣的IO地址被稱為I/O端口。本文只討論IO內(nèi)存的訪問(wèn)。
IO內(nèi)存訪問(wèn)流程
我們知道,為了管理最重要的系統(tǒng)資源并讓物理地址對(duì)進(jìn)程透明,Linux使用了內(nèi)存映射機(jī)制,就是一個(gè)進(jìn)程如果想訪問(wèn)一個(gè)物理內(nèi)存地址(eg.SFR地址),那么首先就是將其映射成虛擬地址。
IO內(nèi)存申請(qǐng)/歸還
Linux提供一組函數(shù)用于申請(qǐng)和釋放IO內(nèi)存的范圍,這兩個(gè)API在訪問(wèn)IO內(nèi)存的時(shí)候并不是必須的,但是建議使用,他們可以檢查申請(qǐng)的資源是否可用,增加IO訪問(wèn)的安全性,如果可用則申請(qǐng)成功,并標(biāo)志為已用,其他驅(qū)動(dòng)想在這個(gè)進(jìn)程歸還資源前申請(qǐng)就會(huì)失敗。
request_mem_region()宏函數(shù)向內(nèi)存申請(qǐng)n個(gè)內(nèi)存地址,這些地址從first開(kāi)始,len長(zhǎng),name表示設(shè)備的名稱,成功返回非NULL失敗返回NULL。
- /**
- * request_mem_region - create a new busy resource region
- * @start: resource start address
- * @n: resource region size
- * @name: reserving caller's ID string
- */
- struct resource * request_mem_region(resource_size_t start, resource_size_t n,const char *name)
release_mem_region()宏函數(shù)顧名思義就是將request_mem_region()申請(qǐng)的IO內(nèi)存資源歸還給內(nèi)核以便其他進(jìn)程也可以訪問(wèn)該IO內(nèi)存。
- /**
- * release_mem_region - release a previously reserved resource region
- * @start: resource start address
- * @n: resource region size
- */
- void release_mem_region(resource_size_t start, resource_size_t n,const char *name)
IO內(nèi)存映射/去映射
申請(qǐng)了IO資源,接下來(lái)就是進(jìn)行物理地址到虛擬地址的映射。內(nèi)核提供的API如下
- static inline void __iomem *ioremap(unsigned long port, unsigned long size)
- static inline void iounmap(volatile void __iomem *addr)
IO內(nèi)存訪問(wèn)API
ARM的SFR是32bit的,我們?cè)诮?jīng)過(guò)了ioremap之后其實(shí)就可以直接通過(guò)強(qiáng)制類型轉(zhuǎn)換來(lái)讀取獲取的虛擬地址,但是這種方法不夠安全,一不小心就會(huì)讀錯(cuò)位,為此,內(nèi)核同樣提供的標(biāo)準(zhǔn)的API來(lái)讀寫IO內(nèi)存,不但代碼的安全性更高,可讀性也得到了改善。
讀IO
- unsigned int ioread8(void *addr)
- unsigned int ioread16(void *addr)
- unsigned int ioread32(void *addr)
寫IO
- void iowrite8(u8 val,void *addr)
- void iowrite16(u8 val,void *addr)
- void iowrite32(u8 val,void *addr)
讀一串IO內(nèi)存
- void ioread8_rep(void *addr,void *buf,unsigned long len)
- void ioread16_rep(void *addr,void *buf,unsigned long len)
- void ioread32_rep(void *addr,void *buf,unsigned long len)
寫一串IO內(nèi)存
- void iowrite8_rep(void *addr,const void *buf,unsigned long len)
- void iowrite16_rep(void *addr,const void *buf,unsigned long len)
- void iowrite32_rep(void *addr,const void *buf,unsigned long len)
復(fù)制IO內(nèi)存
- void memcpy_fromio(void *dest,void *source,unsigned long len)
- void memcpy_toio(void *dest,void *source,unsigned long len)
設(shè)置IO內(nèi)存
- void memset_io(void *addr,u8 value,unsigned int len)