• ioctl接口应用


    系列文章目录

    第一章 Linux 中内核与驱动程序
    第二章 Linux 设备驱动编写 (misc)
    第三章 Linux 设备驱动编写及设备节点自动生成 (cdev)
    第四章 Linux 平台总线platform与设备树
    第五章 Linux 设备树中pinctrl与gpio(lichee nano pi)
    第六章 Linux ioctl接口应用



    前言

    前面提到了,利用file_operations结构体中的write\read函数,在其中利用copy_to_user/copy_from_user进行内核和用户的通信。但在一些场合下,例如:点灯中,需要在用户程序中调用write(1)函数,通过file_operations调用xx_write函数,在里面通过copy_from_user读取1,再进行判断,到最后点灯。这是比较繁琐的,那么就需要一种更简单的方式—ioctrl


    一、ioctrl是什么?

    ioctl接口改成了unlocked_ioctl接口,只是名字改了,但是功能和对应的系统调用均没有发生变化。

    那么unlocked_ioctl和read/write函数有什么相同点和不同点?
    相同点:都可以往内核写数据。
    不同点: read函数只能完成读的功能,write只能完成写的功能。读取大数据的时候效率高。ioctl 既可以读也可以写。读取大数据的时候效率不高。


    二、使用步骤

    1.ioctl讲解

    unlocked_ioctl接口命令规则,实际上传递的是一个cmd参数,其有32位长:

    • 第一个分区:0-7,命令的编号,范围是0-255。
    • 第二个分区:8-15,命令的幻数。
    • 第三个分区:16-29表示传递的数据大小。
    • 第四个分区:30-31 代表读写的方向。
      00:表示用户程序和驱动程序没有数据传递
      10:表示用户程序从驱动里面读数
      01:表示用户程序向驱动里面写数据
      11:先写数据到驱动里面然后在从驱动里面把数据读出来。
      第一个分区和第二个分区主要作用是用来区分命令的。

    头文件还定义了有关于cmd参数的宏:合成宏与分解宏
    合成宏:

    • _lO(type,nr):用来定义没有数据传递的命令
    • _IOR(type,nr,size) :用来定义从驱动中读取数据的命令
    • _IOW(type,nr,size):用来定义向驱动写入数据的命令
    • _IOWR(type,nr, size):用来定义数据交换类型的命令,先写入数据,再读取数据这类命令。
    • 参数:
      type:表示命令组成的幻(魔)数,也就是8~15位
      nr:表示命令组成的编号,也就是0~7位
      size:表示命令组成的参数传递大小,注意这里不是传递数字,而是数据类型,如要传递4字节,就可以写成int。
      其中,读写方向的信息存放在宏的名字里了

    分解宏

    • _IOC_DIR(nr)]分解命令的方向,也就是上面说30~31位的值
    • _IOC_TYPE(nr)分解命令的魔数,也就是上面说8~15位的值
    • _IOC NR(nr)分解命令的编号,也就是上面说0~7位
    • _IOC_SIZE(nr) 分解命令的复制数据大小,也就是上面说的16~29位参数说明:
      nr :要分解的命令,例如,整个_lO(type,nr)
    //测试程序如下:
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define CMD_TEST0 _IO('a',0)
    #define CMD_TEST1 _IO('b',1)
    #define cMD_TEST2 _IOW('c',2,int)
    #define CMD_TEST3 _IOR('d',3,int)
    
    int main(int argc ,char *argv[]){
        printf("30-31 is %d\n",_IOC_DIR(CMD_TEST0));
        printf("30-31 is %d\n",_IOC_DIR(CMD_TEST3));
    
        printf("7-15 is %c\n",_IOC_TYPE(CMD_TEST0));
        printf("7-15 is %c\n",_IOC_TYPE(CMD_TEST1));
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    2.ioctl驱动编写与测试

    写在前面:注意:
    1、在测试程序中和驱动程序中都要定义命令例如:#define CMD_TEST0 _IO(‘a’,0)
    2、向内核传数据时,函数ioctl(fd,CMD,1),代表传1到xx_ioctl函数中的形参value
    3、在从内核读数据时,ioctl(fd,CMD——TEST4,&value(自己定义的变量)),传的是&value,故copy_to_user需要强制转换。
    在这里插入图片描述
    4、_IOWR一般不常用。

    在这里利用前面讲到的misc设备的代码来改写具有ioctl接口的驱动,并测试。
    同样在file_operations结构体中添加unlocked_ioctl项,并编写对应的xx_ioctl。如下:

    long misc_ioctl (struct file *file, unsigned int cmd, unsigned long value)
    {
        switch(cmd){
            case CMD_TEST0:
            printk("LED ON!!n");
            break;
            case CMD_TEST1:
            printk("LED OFF!!n");
            break;
        }
        return 0;
    }
    struct file_operations misc_fops={
    	.owner   =THIS_MODULE,
    	 .open    =misc_open,
    	 .release =misc_release,
    	 .read    =misc_read,
    	 .write   = misc_write,
    	 .unlocked_ioctl=misc_ioctl,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    misc_ioctl 其中打印输出LED ON!!LED OFF!!模拟灯的闪烁。当在测试程序中调用ioctl函数时会调用编写的该xx_ioctl函数(本文中是misc_ioctl )。然后编写测试程序如下:通过不断地传cmd参数就可以改变switch中的条件进而点灯。

    测试程序

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define CMD_TEST0 _IO('a',0)//这里用的是_IO没有传输数据!!!!!!!!!!!!!!!!!!
    #define CMD_TEST1 _IO('a',1)
    
    int main(int argc ,char *argv[]){
        int fd;
        fd = open("/dev/my_misc_ioctl",O_RDWR);
        if(fd < 0)
        {
            printf("open error\n");
            return fd;
        }
        while(1){
            ioctl(fd,CMD_TEST0);
            sleep(2);
            ioctl(fd,CMD_TEST1);
            sleep(2);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在Ubuntu上模拟灯闪烁的程序,输出如下。
    在这里插入图片描述

    完整的驱动程序;

    #include 
    #include 
    #include 
    
    #include    /* printk() */
    #include  /* misc... */
    #include        /* everything... */
    #include     /* size_t */
    
    #include 
    
    #include 
    
    #define CMD_TEST0 _IO('a',0)
    #define CMD_TEST1 _IO('a',1)
    
    int misc_open(struct inode *inode,struct file *file)
    {
        printk( "open my_misc_dev\n");
        return 0;
    }
    
    int misc_release(struct inode *inode,struct file *file)
    {
        printk( "release my_misc_dev\n");
        return 0;
    }
    
    ssize_t misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
    {
        char kbuf[64] = "xieshangle";
    
        if(copy_to_user(ubuf,kbuf,strlen(kbuf))!= 0)
        {
            printk( "copy_to_user error\n");
            return -1;
        }
        printk( "misc_read\n");
        return 0;
    }
    
    ssize_t misc_write(struct file *file,const char __user *ubuf,size_t size,loff_t *loff_t)
    {
        char kbuf[64] = {0};
    
        if(copy_from_user(kbuf,ubuf,size)!= 0)
        {
            printk( "copy_from_user error\n");
            return -1;
        }
    
        return 0;
    }
    
    long misc_ioctl (struct file *file, unsigned int cmd, unsigned long value)
    {
        switch(cmd){
            case CMD_TEST0:
            printk("LED ON!!n");
            break;
            case CMD_TEST1:
            printk("LED OFF!!n");
            break;
        }
        return 0;
    }
    
    
    struct file_operations misc_fops={
      .owner   =THIS_MODULE,
      .open    =misc_open,
      .release =misc_release,
      .read    =misc_read,
      .write   = misc_write,
      .unlocked_ioctl=misc_ioctl,
    };
    
    struct miscdevice misc_dev ={
    	.minor =MISC_DYNAMIC_MINOR,
    	.name  ="my_misc_ioctl",
    	.fops  =&misc_fops,
    
    };
    
    static int misc_init(void)
    {
        int ret;
        ret =misc_register(&misc_dev);
        if(ret < 0){
            printk( "misc register is failed\n");
            return -1;
        }
        printk( "misc register is succeed\n");
    
        return 0;
    
    }
    static void misc_exit(void)
    {
        misc_deregister(&misc_dev);
        printk(KERN_ALERT "Goodbye,misc\n");
    }
    
    /* register the init and exit routine of the module */
    module_init( misc_init );
    module_exit( misc_exit );
    
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
  • 相关阅读:
    win10 安装 rabbitMQ详细步骤
    又一个 Jupyter 神器,操作 Excel 自动生成 Python 代码
    毕业生可以做出新冠患者统计系统,使用的SSM框架yyds
    【React】React学习:从初级到高级(四)
    【gazebo要素4】构建插件
    字符串常量池-StringTable
    SpringBoot 接口 字节数组直接显示为图片
    安达发|可视化APS高级排产系统实现精益制造
    git管理项目的基本使用方法
    2023国赛数学建模C题思路模型代码 高教社杯
  • 原文地址:https://blog.csdn.net/weixin_41544435/article/details/127646400