IOCTL函数用法详解
ioctl是設(shè)備驅(qū)動程序中對設(shè)備的I/O通道進(jìn)行管理的函數(shù)
。所謂對I/O通道進(jìn)行管理,就是對設(shè)備的一些特性進(jìn)行控制,例如串口的傳輸波特率、馬達(dá)的轉(zhuǎn)速等等。它的調(diào)用個數(shù)如下:?
int ioctl(int fd, ind cmd, …);?
??? 其中fd是用戶程序打開設(shè)備時使用open函數(shù)返回的文件標(biāo)示符,cmd是用戶程序?qū)υO(shè)備的控制命令,至于后面的省略號,那是一些補(bǔ)充參數(shù),一般最多一個,這個參數(shù)的有無和cmd的意義相關(guān)。?
??? ioctl函數(shù)是文件結(jié)構(gòu)中的一個屬性分量,就是說如果你的驅(qū)動程序提供了對ioctl的支持,用戶就可以在用戶程序中使用ioctl函數(shù)來控制設(shè)備的I/O通道。
簡單介紹一下函數(shù):
int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);
參數(shù):
1)inode和file:ioctl的操作有可能是要修改文件的屬性,或者訪問硬件。要修改
文件屬性的話,就要用到這兩個結(jié)構(gòu)體了,所以這里傳來了它們的指針。
2)cmd:命令,接下來要長篇大論地說。
3)arg:參數(shù),接下來也要長篇大論。
返回值:
1)如果傳入的非法命令,ioctl返回錯誤號-EINVAL。
2)內(nèi)核中的驅(qū)動函數(shù)返回值都有一個默認(rèn)的方法,只要是正數(shù),內(nèi)核就會傻乎乎的認(rèn)為這是正確的返回,并把它傳給應(yīng)用層,如果是負(fù)值,內(nèi)核就會認(rèn)為它是錯誤號了。
Ioctl里面多個不同的命令,那就要看它函數(shù)的實(shí)現(xiàn)來決定返回值了。打個比方,如果ioctl里面有一個類似read的函數(shù),那返回值也就可以像read一樣返回。
當(dāng)然,不返回也是可以的。
ioctl如何實(shí)現(xiàn)
????在驅(qū)動程序中實(shí)現(xiàn)的ioctl函數(shù)體內(nèi),實(shí)際上是有一個switch{case}結(jié)構(gòu),每一個case對應(yīng)一個命令碼,做出一些相應(yīng)的操作。怎么實(shí)現(xiàn)這些操作,這是應(yīng)用程序自己的事情。
在ioctl中命令碼是唯一聯(lián)系用戶程序命令和驅(qū)動程序支持的途徑。
如果有兩個不同的設(shè)備,但它們的ioctl的cmd(命令碼)卻一樣的,哪天有誰不小心打開錯了,并且調(diào)用ioctl,這樣就完蛋了。因?yàn)檫@個文件里面同樣有cmd對應(yīng)實(shí)現(xiàn),故,我們可以自己生成未使用的命令碼。
所以在Linux核心中是這樣定義一個命令碼的。
一個cmd被分為了4個段,每一段都有各自的意義,cmd的定義在<linux/ioctl.h>。注:但實(shí)際上<linux/ioctl.h>中只是包含了<asm/ioctl.h>,這說明了這是跟平臺相關(guān)的,ARM的定義在<arch/arm/include/asm/ioctl.h>,但這文件也是包含別的文件<asm-generic/ioctl.h>,千找萬找,終于找到了。
在<asm-generic/ioctl.h>中,cmd拆分如下:
全部都在
<asm-generic/ioctl.h>
和
ioctl-number.txt
這兩個文檔有說明
http:/..../linux/include/asm-generic/ioctl.h
?
#define _IOC(dir,type,nr,size) \
??
????
??
(((dir) ?<< _IOC_DIRSHIFT) | \
????
????
((type) << _IOC_TYPESHIFT) | \
???
????
?
((nr) ? << _IOC_NRSHIFT) | \
???
????
?
((size) << _IOC_SIZESHIFT))
____________________________________
| 設(shè)備類型 | 序列號 | 方向 |數(shù)據(jù)尺寸|
|----------|--------|------|--------|
| 8 bit | 8 bit |2 bit |8~14 bit|
|----------|--------|------|--------|
這樣一來,一個命令就變成了一個整數(shù)形式的命令碼;但是命令碼非常的不直觀,所以Linux Kernel中提供了一些宏。這些宏可根據(jù)便于理解的字符串生成命令碼,或者是從命令碼得到一些用戶可以理解的字符串以標(biāo)明這個命令對應(yīng)的設(shè)備類型、設(shè)備序列號、數(shù)據(jù)傳送方向和數(shù)據(jù)傳輸尺寸。
????幻數(shù):說得再好聽的名字也只不過是個0~0xff的數(shù),占8bit(_IOC_TYPEBITS)。這個數(shù)是用來區(qū)分不同的驅(qū)動的,像設(shè)備號申請的時候一樣,內(nèi)核有一個文檔給出一些推薦的或者已經(jīng)被使用的幻數(shù)。在內(nèi)核文件中定義如下:
Ioctl-number.txt (f:\sourceproject\linux-kernel\linux-3.14.26-g2489c02\documentation\ioctl)
點(diǎn)擊(此處)折疊或打開
- Code Seq#(hex)????Include File????????Comments
- ========================================================
- 0x00????00-1F????linux/fs.h????????
- 0x00????00-1F????scsi/scsi_ioctl.h????
- 0x00????00-1F????linux/fb.h????????
- 0x00????00-1F????linux/wavefront.h????
- 0x02????all????linux/fd.h
- 0x03????all????linux/hdreg.h
- 0x04????D2-DC????linux/umsdos_fs.h????Dead since 2.6.11,?but don't reuse these.
- 0x06????all????linux/lp.h
- 0x09????all????linux/raid/md_u.h
- 0x10????00-0F????drivers/char/s390/vmcp.h
- 0x10????10-1F????arch/s390/include/uapi/sclp_ctl.h
- 0x10????20-2F????arch/s390/include/uapi/asm/hypfs.h
- 0x12????all????linux/fs.h
- ????????linux/blkpg.h
- 0x1b????all????InfiniBand Subsystem????<http://infiniband.sourceforge.net/>
- 0x20????all????drivers/cdrom/cm206.h
- 0x22????all????scsi/sg.h
- '#'????00-3F????IEEE 1394 Subsystem????Block?for?the entire subsystem
- '$'????00-0F????linux/perf_counter.h,?linux/perf_event.h
- .....................
- ....................
四、CMD參數(shù)如何得出
????cmd參數(shù)在用戶程序端由一些宏根據(jù)設(shè)備類型、序列號、傳送方向、數(shù)據(jù)尺寸等生成,這個整數(shù)通過系統(tǒng)調(diào)用傳遞到內(nèi)核中的驅(qū)動程序,再由驅(qū)動程序使用解碼宏從這個整數(shù)中得到設(shè)備的類型、序列號、傳送方向、數(shù)據(jù)尺寸等信息,然后通過switch{case}結(jié)構(gòu)進(jìn)行相應(yīng)的操作。
????Linux內(nèi)核已經(jīng)提供了相應(yīng)的宏來自動生成ioctl命令碼:
|
_IO(type,nr) ??//無數(shù)據(jù)傳輸 |
????上面的命令已經(jīng)定義了方向,我們要傳的是幻數(shù)(type)、序號(nr)和大小(size)。在這里szie的參數(shù)只需要填參數(shù)的類型,如int,上面的命令就會幫你檢測類型的正確然后賦值sizeof(int)。????
????相對的,Linux內(nèi)核也提供了相應(yīng)的宏來從ioctl命令號種解碼相應(yīng)的域值:
|
_IOC_DIR(nr)??//從命令中提取方向 |
例:
/*include_cmd.hpp*/
#define LED_IOC_MAGIC 0x13 ?//定義幻數(shù)
#define LED_MAX_NR ? ?3 ? ? ? ? ?//定義命令的最大序數(shù)
#define?LED_GPRS_MAGIC?_IO(LED_IOC_MAGIC,0x00) ?//0x00 ?用”宏+幻數(shù)“來自動生成ioctl命令碼
#define LED_WIFI_MAGIC _IO(LED_IOC_MAGIC,0x01) ?//0x00
#define LED_BT_MAGIC _IO(LED_IOC_MAGIC,0x02) ?//0x00
/*test.cpp*/
fd = open();
ioctl(fd,LED_GPRS_MAGIC,0);
ioctl(fd,LED_GPRS_MAGIC,1);
ioctl(fd,LED_WIFI_MAGIC?,0);
ioctl(fd,LED_WIFI_MAGIC?,1);
/*test_ioctl.c*/
int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
{
? ? if(_IOC_TYPE(cmd) !=LED_IOC_MAGIC?) return -EINVAL;? ?//提取出幻數(shù)做檢驗(yàn)
????if(_IOC_NR(cmd) >?LED_MAX_NR?) return?-EINVAL; ? ? ? ? ?//提取命令序數(shù)
? ? switch(cmd){
? ? case?LED_GPRS_MAGIC:
? ? ?if(arg==0){
? ? //..........
????}else if(arg ==1){
????//..........
????}
? ? break;
? ? case?LED_WIFI_MAGIC:
????//..........
? ? break;
????}
}
arg參數(shù):
如果arg是一個整數(shù),可以直接使用;
如果是指針,我們必須確保這個用戶地址是有效的,因此,使用之前需要進(jìn)行正確檢查。
內(nèi)部有檢查的,
不需要檢測的:
[cpp]?view plain?copy?
- copy_from_user??
- copy_to_user??
- get_user??
- put_user??
需要檢測
的:
[cpp]?view plain?copy?
- __get_user??
- __put_user?
總結(jié)
以上是生活随笔為你收集整理的IOCTL函数用法详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FriendHosting系统管理员日促
- 下一篇: 认识emu8086仿真内存