字符设备驱动的ioctl函数
用户空间的ioctl
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
/*
fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd
*/
//使用例子,framerbuffer技术获取屏幕属性
ioctl(fd, FBIOGET_VSCREENINFO, &varInfo);
内核空间的ioctl
#include <asm/ioctl.c>
int (*ioctl) (struct inode *, struct file *, unsigned int cmd, unsigned long argv);
long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long argv);
long (*compat_ioctl) (struct file *, unsigned int cmd, unsigned long argv);
/*
inode file:文件描述符
cmd:由用户空间直接不经修改的传递给驱动程序
argv:可选参数:具体内容依赖于cmd
*/
用户空间的ioctl
会调用内核空间的ioctl
,内核空间中cmd
有具体的含义。
图片转自知乎,侵删。
分为4个字段:数据方向,数据大小,设备类型,命令编号
- dir(direction),ioctl 命令访问模式(数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
- size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;
- type(device type),设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;
- nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;
在驱动编程中通常使用宏定义制造出这个32位的cmd
或者反向解析
#include <asm/ioctl.h>
/* 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)))
/* used to decode them.. */
#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)
内核空间示例代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/atomic.h>
#include <asm/ioctl.h>
struct led_info {
int age;
char stat;
};
#define LED_ON _IOWR('l', 0, struct led_info)
#define LED_OFF _IOWR('l', 1, struct led_info)
#define GET_INFO _IOWR('l', 2, struct led_info)
#define PUT_INFO _IOWR('l', 3, struct led_info)
struct led_info info = {
.age = 0,
.stat = 0,
};
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long argv)
{
int ret = -1;
switch (cmd)
{
case LED_ON:
led_on();
break;
case LED_OFF:
led_off();
break;
case GET_INFO:
ret = copy_to_user((struct led_info*)argv, &info, sizeof(info));
break;
case PUT_INFO:
ret = copy_from_user(&info, (struct led_info*)argv, sizeof(info));
printk("info.age = %d\n", info.age);
break;
}
return 0;
}
用户空间示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
struct led_info {
int age;
char stat;
};
#define LED_ON _IOWR('l', 0, struct led_info)
#define LED_OFF _IOWR('l', 1, struct led_info)
#define GET_INFO _IOWR('l', 2, struct led_info)
#define PUT_INFO _IOWR('l', 3, struct led_info)
int main(int argc, const char *argv[])
{
char stat[4];
struct led_info info;
memset(&info, 0, sizeof(info));
int fd = open("/dev/myled", O_RDWR);
if (fd < 0)
{
perror("open led fail:");
exit(-1);
}
ioctl(fd, GET_INFO, &info);
printf("user info.age = %d\n", info.age);
info.age = 100;
ioctl(fd, PUT_INFO, &info);
close(fd);
return 0;
}
只是对ioctl内核态到用户态的数据传输的测试,实际工程中会对cmd
的各个域进行判断。