“小濤哥,咱們說(shuō)Linux設(shè)備驅(qū)動(dòng)程序說(shuō)了那么久,怎么從來(lái)不說(shuō)實(shí)際設(shè)備呢,頂多就說(shuō)了下內(nèi)存,總感覺(jué)驅(qū)動(dòng)程序是和設(shè)備分離的,怎么關(guān)聯(lián)起來(lái)..”小王思索著。 “不錯(cuò),這也正是這次講課的內(nèi)容,設(shè)備I/O端口與I/O內(nèi)存的訪問(wèn)”我啊,禁不住拍拍她的頭。 對(duì)于一塊實(shí)際的設(shè)備而言,通常會(huì)提供一組寄存器來(lái)用于控制設(shè)備,讀寫(xiě)設(shè)備和獲取設(shè)備狀態(tài),也就是我們常說(shuō)的控制寄存器,數(shù)據(jù)寄存器和狀態(tài)寄存器。這些寄存器可能位于I/O空間(這時(shí)叫做I/O端口),也可能位于內(nèi)存空間(對(duì)應(yīng)的內(nèi)存空間被成為I/O內(nèi)存)。在Linux中提供了一系列的I/O端口和I/O內(nèi)存操作的接口如下: 1)I/O端口操作:在Linux設(shè)備驅(qū)動(dòng)中,應(yīng)使用Linux內(nèi)核提供的函數(shù)來(lái)訪問(wèn)定位于I/O空間的端口,包括一下幾種: *讀寫(xiě)字節(jié)端口(8位寬) unsigned inb(unsigned port);【讀】 voi outb(unsigned char byte, unsigned port);【寫(xiě)】 *讀寫(xiě)字端口(16位寬) unsigned inw(unsigned port);【讀】 voi outw(unsigned short word, unsigned port);【寫(xiě)】 *讀寫(xiě)長(zhǎng)字端口(32位寬) unsigned inl(unsigned port);【讀】 voi outl(unsigned longword, unsigned port);【寫(xiě)】 *讀寫(xiě)一串字節(jié) unsigned insb(unsigned port, void *addr, unsigned long count);【讀】 voi outsb(unsigned port, void *addr, unsigned long count);【寫(xiě)】 *讀寫(xiě)一串字unsigned insw(unsigned port, void *addr,unsigned long count);【讀】 voi outsb(unsigned port, void *addr, unsigned long count);【寫(xiě)】 *讀寫(xiě)一串長(zhǎng)字 unsigned insl(unsigned port, void * addr, unsigned long count);【讀】 voi outsb(unsigned port, void *addr, unsigned long count);【寫(xiě)】 說(shuō)明:上述各函數(shù)中I/O端口port的類型長(zhǎng)度依賴與具體的硬件平臺(tái),所以只是寫(xiě)出了unsigned 2)I/O內(nèi)存:在內(nèi)核中訪問(wèn)I/O內(nèi)存之前,需首先使用ioremap()函數(shù)將設(shè)備所處的物理地址映射到虛擬地址。 *ioremap()原型:void *ioremap(unsigned long offset, unsigned long size); 它返回一個(gè)特殊的虛擬地址,該地址可用來(lái)存取特定的物理地址范圍。用它返回的虛擬地址應(yīng)該使用iounmap()函數(shù)釋放。 *iounmap()原型:void iounmap(void *addr); 現(xiàn)在,有了物理地址鎖映射出來(lái)的虛擬地址后,我們就可以通過(guò)c指針來(lái)直接訪問(wèn)這些地址,但Linux內(nèi)核也提供了一組函數(shù)來(lái)完成這中虛擬地址的讀寫(xiě)。如下 *讀IO內(nèi)存 unsigned int ioread8(void *addr); unsigned int ioread16(void *addr); unsigned int ioread32(void *addr); 與之對(duì)應(yīng)的較早版本是: unsigned readb(address); unsigned readw(address); unsigned readl(address); 這些在2.6的內(nèi)核中依然可以使用。 *寫(xiě)IO內(nèi)存 void iowrite8(u8 value,void *addr); void iowrite16(u16 value, void *addr); void iowrite32(u32 value, void *addr); 與之對(duì)應(yīng)的較早版本是: void writeb(unsigned value, address); void writew(unsigned value,address); void writel(unsigned value,address); 2.6的內(nèi)核中依然可以使用。 *讀一串IO內(nèi)存 *寫(xiě)一串IO內(nèi)存 void ioread8_rep(void *addr, void *buf, unsigned long count); void iowrite8_rep(void *addr, void *buf, unsigned long count); void ioread16_rep(void *addr, void *buf, unsigned long count); void iowrite8_rep(void *addr, void *buf, unsigned long count); void ioread32_rep(void *addr, void *buf, unsigned long count); void iowrite8_rep(void *addr, void *buf, unsigned long count); *復(fù)制IO內(nèi)存 void memcpy_fromio(void *dest, void *source, unsigned int count); void memcpy_toio(void *dest, void *source, unsigned int count); *設(shè)置IO內(nèi)存 void *ioport_map(unsigned long port, unsigned int count); 3)把IO端口映射到內(nèi)存空間 void *ioport_map(unsigned long port, unsigned int count); 通過(guò)這個(gè)函數(shù),可以把port開(kāi)始的count個(gè)連續(xù)的IO端口重映射為一段“內(nèi)存空間”。然后 就可以在其返回的地址上向訪問(wèn)IO內(nèi)存一樣訪問(wèn)這些端口,當(dāng)不再需要這種映射時(shí),調(diào)用: void ioport_unmap(void *addr); 來(lái)撤銷這種映射 4)IO端口申請(qǐng) struct resource *request_region(unsigned long first, unsigned long n, const char *name); 這個(gè)函數(shù)向內(nèi)核申請(qǐng)n個(gè)端口,這些端口從first開(kāi)始,name參數(shù)為設(shè)備的名稱,成功返回非NULL.一旦申請(qǐng)端口使用完成后,應(yīng)當(dāng)使用: void release_region(unsigned long start, unsigned long n); 5)IO內(nèi)存申請(qǐng) struct resource *request_mem_region(unsigned long start, unsigned long len, char *name); 這個(gè)函數(shù)向內(nèi)核申請(qǐng)n個(gè)內(nèi)存,這些地址從first開(kāi)始,name為設(shè)備的名稱,成功返回非NULL,一旦申請(qǐng)的內(nèi)存使用完成后,應(yīng)當(dāng)使用: void release_mem_region() ; 來(lái)釋放歸回給系統(tǒng)。需要說(shuō)明的是這兩個(gè)函數(shù)也不是必須的,但建議使用。 6)通過(guò)以上的基礎(chǔ),我們就可以歸納出設(shè)備驅(qū)動(dòng)訪問(wèn)IO端口和IO內(nèi)存的步驟。 一種方法是:直接使用IO端口操作函數(shù):在設(shè)備打開(kāi)或驅(qū)動(dòng)模塊被加載時(shí)申請(qǐng)IO端口區(qū)域,之后使用inb(),outb()等進(jìn)行端口訪問(wèn),最后在設(shè)備關(guān)閉或驅(qū)動(dòng)被卸載時(shí)釋放IO端口范圍。流程如下:
另外一種途徑是:將IO端口映射為內(nèi)存進(jìn)行訪問(wèn),在設(shè)備打開(kāi)或驅(qū)動(dòng)模塊被加載時(shí),申請(qǐng)IO端口區(qū)域并使用ioport_map()映射到內(nèi)存,之后使用IO內(nèi)存的函數(shù)進(jìn)行端口訪問(wèn),最后,在設(shè)備關(guān)閉或驅(qū)動(dòng)模塊被卸載時(shí)釋放IO端口并釋放映射,流程如下:
上邊是IO端口的訪問(wèn)方法,至于IO內(nèi)存的訪問(wèn)方法是:首先調(diào)用request_mem_region()申請(qǐng)資源,接著將寄存器地址通過(guò)ioremap()映射到內(nèi)核空間的虛擬地址,之后就可以Linux設(shè)備訪問(wèn)編程接口訪問(wèn)這些寄存器了,訪問(wèn)完成后,使用ioremap()對(duì)申請(qǐng)的虛擬地址進(jìn)行釋放,并釋放release_mem_region()申請(qǐng)的IO內(nèi)存資源。 流程如下:
“小王,感覺(jué)怎么樣呢, 我是盡最大努力了哈”我說(shuō)。 “可以,可以,感覺(jué)挺好,我覺(jué)得哈,你要是多畫(huà)圖,我就沒(méi)問(wèn)題,就像上邊三個(gè)圖一樣”小王調(diào)皮的說(shuō)。 |
|