16_ioctl接口

ioctl接口

struct file_operations {
	......
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	......
};

1.什么是unlocked_ioctl接口?

​ unlocked_ioctl就是ioctl接口,但是功能和对应的系统调用均没有发生变化。

2.unlocked_ioctl和read/write函数有什么相同点和不同点?

​ 相同点:都可以往内核写数据。

​ 不同点:read函数只能完成读的功能,write只能完成写的功能。读取大数据的时候效率高。

​ ioctl既可以读也可以写。读取大数据的时候效率不高。

所以: read/write专注于读写大量数据,因为效率很高, ioctl用来设置设备的配置和数据

3.unlocked_ioctl接口命令规则。

第一个分区:0-7,命令的编号,范围是0-255.

第二个分区:8-15,命令的幻数。

第一个分区和第二个分区主要作用是用来区分命令的。

第三个分区:16-29表示传递的数据大小。

第四个分区:30-31 代表读写的方向。

00:表示用户程序和驱动程序没有数据传递

10:表示用户程序从驱动里面读数据

01:表示用户程序向驱动里面写数据

11:先写数据到驱动里面然后在从驱动里面把数据读出来。

4.命令的合成宏与分解宏

头文件

#include <sys/ioctl.h>

合成宏:

_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)	分解命令的方向,也就是上面说30~31位的值
_IOC_TYPE(nr)  	分解命令的魔数,也就是上面说8~15位的值
_IOC_NR(nr)	   	分解命令的编号,也就是上面说0~7位
_IOC_SIZE(nr)  	分解命令的复制数据大小,也就是上面说的16~29位
参数说明:
nr :要分解的命令

ioctl函数

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。

它的调用函数如下:

int ioctl(int fd, unsigned long request, ...);

其中fd是用户程序打开设备时使用open函数返回的文件标示符,request是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,比如要传输的数据

返回值: 成功 返回0 失败 返回 -1 并置位错误码

_IO没有数据传递示例

ioctl.c

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

#define CMD_TEST0 _IO('L', 0) // 用来定义没有数据传递的命令
#define CMD_TEST1 _IO('L', 1) // 用来定义没有数据传递的命令


int main(int argc, const char *argv[])
{
    int fd;
    fd = open("/dev/hello_mise", O_RDWR);
    if (fd < 0)
    {
        printf("open is error\n");
        return -1;
    }
    while(1)
    {
        ioctl(fd, CMD_TEST0); //触发驱动中ioctl函数
        sleep(2);
        ioctl(fd, CMD_TEST1);
        sleep(2);
    }
    return 0;
}

beep.c

#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_TEST0 _IO('L', 0) // 用来定义没有数据传递的命令
#define CMD_TEST1 _IO('L', 1) // 用来定义没有数据传递的命令

int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n");
    return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
    printk("hello mise_release bye bye\n");
    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;
    default:
        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 = "hello_mise",        // 设备节点的名字
    .fops = &misc_fops,          // 文件操作集

};

static int mise_init(void)
{
    int ret;
    ret = misc_register(&misc_dev); // 注册杂项设备
    if (ret < 0)
    {
        printk("misc register is error!\n");
        return -1;
    }
    printk("mise register is ok!\n");

    return 0;
}

static void mise_exit(void)
{
    misc_deregister(&misc_dev); // 注销杂项设备
    printk("misc gooodbye!\n");
}

module_init(mise_init);
module_exit(mise_exit);

MODULE_LICENSE("GPL");

Makefile

obj-m +=beep.o
KDIR:=/home/mzx/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga 
PWD?=$(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

运行结果

image-20240425205129990

_IOW向驱动写入数据示例

ioctl.c

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

#define CMD_TEST0 _IO('L', 0) // 用来定义没有数据传递的命令
#define CMD_TEST1 _IO('L', 1) // 用来定义没有数据传递的命令

#define CMD_TEST2 _IOW('L', 2, int) //用来定义向驱动写入数据的命令
#define CMD_TEST3 _IOW('L', 3, int) //用来定义向驱动写入数据的命令

int main(int argc, const char *argv[])
{
    int fd;
    fd = open("/dev/hello_mise", O_RDWR);
    if (fd < 0)
    {
        printf("open is error\n");
        return -1;
    }
    while(1)
    {
        ioctl(fd, CMD_TEST2, 0); //触发驱动中ioctl函数
        sleep(2);
        ioctl(fd, CMD_TEST3, 1);
        sleep(2);
    }
    return 0;
}

beep.c

#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_TEST0 _IO('L', 0) // 用来定义没有数据传递的命令
#define CMD_TEST1 _IO('L', 1) // 用来定义没有数据传递的命令

#define CMD_TEST2 _IOW('L', 2, int) // 用来定义向驱动写入数据的命令
#define CMD_TEST3 _IOW('L', 3, int) // 用来定义向驱动写入数据的命令

int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n");
    return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
    printk("hello mise_release bye bye\n");
    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;
    case CMD_TEST2:
        printk("LED ON!!\n");
        printk("value is %lu\n", value);
        break;
    case CMD_TEST3:
        printk("LED OFF!!\n");
        printk("value is %lu\n", value);
        break;
    default:
        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 = "hello_mise",        // 设备节点的名字
    .fops = &misc_fops,          // 文件操作集

};

static int mise_init(void)
{
    int ret;
    ret = misc_register(&misc_dev); // 注册杂项设备
    if (ret < 0)
    {
        printk("misc register is error!\n");
        return -1;
    }
    printk("mise register is ok!\n");

    return 0;
}

static void mise_exit(void)
{
    misc_deregister(&misc_dev); // 注销杂项设备
    printk("misc gooodbye!\n");
}

module_init(mise_init);
module_exit(mise_exit);

MODULE_LICENSE("GPL");

Makefile

obj-m +=beep.o
KDIR:=/home/mzx/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga 
PWD?=$(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

运行结果

image-20240425205911582

_IOR从驱动中读取数据示例

ioctl.c

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

#define CMD_TEST0 _IO('L', 0) // 用来定义没有数据传递的命令
#define CMD_TEST1 _IO('L', 1) // 用来定义没有数据传递的命令

#define CMD_TEST2 _IOW('L', 2, int) //用来定义向驱动写入数据的命令
#define CMD_TEST3 _IOW('L', 3, int) //用来定义向驱动写入数据的命令

#define CMD_TEST4 _IOR('L', 4, int) //用来定义从驱动中读取数据的命令

int main(int argc, const char *argv[])
{
    int fd;
    int value;
    fd = open("/dev/hello_mise", O_RDWR);
    if (fd < 0)
    {
        printf("open is error\n");
        return -1;
    }
    while(1)
    {
        ioctl(fd, CMD_TEST4, &value); //触发驱动中ioctl函数
        printf("value is %d\n", value);
        sleep(2);
    }
    return 0;
}

beep.c

#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_TEST0 _IO('L', 0) // 用来定义没有数据传递的命令
#define CMD_TEST1 _IO('L', 1) // 用来定义没有数据传递的命令

#define CMD_TEST2 _IOW('L', 2, int) // 用来定义向驱动写入数据的命令
#define CMD_TEST3 _IOW('L', 3, int) // 用来定义向驱动写入数据的命令

#define CMD_TEST4 _IOR('L', 4, int) //用来定义从驱动中读取数据的命令

int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n");
    return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
    printk("hello mise_release bye bye\n");
    return 0;
}

long misc_ioctl(struct file *file, unsigned int cmd, unsigned long value)
{
    int val;
    
    switch (cmd)
    {
    case CMD_TEST0:
        printk("LED ON!!\n");
        break;
    case CMD_TEST1:
        printk("LED OFF!!\n");
        break;
    case CMD_TEST2:
        printk("LED ON!!\n");
        printk("value is %lu\n", value);
        break;
    case CMD_TEST3:
        printk("LED OFF!!\n");
        printk("value is %lu\n", value);
        break;
    case CMD_TEST4:
        val = 12;
        if(copy_to_user((int *)value, &val, sizeof(val)) != 0)
        {
            printk("copy_to_user is error\n");
            return -1;
        }
        break;
    default:
        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 = "hello_mise",        // 设备节点的名字
    .fops = &misc_fops,          // 文件操作集

};

static int mise_init(void)
{
    int ret;
    ret = misc_register(&misc_dev); // 注册杂项设备
    if (ret < 0)
    {
        printk("misc register is error!\n");
        return -1;
    }
    printk("mise register is ok!\n");

    return 0;
}

static void mise_exit(void)
{
    misc_deregister(&misc_dev); // 注销杂项设备
    printk("misc gooodbye!\n");
}

module_init(mise_init);
module_exit(mise_exit);

MODULE_LICENSE("GPL");

Makefile

obj-m +=beep.o
KDIR:=/home/mzx/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga 
PWD?=$(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

运行结果

image-20240425211622757

posted @ 2024-04-30 15:29  爱吃冰激凌的黄某某  阅读(19)  评论(0编辑  收藏  举报