驱动-传参实验-ioctl

用户如果要对外设进行操作,对应的设备驱动不仅要具备读写的能力,还需要对硬件进行控制。以点亮LED灯驱动实验为例,应用程序通过向内核空间写入1和0从而控制LED灯的亮灭,但是读写操作主要是数据流对数据进行操作,而一些复杂的控制通常需要非数据操作,这时本章节要学习的ioctl函数就闪耀登场了。


ioctl 基础

ioctl是设备驱动程序中用来控制设备的接口函数,一个字符设备驱动通常需要实现设备的打开、关闭、读取、写入等功能,而在一些需要细分的情况下,就需要扩展新的功能,通常以增设ioctl()命令的方式来实现。

Linux设备驱动开发中,ioctl(Input/OutputControl)是一种用于设备控制的系统调用,允许用户程序与内核驱动程序进行交互,传递自定义命令和数据。本实验将演示如何通过 ioctl 在用户空间和内核空间之间传递参数。

IOCTL 命令

Linux 使用宏 _IO, _IOR, _IOW, _IOWR 定义 ioctl 命令:

比如如下举个例子,通过_IOR, _IOW, _IOWR 宏定义来定义命令

#include <linux/ioctl.h>
#define MY_IOCTL_MAGIC 'k'  // 幻数,唯一标识设备
// 定义命令:
// MY_IOCTL_READ:  从内核读取数据 (_IOR)
// MY_IOCTL_WRITE: 向内核写入数据 (_IOW)
// MY_IOCTL_RW:    读写双向操作 (_IOWR)

#define MY_IOCTL_READ   _IOR(MY_IOCTL_MAGIC, 1, int)
#define MY_IOCTL_WRITE  _IOW(MY_IOCTL_MAGIC, 2, int)
#define MY_IOCTL_RW     _IOWR(MY_IOCTL_MAGIC, 3, int)

ioctl 关联-unlocked_ioctl

我们在以前的字符设备中用到的 read/write 文件操作符,这里的ioctl 关联的函数是 unlocked_ioctl
如下所示:


static struct file_operations fops = {
    .unlocked_ioctl = my_ioctl,
};

实验

client-测试程序 - ioctltest.c


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

#define CMD_TEST0 _IO('L',0)
#define CMD_TEST1 _IOW('L',1,int)
#define CMD_TEST2 _IOR('L',2,int)

int main(int argc,char *argv[]){

	int fd;//定义int类型的文件描述符fd
	int val;//定义int类型的传递参数val
	fd = open("/dev/test",O_RDWR);//打开test设备节点
	if(fd < 0){
		printf("file open fail\n");
	}
	if(!strcmp(argv[1], "write")){
		ioctl(fd,CMD_TEST1,1);//如果第二个参数为write,向内核空间写入1
	}
	else if(!strcmp(argv[1], "read")){
		ioctl(fd,CMD_TEST2,&val);//如果第二个参数为read,则读取内核空间传递向用户空间传递的值
		printf("val is %d\n",val);

    }
	close(fd);
}


驱动测试程序 - ioctl.c


#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>

#define CMD_TEST0 _IO('L',0)
#define CMD_TEST1 _IOW('L',1,int)
#define CMD_TEST2 _IOR('L',2,int)
 
struct device_test
{
   dev_t  dev_num;  //定义dev_t类型变量来表示设备号
   int major,minor; //定义int 类型的主设备号和次设备号
   struct cdev cdev_test;   //定义字符设备
   struct class *class;   //定义结构体变量class 类
   struct device *device;  // 设备
   char kbuf[32];

};
 
struct device_test dev1;  
 
 static long cdev_test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int val;//定义int类型向应用空间传递的变量val
	switch(cmd){
        case CMD_TEST0:
            printk("this is CMD_TEST0\n");
            break;		
        case CMD_TEST1:
            printk("this is CMD_TEST1\n");
			printk("arg is %ld\n",arg);//打印应用空间传递来的arg参数
            break;
        case CMD_TEST2:
			val = 1;//将要传递的变量val赋值为1
            printk("this is CMD_TEST2\n");
			if(copy_to_user((int *)arg,&val,sizeof(val)) != 0){//通过copy_to_user向用户空间传递数据
				printk("copy_to_user error \n");	
			}
            break;			
	default:
			break;
	}
	return 0;
}


/*打开设备函数*/
static int open_test(struct  inode  *inode,struct file *file)
 {
    file->private_data=&dev1;//设置私有数据
    printk("\n this is open_test \n");
     return 0;
 };

static int release_test(struct inode *inode,struct file *file)
{
    printk("\nthis is release_test \n");
   return 0;
}






static struct file_operations fops_test = {
	.owner=THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
    .open = open_test,//将open字段指向chrdev_open(...)函数
    .release = release_test,//将open字段指向chrdev_release(...)函数
    .unlocked_ioctl = cdev_test_ioctl,
};//定义file_operations结构体类型的变量cdev_test_ops


static int __init timer_dev_init(void)//驱动入口函数
{

    if(alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name") < 0){
            printk("alloc_chrdev_region is error\n");
    }   
        printk("alloc_chrdev_region is ok\n");
        dev1.major=MAJOR(dev1.dev_num);//通过MAJOR()函数进行主设备号获取
        dev1.minor=MINOR(dev1.dev_num);//通过MINOR()函数进行次设备号获取
        printk("major is %d\n",dev1.major);
        printk("minor is %d\n",dev1.minor);
 
        ////使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构体
        cdev_init(&dev1.cdev_test,&fops_test);
        dev1.cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 

        cdev_add(&dev1.cdev_test,dev1.dev_num,1);
        printk("cdev_add is ok\n");
        dev1.class  = class_create(THIS_MODULE,"test");//使用class_create进行类的创建,类名称为class_test
        device_create(dev1.class,NULL,dev1.dev_num,NULL,"test");//使用device_create进行设备的创建,设备名称为device_test

    return 0;
}
static void __exit timer_dev_exit(void)//驱动出口函数
{
    cdev_del(&dev1.cdev_test);//使用cdev_del()函数进行字符设备的删除
    unregister_chrdev_region(dev1.dev_num,1);//释放字符驱动设备号 
    device_destroy(dev1.class,dev1.dev_num);//删除创建的设备
    class_destroy(dev1.class);//删除创建的类
    printk("module exit \n");

}
module_init(timer_dev_init);//注册入口函数
module_exit(timer_dev_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("wang fang chen "); //作者信息

编译文件Makefile

#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += ioctl.o
KDIR :=/home/wfc123/Linux/rk356x_linux/kernel
PWD ?= $(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean

生成可执行文件和驱动文件

  • 编译对应的驱动文件 make
    在这里插入图片描述
  • 生成可执行的测试程序
aarch64-linux-gnu-gcc -o ioctl ioctltest.c -static 

在这里插入图片描述

加载驱动-执行测试程序

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

核对下程序源码,都对上了…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

野火少年

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

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

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

打赏作者

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

抵扣说明:

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

余额充值