现在的位置: 首页 > 综合 > 正文

.ioctl的实现

2013年03月24日 ⁄ 综合 ⁄ 共 2993字 ⁄ 字号 评论关闭

linux设备驱动归纳总结(三):4.ioctl的实现

一、ioctl的简介:

虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动,想要一个弹出光驱的操作,这种操作并不是所有的字符设备都需要的,所以文件操作结构体也不会有对应的函数操作。

出于这样的原因,ioctl就有它的用处了————一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。

来个图来说一下应用层与驱动函数的ioctl之间的联系:

上面的图可以看出,fd通过内核后找到对应的inodefile结构体指针并传给驱动函数,而另外两个参数却没有修改(类型改了没什么关系)

简单介绍一下函数:

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

参数:

1)inodefileioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改

文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。

2)cmd:命令,接下来要长篇大论地说。

3)arg:参数,接下来也要长篇大论。

返回值:

1)如果传入的非法命令,ioctl返回错误号-EINVAL

2)内核中的驱动函数返回值都有一个默认的方法,只要是正数,内核就会傻乎乎的认为这是正确的返回,并把它传给应用层,如果是负值,内核就会认为它是错误号了。

Ioctl里面多个不同的命令,那就要看它函数的实现来决定返回值了。打个比方,如果ioctl里面有一个类似read的函数,那返回值也就可以像read一样返回。

当然,不返回也是可以的。

二、ioctlcmd

说白了,cmd就是一个数,如果应用层传来的数值在驱动中有对应的操作,这样就就可以了。

来个最简单的ioctl实现:3rd_char_4/1st

1)要先定义个命令,就用一个简单的0,来个命令的头文件,驱动和应用函数都要包含这个头文件

/*test_cmd.h*/

1 #ifndef _TEST_CMD_H

2 #define _TEST_CMD_H

3

4 #define TEST_CLEAR 0

5

6 #endif /*_TEST_CMD_H*/

2)驱动实现ioctl

命令TEST_CLEAR的操作就是清空驱动中的kbuf

122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

123 {

124 int ret = 0;

125 struct _test_t *dev = filp->private_data;

126

127 switch(cmd){

128 case TEST_CLEAR:

129 memset(dev->kbuf, 0, DEV_SIZE);

130 dev->cur_size = 0;

131 filp->f_pos = 0;

132 ret = 0;

133 break;

134 default: /*命令错误时的处理*/

135 P_DEBUG("error cmd!\n");

136 ret = - EINVAL;

137 break;

138 }

139

140 return ret;

141 }

3)再来个应用程序:

1 #include <stdio.h>

2 #include <sys/types.h>

3 #include <sys/stat.h>

4 #include <fcntl.h>

5 #include <sys/ioctl.h>

6 #include "test_cmd.h"

7

8 int main(void)

9 {

10 char buf[20];

11 int fd;

12 int ret;

13

14 fd = open("/dev/test", O_RDWR);

15 if(fd < 0)

16 {

17 perror("open");

18 return -1;

19 }

20

21 write(fd, "xiao bai", 10); //1先写入

22

23 ioctl(fd, TEST_CLEAR); //2再清空

24

25 ret = read(fd, buf, 10); //3再验证

26 if(ret < 0)

27 {

28 perror("read");

29 }

30

31 close(fd);

32 return 0;

33 }

注:这里为了read返回出错,我修改了驱动的readwrite函数的开始时的第一个

判断,一看就知道了。

4)验证一下:

[root: 1st]# insmod test.ko

major[253] minor[0]

hello kernel

[root: 1st]# mknod /dev/test c 253 0

[root: 1st]# ./app

<kernel>[test_write]write 10 bytes, cur_size:[10]

<kernel>[test_write]kbuf is [xiao bai]

read: No such device or address //哈哈!出错了!因为没数据读取。

按照上面的方法来定义一个命令是完全可以的,但内核开发人员发现这样有点不对劲。

如果有两个不同的设备,但它们的ioctlcmd却一样的,哪天有谁不小心打开错了,并且调用ioctl,这样就完蛋了。因为这个文件里面同样有cmd对应实现。

为了防止这样的事情发生,内核对cmd又有了新的定义,规定了cmd都应该不一样。

三、ioctl中的cmd

一个cmd被分为了4个段,每一段都有各自的意义,cmd的定义在<linux/ioctl.h>。注:但实际上<linux/ioctl.h>中只是包含了<asm/ioctl.h>,这说明了这是跟平台相关的,ARM的定义在<arch/arm/include/asm/ioctl.h>,但这文件也是包含别的文件<asm-generic/ioctl.h>,千找万找,终于找到了。

<asm-generic/ioctl.h>中,cmd拆分如下:

解释一下四部分,全部都在<asm-generic/ioctl.h>ioctl-number.txt这两个文档有说明。

1)幻数:说得再好听的名字也只不过是个0~0xff的数,占8bit(_IOC_TYPEBITS)。这个数是用来区分不同的驱动的,像设备号申请的时候一样,内核有一个文档给出一些推荐的或者已经被使用的幻数。

/*Documentation/ioctl/ioctl-number.txt*/

164 'w' all CERN SCI driver

165 'y' 00-1F packet based user level communications

166 <mailto:zapman@interlan.net>

167 'z' 00-3F CAN bus card

168 <mailto:hdstich@connectu.ulm.circular.de>

169 'z' 40-7F CAN bus card

170 <mailto:oe@port.de>

可以看到'x'是还没有人用的,我就拿这个当幻数!

2)序数:用这个数来给自己的命令编号,占8bit(_IOC_NRBITS),我的程序从1开始排序。

3)数据传输方向:占2bit(_IOC_DIRBITS)。如果涉及到要传参,内核要求描

抱歉!评论已关闭.