Linux系统编程——文件(ioctl 函数)

news2024/9/23 13:30:22

文章目录

  • 概念
  • 用户空间 ioctl
  • 驱动程序 ioctl
  • ioctl 在用户与驱动之间的协议——命令码
  • 实例分析
    • ioctl-test.h
    • ioctl-test-driver.c
    • ioctl-test.c

概念

  • ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,
  • 在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现
  • 用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情,所以分为用户空间ioctl和驱动程序ioctl。

用户空间 ioctl

#include <sys/ioctl.h> 

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

参数描述:

  • fd 文件描述符
  • cmd 交互协议,设备驱动将根据 cmd 执行对应操作
  • … 可变参数 arg,依赖 cmd 指定长度以及类型,根据 request 命令,设备驱动程序返回输出的数据。

返回值:

  • 成功时返回 0,
  • 失败则返回 -1 并设置全局变量 errorno 值,因此,在用户空间使用 ioctl 时,可以使用strerror函数得到出错判断以及处理:
int ret;
ret = ioctl(fd, MYCMD);
if (ret == -1) {
    printf("ioctl: %s\n", strerror(errno));
}

ioctl 的作用非常强大、灵活。不同的驱动程序内部会实现不同的 ioctl,APP 可以使用各种 ioctl 跟驱动程序交互:可以传数据给驱动程序,也可以从驱动程序中读出数据。

驱动程序 ioctl

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

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

ioctl 在用户与驱动之间的协议——命令码

在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径

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

在这里插入图片描述
这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,在内核中,提供了宏接口以生成上述格式的 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 命令访问模式(数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据
  • type(device type),设备类型,占据 8 bit,可以为任意 char 型字符,例如‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识
  • nr(number),命令编号/序数,占据 8 bit,可以为任意 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 */
//定义不带参数的 ioctl 命令,一般用于初始化设备
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
// 定义带读参数的ioctl命令(copy_to_user)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
// 定义带写参数的 ioctl 命令(copy_from_user)
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
//定义带读写参数的 ioctl 命令
#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(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)

检测函数access_ok():

static inline int access_ok(int type, const void *addr, unsigned long size)
/*
type :是VERIFY_READ 或者VERIFY_WRITE用来表明是读用户内存还是写用户内存;
addr:是要操作的用户内存地址;
size:是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数就等于sizeof(int);

返回值:Access_ok返回一个布尔值:1,是成功(存取没问题);0,是失败,ioctl返回-EFAULT;
*/

实例分析

功能:

  • 假设一个带寄存器的设备,设计了一个 ioctl 接口实现:设备初始化、读写寄存器等功能。在本例中,为了携带更多的数据,ioctl 的第三个可变参数为指针类型,指向自定义的结构体 struct msg

ioctl-test.h

用户空间和内核空间共用的头文件,包含 ioctl 命令及相关宏定义,可以理解为一份 “协议” 文件,代码如下:

// ioctl-test.h

#ifndef __IOCTL_TEST_H__
#define __IOCTL_TEST_H__

#include <linux/ioctl.h>    // 内核空间
// #include <sys/ioctl.h>   // 用户空间

/* 定义设备类型 */
#define IOC_MAGIC  'c'

/* 初始化设备 */
#define IOCINIT    _IO(IOC_MAGIC, 0)

/* 读寄存器 */
#define IOCGREG    _IOW(IOC_MAGIC, 1, int)

/* 写寄存器 */
#define IOCWREG    _IOR(IOC_MAGIC, 2, int)

#define IOC_MAXNR  3

//携带的数据
struct msg {
    int addr;
    unsigned int data;
};

#endif

ioctl-test-driver.c

// ioctl-test-driver.c
......

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = test_open,
    .release = test_close,
    .read = test_read,
    .write = etst_write,
    .unlocked_ioctl = test_ioctl,
};

......

static long test_ioctl(struct file *file, unsigned int cmd, \
                        unsigned long arg)
{
    //printk("[%s]\n", __func__);

    int ret;
    struct msg my_msg;

    /* 检查设备类型 */
    if (_IOC_TYPE(cmd) != IOC_MAGIC) {
        pr_err("[%s] command type [%c] error!\n", \
            __func__, _IOC_TYPE(cmd));
        return -ENOTTY; 
    }

    /* 检查序数 */
    if (_IOC_NR(cmd) > IOC_MAXNR) { 
        pr_err("[%s] command numer [%d] exceeded!\n", 
            __func__, _IOC_NR(cmd));
        return -ENOTTY;
    }    

    /* 检查访问模式 */
    if (_IOC_DIR(cmd) & _IOC_READ)
        ret= !access_ok(VERIFY_WRITE, (void __user *)arg, \
                _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        ret= !access_ok(VERIFY_READ, (void __user *)arg, \
                _IOC_SIZE(cmd));
    if (ret)
        return -EFAULT;

    switch(cmd) {
	    /* 初始化设备 */
	    case IOCINIT:
	        init();
	        break;
	
	    /* 读寄存器 */
	    case IOCGREG:
	        ret = copy_from_user(&msg, \
	            (struct msg __user *)arg, sizeof(my_msg));
	        if (ret) 
	            return -EFAULT;
	        msg->data = read_reg(msg->addr);
	        ret = copy_to_user((struct msg __user *)arg, \
	                &msg, sizeof(my_msg));
	        if (ret) 
	            return -EFAULT;
	        break;
	
	    /* 写寄存器 */
	    case IOCWREG:
	        ret = copy_from_user(&msg, \
	            (struct msg __user *)arg, sizeof(my_msg));
	        if (ret) 
	            return -EFAULT;
	        write_reg(msg->addr, msg->data);
	        break;
	
	    default:
	        return -ENOTTY;
	    }
	
    return 0;
}

ioctl-test.c

// ioctl-test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h> 

#include "ioctl-test.h"

int main(int argc, char **argv)
{

    int fd;
    int ret;
    struct msg my_msg;

    fd = open("/dev/ioctl-test", O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(-2);
    }

    /* 初始化设备 */
    ret = ioctl(fd, IOCINIT);
    if (ret) {
        perror("ioctl init:");
        exit(-3);
    }

    /* 往寄存器0x01写入数据0xef */
    memset(&my_msg, 0, sizeof(my_msg));
    my_msg.addr = 0x01;
    my_msg.data = 0xef;
    ret = ioctl(fd, IOCWREG, &my_msg);
    if (ret) {
        perror("ioctl read:");
        exit(-4);
    }

    /* 读寄存器0x01 */
    memset(&my_msg, 0, sizeof(my_msg));
    my_msg.addr = 0x01;
    ret = ioctl(fd, IOCGREG, &my_msg);
    if (ret) {
        perror("ioctl write");
        exit(-5);
    }
    printf("read: %#x\n", my_msg.data);

    return 0;
}

参考博文:
linux 内核 - ioctl 函数详解
Linux设备驱动之Ioctl控制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/758886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GitOps自问自答

GitOps自提出以来受到很多关注&#xff0c;被认为是云原生最佳实践之一。这篇文章回答了关于GitOps的常见问题&#xff0c;帮助感兴趣的相关人员更好理解这一实践。原文: GitOps[1] 自从Weaveworks在2017年提出GitOps以来&#xff0c;已经在Twitter和KubeCon上引发了不少争议。…

Qt实现思维导图功能6『鹰眼视图』

前文链接&#xff1a;Qt实现思维导图功能5『纵向分布模式』 百度网盘体验地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1xotlkSPfG7E_37y_XPfDng 提取码&#xff1a;5li7效果图 1、动态演示效果&#xff1a; 思维导图-鹰眼视图 2、静态展示图片&#xff1a; 新…

Econ3107-econ5116-小组作业知识点精讲

对本文有疑问可以加微信 Tutor_0914联系。也可以访问我的个人辅导网站 &#xff1a; tutoryou 基本概念 option期权 期权&#xff08;Option&#xff09;&#xff0c;是一种选择权&#xff0c;指是一种能在未来某特定时间以特定价格买入或卖出一定数量的某种特定商品的权利…

Linux 知识学习总结

常用命名 du 作用&#xff1a;显示指定目录或文件所占磁盘空间大小。 示例&#xff1a; du -h 以K&#xff0c;M&#xff0c;G为单位自动适配显示 lxlx-virtual-machine:~/test/video$ du -h 1.2G du -m 指定以1MB为单位显示 lxlx-virtual-machine:~/test/video$ du -m 12…

Python潮流周刊#11:如何使用 Golang 运行 Python 代码?

你好&#xff0c;我是猫哥。这里每周分享优质的 Python 及通用技术内容&#xff0c;大部分为英文&#xff0c;已在小标题注明。&#xff08;标题取自其中一则分享&#xff0c;不代表全部内容都是该主题&#xff0c;特此声明。&#xff09; 本周刊精心筛选国内外的 250 信息源&a…

​注意力机制中的掩码详解

注意力机制的掩码允许我们发送不同长度的批次数据一次性的发送到transformer中。在代码中是通过将所有序列填充到相同的长度&#xff0c;然后使用“attention_mask”张量来识别哪些令牌是填充的来做到这一点&#xff0c;本文将详细介绍这个掩码的原理和机制。 我们先介绍下如果…

(简单)设计哈希集合 Java

为了实现哈希集合这一数据结构&#xff0c;有以下几个关键问题需要解决&#xff1a; 哈希函数&#xff1a;能够将集合中任意可能的元素映射到一个固定范围的整数值&#xff0c;并将该元素存储到整数值对应的地址上冲突处理&#xff1a;由于不同元素可能映射到相同的整数值&…

SpringBoot读取配置的方式

读取配置的几种方式 Spring Boot提供了多种方式来读取配置&#xff0c;下面是其中几种常用的方式&#xff1a; 使用application.properties或application.yml文件&#xff1a;在Spring Boot项目的classpath根目录下&#xff0c;可以创建一个名为application.properties或appli…

oc基本控件3

UIButton // // ViewController.m // OcDemoTest // // Created by Mac on 2023/7/14. //#import "ViewController.h"interface ViewController ()endimplementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 1 创建按钮对象UIButton *button…

涂鸦智能打造专业家庭智能生活助手,实现人机交互升级

近年来&#xff0c;智能家居设备的品类不断拓展&#xff0c;同时&#xff0c;人们对AI与智能家居的联动愈发憧憬。自然语言交互是未来人机交互的主要趋势之一&#xff0c;其关键在于使AI具备主动理解信息的能力&#xff0c;让用户的交互更轻松。如何将智能场景的交互变得更“善…

MySQL-DDL-表结构操作-创建-案例

案例 根据页面原型/需求创建表&#xff08;设计合理的数据类型、长度、约束&#xff09; 具体操作 在idea中使用可视化图形界面创建 具体操作如下&#xff1a; 在该界面中进行属性的创建&#xff0c;进行属性名称、数据类型、约束、描述等信息的填写最终运行结果如下&…

800V高压电驱动系统盘点

2023年上海车展共有23家厂商的63个电驱动产品&#xff0c;经过梳理&#xff0c;本次展出的800V高压电驱动共有13款&#xff0c;可以说电驱动全面进入高压化。800V电驱动是一个系统性的话题&#xff0c;对于电机而言&#xff0c;挑战的方向主要围绕高速、高压、散热&#xff0c;…

替换空格

替换空格 请实现一个函数&#xff0c;把字符串 s 中的每个空格替换成"%20"。 题目给的测试用例里有以下限制&#xff1a; 0 < s.length < 14。 split() 把字符串分割为子字符串数组 例如&#xff1a; var txt"ABCD EFGH IJKL MNOP QRSTU VWXYZ"; v…

微信小程序下拉选择

微信小程序中下拉框选择一般的交互方式有以下两种 直接下拉选择点击选择框后&#xff0c;弹出浮层进行选择 下边分别介绍两种方式的实现。在微信小程序中&#xff0c;这两种实现都需要修改三个文件 js 文件&#xff1a;下拉选择逻辑的具体实现 wxml 文件&#xff1a;下拉组件…

C#正则表达式校验某个字符串是否是合格的email

C#正则表达式校验某个字符串是否是合格的email 可以借助正则表达式校验某个字符串是否是合规的电子邮箱。对于邮箱的正则表达式有严格的模式&#xff0c;如&#xff1a;^[a-zA-Z0-9_&*-](?:\\.[a-zA-Z0-9_&*-])*(?:[a-zA-Z0-9-]\\.)[a-zA-Z]{2,7}$ 对应的C#实现如下…

TCP编程流程和粘包

目录 1、TCP编程流程 2、粘包 1、TCP编程流程 socket() 是创建套接字&#xff0c;返回值为监听套接字描述符&#xff0c;有了套接字才能通过网络进行数据的传输。创建套接字的参数要指定服务类型&#xff0c;TCP协议使用的是流式服务&#xff08;SOCK_STREAM&#xff09;。 b…

用Matlab听音乐 - 动态频谱

文章目录 高帧率版本效果: 定时器版本music_play主函数&#xff1a;定时器回调函数&#xff1a;效果: 高帧率版本 由于matlab这款科学计算软件本身庞大略显笨重&#xff0c;执行代码的速度受当前系统影响&#xff0c;很难做到严格定时仿真&#xff08;造成音画不同步&#xff…

互联网行业真的不行了吗?

文章目录 前言一、起因二、互联网真的完了吗&#xff1f;三、是不是要转行&#xff1f;四、十年磨一剑五、统一回复 前言 英雄算法联盟 - 七月集训 已经开始 16 天&#xff0c;八月算法集训 将于 08月01日 正式开始&#xff0c;目前已经提前开始报名&#xff0c;报名方式参见&a…

英国24所顶尖大学撤销禁令,更新AI使用规定!

自从ChatGPT展现了其高超的AI技术后&#xff0c;备受全球年轻人的喜爱。ChatGPT功能多样化&#xff0c;可以节省查阅复杂文献的时间、编写简单的Python代码、辅助学生理解知识点... 同时&#xff0c;ChatGPT引发的学术不诚信问题也让各大院校头疼不已。 连续数月以来&#xff…

js 浮点位数超过17位乘以10^18,精度丢失问题

我有一个浮点型 var num 9.963407954080194743 用num * (10 ** 18) 计算得出的结果是9963407954080195000, 但是我想要得到的结果是9963407954080194743 问ChatGPT问题得以解决&#xff1a; GPT提供的代码&#xff1a; import Big from big.js;const num1 new Big(9.9634…