Linux內(nèi)核中_IO,_IOR,_IOW,_IOWR宏的用法與解析
在驅(qū)動(dòng)程序里,ioctl()
函數(shù)上傳送的變量 cmd 是應(yīng)用程序用于區(qū)別設(shè)備驅(qū)動(dòng)程序請(qǐng)求處理內(nèi)容的值。
cmd除了可區(qū)別數(shù)字外,還包含有助于處理的幾種相應(yīng)信息。
cmd的大小為32位,共分4個(gè)域:
bit31~bit30: 2位為 “區(qū)別讀寫” 區(qū),作用是區(qū)分是讀取命令還是寫入命令。
bit29~bit15: 14位為 "數(shù)據(jù)大小" 區(qū),表示 ioctl() 中的 arg 變量傳送的內(nèi)存大小。
bit20~bit08: 8位為 “魔數(shù)"(也稱為"幻數(shù)")區(qū),這個(gè)值用以與其它設(shè)備驅(qū)動(dòng)程序的 ioctl 命令進(jìn)行區(qū)別。
bit07~bit00: 8位為 "區(qū)別序號(hào)" 區(qū),是區(qū)分命令的命令順序序號(hào)。
像 命令碼中的 “區(qū)分讀寫區(qū)” 里的值可能是:
_IOC_NONE (0值)表示無數(shù)據(jù)傳輸,
_IOC_READ (讀),
_IOC_WRITE (寫),
_IOC_READ|_IOC_WRITE (雙向)。
內(nèi)核定義了?_IO(), _IOR(), IOW() 和 _IOWR()
?這 4 個(gè)宏來輔助生成上面的 cmd 。
下面分析 _IO() 的實(shí)現(xiàn),其它的類似:
在asm-generic/ioctl.h
里可以看到_IO()
的定義:
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
再看_IOC()
的定義:
可見,_IO() 的最后結(jié)果由 _IOC() 中的 4 個(gè)參數(shù)移位組合而成。
再看_IOC_DIRSHIT
的定義:
#define _IOC_DIRSHIFT ? ?(_IOC_SIZESHIFT+_IOC_SIZEBITS)
_IOC_SIZESHIFT
?的定義:
#define _IOC_SIZESHIFT ? ?(_IOC_TYPESHIFT+_IOC_TYPEBITS)
_IOC_TYPESHIF
的定義:
#define _IOC_TYPESHIFT ? ?(_IOC_NRSHIFT+_IOC_NRBITS)
_IOC_NRSHIFT
的定義:
#define _IOC_NRSHIFT ? ?0
_IOC_NRBITS
的定義:
#define _IOC_NRBITS ? ?8
_IOC_TYPEBITS
的定義:
#define _IOC_TYPEBITS ? ?8
由上面的定義,往上推得到:
_IOC_TYPESHIFT = 8
_IOC_SIZESHIFT = 16
_IOC_DIRSHIFT = 30
所以,
((dir) << _IOC_DIRSHIFT)
表示 dir 往左移 30 位,即移到 bit31~bit30 兩位上,得到方向(讀寫)的屬性;
((size) << _IOC_SIZESHIFT)
左移 16 位得到“數(shù)據(jù)大小”區(qū);
((type) << _IOC_TYPESHIFT)
左移 8 位得到"魔數(shù)區(qū)" ;
((nr) << _IOC_NRSHIFT)
?左移 0 位( bit7~bit0) 。
這樣,就得到了_IO()
的宏值。
這幾個(gè)宏的使用格式為:
_IO (魔數(shù), 基數(shù));
_IOR (魔數(shù), 基數(shù), 變量型)
_IOW (魔數(shù), 基數(shù), 變量型)
_IOWR (魔數(shù), 基數(shù),變量型)
魔數(shù) (magic number)
魔數(shù)范圍為 0~255。通常,用英文字符 "A" ~ "Z" 或者 "a" ~ "z" 來表示。
設(shè)備驅(qū)動(dòng)程序從傳遞進(jìn)來的命令獲取魔數(shù),然后與自身處理的魔數(shù)想比較,如果相同則處理,不同則不處理。
魔數(shù)是拒絕誤使用的初步輔助狀態(tài)。
設(shè)備驅(qū)動(dòng)程序可以通過_IOC_TYPE(cmd)
來獲取魔數(shù)。
不同的設(shè)備驅(qū)動(dòng)程序最好設(shè)置不同的魔數(shù),但并不是要求絕對(duì),也是可以使用其他設(shè)備驅(qū)動(dòng)程序已用過的魔數(shù)。
基(序列號(hào))數(shù)
基數(shù)用于區(qū)別各種命令。通常,從0開始遞增,相同設(shè)備驅(qū)動(dòng)程序上可以重復(fù)使用該值。
例如,讀取和寫入命令中使用了相同的基數(shù),設(shè)備驅(qū)動(dòng)程序也能分辨出來,原因在于設(shè)備驅(qū)動(dòng)程序區(qū)分命令時(shí) 使用switch,且直接使用命令變量cmd值。
創(chuàng)建命令的宏生成的值由多個(gè)域組合而成,所以即使是相同的基數(shù),也會(huì)判斷為不同的命令。
設(shè)備驅(qū)動(dòng)程序想要從命令中獲取該基數(shù),就使用下面的宏:
_IOC_NR(cmd)
通常,switch 中的 case 值使用的是命令的本身。
變量型
變量型使用 arg 變量指定傳送的數(shù)據(jù)大小,但是不直接代入輸入,而是代入變量或者是變量的類型,原因是在使用宏創(chuàng)建命令,已經(jīng)包含了sizeof()
編譯命令。
比如_IOR()
宏的定義是:
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
而_IOC_TYPECHECK()
的定義正是:
#define _IOC_TYPECHECK(t) (sizeof(t))
設(shè)備驅(qū)動(dòng)程序想要從傳送的命令獲取相應(yīng)的值,就要使用下列宏函數(shù):
_IOC_SIZE(cmd)
_IO 宏
該宏函數(shù)沒有可傳送的變量,只是用于傳送命令。例如如下約定:
#define TEST_DRV_RESET _IO ('Q', 0)
此時(shí),省略由應(yīng)用程序傳送的 arg 變量或者代入 0 。
在應(yīng)用程序中使用該宏時(shí),比如:
ioctl (dev, TEST_DEV_RESET, 0) 或者 ioctl (dev, TEST_DRV_RESET) 。
這是因?yàn)樽兞康挠行б蛩厥强勺円蛩?。只作為命令使用時(shí),沒有必要判斷出設(shè)備上數(shù)據(jù)的輸出或輸入。
因此,設(shè)備驅(qū)動(dòng)程序沒有必要執(zhí)行設(shè)備文件打開選項(xiàng)的相關(guān)處理。
_IOR 宏
該函數(shù)用于創(chuàng)建從設(shè)備讀取數(shù)據(jù)的命令,例如可如下約定:
#define TEST_DEV_READ _IRQ('Q', 1, int)
這說明應(yīng)用程序從設(shè)備讀取數(shù)據(jù)的大小為int。
下面宏用于判斷傳送到設(shè)備驅(qū)動(dòng)程序的 cmd 命令的讀寫狀態(tài):
_IOC_DIR(cmd)
運(yùn)行該宏時(shí),返回值的類型 如下:
_IOC_NONE : 無屬性
_IOC_READ : 可讀屬性
_IOC_WRIT : 可寫屬性
_IOC_READ | _IOC_WRITE : 可讀,可寫屬性
使用該命令時(shí),應(yīng)用程序的ioctl() 的 arg 變量值指定設(shè)備驅(qū)動(dòng)程序上讀取數(shù)據(jù)時(shí)的緩存(結(jié)構(gòu)體)地址。
_IOW 宏
用于創(chuàng)建設(shè)備上寫入數(shù)據(jù)的命令,其余內(nèi)容與 _IOR 相同。
通常,使用該命令時(shí),ioctl() 的arg 變量值指定設(shè)備驅(qū)動(dòng)程序上寫入數(shù)據(jù)時(shí)的緩存(結(jié)構(gòu)體)地址。
_IOWR 宏
用于創(chuàng)建設(shè)備上讀寫數(shù)據(jù)的命令。其余內(nèi) 容與 _IOR 相同。
通常,使用該命令時(shí),ioctl() 的 arg 變量值指定設(shè)備驅(qū)動(dòng)程序上寫入或讀取數(shù)據(jù)時(shí)的緩存(結(jié)構(gòu)體)地址。
_IOR() , _IOW(), IORW() 的定義: