linux ioctl命令,关于LINUX下的ioctl函数

驱动程序中ioctl函数的函数原型如下:

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

其中cmd和arg参数是ioctl与其它驱动程序函数不同的地方。cmd是预先定义好的一些命令编号,对应要求ioctl执行的命令。arg是与cmd配合使用的参数。

ioctl函数的功能比较繁琐,从函数名可以看出,它一般是实现对设备的各种控制操作。可以这样理解,通过常规的read,write,lseek等等函数实现不合理的功能,

就交给ioctl来实现。例如:要求设备锁门,弹出介质,改变波特率,甚至执行自我破坏,等等。

ioctl的实现一般是通过一个大的switch语句,根据cmd参数执行不同的操作。所以,在实现ioctl函数之前,要先定义好cmd对应的命令编号。

为了防止发生混淆,命令编号应该在系统范围内是唯一的。为此,Linux内核将命令编号分为4个部分,即4个位段,分别是:

type: 幻数(magic number),它占8位。个人理解幻数就是一个标志,代表一个(类)对象。后面我们会看到,scull使用字符’k’作为幻数。

number:序数,即顺序编号,它也占8位。

direction:如果相关命令涉及到数据的传输,则这个位段表示数据传输的方向,可用的值包括_IOC_NONE(没有数据传输),_IOC_READ(读)、_IOC_WRITE(写)、

_IOC_READ | _IOC_WRITE(双向传输数据)。注意,数据传输方向是从应用程序的角度看的,也就是说_IOC_READ意味着从设备中读数据,所以驱动程序

必须向用户空间写数据。

size:所涉及的用户数据大小。这个位段的宽度与体系结构有关,通常是13或14位。

中包含的头文件定义了一些构造命令编号的宏:

_IO(type, nr),用于构造无数据传输的命令编号。

_IOR(type, nr, datatype),用于构造从驱动程序中读取数据的命令编号。

_IOW(type, nr, datatype),用于构造向设备写入数据的命令编号。

_IOWR(type, nr, datatype),用于双向传输命令编号。

其中,type和number位段从以上宏的参数中传入,size位段通过对datatype参数取sizeof获得。

另外,头文件中还定义了一些用于解析命令编号的宏,如_IOC_DIR(cmd),_IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_SIZE(cmd)。

首先我们来看一下scull是如何定义命令编号的,理解scull的ioctl函数的实现,关键是理解这些命令是什么含义,即要求完成什么工作。在scull.h中有如下定义:

/*

* Ioctl definitions

*/

/* Use 'k' as magic number 定义scull的幻数是字符'k'*/

#define SCULL_IOC_MAGIC  'k'

/* Please use a different 8-bit number in your code */

#define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)

/*

* S means "Set" through a ptr,   ’S’代表通过参数arg指向的内容设置

* T means "Tell" directly with the argument value, ’T’代表直接通过参数arg的值设置

* G means "Get": reply by setting through a pointer, ’G’代表通过参数arg指向的地址返回请求的值

* Q means "Query": response is on the return value ,  ’Q’代表通过ioctl函数的返回值返回请求的值。

* X means "eXchange": switch G and S atomically  ’X’代表通过参数arg指向的内容设置,再把原来的值通过arg指向的地址返回。即’S’与’G’两个操作合为一步。

* H means "sHift": switch T and Q atomically   ’H’代表通过参数arg的值直接设置,再通过ioctl函数的返回值将原来的值返回。即’T’和’Q’两个操作合为一步。

*/

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)   该命令表示通过参数arg指向的内容设置quantum。

#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)   该命令表示通过参数arg指向的内容设置qset。

#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)        该命令表示通过参数arg的值直接设置quantum

#define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)        该命令表示通过参数arg的值直接设置qset。

#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)   该命令表示通过参数arg指向的地址返回quantum

#define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)   该命令表示通过参数arg指向的地址返回qset。

#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)        该命令表示通过ioctl的返回值返回quantum

#define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)        该命令表示通过ioctl的返回值返回qset。

#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)   该命令表示通过参数arg指向的内容设置quantum,然后,再把quantum原来的值写入arg指向的地址返回。

#define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)   该命令表示通过参数arg指向的内容设置qset,然后,再把qset原来的值写入arg指向的地址返回。

#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)        该命令表示通过参数arg的值直接设置quantum,然后,再通过ioctl的返回值返回quantum原来的值。

#define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)        该命令表示通过参数arg的值直接设置qset,然后,再通过ioctl的返回值返回qset原来的值。

/*

* The other entities only have "Tell" and "Query", because they're

* not printed in the book, and there's no need to have all six.

* (The previous stuff was only there to show different ways to do it.

*/

#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)       该命令表示通过参数arg的值直接设置scull_p_buffer

#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)       该命令表示通过ioctl的返回值返回scull_p_buffer

/* ... more to come */

#define SCULL_IOC_MAXNR 14                                代表一共有14个命令

理解了ioctl命令的含义后,来看scull.c中的ioctl函数:

/*

* The ioctl() implementation

*/

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

{

int err = 0, tmp;

int retval = 0;

/*

* extract the type and number bitfields, and don't decode

* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

*/

if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;  /*如果不是幻数K,就退出*/

if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;     /*cmd的序数大于14,则退出*/

/*

* the direction is a bitmask, and VERIFY_WRITE catches R/W

* transfers. `Type' is user-oriented, while

* access_ok is kernel-oriented, so the concept of "read" and

* "write" is reversed

*如果要使用arg指向的地址进行数据的读或写,必须保证对该地址的访问是合法的,这可通过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 SCULL_IOCRESET:

scull_quantum = SCULL_QUANTUM;

scull_qset = SCULL_QSET;

break;

case SCULL_IOCSQUANTUM: /* Set: arg points to the value */

if (! capable (CAP_SYS_ADMIN))   //scull允许任何用户查询quantum和qset的大小,

//但只允许被授权的用户修改quantum和qset的值。这种权能的检查是通过capable()函数实现的

return -EPERM;

retval = __get_user(scull_quantum, (int __user *)arg); /*驱动程序与用户空间传递数据,

*采用的是__put_user和__get_user函数,相比copy_to_user和copy_from_user来说,

*这些函数在处理1、2、4、8个字节的数据传输时,效率更高*/

break;

case SCULL_IOCTQUANTUM: /* Tell: arg is the value */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

scull_quantum = arg;

break;

case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */

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

break;

case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */

return scull_quantum;

case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_quantum;

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

if (retval == 0)

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

break;

case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_quantum;

scull_quantum = arg;

return tmp;

case SCULL_IOCSQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

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

break;

case SCULL_IOCTQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

scull_qset = arg;

break;

case SCULL_IOCGQSET:

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

break;

case SCULL_IOCQQSET:

return scull_qset;

case SCULL_IOCXQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_qset;

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

if (retval == 0)

retval = put_user(tmp, (int __user *)arg);

break;

case SCULL_IOCHQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_qset;

scull_qset = arg;

return tmp;

/*

* The following two change the buffer size for scullpipe.

* The scullpipe device uses this same ioctl method, just to

* write less code. Actually, it's the same driver, isn't it?

*/

case SCULL_P_IOCTSIZE:

scull_p_buffer = arg;

break;

case SCULL_P_IOCQSIZE:

return scull_p_buffer;

default:  /* redundant, as cmd was checked against MAXNR */

return -ENOTTY;

}

return retval;

}

/***************************************/

二、测试ioctl

要测试scull驱动中ioctl函数是否实现了我们要求的功能,需要编写用户空间程序对scull模块进行测试。下面是一个比较简单的测试程序:

首先是头文件scull_ioctl.h:

#ifndef _SCULL_IOCTL_H_

#define _SCULL_IOCTL_H_

#include /* needed for the _IOW etc stuff used later */

/*

* Ioctl definitions

*/

/* Use 'k' as magic number */

#define SCULL_IOC_MAGIC  'k'

/* Please use a different 8-bit number in your code */

#define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)

/*

* S means "Set" through a ptr,

* T means "Tell" directly with the argument value

* G means "Get": reply by setting through a pointer

* Q means "Query": response is on the return value

* X means "eXchange": switch G and S atomically

* H means "sHift": switch T and Q atomically

*/

#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)

/*

* The other entities only have "Tell" and "Query", because they're

* not printed in the book, and there's no need to have all six.

* (The previous stuff was only there to show different ways to do it.

*/

#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)

#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)

/* ... more to come */

#define SCULL_IOC_MAXNR 14

#endif /* _SCULL_IOCTL_H_ */

下面是测试程序scull_ioctl_test.c的代码:

#include

#include

#include

#include

#include

#include "scull_ioctl.h"

#define SCULL_DEVICE "/dev/scull0"

int main(int argc, char *argv[])

{

int fd = 0;

int quantum = 8000;

int quantum_old = 0;

int qset = 2000;

int qset_old = 0;

fd = open(SCULL_DEVICE, O_RDWR);

if(fd < 0)

{

printf("open scull device error!\n");

return 0;

}

printf("SCULL_IOCSQUANTUM: quantum = %d\n", quantum);

ioctl(fd, SCULL_IOCSQUANTUM, &quantum);

quantum -= 500;

printf("SCULL_IOCTQUANTUM: quantum = %d\n", quantum);

ioctl(fd, SCULL_IOCTQUANTUM, quantum);

ioctl(fd, SCULL_IOCGQUANTUM, &quantum);

printf("SCULL_IOCGQUANTUM: quantum = %d\n", quantum);

quantum = ioctl(fd, SCULL_IOCQQUANTUM);

printf("SCULL_IOCQQUANTUM: quantum = %d\n", quantum);

quantum -= 500;

quantum_old = ioctl(fd, SCULL_IOCHQUANTUM, quantum);

printf("SCULL_IOCHQUANTUM: quantum = %d, quantum_old = %d\n", quantum, quantum_old);

quantum -= 500;

printf("SCULL_IOCXQUANTUM: quantum = %d\n", quantum);

ioctl(fd, SCULL_IOCXQUANTUM, &quantum);

printf("SCULL_IOCXQUANTUM: old quantum = %d\n", quantum);

printf("SCULL_IOCSQSET: qset = %d\n", qset);

ioctl(fd, SCULL_IOCSQSET, &qset);

qset += 500;

printf("SCULL_IOCTQSET: qset = %d\n", qset);

ioctl(fd, SCULL_IOCTQSET, qset);

ioctl(fd, SCULL_IOCGQSET, &qset);

printf("SCULL_IOCGQSET: qset = %d\n", qset);

qset = ioctl(fd, SCULL_IOCQQSET);

printf("SCULL_IOCQQSET: qset = %d\n", qset);

qset += 500;

qset_old = ioctl(fd, SCULL_IOCHQSET, qset);

printf("SCULL_IOCHQSET: qset = %d, qset_old = %d\n", qset, qset_old);

qset += 500;

printf("SCULL_IOCXQSET: qset = %d\n", qset);

ioctl(fd, SCULL_IOCXQSET, &qset);

printf("SCULL_IOCHQSET: old qset = %d\n", qset);

return 0;

}

为了能看到测试效果,在修改驱动程序中的ioctl函数,打印一些语句。下面直接列出修改后的ioctl函数的实现:

/*

* The ioctl() implementation

*/

int scull_ioctl(struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long arg)

{

int err = 0, tmp;

int retval = 0;

/*

* extract the type and number bitfields, and don't decode

* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

*/

if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;

if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;

/*

* the direction is a bitmask, and VERIFY_WRITE catches R/W

* transfers. `Type' is user-oriented, while

* access_ok is kernel-oriented, so the concept of "read" and

* "write" is reversed

*/

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 SCULL_IOCRESET:

scull_quantum = SCULL_QUANTUM;

scull_qset = SCULL_QSET;

printk("SCULL_IOCRESET: scull_quantum = %d, scull_qset = %d\n", scull_quantum, scull_qset);

break;

case SCULL_IOCSQUANTUM: /* Set: arg points to the value */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

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

printk("SCULL_IOCSQUANTUM: scull_quantum = %d\n", scull_quantum);

break;

case SCULL_IOCTQUANTUM: /* Tell: arg is the value */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

scull_quantum = arg;

printk("SCULL_IOCTQUANTUM: scull_quantum = %d\n", scull_quantum);

break;

case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */

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

printk("SCULL_IOCGQUANTUM: use arg return scull_quantum = %d\n", scull_quantum);

break;

case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */

printk("SCULL_IOCQQUANTUM: return scull_quantum = %d\n", scull_quantum);

return scull_quantum;

case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_quantum;

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

if (retval == 0)

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

printk("SCULL_IOCXQUANTUM: scull_quantum = %d, and use arg return old scull_quantum = %d\n", scull_quantum, tmp);

break;

case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_quantum;

scull_quantum = arg;

printk("SCULL_IOCHQUANTUM: scull_quantum = %d, and return old scull_quantum = %d\n", scull_quantum, tmp);

return tmp;

case SCULL_IOCSQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

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

printk("SCULL_IOCSQSET: scull_qset = %d\n", scull_qset);

break;

case SCULL_IOCTQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

scull_qset = arg;

printk("SCULL_IOCTQSET: scull_qset = %d\n", scull_qset);

break;

case SCULL_IOCGQSET:

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

printk("SCULL_IOCGQSET: use arg return scull_qset = %d\n", scull_qset);

break;

case SCULL_IOCQQSET:

printk("SCULL_IOCQQSET: return scull_qset = %d\n", scull_qset);

return scull_qset;

case SCULL_IOCXQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_qset;

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

if (retval == 0)

retval = put_user(tmp, (int __user *)arg);

printk("SCULL_IOCXQSET: scull_qset = %d, and use arg return old scull_qset = %d\n", scull_qset, tmp);

break;

case SCULL_IOCHQSET:

if (! capable (CAP_SYS_ADMIN))

return -EPERM;

tmp = scull_qset;

scull_qset = arg;

printk("SCULL_IOCHQSET: scull_qet = %d, and return old scull_qset = %d\n", scull_qset, tmp);

return tmp;

/*

* The following two change the buffer size for scullpipe.

* The scullpipe device uses this same ioctl method, just to

* write less code. Actually, it's the same driver, isn't it?

*/

case SCULL_P_IOCTSIZE:

scull_p_buffer = arg;

break;

case SCULL_P_IOCQSIZE:

return scull_p_buffer;

default:  /* redundant, as cmd was checked against MAXNR */

return -ENOTTY;

}

return retval;

}

/******************************************************************************/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值