这里先用例子介绍一下ioctrl接口的使用, 应用层的ioctl函数传入的cmd和arg参数会直接传入驱动层的ioctl接口,ioctl接口的命令有一定规范详细查看ioctl-number.txt文件,这里命令的定义不在规范内,先看下面测试的例子,驱动只实现ioctrl接口并使用ioctl修改和读取内核中的一个整型参数为例,使用两个不同方式读取(值传递和地址传递)。
应用程序测试代码main.c
#include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <unistd.h> #include <sys/ioctl.h> #define IOCTL_RESET 100 /*重置命令*/ #define IOCTL_GET1 101 /*读取命令值返回*/ #define IOCTL_GET2 102 /*读取命令地址返回*/ #define IOCTL_SET1 103 /*设置命令值传入*/ #define IOCTL_SET2 104 /*设置命令地址传入*/ int main (int *argc,char**argv) { int fs;int val; fs=open("/dev/moduledev60",O_RDWR); if(fs<0) { printf("open fail\n"); return -1; } ioctl(fs,IOCTL_SET1,1000); //使用值传入设置参数 printf("ioctl get1 result:%d\n",ioctl(fs,IOCTL_GET1)); //使用返回值读取参数 ioctl(fs,IOCTL_GET2,&val); //使用地址读取参数 printf("ioctl get2 result:%d\n",val); /*当设置参数是负数时 使用返回值读参数会出错 由于ioctl返回负数会被内核认为错误*/ ioctl(fs,IOCTL_SET1,-100); //使用值传入设置参数 printf("ioctl get1 result:%d\n",ioctl(fs,IOCTL_GET1)); //使用返回值读取参数 ioctl(fs,IOCTL_GET2,&val); //使用地址读取参数 printf("ioctl get2 result:%d\n",val); /*使用地址传入设置参数*/ val=5555; ioctl(fs,IOCTL_SET2,&val); printf("ioctl get1 result:%d\n",ioctl(fs,IOCTL_GET1)); close(fs); return 0; }
驱动主要部分 fileops.c
#define IOCTL_RESET 100 /*重置命令*/ #define IOCTL_GET1 101 /*读取命令值返回*/ #define IOCTL_GET2 102 /*读取命令地址返回*/ #define IOCTL_SET1 103 /*设置命令值传入*/ #define IOCTL_SET2 104 /*设置命令地址传入*/ int drive_param=0; int fileops_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { printk(KERN_ALERT "fileops_ioctl \n"); switch(cmd) { case IOCTL_RESET: drive_param=0; break; case IOCTL_GET1: return drive_param; break; case IOCTL_GET2: // __put_user(drive_param,(int __user *)arg); if(copy_to_user((int __user*)arg,&drive_param,4)) return -ENOTTY; break; case IOCTL_SET1: drive_param=arg; break; case IOCTL_SET2: // __get_user(drive_param,(int __user *)arg); if(copy_from_user(&drive_param,(int __user*)arg,4))return -ENOTTY; break; } return 0; }
执行结果
ioctl get1 result:1000 ioctl get2 result:1000 ioctl get1 result:-1 ioctl get2 result:-100 ioctl get1 result:5555
下面是部分ioctl-number.tx的内容
If you are adding new ioctl's to the kernel, you should use the _IO macros defined in <linux/ioctl.h>: _IO an ioctl with no parameters _IOW an ioctl with write parameters (copy_from_user) _IOR an ioctl with read parameters (copy_to_user) _IOWR an ioctl with both write and read parameters. 'Write' and 'read' are from the user's point of view, just like the system calls 'write' and 'read'. For example, a SET_FOO ioctl would be _IOW, although the kernel would actually read data from user space; a GET_FOO ioctl would be _IOR, although the kernel would actually write data to user space. The first argument to _IO, _IOW, _IOR, or _IOWR is an identifying letter or number from the table below. Because of the large number of drivers,many drivers share a partial letter with other drivers. If you are writing a driver for a new device and need a letter, pick an unused block with enough room for expansion: 32 to 256 ioctl commands. You can register the block by patching this file and submitting the patch to Linus Torvalds. Or you can e-mail me at <[email protected]> and I'll register one for you. The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number to distinguish ioctls from each other. The third argument to _IOW,_IOR, or _IOWR is the type of the data going into the kernel or coming out of the kernel (e.g. 'int' or 'struct foo'). NOTE! Do NOT use sizeof(arg) as the third argument as this results in your ioctl thinking it passes an argument of type size_t. Some devices use their major number as the identifier; this is OK, as long as it is unique. Some devices are irregular and don't follow any convention at all. Following this convention is good because: (1) Keeping the ioctl's globally unique helps error checking: if a program calls an ioctl on the wrong device, it will get an error rather than some unexpected behaviour. (2) The 'strace' build procedure automatically finds ioctl numbers defined with _IO, _IOW, _IOR, or _IOWR. (3) 'strace' can decode numbers back into useful names when the numbers are unique. (4) People looking for ioctls can grep for them more easily when this convention is used to define the ioctl numbers. (5) When following the convention, the driver code can use generic code to copy the parameters between user and kernel space.