版本 | 颁布日期 | 修订章节 |
---|---|---|
0.1 | 2015.08.17 | 撰写草稿 |
0.2 | 2015.12.11 | 整理内容 |
0.3 | 2016.03.06 | 整理文档,排序 |
1.0 | 2016.04.17 | 验证程序,正式发布 |
特征
输出输入的控制命令
一般是用来写非标准接口功能可以实现的功能,编写标准接口不能实现的功能。
例:一个eeprom驱动,需要设置地址,这个功能来说,系统上调用的接口没有合适的函数,
像这样的功能就可以把它归为ioctl接口功能
所以ioctl接口可以看出是系统给我们进行功能扩展的专用接口。
应用程序接口ioctl原型
应用程序头文件
#include <sys/ioctl.h>
命令查看:man ioctl
int ioctl(int d, int request, …);
这个函数是一个可变参数函数,最少需要2个参数
参数:
d :文件描述符
request :通常是cmd(命令)
… :可变参数(可以有,也可以没有,根据request情况而定)
例:
一个命令:所有灯全开,只需要一个全开等的命令,
一个命令:指定灯开,需要一个开灯命令,还需要指定那盏灯开的命令。
返回值:
成功:通常返回0,少数ioctl返回非负数,
自定义非标准cmd,返回用户自定义的一个正数(可以用这个函数实现lseek功能)
失败:返回负数,-1
驱动程序ioctl接口驱动原型:
使用ioctl没有头文件, 驱动程序头文件在文件操作方法中。
#include <linux/fs.h>
3.0以上内核
在文件操作集合file_operations中。
ioctl函数原型
long (*unlocked_ioctl) (struct file * pfile, unsigned int cmd, unsigned long arg);
2. 6内核是ioctl
参数:
pfile :文件描述符VFS转换而来
cmd :应用程序传递下来的request参数
arg :对应于应用程序传递下来的第三个参数(变参)
ioctl的命令代码
实例代码:
long chrdev_unlocked_ioctl (struct file *pfile, unsigned int cmd, unsigned long arg)
{
switch ( cmd ) {
case 0 :
printk("line:%d,cmd:%d,arg:%ld",__LINE__,cmd,arg);
break;
case 1 :
printk("line:%d,cmd:%d,arg:%ld",__LINE__,cmd,arg);
break;
case 2 :
printk("line:%d,cmd:%d,arg:%ld",__LINE__,cmd,arg);
break;
case 3 :
printk("line:%d,cmd:%d,arg:%ld",__LINE__,cmd,arg);
break;
case 4 :
printk("line:%d,cmd:%d,arg:%ld",__LINE__,cmd,arg);
break;
default:
printk("line:%d,cmd:%d,arg:%ld",__LINE__,cmd,arg);
return - EINVAL;
}
return arg;
}
应用程序代码:
for(i=0;i<6;i++) {
int ret = ioctl(fd,i,10+i); //args 从10开始,该参数现在无意义
sleep(1);
printf("\nret = %d\n",ret);
}
结果显示,当cmd值为2时,程序没有执行
ioctl接口比较特殊,不是像其他接口通过fd直接找到驱动,接口的cmd值在内核中有规范,不是随便使用一个数字就可以,有特定的编码规则,当cmd值和系统一些预定义的命令相同时,代码所执行的不会是我们的驱动代码中的ioctl,而是被拦截了,去执行相同的预定义命令去了,
从2.6到现在,cmd为2这个命令的值都不能使用
ioctl命令在内核中定义
想要正确使用ioctl接口,必须使用内核规定的定义方法来使用。
从内核文档有说明:ioctl-decoding.txt
把cmd拆分成32位
31-30位
00 - no parameters: uses _IO macro
当cmd值的30、31位为00时,应该不能带arg三个参数,否则[可能]和其他命令冲突。
函数形式为ioctl(fd,cmd)。
10 - read: _IOR
表示读方向
代表想通过ioctl从驱动中读取数据回来,存放在 ioctl(fd,cmd,arg)中的argr所代表的内存空间中(arg应该是一个可写内存地址)。
01 - write: _IOW
表示写方向。
代表想通过ioctl从向驱动中写入数据,数据存放在 ioctl(fd,cmd,arg)中的arg所指向内存空间中(arg应该是一个可读内存地址)。
11 - read/write: _IOWR
表示双向,数据交换
29-16位
29-16 size of arguments
参数大小,16位,最多只能传递16K数据
当cmd 30、31不为零,调用方法ioctl(fd,cmd,arg)表示有数据传递。
Q:应该写多少个字符?
A:read,write有参数指明要读写几个字节。把要读写的数量存放在cmd的 29~16位中。
15- 8位
15-8 ascii character supposedly unique to each driver
驱动魔数,幻数,8位:范围0~255 0~FF。
用来标识一个驱动,原则上讲一个驱动有着唯一的值,只要这个值不同,就不会和其他命令冲突。
内核已经占用大部分的魔数,原则上可以由用户定义
7- 0位
7-0 function
命令功能,8位:范围0~255
真正的命令码,像例子中的0,1,2,3,4,
内核文档指明那些命令被使用
内核文档:ioctl-number.txt
例:
Code Seq#(hex) Include File Comments ========================================================
0x00 00-1F linux/fs.h conflict!
0x00 00-1F scsi/scsi_ioctl.h conflict!
0x00 00-1F linux/fb.h conflict!
0x00 00-1F linux/wavefront.h conflict!
0x02 all linux/fd.h
0x03 all linux/hdreg.h
0x04 D2-DC linux/umsdos_fs.h Dead since 2.6.11, but don't reuse these.
0x06 all linux/lp.h
0x09 all linux/raid/md_u.h
0x10 00-0F drivers/char/s390/vmcp.h
0x12 all linux/fs.h
从表可以看出,
02冲突的几率很大
01,05,07,08比较安全
内核为用户定义的命令宏
注意:参数定义宏在应用程序和驱动两边都要定义,也可以将定义的命令宏放在一个头文件中,应用程序和驱动都将这个头文件包含在内。
参数定义宏
//定义一个没有参数的命令
#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)))
参数
参数:
type :魔数,幻数
nr :功能码(自己定义)
size :参数大小,其实是应该传递参数类型(非指针)
例:两种定义方法,第