在文件操作结构体file_operations中有很多的对应的设备操作函数,这些函数是通用的设备操作函数,但有些设备的操作是特有的,文件操作结构体不会有对应的函数定义。这样ioctl函数就将这些没办法归类的函数放在ioctl函数中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。下图是应用层与驱动函数的ioctl之间的联系:
二、ioctl的cmd分析。
1.函数参数分析int (*ioctl)(struct inode * node, struct file *filp, unsigned int cmd, unsignedlong arg);
1)inode和file:ioctl的操作有可能需要修改文件的属性,或者访问硬件。要修改文件属性就要用到这两个结构体了,所以这里传来了它们的指针。
2)cmd:我们自己定义的命令,下面我将用实现三个命令。
3)arg:用户程序传来的参数,可以是数值参数也可以是指针参数,下面的测试将都会出现。
2.cmd构成部分,cmd由幻数、序数、数据传输方向、数据大小四部分组成,和ioctl-number.txt这两个文档有对其说明。
1)幻数,是个0~0xff的数,占8bit(_IOC_TYPEBITS)。这个数是用来区分不同的驱动的,内核有一个文档给出一些推荐的或者已经被使用的幻数。
2)序数,用这个数来给自己的命令编号,占8bit(_IOC_NRBITS),下面的测试程序从1开始排序。
3)数据传输方向,占2bit(_IOC_DIRBITS)。当涉及到要传参,需要向内核描述一下传输的方向,传输的方向是以应用层的角度来描述的。
3.返回值
1)如果传入的非法命令,ioctl返回错误号-EINVAL。
2)内核中的驱动函数返回值都有一个默认的方法,只要是正数,内核就会默认为正确的返回,并把它传给应用层,如果是负值,内核就会认为它是错误返回值。ioctl里面多个不同的命令返回值有可能是不同的,因此需要根据该函数的功能来决定返回值类型。
三、ioctl实现的驱动代码
1.定义头文件test_cmd.h,在头文件中定义了三个用于ioctl函数指令。定义如下:
#ifndef_TEST_CMD_H
#define_TEST_CMD_H
#define TEST_MAGIC'x'
#define TEST_MAX_NR3
#define TEST_CLEAR_IO(TEST_MAGIC,1)//自定义的清除命令
#define TEST_OFFSET_IO(TEST_MAGIC,2)//自定义的修改文件指针位置的命令
#defineTEST_KBUF_IO(TEST_MAGIC,3)//自定义的修改内核内存数据命令
#endif
structioctl_data{//用于测试的数据结构体
unsigned int size;
char buf[100];
};
2.ioctl函数修改的部分
static intglobalmem_ioctl(struct inode *inodep,struct file *filp,unsigned intcmd,unsigned long arg)
{
int ret;
struct globalmem_dev*dev=filp->private_data;
struct ioctl_data val;
if(_IOC_TYPE(cmd)!=TEST_MAGIC)return-EINVAL;
if(_IOC_NR(cmd)>TEST_MAX_NR)return-EINVAL;
switch(cmd){
case TEST_CLEAR://清零内存
memset(dev->mem,0,GLOBALMEM_SIZE);
filp->f_pos= 0;
printk(KERN_INFO"globalmem id set tozero\n");
break;
case TEST_OFFSET://修改文件指针位置
filp->f_pos+= (int)arg;
break;
case TEST_KBUF://修改kbuf
if(copy_from_user(&val, (structioctl_data *)arg, sizeof(struct ioctl_data)))
{
return - EFAULT;
}
memset(dev->mem,0,GLOBALMEM_SIZE);//初始化全局内存
memcpy(dev->mem,val.buf,val.size);//将内核数据结构体数据拷到全局内存中
filp->f_pos =0;
break;
default:
return -EINVAL;
}
return 0;
}
3.测试应用程序test.c
#include
#include
#include
#include
#include
#include
#include"test_cmd.h"
#include
int main()
{
int fd;
char buf[20]="Welcome toChina",receve[20]={'\0'};
struct ioctl_data test_data={
.size=10,
.buf="123456789"
};
fd = open("/dev/Mac",O_WRONLY);
if(fd < 0)
{
printf("open /dev/Mac error\n");
return 0;
}
write(fd,buf,20);
close(fd);
fd =open("/dev/Mac",O_RDONLY);
if(fd < 0)
{
printf("open /dev/Mac error\n");
return 0;
}
read(fd,receve,20);
printf("buf:%s\n",receve);
//ioctl(fd,TEST_CLEAR);
//ioctl(fd,TEST_OFFSET,-20);
ioctl(fd,TEST_KBUF,&test_data);
read(fd,receve,20);
printf("buf:%s\n",receve);
close(fd);
return 0;
}
4.Makefile文件
ifneq($(KERNELRELEASE),)
obj-m :=globalmem.o
else
KDIR:=/lib/modules/2.6.28/build
all:
make -C $(KDIR) M=`pwd` modules
clean:
-rm -f *.ko *.o *.mod.o *.mod.c *.symversModule.markers modules.order pt_test
endif
到此工作完成,运行测试即可。