Ioctl()方式实现与驱动交互简洁框架

这篇具有很好参考价值的文章主要介绍了Ioctl()方式实现与驱动交互简洁框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

ioctl是linux中一种除read和write之外的数据传递机制

驱动层IOCTL:

int (*ioctl) (struct inode *inode, struct file *fp, unsigned int request, unsigned long args);

以上函数参数的含义如下

  1. inode和fp用来确定被操作的设备。
  2. request就是用户程序下发的命令。
  3. args就是用户程序在必要时传递的参数。

在2.6.36以后ioctl函数已经不存在了,用unlocked_ioctl和compat_ioctl两个函数代替。参数去除了原来ioctl中的struct
inode参数,返回值也发生了改变。

新的代码

#include <linux/ioctl.h>
long (*unlocked_ioctl) (struct file * fp, unsigned int request, unsigned long args);
long (*compat_ioctl) (struct file * fp, unsigned int request, unsigned long args);

应用层IOCTL:

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);

流程梳理:

  • 应用程序调用ioctl函数发送一个数字(cmd)给内核层驱动程序(此时驱动程序已经注册加入到系统里)
  • 驱动程序接收到cmd(一个数字), 将内核数据传递给从应用层传递过来的指针,或者将应用层的数据传递给内核变量。

注意

  • ioctl所发送的数字, 是有一定规则的, 必须符合这个规则,可以直接使用内核提供的API(_IO,_IOR,_IOW,_IOWR)去完成cmd的生成, 驱动层才能正确解析这个数字指令。 这个数字也叫指令码
  • 应用层的ioctl是个变参函数, 但是变参并不意味着它可以传任意多个参数进去,它的意思是第三个参数可传可不传

源码

驱动代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/unistd.h>
 

#define BASEMINOR 0
#define COUNT     3
#define NAME      "ioctl_test"

#define MEMDEV_IOC_MAGIC 'k'			//定义幻数
#define MEMDEV_IOCPRTDATA _IO(MEMDEV_IOC_MAGIC,1)			//控制驱动打印数据
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC,3,int)			//set数据到驱动中
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC,2,int)			//从驱动中读数据
#define MEMDEV_IOCMAXNR 3

dev_t devno;
struct cdev *cdevp = NULL;

static long test_ioctl(struct file *filp,unsigned int cmd,unsigned long arg){
	
	long ret = 0;
	int err = 0;
	int ioarg = 0;
	
	//第一步验证命令cmd参数的有效性
	if(_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC){
	return -EINVAL;}
	if(_IOC_NR(cmd) > MEMDEV_IOCMAXNR){
	return -EINVAL;}
	
	//检测参数空间是否可以正确访问
	//ioctl读取数据,需要向用户空间写入数据
	if(_IOC_DIR(cmd) & _IOC_READ){
		err = !access_ok((void *)arg,_IOC_SIZE(cmd));
	}
	//ioctl设置数据,需要向用户空间读取数据
	else if(_IOC_DIR(cmd) & _IOC_WRITE){
		err = !access_ok((void *)arg,_IOC_SIZE(cmd));
	}
	if(err){
		return -EFAULT;
	}
	
	switch(cmd){
		case MEMDEV_IOCPRTDATA:
			printk("---------打印函数执行成功---------\n");
			break;
		case MEMDEV_IOCSETDATA:
			ret = __get_user(ioarg,(int *)arg);
			printk("从用户空间拿到的值:%d\n",ioarg);
			break;
		case MEMDEV_IOCGETDATA:
			ioarg = 1101;
			ret = __put_user(ioarg,(int *)arg);
			break;
		default:
			return -EINVAL;
	}
	return ret;
}

static const struct file_operations fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = test_ioctl
};

static int __init ioctl_init(void)
{
	int ret;
	ret = alloc_chrdev_region(&devno,BASEMINOR,COUNT,NAME);
	if(ret < 0){
		printk(KERN_ERR "alloc_chrdev_region failed...\n");
		goto err1;
	}
	//设备号申请成功打印出来
	printk(KERN_INFO "major = %d \n",MAJOR(devno));
	cdevp = cdev_alloc();
	if(NULL == cdevp){
		printk(KERN_ERR "cdev_alloc failed...\n");
		ret = -ENOMEM;
		goto err2;
		//cdev结构体申请失败需要将上一步申请到的设备号资源释放。
	}
	cdev_init(cdevp,&fops);
	ret = cdev_add(cdevp,devno,COUNT);
	if(ret < 0){
		printk(KERN_ERR "cdev_add failed...\n");
		goto err2;
	}

	printk(KERN_INFO "---init over :%s---%s---%d---\n",__FILE__,__func__,__LINE__);
	return 0;
err2:
	unregister_chrdev_region(devno,COUNT);			//释放申请到的设备号资源
err1:
	return ret;
}

static void __exit ioctl_exit(void)
{
	cdev_del(cdevp);
	unregister_chrdev_region(devno,COUNT);			//释放申请到的设备号资源
	printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
}

module_init(ioctl_init);
module_exit(ioctl_exit);
MODULE_LICENSE("GPL");

编译驱动的Makefile

KERNDIR = /lib/modules/`uname -r`/build
PWD = $(shell pwd)

obj-m:=char_ioctl_driver.o

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

应用层代码

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

int main()
{
	int fd = 0;
	int cmd;
	int arg = 0;
	
	fd = open("/dev/ioctl_test",O_RDWR);
	if(fd < 0){
		printf("open memdev0 failed!!!\n");
		return -1;
	}
	
	cmd = _IO('k',1);//利用kernel api构造cmd
	if(ioctl(fd,cmd,&arg) < 0){
		printf("-------打印命令传输失败--------\n");
		return -1;
	}
	
	cmd = _IOW('k',3,int);//利用kernel api构造cmd
	arg = 2007;
	if(ioctl(fd,cmd,&arg) < 0){
		printf("-----------应用层数据copy到驱动失败--------\n");
		return -1;
	}
	
	cmd = _IOR('k',2,int);//利用kernel api构造cmd
	if(ioctl(fd,cmd,&arg) < 0){
		printf("-------驱动数据copy到应用层数据失败---------\n");
		return -1;
	}
	printf("从内核获取到的数据值:%d\n",arg);
	
	close(fd);
	
	return 0;
}

编译与验证流程
驱动make直接编译:

ubuntu:~/Desktop/huangrui/project_1/char_ioctl$ make
make -C /lib/modules/`uname -r`/build M=/home/xj/Desktop/huangrui/project_1/char_ioctl modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-149-generic'
  CC [M]  /home/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  /home/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.mod.o
  LD [M]  /home/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-149-generic'

文件罗列

char_ioctl$ ls
char_ioctl_driver.c   char_ioctl_driver.mod    char_ioctl_driver.mod.o  char_ioctl_user    Makefile       Module.symvers
char_ioctl_driver.ko  char_ioctl_driver.mod.c  char_ioctl_driver.o      char_ioctl_user.c  modules.order

驱动加载

sudo insmod char_ioctl_driver.ko

驱动模块查看

project_1/char_ioctl$ lsmod
Module                  Size  Used by
char_ioctl_driver      16384  0
btrfs                1249280  0
xor                    24576  1 btrfs

查看驱动程序生成的设备及其主设备号:(可以作为mknod的参数)

$ cat /proc/devices
Character devices:
240 ioctl_test

创建设备节点:(cd到/dev下)

/dev$ sudo mknod ioctl_test c 240 0

查看设备节点情况

/dev$ ls
ioctl_test

应用层代码编译

$ gcc char_ioctl_user.c -o char_ioctl_user

执行结果

$ sudo ./char_ioctl_user
从内核获取到的数据值:1101

驱动打印

$ dmesg
[1192849.439778] major = 240
[1192849.439781] ---init over :/home/xj/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.c---ioctl_init---97---
[1193159.265924] ---------打印函数执行成功---------
[1193159.265925] 从用户空间拿到的值:2007

验证成功!!!

说明

  • arg参数如果是一个整数,可以直接使用,如果是一个指针,需要先确保用户地址的有效性,使用前需要做正确检查,需要检测的函数:__get_user 和 __put_user,不需要检测的函数:copy_from_user & copy_to_user & get_user & put_user

这个就代表正在定义新的cmd文章来源地址https://www.toymoban.com/news/detail-488865.html

  • #define MEMDEV_IOCPRTDATA _IO(MEMDEV_IOC_MAGIC,1)

到了这里,关于Ioctl()方式实现与驱动交互简洁框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux用户与内核空间交互—ioctl

    目录 简介 一、交互方法笔记与总结 二、ioctl 三、实战 1、头文件 2、应用程序 3、内核程序 4、 程序输出 用户空间与内核的交互方式,使用copy_from_user(), copy_to_user(). 除了这两种交互方式,内核还提供了其他高级的方式,对于写驱动来说很重要。有proc、sysfs、debugfs、netlink、

    2024年02月10日
    浏览(198)
  • linux驱动开发(四):ioctl()函数

    前文中我们介绍了应用程序通过使用虚拟文件系统VFS提供的接口,来控制字符驱动程序,完成字符驱动设备的open、close、read、write操作。但是如果我们想进行除此以外的其他操作,拓展一些file_operations给出的接口中没有的自定义功能,则需要使用到ioctl()函数。 首先,我们需

    2024年01月16日
    浏览(40)
  • 53、springboot对websocket的支持有两种方式-------1、基于注解开发 WebSocket ,简洁实现多人聊天界面

    –注解就是: @OnOpen、 @OnClose 、 @OnMessage 、@OnError这些 ▲ Spring Boot为WebSocket提供了两种开发方式: 基于spring-boot-starter-websocket.jar开发WebSocket 基于Spring WebFlux开发WebSocket 两种方式对比: springboot API Socket:套接字。 插座。 在通信的两端分别建立虚拟的Socket(插座),网络协议

    2024年02月09日
    浏览(39)
  • 【IMX6ULL驱动开发学习】04.应用程序和驱动程序数据传输和交互的4种方式:非阻塞、阻塞、POLL、异步通知

    目录 一、数据传输 1.1 APP和驱动  1.2 驱动和硬件 二、APP使用驱动的4种方式 2.1 非阻塞(查询) 2.2 阻塞(休眠+唤醒) 2.3 POLL(休眠+唤醒+超时时间) 2.3.1 POLL机制流程 2.3.2 POLL执行流程 2.3.3 POLL应用和驱动编程  2.4 异步通知 2.4.1 异步通知流程 2.4.1 异步通知应用和驱动编程

    2024年02月06日
    浏览(52)
  • 【IMX6ULL驱动开发学习】04.应用程序和驱动程序数据传输和交互的4种方式:非阻塞、阻塞、POLL、异步通知 一、数据传输

    目录 一、数据传输 1.1 APP和驱动  1.2 驱动和硬件 二、APP使用驱动的4种方式 2.1 非阻塞(查询) 2.2 阻塞(休眠+唤醒) 2.3 POLL(休眠+唤醒+超时时间) 2.3.1 POLL机制流程 2.3.2 POLL执行流程 2.3.3 POLL应用和驱动编程  2.4 异步通知 2.4.1 异步通知流程 2.4.1 异步通知应用和驱动编程

    2024年02月13日
    浏览(44)
  • 【自制C++深度学习框架】前言

    此GitHub项目是一个初学者的深度学习框架,使用C++编写,旨在为用户提供一种简单、易于理解的深度学习实现方式。以下是本项目的主要特点和功能: 计算图:使用计算图来描述深度学习模型的计算过程,利用计算图将神经网络的计算过程视为一个有向无环图。通过构建计算

    2024年02月07日
    浏览(42)
  • 【Linux驱动开发】013 与gpio相关的OF函数 一、前言

    在上节,我们提供了驱动中gpio子系统相关的API函数,主要用来申请释放gpio、设置gpio输入输出、获取设置gpio的值。 我们进行上述设置的前提是:在驱动程序中需要读取 gpio 属性内容。为此,Linux 内核提供了几个与 GPIO 有关的 OF 函数。 用于统计设备树某个属性里面定义了几个

    2024年02月14日
    浏览(47)
  • Linux内核4.14版本——drm框架分析(9)——DRM_IOCTL_MODE_GETCONNECTOR(drm_mode_getconnector)

    目录  1. drm_mode_getconnector分析 1.1 找到connector 1.2 计算该connector的encoder数量 1.3 把connector支持的encoder和数量返回给用户 1.4 把找到的connector的参数返回给应用 1.5 填充mode(很重要) 1.6 把找到的connector的参数返回给应用 1.7 计算mode的数量 1.8 把mode的参数和mode的数量返回给应用

    2024年02月11日
    浏览(40)
  • 【opencv-python Gstreamer支持】Jetson Agx Orin手动编译opencv4.5.1加入gstreamer库,实现opencv简洁驱动GMSL2 IMX390相机

    # opencv官网下载opencv4.5.1源代码 opencv下载网址 # libtiff官网下载libtiff4的包,解决opencv编译问题 libtiff4下载地址 # libtiff安装 下载对应的安装包 tiff-4.0.10.zip ,( tiff-4.0.x.zip 都可以),解压之后,然后编译安装: # 编译安装

    2024年02月10日
    浏览(46)
  • 【AutoJs】AutoJs实现用户界面交互的方式

    Auto.js 中渲染用户界面(UI)用于用户与系统交互有三种不同方式:UI、Floaty 和 Dialog,它们在用途和实现上有一些区别。下面各自介绍一下相应方法。 Dialog 模块用于显示各种对话框。对话框是一种弹出式窗口,通常用于与用户进行短期交互,例如显示警告、确认信息、输入文

    2024年02月04日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包