Linux设备驱动之Ioctl控制

在 Linux 设备驱动开发中,ioctl(Input/Output Control)是一种重要的控制接口,允许用户空间程序通过自定义命令与内核驱动交互,实现设备配置、状态查询、特殊操作等功能。以下是 ioctl 的完整实现指南。


1. ioctl 的核心概念

​(1) 作用
  • 用户空间与驱动的交互:通过自定义命令实现设备控制(如设置波特率、读取寄存器值)。
  • 灵活的参数传递:支持传递整型、结构体等复杂数据类型。
​(2) 用户空间接口

用户程序通过系统调用 ioctl 发送命令和参数:

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long cmd, ... /* void *arg */);
​(3) 内核驱动接口

在 file_operations 中实现 unlocked_ioctl 方法:

long (*unlocked_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg);

2. ioctl 命令码的构造

Linux 内核定义了命令码的构造规则,需使用宏 _IO_IOR_IOW_IOWR 生成唯一的命令码。

含义参数方向
_IO(type, nr)无参数命令无数据传递
_IOR(type, nr, datatype)读数据命令驱动 → 用户空间
_IOW(type, nr, datatype)写数据命令用户空间 → 驱动
_IOWR(type, nr, datatype)双向读写命令用户空间 ↔ 驱动
  • 参数说明
    • type:幻数(Magic Number),8 位宽,用于区分命令组(如 'k' 表示自定义驱动)。
    • nr:命令序号(0~255),同一幻数下唯一。
    • datatype:数据类型的字节大小(如 int 对应 sizeof(int))。
示例:定义命令码
// 定义幻数(需唯一)
#define MYDEV_IOC_MAGIC 'k'

// 定义命令
#define MYDEV_GET_STATUS _IOR(MYDEV_IOC_MAGIC, 0, int)
#define MYDEV_SET_MODE   _IOW(MYDEV_IOC_MAGIC, 1, int)
#define MYDEV_READ_DATA  _IOWR(MYDEV_IOC_MAGIC, 2, struct mydev_data)

3. 驱动中的 ioctl 实现

​(1) 基本框架
static long mydev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
    struct mydev_private *priv = filp->private_data;
    void __user *argp = (void __user *)arg;

    switch (cmd) {
        case MYDEV_GET_STATUS:
            // 从驱动读取状态到用户空间
            return mydev_get_status(priv, argp);
        case MYDEV_SET_MODE:
            // 从用户空间设置模式到驱动
            return mydev_set_mode(priv, argp);
        case MYDEV_READ_DATA:
            // 双向数据传递
            return mydev_read_data(priv, argp);
        default:
            return -ENOTTY; // 无效命令
    }
}
​(2) 参数处理示例
简单整型参数
case MYDEV_SET_MODE: {
    int mode;
    if (copy_from_user(&mode, argp, sizeof(int)))
        return -EFAULT;
    priv->mode = mode;
    return 0;
}
结构体参数
struct mydev_data {
    int id;
    char buf[32];
};

case MYDEV_READ_DATA: {
    struct mydev_data data;
    if (copy_from_user(&data, argp, sizeof(data)))
        return -EFAULT;

    // 处理数据(例如根据 id 填充 buf)
    snprintf(data.buf, sizeof(data.buf), "Data for ID %d", data.id);

    // 将结果拷贝回用户空间
    if (copy_to_user(argp, &data, sizeof(data)))
        return -EFAULT;
    return 0;
}

4. 用户空间调用示例

​(1) 基础调用
#include <sys/ioctl.h>

int main() {
    int fd = open("/dev/mydevice", O_RDWR);
    int status;

    // 获取设备状态
    if (ioctl(fd, MYDEV_GET_STATUS, &status) < 0) {
        perror("ioctl failed");
        return -1;
    }
    printf("Device status: 0x%x\n", status);

    close(fd);
    return 0;
}
​(2) 结构体参数调用
struct mydev_data data = { .id = 123 };
if (ioctl(fd, MYDEV_READ_DATA, &data) < 0) {
    perror("ioctl failed");
    return -1;
}
printf("Received data: %s\n", data.buf);

5. 安全性关键点

​(1) 用户指针验证
  • access_ok 检查:确保用户空间地址合法。
    if (!access_ok(VERIFY_READ, argp, sizeof(int)) || 
        !access_ok(VERIFY_WRITE, argp, sizeof(int))) {
        return -EFAULT;
    }
  • copy_from_user/copy_to_user:始终使用安全拷贝函数。
​(2) 命令权限控制
  • 结合 CAP_SYS_ADMIN 等权能限制敏感命令:
    if (!capable(CAP_SYS_ADMIN))
        return -EPERM;
​(3) 防止整数溢出

校验用户传入的缓冲区大小:

if (data.size > MAX_BUFFER_SIZE)
    return -EINVAL;

6. 高级用法

​(1) 兼容 32/64 位用户空间
  • 使用 compat_ioctl 处理 32 位程序与 64 位内核的兼容性:
    #ifdef CONFIG_COMPAT
    static long mydev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
        // 转换 32 位参数到 64 位格式
        return mydev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
    }
    #endif
    
    static struct file_operations mydev_fops = {
        .unlocked_ioctl = mydev_ioctl,
        #ifdef CONFIG_COMPAT
        .compat_ioctl = mydev_compat_ioctl,
        #endif
    };
​(2) 可变长度数据传递

通过指针和大小参数动态处理数据:

struct variable_data {
    size_t size;
    void __user *data;
};

case MYDEV_VARIABLE_DATA: {
    struct variable_data vdata;
    if (copy_from_user(&vdata, argp, sizeof(vdata)))
        return -EFAULT;

    char *buf = kmalloc(vdata.size, GFP_KERNEL);
    if (copy_from_user(buf, vdata.data, vdata.size)) {
        kfree(buf);
        return -EFAULT;
    }

    // 处理数据...
    kfree(buf);
    return 0;
}

7. 调试与错误处理

​(1) 错误码列表
  • -ENOTTY:无效命令。
  • -EFAULT:用户空间地址非法。
  • -EINVAL:参数无效。
  • -EPERM:权限不足。
​(2) 内核日志追踪

在 ioctl 中添加调试日志:

pr_debug("Received command 0x%x, arg=0x%lx\n", cmd, arg);
​(3) 用户空间测试工具

使用 strace 跟踪 ioctl 调用:

strace -e trace=ioctl ./user_app

总结

  • 命令码构造:使用 _IO_IOR_IOW_IOWR 宏确保唯一性。
  • 参数处理:通过 copy_from_user/copy_to_user 安全拷贝数据。
  • 安全验证:检查用户地址合法性和命令权限。
  • 兼容性:支持 32/64 位混合环境。

合理设计 ioctl 接口,可显著提升驱动的灵活性和安全性,是设备控制交互的核心手段。

参考:

  1. (十二)ioctl的介绍和使用_ioctl读写数据-优快云博客 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浩瀚之水_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值