当前位置: 首页 > news >正文

【Linux】ioctl分析

简介

一个字符设备驱动通常会实现常规的openreleasereadwrite接口,但是如果需要扩展新的功能,通常以ioctl接口的方式实现。

user space vfs driver ioctl() sys_ioctl() ulocked_ioctl()或compat_ioctl() user space vfs driver

用户空间的ioctl

#include <sys/ioctl.h> 
int ioctl(int fd, int cmd, ...) ;
参数描述
fd文件描述符
cmd交互协议,设备驱动将根据cmd执行对应操作
可变参数arg,依赖cmd指定的长度以及类型
返回值执行成功时返回0,失败则返回-1

驱动中的ioctl

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

在新版内核中,unlocked_ioctl()与compat_ioctl()取代了ioctl()。unlocked_ioctl(),顾名思义,应该在无大内核锁(BKL)的情况下调用;compat_ioctl(),compat全称compatible(兼容的),主要目的是为64位系统提供32位ioctl的兼容方法,也是在无大内核锁的情况下调用。

在字符设备驱动开发中,一般情况下只要实现unlocked_ioctl()即可,因为在vfs层的代码是直接调用unlocked_ioctl()。

ioctl命令,用户与驱动之间的协议

前文提到ioctl方法第二个参数cmd为用户与驱动的“协议”,理论上可以为任意int型数据,可以为0、1、2、3……,但是为了确保该“协议”的唯一性,ioctl命令应该使用更科学严谨的方法赋值,在linux中,提供了一种ioctl命令的统一格式,将32位int型数据划分为四个位段,如下图所示:

img

在内核中,提供了宏接口以生成上述格式的ioctl命令:

// include/uapi/asm-generic/ioctl.h#define _IOC(dir,type,nr,size) \(((dir)  << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr)   << _IOC_NRSHIFT) | \((size) << _IOC_SIZESHIFT))
  • dir(direction),ioctl命令访问模式(数据传输方向),占据2bit,可以为_IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
  • type(device type),设备类型,占据8bit,在一些文献中翻译为“幻数”或者“魔数”,可以为任意char型字符,例如‘a’、‘b’、‘c’等等,其主要作用是使ioctl命令有唯一的设备标识。**tips:**Documentions/ioctl-number.txt记录了在内核中已经使用的“魔数”字符,为避免冲突,在自定义ioctl命令之前应该先查阅该文档。
  • nr(number),命令编号/序数,占据8bit,可以为任意unsigned char型数据,取值范围0~255,如果定义了多个ioctl命令,通常从0开始编号递增;
  • size,涉及到ioctl第三个参数arg,占据13bit或者14bit(体系相关,arm架构一般为14位),指定了arg的数据类型及长度,如果在驱动的ioctl实现中不检查,通常可以忽略该参数。

通常而言,为了方便会使用宏_IOC()衍生的接口来直接定义ioctl命令:

// include/uapi/asm-generic/ioctl.h/* 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)))

同时,内核还提供了反向解析ioctl命令的宏接口:

// include/uapi/asm-generic/ioctl.h/* used to decode ioctl numbers */
#define _IOC_DIR(cmd)        (((cmd) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(cmd)       (((cmd) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(cmd)     (((cmd) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(cmd)       (((cmd) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

示例

#ifndef __HAPTCIS_IOCTL_H__
#define __HAPTCIS_IOCTL_H__/** @Date: 2024-10-12 15:53:37* @LastEditors: zdk* @LastEditTime: 2024-10-14 11:47:49* @FilePath: \kernel\drivers\haptics\haptics_ioctl.h*/#include <linux/ioctl.h>
// #include <sys/ioctl.h>   // 用户空间/*这里使用ioctl定义两个协议,读寄存器和写寄存器
*用户空间和内核空间共用的头文件,包含ioctl命令及相关宏定义,可以理解为一份“协议”文件
*/
//cmd中的type
#define DEVICE_TYPE           'H'
#define HAPTICS_READ_REG_NR   (0)
#define HAPTICS_WRITE_REG_NR  (1)
#define HAPTICS_READ_REG      _IOR(DEVICE_TYPE,HAPTICS_READ_REG_NR,ioctl_protocol_t *)  
#define HAPTCIS_WRITE_REG     _IOW(DEVICE_TYPE,HAPTICS_WRITE_REG_NR,ioctl_protocol_t *)typedef struct
{uint8_t reg_addr;uint8_t reg_value;
}ioctl_protocol_t;#endif

这个头文件是共用的,因为他定义了协议的具体内容。

/**  Silicon Integrated Co., Ltd haptic sih688x haptic driver file**  Copyright (c) 2021 heater <daokuan.zhu@si-in.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include <linux/init.h>  //包含宏定义的头文件
#include <linux/module.h>   //包含初始化加载模块的头文件
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include "haptics_ioctl.h"#define HAPTICS_MISC_DEV_NAME  "haptics"//打开设备
static int haptics_open(struct inode* inode,struct file * filp)
{printk("%s\n",__FUNCTION__);return 0;
}//关闭设备
static int haptics_release(struct inode* inode ,struct file* filp)
{printk("%s\n",__FUNCTION__);return 0;
}//ioctl
static long haptics_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{int ret = 0;ioctl_protocol_t msg;//反解cmd中的字段int type = _IOC_TYPE(cmd);int dir = _IOC_DIR(cmd);int nr = _IOC_NR(cmd);int size = _IOC_SIZE(cmd);printk("dir=%d size=%d\n",dir,size);//检验cmd是否正确//1.校验cmd_typeif(DEVICE_TYPE != type){printk(KERN_ERR "cmd type error\n");ret =-1;return ret;}if(HAPTICS_READ_REG == cmd)//读寄存器{//校验nrif(HAPTICS_READ_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("read_reg:%#02x\n",msg.reg_addr);msg.reg_value=0xff;//这里模拟读寄存器的值//将读取到的值传给用户空间ret = copy_to_user((ioctl_protocol_t __user *)arg, &msg, sizeof(ioctl_protocol_t));}}else if(HAPTCIS_WRITE_REG == cmd)//写寄存器{//校验nrif(HAPTICS_WRITE_REG_NR == nr){ret = copy_from_user(&msg, (ioctl_protocol_t __user *)arg, sizeof(ioctl_protocol_t));printk("write_reg:%#02x=%#02x\n",msg.reg_addr,msg.reg_value);//模拟写寄存器的值}}else//{printk(KERN_ERR "unknown cmd\n");}return ret;
}static struct file_operations haptics_fops=
{.owner = THIS_MODULE,.open = haptics_open,.release = haptics_release,.unlocked_ioctl = haptics_ioctl,
};struct miscdevice mdev =
{.minor = MISC_DYNAMIC_MINOR,.name = HAPTICS_MISC_DEV_NAME,.fops = &haptics_fops,
};//定义一个杂项设备结构体static int __init haptics_init(void)
{int ret = 0;//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "%s\n",__FUNCTION__); //输出等级为0ret = misc_register(&mdev);if(0 == ret){printk(KERN_EMERG "misc_register ok minor=%d\n",mdev.minor);}return 0;
}static void __exit haptics_exit(void)
{misc_deregister(&mdev);printk(KERN_EMERG "%s\n",__FUNCTION__); //输出等级为0
}module_init(haptics_init);//驱动入口
module_exit(haptics_exit);//驱动出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptics Driver V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本

上面是驱动代码,负责解析ioctl命令。

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "haptics_ioctl.h"
/*
argc:应用程序参数个数,包括应用程序本身
argv[]:具体的参数内容,字符串形式
./main <filename> <r:w> r表示读 w表示写
*/int main(int argc,char * argv[])
{ioctl_protocol_t msg;char* filename;int fd=0;if(argc!=3){printf("error usage\n");return -1;}filename=argv[1];fd = open(filename,O_RDWR);if(fd<0){printf("can not open file %s\n",filename);return -2;}if(0 == strcmp(argv[2],"r")){msg.reg_addr = 0x01;ioctl(fd,HAPTICS_READ_REG,&msg);printf("read %#02x=%#02x\n", msg.reg_addr,msg.reg_value);}else if(0 == strcmp(argv[2],"w")){msg.reg_addr = 0x01;msg.reg_value = 0xff;ioctl(fd,HAPTCIS_WRITE_REG,&msg);}else{printf("error usage\n");}close(fd);
}

上面是应用层代码,负责发送ioctl消息。

在这里插入图片描述

注意:

  • _IOR中第三个参数size是参数的大小,用于驱动中解析使用,但是如果是固定长度,这个参数也没有用到。理论上使用_IOR_IOW_IO是没有严格区别的,所以我们上述代码中可以改为\#define HAPTICS_READ_REG _IO(DEVICE_TYPE,HAPTICS_READ_REG_NR)
  • ioctl函数中第三个参数,表示传入驱动的实际参数,可以是一个整型值,也可以是一个结构体指针。我们示例中是传入的结构体指针。
http://www.lryc.cn/news/460167.html

相关文章:

  • 物联网通信会给人们的生活带来什么样的变化
  • Android 中获取当前 CPU 频率和占用率
  • pymobiledevice3使用介绍(安装、常用命令、访问iOS沙盒目录)
  • python 爬虫模拟登录
  • AOP基础、快速入门、进阶
  • 哪款宠物空净运行吸毛好、噪音小?希喂、霍尼韦尔、安德迈测评!
  • 新兴的安全职业挑战
  • 代码随想录算法训练营Day32 | 122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组和
  • 3D Slicer 教程一
  • github pages + hugo 搭建静态博客网站
  • Python爬虫如何爬取并解析JSON数据
  • 【C++】精妙的哈希算法
  • 智慧链动青春:国家区块链中心接待北京市十一学校青少年访学探索
  • 利用C++封装鼠标轨迹算法为DLL:游戏行为检测的利器
  • Qt- QSS风格选择器常用属性选择器样式表盒子
  • 粤智助自助一体机大厂浮出水面 OBOO鸥柏已成服务终端中坚力量
  • SpringBoot-application.properties配置
  • STM32-ADC模数转换
  • lspci | grep VGA
  • 智慧厂区车辆导航解决方案;智慧工厂电子地图应用解决方案;大型工厂内部导航解决方案;智慧工厂可视化地图应用方案
  • 决策树C4.5算法详解及实现
  • prompt learning
  • 适用于 Windows 11 的 5 大数据恢复软件 [免费和付费]
  • vue实现获取当前时间并实时显示
  • 【论文阅读】SRCNN
  • 数据结构与算法——Java实现 32.堆
  • 深度学习 .dot()
  • idea2024 git merge 时丢失 Merge remote-tracking branch问题
  • pdf怎么删除多余不想要的页面?删除pdf多余页面的多个方法
  • 树莓派应用--AI项目实战篇来啦-3.OpenCV 读取写入和显示图像