一、用戶空間的ioctl
int ioctl(int fd, unsigned long cmd, void *data);
第一個參數是文件描述符,第二個參數代表傳遞的命令,它會原樣傳遞給驅動,第三個參數是可選類型的,主要根據第二個參數選擇,第三個參數無論是整數還是指針,都會以unsigned long的形式傳遞給驅動程序。
二、內核空間的ioctl
1、參數的定義
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);
這是linux-2.6.38中的定義,與以前的不太一樣,它是三個參數。它定義在file_operations結構體中,vfs.txt這樣說:
unlocked_ioctl: called by the ioctl(2) system call.
第二個參數cmd是一個32位的整數,關於cmd各個位的含義可在Documentation\ioctl\ioctl-decoding.txt中找到:
*********************************************************************************************************
*********************************************************************************************************To decode a hex IOCTL code:Most architectures use this generic format, but checkinclude/ARCH/ioctl.h for specifics, e.g. powerpcuses 3 bits to encode read/write and 13 bits for size.bits meaning31-30 00 - no parameters: uses _IO macro10 - read: _IOR01 - write: _IOW11 - read/write: _IOWR29-16 size of arguments15-8 ascii character supposedlyunique to each driver7-0 function #So for example 0x82187201 is a read with arg length of 0x218,character 'r' function 1. Grepping the source reveals this is:#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
0~7位爲type(幻數),8~15位爲number(序數,順序編號),16~29位爲size(用戶數據的大小,13或者14位),30~31位爲direction(數據傳輸的方向,這裏的讀取是從用戶角度來說的)。
2、cmd參數的構造
可用宏來構造幻數(它們定義在<linux/ioctl.h>):
#ifndef _IOC_NONE
# define _IOC_NONE 0U
#endif
#ifndef _IOC_WRITE
# define _IOC_WRITE 1U
#endif
#ifndef _IOC_READ
# define _IOC_READ 2U
#endif
#define _IO(type, nr) _IOC(_IOC_NONE, (type), (nr), 0)
#define _IOR(type, nr, size) _IOC(_IOC_READ, (type), (nr), (_IOC_TYPECHECK(size)))
#define _IOW(type, nr, size) _IOC(_IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(size)))
#define _IOWR(type, nr, size) _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(size)))
注意:第三個參數爲要進行傳遞的參數的類型,比如int或者struct foo,不要使用sizeof(arg)作爲第三個參數,因爲ioctl可能會認爲你傳遞給他的是size_t的類型。
幻數可以選擇一個字母,但是要注意不能重複,可以查看Documentation\ioctl\ioctl-number.txt
比如'k',在ioctl-number.txt文件裏有說明(不過這個是x86平臺的):
'k' 00-0F linux/spi/spidev.h conflict!
'k' 00-05 video/kyro.h conflict!
說以上面出現的序號我們就不能再用了。
3、使用實例
下面的代碼示例是用來檢測cmd的正確性和用戶空間對應的地址是否可讀寫:
access_ok定義在<arch/arm/include/asm/uaccess.h>中,使用時要包含<asm/uaccess.h>這個頭文件。
if(_IOC_TYPE(cmd) != POWMAN_MAGIC){
return -EINVAL;
}
if(_IOC_NR(cmd) > POWMAN_MAGIC_MAXNR){
return -EINVAL;
}
if (_IOC_DIR(cmd) & _IOC_WRITE)
if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd))){
return -EFAULT;
}
if (_IOC_DIR(cmd) & _IOC_READ)
if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd))){
return -EFAULT;
}
除了用copy_to_user和copy_from_user外,如果傳遞常用的數據大小(1、2、4、8字節)的單個數據可用下面的一組函數,它們速度相對較快。
它們定義在<arch/arm/include/asm/uaccess.h>中,使用時要包含<asm/uaccess.h>這個頭文件。
put_user(datum, ptr)
把datum傳遞到用戶空間,ptr指向用戶空間地址,如果ptr是字符指針,就傳遞1個字節,2、4、8字節類似。
成功返回0,出錯返回-EFAULT。
get_user(local, ptr)
從用戶空間接收一個數據,放在local變量裏。
實例:
if (get_user(val, (int __user *)arg))
return -EFAULT;