IOCTL函式用法詳解
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被分為了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) //無資料傳輸 |
相對的,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是一個整數,可以直接使用;
如果是指標,我們必須確保這個使用者地址是有效的,因此,使用之前需要進行正確檢查。
內部有檢查的,
不需要檢測的:
- copy_from_user
- copy_to_user
- get_user
- put_user
需要檢測的: [cpp] view plain copy print?
- __get_user
- __put_user