在使用者空間,ioctl系調用具有如下原型:
int ioctl(int fd, unsigned long cmd, ...);
這裡的第三個參數,...在unix系統中一般代表可變參數, 但是這裡代表可選參數。
在驅動空間,ioctl方法的原型如下:
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
定義ioctl指令号碼使用了4個字段,具體字段的操作宏方法定義在中。
type:幻數,這個字段有8位寬(_IO_TYPEBITS)
number:序數,這個字段也是8位寬(_IO_NRBITS)
direction:指令傳輸方向,這個字段是2位寬度(_IOC_DIRBITS)
size:所涉及使用者資料大小,這個字段通常是14位(_IOC_SIZEBITS)
在中定義了構造指令編号的宏:
#d#define _IOC(dir,type,nr,size) \
(((dir) <
((type) <
((nr) <
((size) <
#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)))
#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驗證位址的合法性,該函數在中聲明:
int access_ok(int type, const void *addr, unsigned long size);
第一個參數應該是VERIFY_READ或VERIFY_WRITE,取決于要執行的動作是讀取還是寫入使用者空間
第二個參數是一個使用者空間位址
第三個參數是使用者空間位址的大小
驅動ioctl代碼:
#define SCULL_IOC_MAGIC 'k'
#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)
#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
long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0, tmp;
int retval = 0;
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
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:
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:
printk(KERN_NOTICE "ioctl switch SCULL_IOCTQUANTUM\n");
if (!capable(CAP_SYS_ADMIN)) {
return -EPERM;
}
scull_quantum = arg;
break;
case SCULL_IOCGQUANTUM:
if (!capable(CAP_SYS_ADMIN)) {
return -EPERM;
}
retval = __put_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCQQUANTUM:
return scull_quantum;
case SCULL_IOCXQUANTUM:
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:
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測試代碼:
#include
#include
#include
#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)
{
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
運作結果:
# [email protected]:/home/liangqi/workspace/driver/scull# ./scull_test
ioctl SCULL_IOCQQUANTUM get scull_quantum = 5000
ioctl SCULL_IOCQQUANTUM get scull_quantum = 6000
[email protected]:/home/liangqi/workspace/driver/scull# dmesg | tail
[185903.939965] ioctl switch SCULL_IOCSQUANTUM
[185903.940068] ioctl switch SCULL_IOCTQUANTUM