ioctl
驅動程序可以使用ioctl執行硬件控制。
兩種原型:
1.在用戶空間
int ioctl(int fd,unsigned long cmd,...);
fd:文件描述符
cmd:控制命令
,,,:可選參數:插入*argp,具體內容依賴於cmd
2.驅動程序
int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
inode與filp兩個指針對應於應用程序傳遞的文件描述符fd,這和傳遞open方法的參數一樣。
cmd 由用戶空間直接不經修改的傳遞給驅動程序
arg 可選
cmd:
四個位字段:type,number,direction,size
type:幻數,8位
number:序數,8位
direction:涉及內容包括_IOC_NONE(無數據傳輸),_IOC_READ(從設備中讀),_IOC_WRITE,_IOC_READ|_IOC_WRITE(雙向數據傳輸)
size:表示所涉及的用戶數據大小,通常爲13位或是14位,具體可通過宏_IOC_SIZEBITS找到針對特定體系結構的具體數值。內核不會檢查這個位字段,對該字段的檢查可以幫助我們檢測用戶空間的錯誤。
另外,<asm/ioctl.h>定義了一些構造命令編號的宏,
_IOR(type,nr,datetype) 構造從驅動程序中讀取數據的命令
_IO(type,nr) 用於構造無參數的命令編號
_IOW(type,nr,datetype) 用於寫入命令的編號
_IOWR(type,nr,datatype) 雙向傳輸
type,number通過參數傳入,size通過對datatype參數取sizeof獲取
還有一些解開位字段的宏:_IOC_DIR(nr),_IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)
對非法的ioctl命令一般會返回-EINVAL
預定義命令
在使用ioctl命令編號時,一定要避免與預定義命令重複,否則,命令衝突,設備不會響應
下列ioctl命令對任何文件(包括設備特定文件)都是預定義的:
FIOCTLX 設置執行時關閉標誌
FIONCLEX 清除執行時關閉標誌
FIOASYNC 設置或復位文件異步通知
FIOQSIZE 返回文件或目錄大小
FIONBIO 文件非阻塞型IO,file ioctl non-blocking i/o
如何使用ioctl的附加參數:arg
1:arg是個整數,那簡單,直接用
2:arg是個指針,麻煩點,需檢測後才能用
分析:使用指針,首先得保證指針指向的地址合法。因此,在使用這個指針之前,我們應該使用
<asm/uaccess.h>中聲明的 int access_ok(int type,const void *addr,unsigend long size) 返回值爲1(成功)或0(失敗),如果返回失敗,驅動程序通常返回-EFAULT給調用者。
來驗證地址的合法性。
type: VERIFY_READ 或是 VERIFY_WRITE,取決於是讀取還是寫入用戶空間內存區。
addr: 一個用戶空間的地址
size: 如果要讀取或寫入一個int型數據,則爲sizeof(int)
如果在該地址處既要讀取,又要寫入,則應該用:VERIFY_WRITE,因爲它是VERIFY_READ的超集
注意:首先, access_ok不做校驗內存存取的完整工作; 它只檢查內存引用是否在這個進程有合理權限的內存範圍中,且確保這個地址不指向內核空間內存。其次,大部分驅動代碼不需要真正調用 access_ok,而直接使用put_user(datum, ptr)和get_user(local, ptr),它們帶有校驗的功能,確保進程能夠寫入給定的內存地址,成功時返回 0, 並且在錯誤時返回 -EFAULT.。
使用舉例:
int err=0,tmp; int retval; /*抽取類型和編號位字段,並拒絕錯誤的命令號:在調用access_ok之前返回ENOTTY(不恰當的ioctl)*/ if(_IOC_TYPE(cmd)!=SCULL_IOC_MAGIC) return -ENOTTY; if(_IOC_NR(cmd)>SCULL_IOC_MAXNR) return -ENOTTY;
/*方向是一個位掩碼,而VERIFY_WRITE用於R/W*傳輸。“類型”是針對用戶空間而言,而access_ok面向內核,因此,讀取和寫入,恰好相反*/ if(_IOC_DIR(cmd) & _IOC_READ) err=!access_ok(VERIFY_WRITE,(void __user *)arg,_IOC_SIZE(cmd)); else if(_IOC_DIR(cmd) & _IOC_WRITE) err=!access_ok(VERIFY_READ,(void __user *)arg,_IOC_SIZE(cmd)); if(err) return -EFAULT; |
<asm/uaccess.h>
put_user(datum,ptr);
__put_user(datum,ptr);
使用時,速度快,不做類型檢查,使用時可以給ptr傳遞任意類型的指針參數,只要是個用戶空間的地址就行,傳遞的數據大小依賴於ptr參數的類型。
put_user vs __put_user:使用前做的檢查,put_user多些,__put_user少些,
一般用法:實現一個讀取方法時,可以調用__put_user來節省幾個時鐘週期,或者在複製多項數據之前調用一次access_ok,像上面代碼一樣。
get_user(datum.ptr);
__get_user(datum,ptr);
接收的數據被保存在局部變量local中,返回值說明其是否正確。同樣,__get_user應該在操作地址被access_ok後使用。
權能與受限操作
來由:驅動程序必須進行附加的檢查以確認用戶是否有權進行請求的操作
權能作用:基於權能的系統拋棄了那種要麼全有,要麼全無的特權分配方式,而是把特權操作劃分成了獨立的組。
<linux/capability.h>
int capable(int capability);
在執行一項特權之前,應先檢查其是否具有這個權利
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
|
switch(cmd) {
/*
}
|