• ioctl命令详解


    昨天复习了一下对于ioctl 的使用和实现

    举个例子 吧。
    /* ioctl command for BMA220 device file */
    static long bma220_ioctl(struct file *file,
    unsigned int cmd, unsigned long arg)
    {
    int err = 0;
    unsigned char data[6];
    struct bma220_data *sensor_data = i2c_get_clientdata(bma220_client);


    /* check cmd */
    if (_IOC_TYPE(cmd) != BMA220_IOC_MAGIC) {
    #if DEBUG
    printk(KERN_ERR "cmd magic type error\n");
    #endif
    return -ENOTTY;
    }
    if (_IOC_NR(cmd) > BMA220_IOC_MAXNR) {
    #if DEBUG
    printk(KERN_ERR "cmd number error\n");
    #endif
    return -ENOTTY;
    }


    if (_IOC_DIR(cmd) & _IOC_READ)
    err = !access_ok(VERIFY_WRITE,
    (void __user *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
    err = !access_ok(VERIFY_READ,
    (void __user *)arg, _IOC_SIZE(cmd));
    if (err) {
    #if DEBUG
    printk(KERN_ERR "cmd access_ok error\n");
    #endif
    return -EFAULT;
    }
    /* check bam120_client */
    if (bma220_client == NULL) {
    #if DEBUG
    printk(KERN_ERR "I2C driver not install\n");
    #endif
    return -EFAULT;
    }


    /* cmd mapping */
    switch (cmd) {
    case BMA220_IOCTL_EVENT_CTRL:
    if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_from_user error\n");
    #endif
    return -EFAULT;
    }
    printk(KERN_INFO "%s: set bma220_no_event = %d\n",
    __func__, data[0]);
    bma220_no_event = (int)data[0];
    return err;
    case BMA220_IOCTL_CALIBRATION:
    if (sensor_data->enabled == 0) {
    bma220_set_suspend_mode(1);
    bma220_i2c_delay(30);
    err = bma220_reset_offset_xyz();
    if (err)
    printk(KERN_ERR "%s: reset offset error!\n",
    __func__);
    else
    err = bma220_calibration();
    bma220_i2c_delay(30);
    bma220_set_suspend_mode(0);
    return err;
    }
    err = bma220_reset_offset_xyz();
    if (err)
    printk(KERN_ERR "%s: reset offset error!\n", __func__);
    else
    err = bma220_calibration();
    return err;
    case BMA220_IOCTL_WRITE:
    if (copy_from_user(data, (unsigned char *)arg, 2) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_from_user error\n");
    #endif
    return -EFAULT;
    }
    printk(KERN_INFO "%s: write = %x %x\n",
    __func__, data[1], data[0]);
    err = bma220_i2c_write(BMA220_I2C_ADDR, data[1], &data[0], 1);
    return err;


    case BMA220_IOCTL_READ:
    if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_from_user error\n");
    #endif
    return -EFAULT;
    }
    err = bma220_i2c_read(BMA220_I2C_ADDR, data[0], &data[0], 1);
    printk(KERN_INFO "%s: read = %d\n", __func__, data[0]);
    if (copy_to_user((unsigned char *)arg, data, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_to_user error\n");
    #endif
    return -EFAULT;
    }
    return err;


    case BMA220_SOFT_RESET:
    err = bma220_soft_reset();
    return err;


    case BMA220_SET_SUSPEND:
    if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_from_user error\n");
    #endif
    return -EFAULT;
    }
    bma220_set_suspend_mode((int)data[0]);
    return err;
     
    (signed char *)data, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_to_user error\n");
    #endif
    return -EFAULT;
    }
    return err;


    case BMA220_READ_ACCEL_Y:
    err = bma220_read_accel_y((signed char *)data);
    if (copy_to_user((signed char *)arg,
    (signed char *)data, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_to_user error\n");
    #endif
    return -EFAULT;
    }
    return err;


    case BMA220_READ_ACCEL_Z:
    err = bma220_read_accel_z((signed char *)data);
    if (copy_to_user((signed char *)arg,
    (signed char *)data, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_to_user error\n");
    #endif
    return -EFAULT;
    }
    return err;


    case BMA220_SET_EN_LOW:
    if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
    #if DEBUG
    printk(KERN_ERR "copy_from_user error\n");
    #endif
    return -EFAULT;
    }
    err = bma220_set_en_low(*data);
    return err;


    case BMA220_SET_EN_HIGH_XYZ:
     
    printk(KERN_ERR "copy_from_user error\n");
    #endif
    return -EFAULT;
    }
    err = bma220_set_en_tt_xyz(*data);
    return err;
     
     命令号的定义


    #define BMA220_IOC_MAGIC 'B'


    #define BMA220_SET_SLEEP_EN \
    _IOWR(BMA220_IOC_MAGIC, 0, unsigned char)
    #define BMA220_SET_SUSPEND \
    _IOW(BMA220_IOC_MAGIC, 1, unsigned char)
    #define BMA220_READ_ACCEL_X \
    _IOWR(BMA220_IOC_MAGIC, 2, signed char)
    #define BMA220_READ_ACCEL_Y \
    _IOWR(BMA220_IOC_MAGIC, 3, signed char)
    #define BMA220_READ_ACCEL_Z \
    _IOWR(BMA220_IOC_MAGIC, 4, signed char)
    #define BMA220_SET_MODE \
    _IOWR(BMA220_IOC_MAGIC, 5, unsigned char)
    #define BMA220_GET_MODE \
    _IOWR(BMA220_IOC_MAGIC, 6, unsigned char)
    #define BMA220_SET_RANGE \
    _IOWR(BMA220_IOC_MAGIC, 7, unsigned char)
    #define BMA220_GET_RANGE \
    _IOWR(BMA220_IOC_MAGIC, 8, unsigned char)
    #define BMA220_SET_BANDWIDTH \
    _IOWR(BMA220_IOC_MAGIC, 9, unsigned char)
    #define BMA220_GET_BANDWIDTH \
    _IOWR(BMA220_IOC_MAGIC, 10, unsigned char)
    #define BMA220_SET_SC_FILT_CONFIG \
    _IOWR(BMA220_IOC_MAGIC, 11, unsigned char)
    #define BMA220_RESET_INTERRUPT \
    _IO(BMA220_IOC_MAGIC, 12)
    #define BMA220_GET_DIRECTION_STATUS_REGISTER \
    _IOWR(BMA220_IOC_MAGIC, 13, unsigned char)
    #define BMA220_GET_INTERRUPT_STATUS_REGISTER \
    _IOWR(BMA220_IOC_MAGIC, 14, unsigned char)
     
     ..................
    #define BMA220_IOC_MAXNR 100












    功能:




    大部分驱动除了需要具备读写设备的能力外,还需要具备对硬件控制的能力。例如,要求设备报告错误信息,改变波特率,这些操作常常通过ioctl方法来实现。




    用户使用方法:




    在用户空间,使用ioctl系统调用来控制设备,原型如下:




    int ioctl(int fd,unsigned long cmd,...);
    原型中的点表示这是一个可选的参数,存在与否依赖于控制命令(第二个参数)是否涉及到与设备的数据交互。




    驱动ioctl方法:




    ioctl驱动方法有和用户空间版本不同的原型:




    int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)




    cmd参数从用户空间传下来,可选的参数 arg以一个unsigned long 的形式传递,不管它是一个整数或一个指针。如果cmd命令不涉及数据传输,则第3个参数arg的值无任何意义。




    ioctl实现




    如何实现ioctl方法?




    1.定义命令
    2.实现命令








    定义命令




    在编写ioctl代码之前,首先需要定义命令。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的中定义。ioctl命令编码被划分为几个位段,include/asm/ioctl.h定义了这些位字段:类型(幻数),序号,传送方向,参数的大小。




    Documentation/ioctl-number.txt文件中罗列了在内核中已经使用了的幻数。








    定义命令




    定义ioctl命令的正确方法是使用4 个位段,这个列表中介绍的符号定义在<linux/ioctl.h>中:




    Type




    幻数(类型):表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽。




    Number
    序号,表明设备命令中的第几个,8位宽。




    Direction
    数据传送的方向,可能的值是_IOC_NONE(没有数据传输),_IOC_READ,_IOC_WRITE。数据传送是从应用程序的观点来看待的,_IOC_READ意思是从设备读。




    size
    用户数据的大小。(13/14位宽,视处理器而定)








    内核提供了下列宏来帮助定义命令:




    _IO(type,nr);
    没有 参数的命令




    _IOR(type,nr,datatype)
    从驱动中读取数据




    _IOW(type,nr,datatype)
    写数据到驱动




    _IOWR(type,nr,datatype)
    双向传送,type和number成员作为参数被传递。








    列子(example)




    #define MEM_IOC_MAGIC 'm' //定义幻数




    #define MEM_IOCSET _IOW(MEM_IOC_MAGIC,0,int)




    #define MEM_IOCGQSET  _IOR(MEM_IOC_MAGIC,1,int)








    ioctl函数实现
    定义好了命令,下一步就是要实现ioctl函数了,ioctl函数的实现包括如下3个技术环节:




    1.返回值
    2. 参数使用
    3.命令操作




    ioctl函数实现(返回值)
    ioctl函数的实现通常是根据命令执行的一个 switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时, 通常返回-EINVAL("非法参数")








    ioctl函数实现(参数)




    如何使用ioctl中的参数?
    如果是一个整数,可以直接使用。如果是指针,我们必须确保这个用户地址是有效的,因此使用前需进行正确的检查。








    ioctl函数实现(参数检查)
    不需要检测:
    copy_from_user
    copy_to_user
    get_user
    put_user




    需要检测:
    __get_user
    __put_user








    参数检查函数:




    int access_ok(int type,const void *addr,unsigned long size);




    第一个参数是VERIFY_READ或者VERIFY_WRITE,用来表明是读用户内存还是写用户内存。addr参数是要操作的用户内存地址,size是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数等于sizeof(int)。




    access_ok返回一个布尔值:1是成功(存取没问题)和0是失败(存取有问题),如果该函数返回失败,则ioctl应当返回-EFAULT。








    ioctl 函数实现(参数检查)




    if(_IOC_DIR(cmd) & _IOC_READ)
    err = !access_ok(VERIFY_WRITE,(void __user *)arg,_IOC_SIZE(cmd));




    //为什么_IOC_READ对应VERIFY_WRITE??
    else if(_IOC_DIR(cmd) & _IOC_WRITE)
    err = !access_ok(VERIFY_READ,(void _user)*arg,_IOC_SEIZE(cmd));




    if(err)
       return -EFAULT;








    ioctl函数实现(命令操作)




    switch(cmd)
    {
       case MEM_IOCSQUANTUM:
    retval = _get_user(scull_quantum,(int *)arg);
    break;




       case MEM_IOCGQUANTUM:
    retval = _put_user(scull_quantum,(int *)arg);
    break;
       
       default:
    return -EINVAL;
    }

  • 相关阅读:
    React Native配置和使用
    使用ES6语法重构React代码
    git 起点
    Win32API程序中自建按钮
    C语言中数组与指针
    我的第一个博客
    Solr6.5配置中文分词IKAnalyzer和拼音分词pinyinAnalyzer (二)
    Solr6.5在Centos6上的安装与配置 (一)
    PHP版微信公共平台消息主动推送,突破订阅号一天只能发送一条信息限制
    MariaDB+Keepalived双主高可用配置MySQL-HA
  • 原文地址:https://www.cnblogs.com/yuzaipiaofei/p/4124158.html
Copyright © 2020-2023  润新知