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
  }
}
下面是一个示例,展示如何使用ioctl系统调用来控制串口设备。 假设我们有一个串口设备文件/dev/ttyS0,我们可以使用以下代码来打开设备文件并设置串口参数: ``` int fd = open("/dev/ttyS0", O_RDWR); struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &= ~OPOST; tcsetattr(fd, TCSANOW, &options); ``` 在设置完串口参数后,我们可以使用ioctl系统调用来进行串口控制操作。以下是一些常见的ioctl命令及其用途: 1. TIOCMGET:获取串口状态位,如CTS、DSR、DCD、RI等。 2. TIOCMSET:设置串口状态位,如DTR、RTS等。 3. TIOCMBIS:打开串口状态位,如DTR、RTS等。 4. TIOCMBIC:关闭串口状态位,如DTR、RTS等。 5. TIOCGSERIAL:获取串口的硬件特性,如波特率、数据位、停止位等。 6. TIOCSSERIAL:设置串口的硬件特性,如波特率、数据位、停止位等。 例如,我们可以使用以下代码来获取并打印串口状态位信息: ``` int status; ioctl(fd, TIOCMGET, &status); if (status & TIOCM_CTS) { printf("CTS is on\n"); } if (status & TIOCM_DSR) { printf("DSR is on\n"); } if (status & TIOCM_DCD) { printf("DCD is on\n"); } if (status & TIOCM_RI) { printf("RI is on\n"); } ``` 在上面的代码中,我们使用了TIOCMGET命令来获取串口状态位信息,并使用了位运算符&来判断每个状态位是否被设置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值