ioctl 详细介绍

来源:互联网 发布:fedora13 yum源 编辑:程序博客网 时间:2024/06/07 10:39
 

ioctl 详细介绍

(一)ioctl 的作用:

       通过设备驱动程序执行各种类型的硬件控制。除了简单数据传输外,大部分设备可以执行其他的一些操作,比如,用户空间经常请求设备锁门、弹出介质、报告错误信息、改变波特率或者执行自破坏等等。

       Ioctl的操作通过流程图简言之:

    
从图1可知,user mode commandkernel mode,然后控制硬件。

(二)ioctl的函数模型:

User mode (用户空间)

     int ioctl(int fd, unsigned long cmd, …);

其中...省略号表示可变参数,cmd表示指令,fd表示应用程序传递的文件描述符。

Kernel mode (内核空间)

    int ( *ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

   inode filp两个指针的值对应于fd cmduser mode cmd相同、arg是附加参数,可以设置为Null或者变量,也可以是指针。

 

() ioctl command解析:

   Ioctl command编号的约定方法:

名称

Type

Number

Direction

size

位数

8bits

8bits

2bits

13/14bits

 

    Type:又称幻数,选择一个号码,并在整个驱动中使用这个号码,这个字段为8bits

    Number:序数(顺序编号)。也为8bits

    Direction: 如果command涉及数据传输,则该字段定义数据传输的方向。可以使用的值包括_IOC_NONE(没有数据传输), _IOC_READ(从设备中读取数据),_IOC_WRITE(向设备中传人数据)_IOC_WRITE|_IOC_READ(双向传输数据)。该段占2bits

Size:涉及用户数据的大小,该段占13/14bits

  内核定义一些生产command的宏:

_IO()_IOR(), _IOW, _IOWR.

 

#define _IOC(dir, type, nr, size) (((dir) <<_IOC_DIRSHIFT) \ 

|((type) <<_IOC_TYPESHIFT)) \

|((nr) <<_IOC_NRSHIFT) \

|((size)<< _OC_SIZESHIFT))

#define _IO(type, nr)  _IOC(_IOC_NONE, type, nr, 0)

#define _IOR(type, nr, size) _IOC(_IOC_READ, (type), (nr), \

(_IOC_TYPECHECKSIZE))

#define _IOW(type, nr, size) _IOC(_IOC_WRITE, (type), (nr), \

(_IOC_TYPECHECKSIZE))

#define _IOWR(type, nr, size) _IOC(_IOC_WRITE |_IOC_READ , (type), (nr), \

(_IOC_TYPECHECKSIZE))

 

_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd)_IOC_SIZE(cmd), 这几个宏用于解开位字段的宏。

type是幻数不是类型,其中涉及一些宏在此没有明说,可以参考linux内核docment

 

()ioctl的返回值:

    Ioctl函数的返回值目前有两种版本,其一返回-ENVAL (Invalid argument, 非法参数),因为命令参数确实不合法,所以范围该这是合理的。另一种返回值为-ENTTY(Inappropriate ioctl for device, 不合适的设备ioctl),这看起来更加确切。但实际中,更多的返回-ENVAL

 

()ioctl 函数kernel mode实现例子

   

   在本例子中假设一种设备device,通过ioctl实现设置、获取device模块的属性值,其设备驱动的ioctl()函数如下:

 

#define DEVICE_IOC_MAGIC    ‘k’

#define DEVICE_IOC_SET   _IOW(DEVICE_IOC_MAGIC, 1, int) //int is “size”

#define DEVICE_IOC_GET   _IOR(DEVICE_IOC_MAGIC, 2, int)

#define DEVICE_IOC_CLR   _IO(DEVICE_IOC_MAGIC,3, int)

#define DEVICE_IOC_MAXNR      5

static int device_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,

 unsigned long arg)

{

   int err = 0, temp = 0;

   int retval = 0;

 

   /*验证typenumber*/

   if(_IOC_TYPE(cmd)!= DEVICE_IOC_MAGIC)  return –ENOTTY;

   if(_IOC_NR(cmd)!= DEVICE_IOC_MAXNR)    return  –ENOTTY;

   

   /*direction 是一个位掩码,而VERIFY_WRITE用于R/W*传输

*type”是针对用户空间而言的,而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;

 

switch(cmd){

    

case DEVICE_IOC_SET:

   if(!capable(CAP_SYS_ADMIN))

      return –EPERM;

   retval = __get_user(device_value, (int __user*arg));

   break;

case DEVICE_IOC_GET:

   retval = __put_user(device_value, (int __user*)arg);

   break;

case DEVICE_IOC_CLR:

   memset(device->mem, 0, 512*sizeof(int));

   printk(KERN_INFO, “device memory is set to zero\n”);

   retval = 1;

   break;

default:

   return -EINVAL

}

}//end driver_ioctl funtion.

 

对函数access_ok()__get_user()__put_user()的模型说明:

    在ioctl函数中,如果附件参数是指针时,该指针指向用户空间时,必须确保指向的用户空间是合法的。对未验证的用户空间指针的访问,可能导致内核oops、系统崩溃或者安全问题。如果用copy_from_user()copy_to_user()进行用户空间和内核空间的数据传输时,用户空间地址已经得到验证,而对于用__get_user()__put_user进行用户空间和内核空间的数据传输时,指针指向的地址空间没有进行验证,因此需要用access_ok()函数进行验证。此时会产生一种疑问,为什么不用copy_from_user()copy_to_user()进行,下文可以获得解释。

        access_ok(int type, const void*addr, unsigned long size);

        type: 应该是VERIFY_READ或者VERIFY_WRITE,取决于要执行的动作是读取还是写入用户空间内存区。

        addr: 用户空间的地址。

        size: 字节数目。

       Return 返回一个布尔值:1表示成功,0表示失败。如果返回失败,驱动程序通常返回-EFAULT给调用者。

 

      put_user(datum, ptr)__put_user(datum, ptr)这宏用于把datum写入到ptr指向的用户空间,其传递的速度比较快,因此传递单个数据时,应该用这个宏而不用copy_to_user()copy_from_user()

     get_user(local, ptr)__get_user(local, ptr): 这宏用于从用户空间接受一个数据。接收的数值被保存在局部变量local中,返回值则指明了操作是否成功。因此put_user,__put_user,get_user以及__get_userptr指向的用户空间应该被access_ok检验后才使用。

 

()ioctl 函数User mode实现例子

int device_value =0;

ioctl(fd, DEVICE_IOC_SET, &device_value);//set value

ioctl(fd, DEVICE_IOC_GET, &device_value); //get value

ioctl(fd, DEVICE_IOC_CLR ) ;//clear memory

 

转载请注明出处

原创粉丝点击