這裡先用例子介紹一下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;}
傳送單個值時使用 __put_user __get_user要比copy相對快, 注意 __put_user,__get_user 應當只用在已經使用 access_ok 校正過的地址.copy_from_user和copy_to_user跟蹤代碼會發現已經加了access_ok的校正。
執行結果
ioctl get1 result:1000ioctl get2 result:1000ioctl get1 result:-1ioctl get2 result:-100ioctl get1 result:5555
會發現使用值返回負數-100時,驅動介面內返回-100應用程式的ioctl返回的是-1,使用地址傳遞參數則正確讀取。
下面是部分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 <mec@shout.net> 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.