
这篇文章来讲讲:ioctl的概念、函数的使用方法。
在之前的文章中,驱动程序都是使用read()和write()来操作设备,但是大部分的驱动程序还需要另外一种能力,就是通过设备驱动程序执行各种类型的硬件控制。比如:用户控件经常会请求设备锁门、弹出介质、报告错误信息、改变波特率或执行破坏等操作。这些操作通常是通过ioctl方法来实现。
用户空间:
ioctl系统调用函数原型:int ioctl(int fd,int cmd, ...);
其中:fd 使要操作的文件描述符
cmd:控制命令
...:可选参数,具体形式依赖于第二个参数cmd,因为有些控制命令需要参数,有些命令不需要参数。
内核驱动程序:
ioctl方法函数原型:int (*ioctl)(struct inode *inode,struct file *filp,unsigned intcmd,unsigned long arg);
参数inode和filp:和open方法的参数一样,表示文件节点和打开的file结构
参数cmd:由用户空间不经修改地传递进来
参数arg:使用户空间传递的整数值;如果用户空间的调用程序没有第三个参数,则驱动的arg参数为未定义状态。

其中ioctl方法中的cmd:
1需要选择对应不同命令的编号。
2为了防止对错误的设备使用正确的命令,命令号必须在系统范围内唯一。
3内核规定每一个命令号被分为4个位段,对于ARM来说,4个位段的含义分别是:
dr | sizes | type | number |
|---|
在内核头文件<asm-generic/ioctl.h>中,
提供了一些宏来构造命令号:
_IO(type,nr):用于构造无参数的命令号
_IOR(type,nr,datetype):用于构造从驱动程序中读取数据的命令号
_IOW(type,nr,datatype):用于构造向驱动程序写入数据的命令号
_IORW(type,nr,datatype):用于构造双向传输的命令号
解开位字段的宏:
_IOC_DIR(cmd):获得传输方向位段的值
_IOC_WRITE应用层写数据掩码,_IOC_READ应用层读数据掩码
_IOC_TYPE(cmd):获得类型的值
_IOC_NR(cmd);获得编号的值
_IOC_SIZE(cmd):获得大小的值
例程实现:
首先需要定义头文件,用于定义命令编号,提供给内核和应用层使用。这里定义了两个编号,分别是读命令号,写命令号。
#ifndef __CMD_H
#define __CMD_H
#include <linux/ioctl.h>
#define BIN_TYPE 'f'
#define BIN_SET _IOW(BIN_TYPE, 1, char)
#define BIN_GET _IOR(BIN_TYPE, 2, char)
#endif驱动程序的实现:加入ioctl方法,并在将该方法注册到file_operations结构体中:
static long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case BIN_SET:
copy_from_user(&gpio_val, (void __user *)arg, sizeof(char));
GPIO_SET_PIN(GPF4, gpio_val);
break;
case BIN_GET:
gpio_val = GPIO_GET_PIN(GPF4);
copy_to_user((void __user *)arg, &gpio_val, sizeof(char));
break;
}
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = led_open,
.ioctl = led_ioctl,
};应用层的实现:
#include <fcntl.h>
#include <sys/ioctl.h>
#include "cmd.h"
#define file_name "/dev/led"
int main(void)
{
int fd;
char val;
fd = open(file_name, O_RDWR);
if(fd < 0) {
printf("error: can`t open %s", file_name);
return 0;
}
val = 1;
ioctl(fd,BIN_SET, &val);
ioctl(fd, BIN_GET, &val);
printf("ioctl: get val1 %d\n",val);
val = 0;
ioctl(fd, BIN_SET, &val);
ioctl(fd, BIN_GET, &val);
printf("ioctl: get val2 %d\n",val);
return 0;
}实验结果:
