ioctl系统调用

简介

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令,有些命令需要控制参数,这些数据是不能用read / write 读写的,称为Out-of-band数据。也就是说,read / write读写的数据是in-band数据,是I/O操作的主体,而ioctl 命令传送的是控制信息,其中的数据是辅助的数据。
函数声明:
int ioctl(int, int, ...);
int ioctl(int handle, int cmd,[int *argdx, int argcx]);
第一个参数一般为open()打开的文件描述符fd。

include/asm-generic/ioctl.h中宏定义

#define _IOC_NRBITS	8 //序数(number)字段的字位宽度,8bits
#define _IOC_TYPEBITS	8 //幻数(type)字段的字位宽度,8bits
#ifndef _IOC_SIZEBITS
# define _IOC_SIZEBITS	14 //大小(size)字段的字位宽度,14bits
#endif

#ifndef _IOC_DIRBITS
# define _IOC_DIRBITS	2 //方向(direction)字段的字位宽度,2bits
#endif

#define _IOC_NRMASK	((1 << _IOC_NRBITS)-1) //序数字段的掩码,0x000000ff
#define _IOC_TYPEMASK	((1 << _IOC_TYPEBITS)-1) //幻数字段的掩码,0x000000ff
#define _IOC_SIZEMASK	((1 << _IOC_SIZEBITS)-1) //大小字段的掩码,0x00003fff
#define _IOC_DIRMASK	((1 << _IOC_DIRBITS)-1) //方向字段的掩码,0x00000003

#define _IOC_NRSHIFT	0 //序数字段在整个字段中的偏移,0
#define _IOC_TYPESHIFT	(_IOC_NRSHIFT+_IOC_NRBITS) //幻数字段的偏移,8
#define _IOC_SIZESHIFT	(_IOC_TYPESHIFT+_IOC_TYPEBITS) //大小字段的偏移,16
#define _IOC_DIRSHIFT	(_IOC_SIZESHIFT+_IOC_SIZEBITS) //方向字段的偏移,30
#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 _IOC(dir,type,nr,size) \
	(((dir)  << _IOC_DIRSHIFT) | \
	 ((type) << _IOC_TYPESHIFT) | \
	 ((nr)   << _IOC_NRSHIFT) | \
	 ((size) << _IOC_SIZESHIFT))

#ifndef __KERNEL__
#define _IOC_TYPECHECK(t) (sizeof(t))
#endif
/* used to create numbers */
#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))) //用于双向传输
根据size的设置情况,我猜它会取值sizeof(int、long或者一个数组)......
#define _IOR_BAD(type,nr,size)	_IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)		(((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)//解析数据方向
#define _IOC_TYPE(nr)		(((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)//解析幻数
#define _IOC_NR(nr)		(((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)//解析序数
#define _IOC_SIZE(nr)		(((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)//解析用户数据大小

/* ...and for the drivers/sound files... */

#define IOC_IN		(_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT		(_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT	((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK	(_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT	(_IOC_SIZESHIFT)

应用实例

应用层用户接口

// Get device name.
char buffer[80];
ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer)
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len)  
#define _IOC(dir,type,nr,size)   (((dir) << _IOC_DIRSHIFT) |   ((type) << _IOC_TYPESHIFT) 
|   ((nr) << _IOC_NRSHIFT) |   ((size) << _IOC_SIZESHIFT))

内核ioctl实现

用户传下来的就是一个cmd,看下kernel中对命令的解析。
#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)	/* get device name */
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
			   void __user *p, int compat_mode)
{
	size = _IOC_SIZE(cmd); //获取用户需要的数据长度

	/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr)	((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))//去掉size
	switch (EVIOC_MASK_SIZE(cmd)) {
  	case EVIOCGNAME(0):
  		return str_to_user(dev->name, size, p);//将dev->name copy到user空间p,dev是一个input_dev
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值