字符设备之ioctl

来源:互联网 发布:社会支持网络理论案例 编辑:程序博客网 时间:2024/06/11 22:05

1.ioctl()函数的存在意义:

    除了简单的数据传输之外,大部分设备还可以执行一些其他的动作,比如用户空间请求设备锁门、弹出介质、改变波特率等.这些动作可以通过ioctl来实现.


2.ioctl()函数的参数说明:

    ioctl()函数原型如下:

int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
    各参数的意义说明如下:

inode:对应于应用程序传递的设备文件节点,和open相同;filp:对就于应用程序传递的设备文件描述符,和open相同;
    第三个参数比较特别,它虽然是一个无符号整形,在标准的做法里面,它是一个"位数据".每个位都是有规定的意义的.如下:

bit[31]~bit[24]:一共8位.所谓的"幻数",表征此ioctl命令的隶属设备,一般是某个字符.后续所有的ioctl命令对应的都有这个公共部分.
    如:

#define SCULL_IOC_MAGIC  'k'
    SCULL_IOC_MAGIC将作为公共部分来参与到后续的ioctl命令中.因为它是表征后续的ioctl命令隶属于哪个驱动而不是具体的某个命令.内核将此位段标识为"type".

bit[23]~bit[17]:一共8位,表示ioctl的序号.比如一个驱动里面支持16个ioctl命令,则可以排序为1~16.
    如:

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)#define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)#define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)#define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)#define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)#define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)
    上述的1~12便是对应bit[23]~bit[17]的设置.内核将此位段标识为"number".

bit[16]~bit[15]:一共两位,标识数据的传输方向.此时的方向是在用户空间的角度看的.比如标识此命令是用户读还是写还是可读可写.
    这两个bit的设备可以通过内核提供的宏_IOW、_IOR和_IOWR来设置.如下:

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)#define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)#define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
    其中"W"表征"写","R"表征"读".内核将此位段标识为"direction".

bit[13]~bit[0]:所涉及的用户数据的大小,以字节为单位,是对宏_IOWR、_IOW、_IOR的第三个参数用sizeof取得.
    比如下面命令:

#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
    则此位段值为sizeof(int) = 4.内核将此位段标识为size.


    第四个参数也是比较特别,特别在两点:

   

1).它的存在与否与第二个参数息息相关,如果参数cmd涉及到参数的传递,则需要它来承载参数的传递桥梁,否则其存在与否就一点意义都没有了;2).它恒为unsigned long类型,无论用户空间在arg上传递的是一个整形数据还是指针,它都以unsigned long的形式传递给内核.因此,当用户空间传递的是一个指针的时候,必须对其进行检查.
    如果传递过来是一个常规数据,直接使用即可.

    针对第2)点,我们需要对用户传递过来的参数指针的合法性进行检查,以确保其对内核空间是有效的.如果用copy_to_user()和copy_from_user()两个函数来保证指针的有效性,未免有点"牛刀杀鸡"的嫌疑.这里推荐使用access_ok()函数.原型如下:

int access_ok(int type,const void *addr,unsigned long size);
    各参数意义如下:

type:应该为VERIFY_READ或VERIFY_WRITE,标识读或写入用户空间,其中VERIFY_WRITE是VERIFY_READ的超集,当读写动作都需要时,则需要用VERIFY_WRITE标识;addr:用户空间的指针;size:字节数,如果ioctl从用户空间读取一个整数,size就是sizeof(int).
    access_ok()函数的返回值:1表示成功;0表示失败.

    针对传递过来的参数是指针,可以通过下面的形式来引用其指向内容:

retval = __get_user(scull_quantum,(int __user *)arg);
    这里需要注意的是把unsigned long arg强制类型转化为(int __user *)指针.


3.ioctl()的返回值:

    当出错时,驱动中一般返回-ENVAL或-ENOTTY.


4.ioctl()所涉及到的数据交互:

    ioctl()涉及到的数据量交互都比较小,如果用copy_to_user()和copy_from_user()显得"牛刀杀鸡"而且浪费一定的系统资源.如果只涉及到一个简单的变量传递,可以调用下面两个函数:

    get_user(x,ptr):

get_userNameget_user -- Get a simple variable from user space.Synopsisget_user ( x, ptr);ArgumentsxVariable to store result.ptrSource address, in user space.ContextUser context only. This function may sleep.DescriptionThis macro copies a single simple variable from user space to kernel space. It supports simple types like char and int, but not larger data types like structures or arrays.ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.Returns zero on success, or -EFAULT on error. On error, the variable x is set to zero.

    put_user(x,ptr):

put_userNameput_user -- Write a simple value into user space.Synopsisput_user ( x, ptr);ArgumentsxValue to copy to user space.ptrDestination address, in user space.ContextUser context only. This function may sleep.DescriptionThis macro copies a single simple value from kernel space to user space. It supports simple types like char and int, but not larger data types like structures or arrays.ptr must have pointer-to-simple-variable type, and x must be assignable to the result of dereferencing ptr.Returns zero on success, or -EFAULT on error.