天天看點

linux ioctl 應用程式,ioctl使用(驅動層和應用層)簡介

在使用者空間,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