ioctl使用(驱动层和应用层)简介

在用户空间,ioctl系调用具有如下原型:

int ioctl(int fd, unsigned long cmd, ...);

这里的第三个参数,...在unix系统中一般代表可变参数, 但是这里代表可选参数。

在驱动空间,ioctl方法的原型如下:

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

定义ioctl命令号码使用了4个字段,具体字段的操作宏方法定义在<linux/ioctl.h>中。

  1. type:幻数,这个字段有8位宽(_IO_TYPEBITS)

  2. number:序数,这个字段也是8位宽(_IO_NRBITS)

  3. direction:命令传输方向,这个字段是2位宽度(_IOC_DIRBITS)

  4. 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

你可能感兴趣的:(ioctl)