在用户空间,ioctl系调用具有如下原型:
int ioctl(int fd, unsigned long cmd, ...);
这里的第三个参数,...在unix系统中一般代表可变参数, 但是这里代表可选参数。
在驱动空间,ioctl方法的原型如下:
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
定义ioctl命令号码使用了4个字段,具体字段的操作宏方法定义在<linux/ioctl.h>中。
type:幻数,这个字段有8位宽(_IO_TYPEBITS)
number:序数,这个字段也是8位宽(_IO_NRBITS)
direction:命令传输方向,这个字段是2位宽度(_IOC_DIRBITS)
size:所涉及用户数据大小,这个字段通常是14位(_IOC_SIZEBITS)
在<linux/ioctl.h>中定义了构造命令编号的宏:
#d#define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) /* used to create numbers */ #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))) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
在涉及用户空间和系统空间安全交换数据时,使用copy_from_user和copy_to_user函数,但是比较耗费资源。ioctl通常是传输小数据,可以通过其他更有效的方法来传递数据,因此需要通过函数access_ok验证地址的合法性,该函数在<asm/uaccess.h>中声明:
int access_ok(int type, const void *addr, unsigned long size);
第一个参数应该是VERIFY_READ或VERIFY_WRITE,取决于要执行的动作是读取还是写入用户空间
第二个参数是一个用户空间地址
第三个参数是用户空间地址的大小
驱动ioctl代码:
/* * Ioctl definitions */ /* Use 'k' as magic number */ #define SCULL_IOC_MAGIC 'k' /* Please use a different 8-bit number in your code */ #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) /* * S means "Set" through a ptr, * T means "Tell" directly with the argument value * G means "Get": reply by setting throught a pointer * Q means "Query": response is on the return value * x means "eXchange": switch G and S atomically * H means "sHift" switch T and Q atmoically */ #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC, 10, int) #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) #define SCULL_IOC_MAXNR 12 /* * The ioctl() implementation */ long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0, tmp; int retval = 0; /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY; /* * the direct is bitmask, and VERIFY_WRITE catches R/W * transfers. 'TYPE' is user-oriented, while * access_ok is kernel-eriented, so the concept of "read" and * "write" is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) { err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); } else if (_IOC_DIR(cmd) & _IOC_WRITE) { err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); } if (err) { return -EFAULT; } switch (cmd) { case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break; case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ printk(KERN_NOTICE "ioctl switch SCULL_IOCSQUANTUM\n"); if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } retval = __get_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCTQUANTUM: /* Tell: arg is the value */ printk(KERN_NOTICE "ioctl switch SCULL_IOCTQUANTUM\n"); if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } scull_quantum = arg; break; case SCULL_IOCGQUANTUM: /* Get: arg is point to result */ if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } retval = __put_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCQQUANTUM: /* Querry: return it (it's positive) */ return scull_quantum; case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */ if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } tmp = scull_quantum; retval = __get_user(scull_quantum, (int __user *)arg); if (retval == 0) { retval = __put_user(tmp, (int __user *)arg); } break; case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */ if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } tmp = scull_quantum; scull_quantum = arg; return tmp; case SCULL_IOCSQSET: if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } break; case SCULL_IOCTQSET: if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } scull_qset = arg; break; case SCULL_IOCGQSET: retval = __put_user(scull_qset, (int __user *)arg); break; case SCULL_IOCQQSET: return scull_qset; case SCULL_IOCXQSET: if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } tmp = scull_qset; retval = __get_user(scull_qset, (int __user *)arg); if (retval == 0) { retval = __put_user(tmp, (int __user *)arg); } break; case SCULL_IOCHQSET: if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } tmp = scull_qset; scull_qset = arg; return tmp; default: return -ENOTTY; } return retval; } struct file_operations scull_fops = { .owner = THIS_MODULE, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_open, .release = scull_release, };
应用程序ioctl测试代码:
/************************************************************************* > File Name: scull_test.c > Author: liangqi > Mail: [email protected] > Created Time: 2015年08月17日 星期一 09时12分13秒 ************************************************************************/ #include <stdio.h> #include <sys/ioctl.h> #include <fcntl.h> /* Use 'k' as magic number */ #define SCULL_IOC_MAGIC 'k' #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC, 10, int) #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) #define SCULL_IOC_MAXNR 12 char *deviceName = "/dev/scull0"; int deviceHandler = 0; int scull_quantum = 4000; int scull_qset = 1000; int main(int argc, char **argv) { /* open scull device */ deviceHandler = open(deviceName, O_RDWR); if (deviceHandler == -1) { printf("can't open %s:", deviceName); } scull_quantum = 5000; if(ioctl(deviceHandler, SCULL_IOCSQUANTUM, &scull_quantum) == -1) { printf("ioctl SCULL_IOCSQUANTUM failed\n"); } printf("ioctl SCULL_IOCQQUANTUM get scull_quantum = %d\n", ioctl(deviceHandler, SCULL_IOCQQUANTUM)); scull_quantum = 6000; if(ioctl(deviceHandler, SCULL_IOCTQUANTUM, scull_quantum) == -1) { printf("ioctl SCULL_IOCTQUANTUM failed\n"); } printf("ioctl SCULL_IOCQQUANTUM get scull_quantum = %d\n", ioctl(deviceHandler, SCULL_IOCQQUANTUM)); }
makefile文件:
# # Comment/uncommnet the following line to disable/enable debugging # DEBUG = y # Add your debugging flay (or not) to CFLAGS ifeq ($(DEBUG),y) DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines else DEBFLAGS = -O2 endif ifneq ($(KERNELRELEASE),) # call from kernel build system scull-objs := main.o obj-m := scull.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) .PHONY:all all:scull_test modules scull_test:scull_test.o $(CC) $(CFLAGS) scull_test.o -o scull_test modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions scull_test
运行结果:
# root@K2450:/home/liangqi/workspace/driver/scull# ./scull_test ioctl SCULL_IOCQQUANTUM get scull_quantum = 5000 ioctl SCULL_IOCQQUANTUM get scull_quantum = 6000 root@K2450:/home/liangqi/workspace/driver/scull# dmesg | tail [185903.939965] ioctl switch SCULL_IOCSQUANTUM [185903.940068] ioctl switch SCULL_IOCTQUANTUM