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:先写数据到驱动里面然后在从驱动里面把数据读出来。
    第一个分区和第二个分区主要作用是用来区分命令的。

头文件<sys/ioctl.h>还定义了有关于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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#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;
}

在这里插入图片描述

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,
};

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

测试程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#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;
}

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

完整的驱动程序;

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>   /* printk() */
#include <linux/miscdevice.h> /* misc... */
#include <linux/fs.h>       /* everything... */
#include <linux/types.h>    /* size_t */

#include <linux/uaccess.h>

#include <linux/io.h>

#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");
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

请叫我7plus

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

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

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

打赏作者

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

抵扣说明:

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

余额充值