【Linux】设备驱动中的ioctl详解

在这里插入图片描述

在Linux设备驱动开发中,ioctl(输入输出控制)是一个非常重要的接口,用于用户空间应用程序与内核空间设备驱动之间进行通信。通过ioctl,应用程序可以发送命令给设备驱动,控制设备的行为或获取设备的状态信息。本文将详细介绍ioctl的基本原理、实现方法及其应用场景,并给出相应的示例代码。

1. ioctl概述

ioctl 是一个通用的系统调用,用于对打开的文件描述符执行各种控制操作。在Linux中,ioctl 主要有两个用途:

  1. 控制设备:应用程序可以通过ioctl发送命令给设备驱动,实现对设备的控制。
  2. 获取设备信息:应用程序可以通过ioctl从设备驱动获取设备的状态信息。

2. ioctl的基本原理

2.1 ioctl函数原型

ioctl 的函数原型如下:

#include <unistd.h>
int ioctl(int fd, unsigned long request, ...);
  • fd:文件描述符,通常通过open函数获得。
  • request:指定的控制命令,通常是一个宏定义。
  • ...:命令相关的参数,根据不同的命令可能需要传递不同的参数。
2.2 ioctl命令定义

在内核空间,每个ioctl命令都由一个宏定义来表示。这个宏定义通常包含命令的类型(读、写、读写)、命令号、数据类型和数据长度等信息。常用的宏定义包括:

#define _IOC(dir, type, nr, len) \(((dir)  << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr)   << _IOC_NRSHIFT) |  \((len)  << _IOC_SIZESHIFT))#define _IO(type, nr) _IOC(_IOC_NONE, (type), (nr), 0)
#define _IOR(type, nr, len) _IOC(_IOC_READ, (type), (nr), (len))
#define _IOW(type, nr, len) _IOC(_IOC_WRITE, (type), (nr), (len))
#define _IORW(type, nr, len) _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), (len))
  • _IO:用于没有参数的命令。
  • _IOR:用于从内核读取数据到用户空间。
  • _IOW:用于从用户空间写入数据到内核。
  • _IORW:用于读写操作。
2.3 ioctl的实现

在设备驱动中,需要实现一个unlocked_ioctl函数来处理来自用户空间的ioctl请求。通常情况下,还需要实现一个compat_ioctl函数来兼容32位和64位的用户空间。

static long my_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{// 实现ioctl处理逻辑
}static long my_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{// 实现兼容ioctl处理逻辑
}

3. ioctl的应用场景

3.1 控制设备

假设有一个简单的设备,用户空间应用程序可以通过ioctl来控制设备的开关。

3.2 获取设备信息

用户空间应用程序可以通过ioctl来获取设备的状态信息,如设备的工作模式、配置参数等。

4. 示例代码

下面是一个具体的示例,展示了如何在Linux设备驱动中实现ioctl接口。

4.1 定义设备结构
#define DEVICE_NAME_LEN 32
struct my_device {struct cdev cdev;struct class *class;struct device *device;dev_t devno;int state; // 设备状态
};
4.2 ioctl命令定义
#define MY_IOCTL_MAGIC 'M' // 自定义的命令类型// 开启设备
#define MY_IOCTL_OPEN _IO(MY_IOCTL_MAGIC, 0)// 关闭设备
#define MY_IOCTL_CLOSE _IO(MY_IOCTL_MAGIC, 1)// 获取设备状态
#define MY_IOCTL_GET_STATE _IOR(MY_IOCTL_MAGIC, 2, int)// 设置设备状态
#define MY_IOCTL_SET_STATE _IOW(MY_IOCTL_MAGIC, 3, int)
4.3 初始化模块
static int __init my_device_init(void)
{struct my_device *dev;int ret;dev = kzalloc(sizeof(struct my_device), GFP_KERNEL);if (!dev)return -ENOMEM;// 分配设备号alloc_chrdev_region(&dev->devno, 0, 1, "my_device");// 初始化字符设备dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_device_fops;cdev_init(&dev->cdev, &my_device_fops);ret = cdev_add(&dev->cdev, dev->devno, 1);if (ret)goto err_free_dev;// 创建设备类dev->class = class_create(THIS_MODULE, "my_device_class");if (IS_ERR(dev->class)) {ret = PTR_ERR(dev->class);goto err_free_cdev;}// 创建设备实例dev->device = device_create(dev->class, NULL, dev->devno, NULL, "my_device");if (IS_ERR(dev->device)) {ret = PTR_ERR(dev->device);goto err_free_class;}return 0;err_free_class:class_destroy(dev->class);
err_free_cdev:cdev_del(&dev->cdev);
err_free_dev:kfree(dev);return ret;
}module_init(my_device_init);
4.4 文件操作结构体
static const struct file_operations my_device_fops = {.owner       = THIS_MODULE,.open        = my_device_open,.release     = my_device_release,.unlocked_ioctl = my_device_ioctl,.compat_ioctl = my_device_compat_ioctl,
};static int my_device_open(struct inode *inode, struct file *file)
{// 实现设备打开逻辑return 0;
}static int my_device_release(struct inode *inode, struct file *file)
{// 实现设备关闭逻辑return 0;
}
4.5 实现ioctl处理函数
static long my_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct my_device *dev = filp->private_data;int ret = -EINVAL;switch (cmd) {case MY_IOCTL_OPEN:dev->state = 1; // 设备开启ret = 0;break;case MY_IOCTL_CLOSE:dev->state = 0; // 设备关闭ret = 0;break;case MY_IOCTL_GET_STATE:if (copy_to_user((int *)arg, &dev->state, sizeof(int))) {ret = -EFAULT;} else {ret = 0;}break;case MY_IOCTL_SET_STATE:if (copy_from_user(&dev->state, (int *)arg, sizeof(int))) {ret = -EFAULT;} else {ret = 0;}break;default:ret = -ENOTTY;break;}return ret;
}static long my_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct my_device *dev = filp->private_data;int ret = -EINVAL;union {int int_val;long long_val;} uval;switch (cmd) {case MY_IOCTL_GET_STATE:uval.int_val = dev->state;if (put_user(uval.long_val, (long *)arg)) {ret = -EFAULT;} else {ret = 0;}break;case MY_IOCTL_SET_STATE:if (get_user(uval.long_val, (long *)arg)) {ret = -EFAULT;} else {dev->state = uval.int_val;ret = 0;}break;default:ret = my_device_ioctl(filp, cmd, arg);break;}return ret;
}
4.6 清理模块
static void __exit my_device_exit(void)
{struct my_device *dev;// 获取设备结构dev = container_of(cdev, struct my_device, cdev);// 删除设备实例device_destroy(dev->class, dev->devno);// 销毁设备类class_destroy(dev->class);// 注销字符设备unregister_chrdev_region(dev->devno, 1);// 释放设备结构kfree(dev);
}module_exit(my_device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple device driver demonstrating ioctl usage.");

5. 用户空间示例

下面是一个简单的用户空间应用程序示例,展示了如何通过ioctl来控制设备。

5.1 用户空间程序
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#define MY_IOCTL_MAGIC 'M'#define MY_IOCTL_OPEN _IO(MY_IOCTL_MAGIC, 0)
#define MY_IOCTL_CLOSE _IO(MY_IOCTL_MAGIC, 1)
#define MY_IOCTL_GET_STATE _IOR(MY_IOCTL_MAGIC, 2, int)
#define MY_IOCTL_SET_STATE _IOW(MY_IOCTL_MAGIC, 3, int)int main()
{int fd;int state = 0;// 打开设备文件fd = open("/dev/my_device", O_RDWR);if (fd == -1) {perror("Failed to open device");return 1;}// 开启设备if (ioctl(fd, MY_IOCTL_OPEN) == -1) {perror("Failed to open device");close(fd);return 1;}// 设置设备状态state = 1;if (ioctl(fd, MY_IOCTL_SET_STATE, &state) == -1) {perror("Failed to set device state");close(fd);return 1;}// 获取设备状态if (ioctl(fd, MY_IOCTL_GET_STATE, &state) == -1) {perror("Failed to get device state");close(fd);return 1;}printf("Device state: %d\n", state);// 关闭设备if (ioctl(fd, MY_IOCTL_CLOSE) == -1) {perror("Failed to close device");close(fd);return 1;}// 关闭文件描述符close(fd);return 0;
}

6. 总结

ioctl 是Linux设备驱动开发中的重要接口之一,用于实现用户空间应用程序与内核空间设备驱动之间的通信。本文详细介绍了ioctl的基本原理、实现方法及其应用场景,并给出了相应的示例代码。希望上述内容能帮助读者更好地理解和掌握Linux设备驱动中的ioctl机制及其应用。在实际开发中,可以根据具体的需求选择合适的ioctl命令,并注意处理好用户空间与内核空间之间的数据传输。通过深入理解ioctl机制的底层原理,开发者可以更好地应对各种设备驱动开发中的挑战。

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

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

相关文章

再次梳理ISP的大致流程

前言&#xff1a; 随着智能手机的普及&#xff0c;相机与我们的生活越来越紧密相关。在日常生活中&#xff0c;我们只需要轻轻按下手机上的拍照按钮&#xff0c;就能记录下美好时刻。那么问题来了&#xff1a;从我们指尖按下拍照按钮到一张色彩丰富的照片呈现在我们面前&#x…

基于R语言森林生态系统的结构、功能与稳定性

在生态学研究中&#xff0c;森林生态系统的结构、功能与稳定性是核心研究内容之一。这些方面不仅关系到森林动态变化和物种多样性&#xff0c;还直接影响森林提供的生态服务功能及其应对环境变化的能力。森林生态系统的结构主要包括物种组成、树种多样性、树木的空间分布与密度…

nacos学习笔记(一)

1.前言 何为nacos&#xff0c;nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。可以说集注册中心&#xff0c;配置中心&#xff0c;服务管理于一起的平台。注册中心&#xff1a;相当于我们可以把服务注册到注册中心上&#xff0c;我们以后可以通过服…

在Java中实现集合排序

使用字面量的方式创建一个集合 //使用字面量的方式初始化一个List集合List<User> userList Arrays.asList(new User("小A",5),new User("小鑫",18),new User("小昌",8),new User("小鑫",8));注意&#xff1a;使用Arrays.asLis…

logback日志

一、使用两个以上spring环境变量做三目操作 <springProperty name"application_name" scope"context" source"spring.application.name"/><springProperty name"trace_app_name" scope"context" source"sprin…

mysql和redis的最大连接数

平时我们要评估mysql和redis的最大连接数&#xff0c;可以选择好环境&#xff08;比如4核8G&#xff09;,定好压测方法&#xff08;没有索引的mysql单表&#xff0c;redis单key&#xff09;进行压测&#xff0c;评估其最大并发量。 也可以查看各大云厂商的规格进行评估。 mys…

QEMU通过OVS实现联网

这篇笔记也是记录了一下自己的辛酸历程&#xff0c;仅供有需要的人参考。 首先关于qemu虚拟机的搭建&#xff0c;这不多赘述了&#xff0c;大家应该都会&#xff0c;这里可以给大家提供一个链接和一些命令。 QEMU搭建X86_64 Ubuntu虚拟系统环境https://blog.csdn.net/m0_531…

seleniun 自动化程序,python编程 我监控 chrome debug数据后 ,怎么获取控制台的信息呢

python 好的&#xff0c;使用 Python 来监控 Chrome 的调试数据并获取控制台信息&#xff0c;可以使用 websocket-client 库来连接 Chrome 的 WebSocket 接口。以下是一个详细的示例&#xff1a; 1. 安装必要的库 首先&#xff0c;你需要安装 websocket-client 库。可以使用…

IT面试求职系列主题-Jenkins

想成功求职&#xff0c;必要的IT技能一样不能少&#xff0c;先说说Jenkins的必会知识吧。 1) 什么是Jenkins Jenkins 是一个用 Java 编写的开源持续集成工具。它跟踪版本控制系统&#xff0c;并在发生更改时启动和监视构建系统。 2&#xff09;Maven、Ant和Jenkins有什么区别…

WEBRTC前端播放 播放器组件封装

组件封装 <template><div><div class"option"><input v-model"useStun" type"checkbox" /><label for"use-stun">Use STUN server</label></div><button click"startPlay"&g…

(五)ROS通信编程——参数服务器

前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享&#xff08;P2P&#xff09;。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据&#xff0c;关…

攻防靶场(34):隐蔽的计划任务提权 Funbox1

目录 1. 侦查 1.1 收集目标网络信息&#xff1a;IP地址 1.2 主动扫描&#xff1a;扫描IP地址段 1.3 搜索目标网站 2. 初始访问 2.1 有效账户&#xff1a;默认账户 2.2 利用面向公众的应用 2.3 有效账户&#xff1a;默认账户 3. 权限提升 3.1 计划任务/作业&#xff1a;Cron 靶场…

嵌入式入门Day38

C Day1 第一个C程序C中的输入输出输出操作coutcin练习 命名空间使用方法自定义命名空间冲突问题 C对字符串的扩充C风格字符串的使用定义以及初始化C风格字符串与C风格字符串的转换C风格的字符串的关系运算常用的成员变量输入方法 布尔类型C对堆区空间使用的扩充作业 第一个C程序…

kubernetes第七天

1.影响pod调度的因素 nodeName 节点名 resources 资源限制 hostNetwork 宿主机网络 污点 污点容忍 Pod亲和性 Pod反亲和性 节点亲和性 2.污点 通常是作用于worker节点上&#xff0c;其可以影响pod的调度 语法&#xff1a;key[value]:effect effect:[ɪˈfek…

docker minio镜像arm64架构

minio版本为RELEASE.2021-09-03T03-56-13Z 原项目信创改造&#xff0c;服务器资源改为了arm64架构&#xff0c;统信uos docker镜像库内没有对应的minio镜像&#xff0c;当前镜像为拉取源码后&#xff0c;自编译打包镜像&#xff0c;亲测可用。 使用方式 将tar包导入到服务器…

【DNS 阿里云,域名解析,解析到IP的指定端口】

- 进入 阿里云域名解析界面 - 点击 解析设置 - 添加记录 1.添加一条 A/AAAA 类型解析你的服务器的IP地址&#xff08;不需要带端口号&#xff0c;这条解析只是起到中转作用&#xff09; 示例&#xff1a;主机记录&#xff1a;aa.bb.com 记录值&#xff1a;xxx.xxx.xxx.xxx (…

前端实时显示当前在线人数的实现

实时显示当前在线人数的实现 本文档提供了在网页上实时显示当前在线人数的多种实现方法&#xff0c;包括使用 WebSocket 实现实时更新和轮询方式实现非实时更新。 方法一&#xff1a;使用 WebSocket 实现实时更新 服务器端设置 通过 Node.js 和 WebSocket 库&#xff08;如 …

线性表的接口定义及使用

定义接口 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace _001_线性表 {interface IListDS<T>//定义接口{int GetLength();void Clear();bool IsEmpty();void Add(T item);void Inser…

直流无刷电机控制(FOC):电流模式

目录 概述 1 系统框架结构 1.1 硬件模块介绍 1.2 硬件实物图 1.3 引脚接口定义 2 代码实现 2.1 软件架构 2.2 电流检测函数 3 电流环功能实现 3.1 代码实现 3.2 测试代码实现 4 测试 概述 本文主要介绍基于DengFOC的库函数&#xff0c;实现直流无刷电机控制&#x…

Vue3 + vue-virtual-scroller虚拟列表实现加载长列表

一、安装 github地址&#xff1a;https://vue-virtual-scroller-demo.netlify.app/chat demo运行地址&#xff1a;https://vue-virtual-scroller-demo.netlify.app/chat npm安装&#xff1a; npm install --save vue-virtual-scrollernextyarn安装&#xff1a; yarn add vu…