【linux驅動分析】ioctl函數的使用

一、用戶空間的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 check
include/ARCH/ioctl.h for specifics, e.g. powerpc
uses 3 bits to encode read/write and 13 bits for size.

 bits meaning
 31-30 00 - no parameters: uses _IO macro
       10 - read: _IOR
       01 - write: _IOW
       11 - read/write: _IOWR

 29-16    size of arguments

 15-8      ascii character supposedly
       unique to each driver

 7-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;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章