ioctl使用方法

ioctl简介

kernel3.0之前,叫ioctl,之后改名为unlocked_ioctl。功能和接口基本相同,名字发生了变化

ioctl既可以往内核读也可以写,read/write在执行大数据量读/写时比较有优势。

在应用层调用ioctl函数时,内核会调用对应驱动中的ublocked_ioctl函数,向内核读写数据。

驱动内的unlocked_ioctl函数

unlocked_ioctl函数属于file_operations文件操作集的一个成员,结构体内函数的定义为:

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//参数:句柄;接口命令;从应用层传入的据
//返回值:返回给应用层的数据

应用层的ioctl函数

定义在头文件<sys/ioctl.h>中,是个可变参数的函数

int ioctl(int fd, ind cmd, …);
//参数和返回值,和文件操作集里的unlocked_ioctl函数一致
//传给内核的参数可以省略

unlocked_ioctl接口命令规则

命令是一个整型参数(32位)

第一个分区:0-7,命令的编号,范围是0-255
第二个分区:8-15,命令的幻数
第三个分区:16-29,表示传递的数据的大小
第四分区:30-31,代表读写的方向


第一和第二分区,主要用来区分命令,不能有两个完全一样的“一+二”
第四分区:00:没有数据传递;10:用户从驱动读数据;01:用户向驱动写数据;11:先写数据到驱动再把数据读出来

接口命令相关的宏

合成宏,返回一个接口命令,可传入ioctl函数

_IO(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)   方向
_IOC_TYPE(nr)  幻数
_IOC_NR(nr)    编号
_IOC_SIZE(nr)   大小

//nr要分解的命令

示例代码

以杂项设备为例

驱动部分

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define CMD_TEST_0 _IO('A', 0)       //不需要读写的命令
#define CMD_TEST_1 _IOR('A', 1, int) //从内核读取一个int的命令
#define CMD_TEST_2 _IOW('A', 2, int) //向内核写入一个int的命令
#define CMD_TEST_3 _IOWR('A', 3, int) //读写一个int的命令

int misc_open(struct inode *a,struct file *b){
    printk("misc open \n");
    return 0;
}

int misc_release (struct inode * a, struct file * b){
    printk("misc file release\n");
    return 0;
}

long misc_ioctl(struct file *fd, unsigned int cmd, unsigned long b){
    /*将命令按内容分解,打印出来*/
    printk("cmd type=%c\t nr=%d\t dir=%d\t size=%d\n", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));

    switch(cmd){
        case CMD_TEST_0:
            printk("CMD_TEST_0\n");
            break;
        case CMD_TEST_1:
            printk("CMD_TEST_1\n");
            return 1;
            break;
        case CMD_TEST_2:
            printk("CMD_TEST_2 date=%d\n",b);
            break;
        case CMD_TEST_3:
            printk("CMD_TEST_3 date=%d\n",b);
            return b+1;
            break;
    }

    return 0;
}

//文件操作集
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .unlocked_ioctl = misc_ioctl
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "test_ioctrl",   //设备节点名
    .fops = &misc_fops
};

static int hello_init(void){
    int ret;

    ret = misc_register(&misc_dev);  //注册杂项设备
    if(ret < 0){
        printk("misc regist failed\n");
        return -1;
    }

    printk("misc regist succeed\n");
    return 0;
}

static void hello_exit(void){
    misc_deregister(&misc_dev);
}

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TAXUE");

module_init(hello_init); 
module_exit(hello_exit);  

应用层部分

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<sys/ioctl.h>

#define CMD_TEST_0 _IO('A', 0)
#define CMD_TEST_1 _IOR('A', 1, int)
#define CMD_TEST_2 _IOW('A', 2, int)
#define CMD_TEST_3 _IOWR('A', 3, int)

int main(int argc, char *argv[]){
    int fd=0;
    int revData=0;

    fd = open("/dev/test_ioctrl", O_RDWR);
    if(fd < 0){
        printf("open failed\n");
        exit(1);
    }
    printf("open success\n");

    /*依次调用四个命令*/
    ioctl( fd, CMD_TEST_0);
    
    revData = ioctl( fd, CMD_TEST_1);
    printf("receive 1 data=%d\n", revData);

    ioctl( fd, CMD_TEST_2, 99);

    revData = ioctl( fd, CMD_TEST_3, 101);
    printf("receive 3 data=%d\n", revData);

    close(fd);
    return 0;
}
posted @ 2021-09-20 22:51  WuYunTaXue  阅读(3844)  评论(0编辑  收藏  举报