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

【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.kler.cn/a/503970.html

相关文章:

  • RuoYi-Vue-Plus 加入 GitCode:驱动多租户后台管理创新发展
  • JAVA:利用 RabbitMQ 死信队列实现支付超时场景的技术指南
  • 逻辑测试题
  • 开源项目stable-diffusion-webui部署及生成照片
  • SpringBoot-Day1
  • docker mysql5.7如何设置不区分大小写
  • 初学stm32 --- II2C_AT24C02,向EEPROM中读写数据
  • 如何通过 Nginx 配置防盗链保护静态资源(详细配置)
  • 抢十八游戏
  • web服务器+selinux实验
  • SQL面试题2:留存率问题
  • 1.14学习
  • 用 Python 从零开始创建神经网络(二十):模型评估
  • 《C++11》nullptr介绍:从NULL说起
  • 【前端】自学基础算法 -- 25.动态规划-01背包问题
  • CloudCompare视图透视问题与裁剪平面设置详解
  • RPC 源码解析~Apache Dubbo
  • 图像模糊度(清晰度)检测 EsFFT 算法详细分析
  • 测试模型安全的一些高级手段
  • Swagger学习⑲——@Webhook注解
  • 力扣6-合并两个有序链表
  • C++中引用参数与指针参数的区别与联系详解
  • Mysql 和 navicat 的使用
  • LeetCode 283题:移动零
  • 【动态规划-矩阵】4.三角形最小路径和
  • dockerfile2.0