IOCTL
除了讀取和寫入設(shè)備之外炭序,大部分驅(qū)動(dòng)程序還需要另外一種能力,即通過(guò)設(shè)備驅(qū)動(dòng)程序執(zhí)行各種類型的硬件控制。簡(jiǎn)單的數(shù)據(jù)傳輸之外脖卖,大部分設(shè)備可以執(zhí)行其他一些操作袱吆,比如用戶空間經(jīng)常會(huì)要求設(shè)備鎖門、彈出介質(zhì)辜纲、報(bào)告錯(cuò)誤信息笨觅、改變波特率或者執(zhí)行自破壞拦耐,等等。這些操作通常通過(guò)ioctl方法支持见剩,該方法實(shí)現(xiàn)了同名的系統(tǒng)的調(diào)用揩魂。
用戶空間
int ioctl(int fd,unsigned long cmd,...);
這里用點(diǎn)只是為了在編譯時(shí)防止編譯器進(jìn)行類型檢查。第三個(gè)參數(shù)的具體形式依賴于要完成的控制命令炮温,也就是第二個(gè)參數(shù)火脉。有些控制命令不要參數(shù),有些需要一個(gè)整數(shù)參數(shù)柒啤,某些需要一個(gè)指針參數(shù)倦挂。使用指針可以向ioctl調(diào)用傳遞任意數(shù)據(jù),這樣設(shè)備可以與用戶空間交換任意數(shù)量的數(shù)據(jù)担巩。
本質(zhì)上說(shuō)每一個(gè)ioctl就是一個(gè)獨(dú)立地系統(tǒng)調(diào)用方援,而且是非公開的。有些需求需要我們通過(guò)其他途徑實(shí)現(xiàn)繁雜的控制操作涛癌,可能的方式包括:將命令嵌入到數(shù)據(jù)流中犯戏,或者使用虛擬文件系統(tǒng),比如sysfs或者設(shè)備相關(guān)的文件系統(tǒng)拳话。但是對(duì)于真正的設(shè)備操作來(lái)說(shuō)先匪,ioctl仍然是最簡(jiǎn)單且最有效的選擇。
驅(qū)動(dòng)程序的ioctl方法原型和用戶空間的版本存在有些不同:int(*ioctl)(struct inode *inode,struct file *flip,unsigned int cmd,unsigned long arg);
inode和flip兩個(gè)指針的值對(duì)應(yīng)于應(yīng)用程序傳遞的文件描述符fd弃衍,這和傳給open方法的參數(shù)一樣呀非。參數(shù)cmd由用戶不經(jīng)修改地傳遞給驅(qū)動(dòng)程序,可選的arg參數(shù)則無(wú)論用戶使用的是指針還是整數(shù)值镜盯,都會(huì)以u(píng)nsigned long的形式傳遞給驅(qū)動(dòng)程序岸裙,如果用戶空間調(diào)用程序沒(méi)有傳遞第三個(gè)參數(shù),那么驅(qū)動(dòng)程序所接受的arg參數(shù)就處在未定義狀態(tài)速缆。由于對(duì)這個(gè)附加參數(shù)的類型檢查就被關(guān)閉了降允,所以如果為ioctl傳遞一個(gè)非法參數(shù),編譯器是無(wú)法報(bào)警的艺糜,這樣相關(guān)聯(lián)的程序錯(cuò)誤就很難被發(fā)現(xiàn)剧董。
ioctl實(shí)現(xiàn)都包括一個(gè)switch語(yǔ)句來(lái)根據(jù)cmd參數(shù)選擇對(duì)應(yīng)的操作。不同的命令被賦予不同的數(shù)值倦踢,為了簡(jiǎn)化代碼送滞,通常會(huì)在代碼中使用符號(hào)名代替數(shù)值,這些符號(hào)名由C的預(yù)處理語(yǔ)句定義辱挥。定制的設(shè)備驅(qū)動(dòng)程序通常會(huì)在它們的頭文件中聲明這些符號(hào)犁嗅,如scull中聲明了所使用的符號(hào)。為了訪問(wèn)這些符號(hào)晤碘,用戶程序自然也要包含這些頭文件褂微。
選擇ioctl命令為了選擇對(duì)錯(cuò)誤的設(shè)備使用正確的命令功蜓,命令好應(yīng)該在系統(tǒng)范圍內(nèi)唯一。這種錯(cuò)誤匹配并不是不會(huì)發(fā)生宠蚂,程序可能發(fā)現(xiàn)自己正在試圖對(duì)FIFO和audio等非串行設(shè)備輸入流修改波特率式撼。如果每一個(gè)ioctl命令是唯一的,應(yīng)用程序進(jìn)行這種操作時(shí)就會(huì)得到一個(gè)EINVAL錯(cuò)誤求厕,而不是無(wú)意間成功的完成了意想不到的操作著隆。
要按照l(shuí)inux內(nèi)核的約定的方法為ioctl編號(hào),應(yīng)該首先看看include/asm/ioctl.h和Document/ioctl-number.txt這兩個(gè)文件呀癣。頭文件規(guī)定了要使用的位段:類型(幻數(shù))美浦、序數(shù)、傳送方向以及參數(shù)大小等等项栏。ioctl-number.txt文件中羅列了內(nèi)核所使用的幻數(shù)浦辨,這樣,在選擇自己的幻數(shù)時(shí)就可以避免和內(nèi)和沖突沼沈。這個(gè)文件也給出了為什么應(yīng)該使用這個(gè)約定的原因流酬。
以上來(lái)自LDD3
/*
* The original linux ioctl numbering scheme was just a general
* "anything goes" setup, where more or less random numbers were
* assigned.? Sorry, I was clueless when I started out on this.
*
* On the alpha, we'll try to clean it up a bit, using a more sane
* ioctl numbering, and also trying to be compatible with OSF/1 in
* the process. I'd like to clean it up for the i386 as well, but
* it's so painful recognizing both the new and the old numbers..
*/
最初linux ioctl命令號(hào)命令僅僅是一個(gè)通用的“怎么寫都行”的初始版本,使用了很多隨機(jī)的分配的數(shù)字列另。抱歉芽腾,我開始的時(shí)候毫無(wú)頭緒。
在alpha上访递,我們?cè)噲D清理一點(diǎn)晦嵌,使用更清楚點(diǎn)的ioctl的序列號(hào),并且也試圖努力和OSF/1在處理上兼容拷姿。我也愿意清理一下i386,但是同時(shí)識(shí)別老的和新的序列號(hào)實(shí)在是非常痛苦的。
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 13
#define _IOC_DIRBITS 3
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
/*
* Direction bits _IOC_NONE could be 0, but OSF/1 gives it a bit.
* And this turns out useful to catch old ioctl numbers in header
* files for us.
*/
#define _IOC_NONE 1U
#define _IOC_READ 2U
#define _IOC_WRITE 4U
#define _IOC(dir,type,nr,size) \
((unsigned int) \
(((dir)? << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr)? << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT)))
/* used to create numbers */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
/* used to decode them.. */
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
/* ...and for the drivers/sound files... */
#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
今天就先讀3頁(yè)書吧旱函,畢竟每天自己的時(shí)間也不是太多响巢,每天在公司做自己的事情,回到住處打開書看一會(huì)就已經(jīng)11點(diǎn)了棒妨,考慮明天把書帶到公司踪古。明天繼續(xù),come on券腔!