【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机制的底层原理,开发者可以更好地应对各种设备驱动开发中的挑战。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux上使用cmake编译的方法

一、hello 例程仅基于一个cpp文件 C文件或工程进行编译时可以使用g指令&#xff08;需要对每一个程序和源文件分别使用g指令编译&#xff09;&#xff0c;当程序变大时&#xff0c;一个工程文件往往会包含很文件夹和源文件&#xff0c;这时我们需要的编译指令将越来越长&#…

(vue)el-table-column type=“selection“表格选框怎么根据条件添加禁选

(vue)el-table-column type"selection"表格选框怎么根据条件添加禁选 html <el-table:data"tableData"style"width: 100%"><el-table-columntype"selection"width"55":selectable"checkSelectable">…

linux nginx 安装后,发现SSL模块未安装,如何处理?

&#x1f468;‍⚕ 主页&#xff1a; gis分享者 &#x1f468;‍⚕ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕ 收录于专栏&#xff1a;运维工程师 文章目录 前言SSL模块安装 前言 nginx 安装后&#xff0c;发现SSL模块未安装&…

kubeneters-循序渐进Cilium网络(二)

文章目录 概要IP 地址配置接口配置解析结论 概要 接续前一章节&#xff0c;我们还是以这张图继续深入Cilium网络世界 IP 地址配置 通过检查 Kubernetes 集群的当前环境&#xff0c;可以获取实际的 IP 地址和配置信息。这些信息将被补充到之前的网络示意图中&#xff0c;以使…

虚拟机使用MQ及介绍

mq官网&#xff1a;https://www.rabbitmq.com 一、虚拟机与 MQ 的结合优势 隔离与安全&#xff1a;虚拟机为 MQ 的运行提供了一个独立的环境&#xff0c;与宿主机以及其他虚拟机相互隔离。这意味着即使 MQ 所在的虚拟机出现故障或遭受安全威胁&#xff0c;也不会直接影响到宿主…

比亚迪夏直插家用MPV腹地,“迪王”开启全面销冠新征程

文/王俣祺 导语&#xff1a;比亚迪前脚刚收获2024年的全面成功&#xff0c;后脚立刻就开始布局2025年的产品矩阵了。比亚迪夏的横空出世&#xff0c;看来家用MPV市场也要感受“迪王”的恐怖如斯了。 家用MPV市场的“意外之喜” 1月8日&#xff0c;比亚迪夏终于在万众瞩目之下…

c++入门之 命名空间与输入输出

1、命名空间 1.1使用命名空间的原因 先看一个例子&#xff1a; #include <iostream>int round 0;int main() {printf("%d", round);return 0; }请问&#xff0c;这个程序能跑起来吗&#xff1f; 答案是否定的 原因是&#xff0c;当我们想创建一个全局变量 …

php 使用simplexml_load_string转换xml数据格式失败

本文介绍如何使用php函数解析xml数据为数组。 <?php$a <xml><ToUserName><![CDATA[ww8b77afac71336111]]></ToUserName><FromUserName><![CDATA[sys]]></FromUserName><CreateTime>1736328669</CreateTime><Ms…

12 USART串口通讯

1 串口物理层 两个设备的“DB9接口”之间通过串口信号建立连接&#xff0c;串口信号线中使用“RS232标准”传输数据信号。由于RS232电平标准的信号不能直接被控制器直接识别&#xff0c;所以这些信号会经过“电平转换芯片”转换成控制器能识别的“TTL校准”的电平信号&#xff…

FreePBX 17 on ubuntu24 with Asterisk 20

版本配置&#xff1a; FreePBX 17&#xff08;最新&#xff09; Asterisk 20&#xff08;最新Asterisk 22&#xff0c;但是FreePBX 17最新只支持Asterisk 21&#xff0c;但是21非LTS版本&#xff0c;所以选择Asterisk 20&#xff09; PHP 8.2 Maria DB (v10.11) Node J…

搜广推面经五

饿了么推荐算法 一、介绍InfoNCE Loss、InfoNCE温度系数的作用 InfoNCE Loss&#xff08;Information Noise Contrastive Estimation Loss&#xff09;是一种常用于自监督学习和对比学习中的损失函数&#xff0c;特别是在信息论和无监督学习中有广泛应用。 它的核心思想是通过…

机器学习免费使用的数据集及网站链接

机器学习领域存在许多可以免费使用的数据集&#xff0c;这些数据集来自于学习、研究、比赛等目的。 一、综合性数据集平台 1.Kaggle 网址&#xff1a;Kaggle 数据集https://www.kaggle.com/datasets Kaggle是一个数据科学竞赛和社区平台&#xff0c;提供了大量的数据集供用…

浅尝Appium自动化框架

浅尝Appium自动化框架 Appium自动化框架介绍Appium原理Appium使用安装平台驱动实战 坑 Appium自动化框架介绍 Appium 是一个开源的自动化测试框架&#xff0c;最初设计用于移动应用的测试&#xff0c;但现在它也扩展了对桌面端应用的支持。Appium 使得自动化测试变得更加简单&…

ubuntu 20.04 安装docker--小白学习之路

更新包 sudo apt-get update # 安装需要的软件包以使apt能够通过HTTPS使用仓库 sudo apt-get install ca-certificates curl gnupg lsb-release 使用清华大学源 # 添加Docker官方的GPG密钥 curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo…

MMDetection框架下的常见目标检测与分割模型综述与实践指南

目录 综述与实践指南 SSD (Single Shot MultiBox Detector) 基本配置和使用代码 RetinaNet 基本配置和使用代码 Faster R-CNN 基本配置和使用代码 Mask R-CNN 基本配置和使用代码 Cascade R-CNN 基本配置和使用代码 总结 综述与实践指南 MMDetection是一个基于Py…

语音机器人外呼的缺点

也许是因为经济形式变差&#xff0c;大部分都是消费降级的策略。企业也一样&#xff0c;开源不行就只能重点节流。以前10个人做的工作&#xff0c;希望能用2个语音机器人就能完成。确实语音机器人是可以大幅提升外呼效率的&#xff0c;节约成本也很明显&#xff0c;但是今天不说…

微机原理期末复习(一)

编程题 汇编语言程序的整体结构 STACK SEGMENT STACK STACKDW 100H DUP(?) TOP LABEL WORD ; 使用LEBEL获取栈的尾部偏移地址存储到TOP中&#xff0c;以便初始化sp STACK ENDSDATA SEGMENT... ; 用户定义的变量 DATA ENDSCODE SEGMENTASSUME CS: CODE, DS: DATA, ES: DATA, …

UML(统一建模语言)

目录 一、用例图&#xff08;Use Case Diagram&#xff09; 二、类图&#xff08;Class Diagram&#xff09; 2.1、泛化&#xff08;Generalization&#xff09; 2.2、实现&#xff08;Realization&#xff09; 2.3、关联&#xff08;Association&#xff09; 2.4、聚合&…

流浪猫流浪狗领养PHP网站源码

源码介绍 流浪猫流浪狗领养PHP网站源码&#xff0c;适合做猫狗宠物类的发信息发布。当然其他信息发布也是可以的。 导入数据库&#xff0c;修改数据库配置/application/database.php 设置TP伪静态&#xff0c;设置运行目录&#xff0c; 后台&#xff1a;/abcd.php/dashboard?…

轻量级适合阅读的优秀 C++ 开源项目

CTPL 这是一个现代简易版的、高效的C线程池库&#xff0c;代码行数500行左右。 代码示例&#xff1a; void first(int id) { std::cout << "hello from " << id << \n; } struct Second { void operator()(int id) const { std::cout << &q…