• ioctl


    1. 概念

    ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

    在文件 I/O 中,ioctl 扮演着重要角色,本文将以驱动开发为侧重点,从用户空间到内核空间纵向分析 ioctl 函数。

    2. 用户空间 ioctl

    #include <sys/ioctl.h>
    
    int ioctl(int fd, int cmd, ...) ;
    参数    描述
    fd    文件描述符
    cmd    交互协议,设备驱动将根据 cmd 执行对应操作
    …    可变参数 arg,依赖 cmd 指定长度以及类型
    ioctl() 函数执行成功时返回 0,失败则返回 -1 并设置全局变量 errorno 值

    因此,在用户空间使用 ioctl 时,可以做如下的出错判断以及处理:

    int ret;
    ret = ioctl(fd, MYCMD,(unsigned long)&stPhyInfo);
    if (ret == -1)
    {
        printf("ioctl: %s
    ", strerror(errno));
    }

    在实际应用中,ioctl 最常见的 errorno 值为 ENOTTY(error not a typewriter),顾名思义,即第一个参数 fd 指向的不是一个字符设备,不支持 ioctl 操作,这时候应该检查前面的 open 函数是否出错或者设备路径是否正确。

    3. 驱动程序 ioctl

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

    在新版内核中, 与 取代了 。unlocked_ioctl,顾名思义,应该在无大内核锁(BKL)的情况下调用;compat_ioctl,compat 全称 compatible(兼容的),主要目的是为 64 位系统提供 32 位 ioctl 的兼容方法,也是在无大内核锁的情况下调用。

    在《Linux Kernel Development》中对两种 ioctl 方法有详细的解说。

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

    // fs/ioctl.c
    
    static long vfs_ioctl(struct file *filp, unsigned int cmd,
    unsigned long arg)
    {
        int error = -ENOTTY;
    
        if (!filp->f_op || !filp->f_op->unlocked_ioctl) 
            goto out;
    
        error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
        if (error == -ENOIOCTLCMD) {
            error = -ENOTTY;
        } 
        out:
        return error;
    }
    内核态// 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]
    ", __func__);
    
        int ret;
        struct msg my_msg;
    
        /* 检查设备类型 */
        if (_IOC_TYPE(cmd) != IOC_MAGIC) {
            pr_err("[%s] command type [%c] error!
    ", 
                __func__, _IOC_TYPE(cmd));
            return -ENOTTY; 
        }
    
        /* 检查序数 */
        if (_IOC_NR(cmd) > IOC_MAXNR) { 
            pr_err("[%s] command numer [%d] exceeded!
    ", 
                __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
    
    #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
    ", my_msg.data);
    
        return 0;
    }

    引用https://blog.csdn.net/qq_19923217/article/details/82698787。

  • 相关阅读:
    ORACLE数据库备份与恢复详解
    Oracle块,区,段
    Oracle触发器
    SQL
    Oracle 用户管理权限
    Mybatis_One
    面向对象编程应用实例
    面向对象之方法2
    面向对象之方法1
    面向对象之结构体2
  • 原文地址:https://www.cnblogs.com/ggzhangxiaochao/p/12895733.html
Copyright © 2020-2023  润新知