字符设备ioctl接口使用:
Linux驱动编写除了对设备进行读写数据之外,通常还希望可以对设备进行控制。
从应用层传递一些命令参数,并在驱动层实现相应设备操作,这时候就用到了 ioctl函数:
应用层:
#include <sys/ioctl.h>
/*
* @description : 应用层 ioctl
* @param - fd : 打开设备文件的时候获得文件描述符
* @param - request : 给驱动层传递的命令,需要注意的时候,驱动层的命令和应用层的命令一定要统一
* @param - "..." : 可变参数
* @return : 0 成功; -1 失败,同时设置errno
*/
int ioctl(int fd, unsigned long request, ...);
驱动层:
#include <linux/ioctl.h>
/*
* @description : 驱动层 ioctl
* @param - file * : 为打开字符设备文件的进程创建的结构体,用于存放文件的动态信息
* @param - cmd : 用户层传入的命令,根据不同的命令执行不一样的操作
* @param - arg : 用户层传入的可变参数
* @return : 0 成功; 失败,返回带错误码的负值
*/
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)
long (*compat_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)
//unlocked_ioctl,顾名思义,应该在无大内核锁(BKL)的情况下调用;
//compat_ioctl,compatible(兼容的),主要目的是为 64 位系统提供 32 位 ioctl 的兼容方法,同样也是在无大内核锁的情况下调用。
access_ok 函数:
/*
* @description : 检查用户空间中的内存地址是否可用
* @param - type : 检查的访问类型,可为 VERIFY_READ(可读) 或者 VERIFY_WRITE(可写),可写必可读。
* @param - addr : 用户空间的指针变量,其指向一个要检查的内存块开始处。
* @param - size : 检查的内存块大小
* @return : 该内存地址可用,则返回真(非0值),否则返回 0
*/
access_ok (type, addr, size);
ERRORS:
EBADF fd is not a valid descriptor. //该fd文件描述符无效
EFAULT argp references an inaccessible memory area. //参数argp引用了一个不可访问的内存区域
EINVAL request or argp is not valid. //命令request或参数argp无效。
ENOTTY fd is not associated with a character special device. //描述符fd与字符特殊设备没有关联,设备类型不匹配
ENOTTY The specified request does not apply to the kind of object that the descriptor fd references.//指定的请求不适用于描述符fd引用的对象的类型。
传递的命令参数(request ) :
在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段:
设备类型,type(device type),占据 8 bit,也叫做 “幻数” 或者 “魔数”,可以为任意 char 型字符,
例如’a’、‘b’、‘c’、‘k’ 等等,其作用是使 ioctl 命令有唯一的设备标识;
序列号,nr(number),占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增,代表这个设备的第几个命令;
方向,ioctl 命令访问模式(数据传输方向),dir(direction),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
数据尺寸,size,涉及到 ioctl 函数 第三个参数 arg ,占据 8~14bit,指定 arg 的数据类型及长度,表示了需要读写的参数大小,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;
ioctl_cmd.h
#ifndef __IOCTL_CMD_H__
#define __IOCTL_CMD_H__
/* 定义设备类型,任意 char 型字符 */
#define DEV_FIFO_TYPE 'k'
/*
* 访问模式:
* _IOC_NONE 不带参数操作,值为 0
* _IOC_READ 带读参数,读数据操作,值为 1
* _IOC_WRITE 带写参数,写数据操作,值为 2
* _IOC_READ|_IOC_WRITE 带读写参数,读写数据操作
*/
/*
* 宏
* _IO: 定义 不带参数的 ioctl 命令
* _IOR: 定义带 读参数的ioctl命令 (copy_to_user)
* _IOW: 定义带 写参数的 ioctl 命令 (copy_from_user)
* _IOWR: 定义带 读写参数的 ioctl 命令
*/
/* 不带参数操作,常用于初始化 */
#define DEV_FIFO_CLEAN _IO(DEV_FIFO_TYPE,0)
/* 读操作 */
#define DEV_FIFO_GETVALUE _IOR(DEV_FIFO_TYPE,1,int)
/* 写操作 */
#define DEV_FIFO_SETVALUE _IOW(DEV_FIFO_TYPE,2,int)
/* 命令数 */
#define DEV_FIFO_MAXNR 3
#endif
app_cdev.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "ioctl_cmd.h"
int main()
{
int fd;
int ret;
int num = 0;
char *filename = "/dev/hello";
/* 打开驱动文件 */
fd = open(filename,O_RDWR);
if(fd<0)
{
printf("Can't open file %s