IOCTL函数用法详解
ioctl是設備驅動程序中對設備的I/O通道進行管理的函數
。所謂對I/O通道進行管理,就是對設備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉速等等。它的調用個數如下:?
int ioctl(int fd, ind cmd, …);?
??? 其中fd是用戶程序打開設備時使用open函數返回的文件標示符,cmd是用戶程序對設備的控制命令,至于后面的省略號,那是一些補充參數,一般最多一個,這個參數的有無和cmd的意義相關。?
??? ioctl函數是文件結構中的一個屬性分量,就是說如果你的驅動程序提供了對ioctl的支持,用戶就可以在用戶程序中使用ioctl函數來控制設備的I/O通道。
簡單介紹一下函數:
int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);
參數:
1)inode和file:ioctl的操作有可能是要修改文件的屬性,或者訪問硬件。要修改
文件屬性的話,就要用到這兩個結構體了,所以這里傳來了它們的指針。
2)cmd:命令,接下來要長篇大論地說。
3)arg:參數,接下來也要長篇大論。
返回值:
1)如果傳入的非法命令,ioctl返回錯誤號-EINVAL。
2)內核中的驅動函數返回值都有一個默認的方法,只要是正數,內核就會傻乎乎的認為這是正確的返回,并把它傳給應用層,如果是負值,內核就會認為它是錯誤號了。
Ioctl里面多個不同的命令,那就要看它函數的實現來決定返回值了。打個比方,如果ioctl里面有一個類似read的函數,那返回值也就可以像read一樣返回。
當然,不返回也是可以的。
ioctl如何實現
????在驅動程序中實現的ioctl函數體內,實際上是有一個switch{case}結構,每一個case對應一個命令碼,做出一些相應的操作。怎么實現這些操作,這是應用程序自己的事情。
在ioctl中命令碼是唯一聯系用戶程序命令和驅動程序支持的途徑。
如果有兩個不同的設備,但它們的ioctl的cmd(命令碼)卻一樣的,哪天有誰不小心打開錯了,并且調用ioctl,這樣就完蛋了。因為這個文件里面同樣有cmd對應實現,故,我們可以自己生成未使用的命令碼。
所以在Linux核心中是這樣定義一個命令碼的。
一個cmd被分為了4個段,每一段都有各自的意義,cmd的定義在<linux/ioctl.h>。注:但實際上<linux/ioctl.h>中只是包含了<asm/ioctl.h>,這說明了這是跟平臺相關的,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))
____________________________________
| 設備類型 | 序列號 | 方向 |數據尺寸|
|----------|--------|------|--------|
| 8 bit | 8 bit |2 bit |8~14 bit|
|----------|--------|------|--------|
這樣一來,一個命令就變成了一個整數形式的命令碼;但是命令碼非常的不直觀,所以Linux Kernel中提供了一些宏。這些宏可根據便于理解的字符串生成命令碼,或者是從命令碼得到一些用戶可以理解的字符串以標明這個命令對應的設備類型、設備序列號、數據傳送方向和數據傳輸尺寸。
????幻數:說得再好聽的名字也只不過是個0~0xff的數,占8bit(_IOC_TYPEBITS)。這個數是用來區分不同的驅動的,像設備號申請的時候一樣,內核有一個文檔給出一些推薦的或者已經被使用的幻數。在內核文件中定義如下:
Ioctl-number.txt (f:\sourceproject\linux-kernel\linux-3.14.26-g2489c02\documentation\ioctl)
點擊(此處)折疊或打開
- 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參數如何得出
????cmd參數在用戶程序端由一些宏根據設備類型、序列號、傳送方向、數據尺寸等生成,這個整數通過系統調用傳遞到內核中的驅動程序,再由驅動程序使用解碼宏從這個整數中得到設備的類型、序列號、傳送方向、數據尺寸等信息,然后通過switch{case}結構進行相應的操作。
????Linux內核已經提供了相應的宏來自動生成ioctl命令碼:
|
_IO(type,nr) ??//無數據傳輸 |
????上面的命令已經定義了方向,我們要傳的是幻數(type)、序號(nr)和大小(size)。在這里szie的參數只需要填參數的類型,如int,上面的命令就會幫你檢測類型的正確然后賦值sizeof(int)。????
????相對的,Linux內核也提供了相應的宏來從ioctl命令號種解碼相應的域值:
|
_IOC_DIR(nr)??//從命令中提取方向 |
例:
/*include_cmd.hpp*/
#define LED_IOC_MAGIC 0x13 ?//定義幻數
#define LED_MAX_NR ? ?3 ? ? ? ? ?//定義命令的最大序數
#define?LED_GPRS_MAGIC?_IO(LED_IOC_MAGIC,0x00) ?//0x00 ?用”宏+幻數“來自動生成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;? ?//提取出幻數做檢驗
????if(_IOC_NR(cmd) >?LED_MAX_NR?) return?-EINVAL; ? ? ? ? ?//提取命令序數
? ? switch(cmd){
? ? case?LED_GPRS_MAGIC:
? ? ?if(arg==0){
? ? //..........
????}else if(arg ==1){
????//..........
????}
? ? break;
? ? case?LED_WIFI_MAGIC:
????//..........
? ? break;
????}
}
arg參數:
如果arg是一個整數,可以直接使用;
如果是指針,我們必須確保這個用戶地址是有效的,因此,使用之前需要進行正確檢查。
內部有檢查的,
不需要檢測的:
[cpp]?view plain?copy?
- copy_from_user??
- copy_to_user??
- get_user??
- put_user??
需要檢測
的:
[cpp]?view plain?copy?
- __get_user??
- __put_user?
總結
以上是生活随笔為你收集整理的IOCTL函数用法详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FriendHosting系统管理员日促
- 下一篇: 认识emu8086仿真内存