1. 程式人生 > >驅動中ioctl引數分析

驅動中ioctl引數分析

一、ioctl的簡介:

雖然在檔案操作結構體"structfile_operations"中有很多對應的裝置操作函式,但是有些命令是實在找不到對應的操作函式。如CD-ROM的驅動,想要一個彈出光碟機的操作,這種操作並不是所有的字元裝置都需要的,所以檔案操作結構體也不會有對應的函式操作。

出於這樣的原因,ioctl就有它的用處了————一些沒辦法歸類的函式就統一放在ioctl這個函式操作中,通過指定的命令來實現對應的操作。所以,ioctl函式裡面都實現了多個的對硬體的操作,通過應用層傳入的命令來呼叫相應的操作。

來個圖來說一下應用層與驅動函式的ioctl之間的聯絡:

上面的圖可以看出,fd

通過核心後找到對應的inodefile結構體指標並傳給驅動函式,而另外兩個引數卻沒有修改(型別改了沒什麼關係)

簡單介紹一下函式:

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一樣返回。當然,不返回也是可以的。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

       這裡主要說一下cmd命令,因為我在寫驅動的時候出錯,定義的cmd命令無法呼叫,網上各種查資料,才解決。。。。。。

Linux核心一般會自動地過濾到一些不合法的cmd定義,比如你自己定義的1,2,
cmd為1,2沒有type、沒有number,沒有direction,也沒有size,Linux核心自動過濾掉你的ioctl請求,你的ioctl根本就沒有到驅動ioctl上就被返回錯誤了。

二,ioctl引數cmd

一個cmd被分為了4個段,每一段都有各自的意義,cmd的定義在。注:但實際上中只是包含了,這說明了這是跟平臺相關的,ARM的定義在,但這檔案也是包含別的檔案,千找萬找,終於找到了。

在中,cmd拆分如下:

解釋一下四部分,全部都在和ioctl-number.txt這兩個文件有說明。

1)幻數:說得再好聽的名字也只不過是個0~0xff的數,佔8bit(_IOC_TYPEBITS)。這個數是用來區分不同的驅動的,像裝置號申請的時候一樣,核心有一個文件給出一些推薦的或者已經被使用的幻數。

/*Documentation/ioctl/ioctl-number.txt*/

164 'w' all CERN SCIdriver

165 'y' 00-1F packetbased user level communications

166

167 'z' 00-3F CAN buscard

168

169 'z' 40-7F CAN buscard

170

可以看到'x'是還沒有人用的,我就拿這個當幻數!

2)序數:用這個數來給自己的命令編號,佔8bit(_IOC_NRBITS),我的程式從1開始排序。

3)資料傳輸方向:佔2bit(_IOC_DIRBITS)。如果涉及到要傳參,核心要求描述一下傳輸的方向,傳輸的方向是以應用層的角度來描述的。

1)_IOC_NONE:值為0,無資料傳輸。

2)_IOC_READ:值為1,從裝置驅動讀取資料。

3)_IOC_WRITE:值為2,往裝置驅動寫入資料。

4)_IOC_READ|_IOC_WRITE:雙向資料傳輸。

4)資料大小:與體系結構相關ARM下佔14bit(_IOC_SIZEBITS),如果資料是int,核心給這個賦的值就是sizeof(int)

強調一下,核心是要求按這樣的方法把cmd分類,當然你也可以不這樣幹,這只是為了迎合核心的要求,讓自己的程式看上去很正宗。上面我的程式沒按要求照樣執行。

既然核心這樣定義cmd,就肯定有方法讓使用者方便定義:

_IO(type,nr) //沒有引數的命令

_IOR(type,nr,size) //該命令是從驅動讀取資料

_IOW(type,nr,size) //該命令是從驅動寫入資料

_IOWR(type,nr,size) //雙向資料傳輸

上面的命令已經定義了方向,我們要傳的是幻數(type)、序號(nr)和大小(size)。在這裡szie的引數只需要填引數的型別,如int,上面的命令就會幫你檢測型別的正確然後賦值sizeof(int)

有生成cmd的命令就必有拆分cmd的命令:

_IOC_DIR(cmd) //從命令中提取方向

_IOC_TYPE(cmd) //從命令中提取幻數

_IOC_NR(cmd) //從命令中提取序數

_IOC_SIZE(cmd) //從命令中提取資料大小

越講就越複雜了,既然講到這,隨便就講一下預定義命令。

預定義命令是由核心來識別並且實現相應的操作,換句話說,一旦你使用了這些命令,你壓根也不要指望你的驅動程式能夠收到,因為核心拿掉就把它處理掉了。

分為三類:

1)可用於任何檔案的命令

2)只用於普通檔案的命令

3)特定檔案系統型別的命令

其實上面的我三類我也沒搞懂,反正我自己隨便編了幾個數當命令都沒出錯,如果真的怕出錯,那就不要用別人已經使用的幻數就行了。

講了這麼多,終於要上程式了,修改一下上一個程式,讓它看起來比較有內涵。

/3rd_char/3rd_char_4/2nd

1)先改一下命令:

/*test_cmd.h*/

1 #ifndef _TEST_CMD_H

2 #define _TEST_CMD_H

3

4 #define TEST_MAGIC 'x' //定義幻數

5 #define TEST_MAX_NR 1 //定義命令的最大序數,只有一個命令當然是1

6

7 #define TEST_CLEAR _IO(TEST_MAGIC, 0)

8

9 #endif /*_TEST_CMD_H*/

2)既然這麼辛苦改了cmd,在驅動函式當然要做一些引數檢驗:

/*test.c*/

122 int test_ioctl (struct inode*node, struct file *filp, unsigned int cmd, unsigned long arg)

123 {

124 int ret = 0;

125 struct _test_t *dev =filp->private_data;

126

127 /*既然這麼費勁定義了命令,當然要檢驗命令是否有效*/

128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

129 if(_IOC_NR(cmd)> TEST_MAX_NR) return - EINVAL;

130

131 switch(cmd){

132 case TEST_CLEAR:

133 memset(dev->kbuf,0, DEV_SIZE);

134 dev->cur_size =0;

135 filp->f_pos = 0;

136 ret = 0;

137 break;

138 default: /*命令錯誤時的處理*/

139 P_DEBUG("errorcmd!\n");

140 ret = - EINVAL;

141 break;

142 }

143

144 return ret;

145 }

每個引數的傳入都會先檢驗一下幻數還有序數是否正確。

                                                                           -----lecho---------20150915-------------這個幻數的使用看,經過本人測試是,正確的