本篇講解下如何訪問IO外設(shè)的內(nèi)存和端口資源
What is io mem and io port

無論是x86,還是arm平臺,都需要使用大量的外設(shè)提供豐富多彩的功能,對于PC而言,有鍵盤,鼠標(biāo),觸摸板,觸摸屏等等,手機還有更多傳感器,加速度,陀螺儀等等。IO設(shè)備一般擁有3類資源,中斷(irq),內(nèi)存(mem)和端口(port),例如x86上的指紋模塊就需要port資源,而touchpad需要irq來通知中斷產(chǎn)生。
一般來說,x86上的設(shè)備資源都定義在ACPI表中,而arm上的設(shè)備資源寫在devicetree中。不過也有一些資源寫死在hardcode中。
Address concept
物理地址空間,一部分給物理RAM(內(nèi)存)用,一部分給總線用。在PC機中,一般是把低端物理地址給RAM用,高端物理地址給總線用。
- 一致編址:外設(shè)接口中的IO寄存器(即IO端口)與主存單元相同看待,每個端口占用一個存儲單元的地址,將主存的一部分劃出來用作IO地址空間
- 獨立編址:x86就是獨立編址,IO地址與存儲地址分隔獨立編址,也就是說內(nèi)存的部分地址和IO是重疊的。因此CPU也不能通過使用訪問內(nèi)存的辦法訪問IO,必須使用專用的IO指令,即readb/writeb for iomem, inb/outb for ioport. PC架構(gòu)一共有65536個8bit的I/O端口,組成64K個I/O地址空間,編號從0~0xFFFF,有16位。
How to read/write IO port
-
直接使用IO端口操作函數(shù):在設(shè)備打開或驅(qū)動模塊被加載時申請IO端口區(qū)域,之后使用inb(),outb()等進行端口訪問,最后在設(shè)備關(guān)閉或驅(qū)動被卸載時釋放IO端口范圍。
io訪問
inb( )、inw( )、inl( )分別從I/O端口讀取1、2或4個連續(xù)字節(jié)。后綴“b”、“w”、“l(fā)”分別代表一個字節(jié)(8位)、一個字(16位)以及一個長整型(32位)。
inb_p( )、inw_p( )、inl_p( )分別從I/O端口讀取1、2或4個連續(xù)字節(jié),然后執(zhí)行一條“啞元(dummy,即空指令)”指令使CPU暫停。
2.將IO端口映射為內(nèi)存進行訪問,在設(shè)備打開或驅(qū)動模塊被加載時,申請IO端口區(qū)域并使用ioport_map()映射到內(nèi)存,之后使用IO內(nèi)存的函數(shù)進行端口訪問,最后,在設(shè)備關(guān)閉或驅(qū)動模塊被卸載時釋放IO端口并釋放映射。
在進行映射前,還必須通過request_region()分配I/O resource
'''
void *ioport_map(unsigned long port, unsigned int count);
void ioport_unmap(void *addr);
'''
返回映射的內(nèi)存地址,可以使用指針訪問,建議使用函數(shù)
'''
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
'''
ioremap()將vmalloc區(qū)的某段虛擬內(nèi)存塊映射到io mem, 首先找到塊空閑的vmalloc塊,然后修改內(nèi)核頁表進行映射,不過不需要通過伙伴系統(tǒng)分配物理頁面,因為映射的是io mem,不是ram
ARM上使用of_iomap()
How to read/write IO mem
IO內(nèi)存的訪問方法是:首先調(diào)用request_mem_region()申請資源,接著將寄存器地址通過ioremap()映射到內(nèi)核空間的虛擬地址,之后就可以Linux設(shè)備訪問編程接口訪問這些寄存器了,訪問完成后,使用ioremap()對申請的虛擬地址進行釋放,并釋放release_mem_region()申請的IO內(nèi)存資源。
struct resource*requset_mem_region(unsigned long start, unsigned long len,char *name);
這個函數(shù)從內(nèi)核申請len個內(nèi)存地址(在3G~4G之間的虛地址),而這里的start為I/O物理地址,name為設(shè)備的名稱。注意,如果分配成功,則返回非NULL,否則,返回NULL。
另外,可以通過/proc/iomem查看系統(tǒng)給各種設(shè)備的內(nèi)存范圍。
要釋放所申請的I/O內(nèi)存,應(yīng)當(dāng)使用release_mem_region()函數(shù):
void release_mem_region(unsigned longstart, unsigned long len)
申請一組I/O內(nèi)存后,調(diào)用ioremap()函數(shù):
void* ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
其中三個參數(shù)的含義為:phys_addr:與requset_mem_region函數(shù)中參數(shù)start相同的I/O物理地址;size:要映射的空間的大小;flags:要映射的IO空間的和權(quán)限有關(guān)的標(biāo)志;
功能:將一個I/O地址空間映射到內(nèi)核的虛擬地址空間上(通過requset _mem_region()申請到的)
