驱动设备的IOCTL

news2024/9/22 3:20:06

一、ioctl操作实现

已知成员的地址获得所在结构体变量的地址:

container_of(成员地址,结构体类型名,成员在结构体中的名称)

long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg);
功能:对相应设备做指定的控制操作(各种属性的设置获取等等)
参数:
    filp:指向open产生的struct file类型的对象,表示本次ioctl对应的那次open
    cmd:用来表示做的是哪一个操作
    arg:和cmd配合用的参数
返回值:成功为0,失败-1

cmd组成

 

  1. dir(direction),ioctl 命令访问模式(属性数据传输方向),占据 2 bit,可以为 IOC_NONE、IOC_READ、IOC_WRITE、IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;

  2. type(device type),设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如 ‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;

  3. nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;

  4. size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;

#define _IOC(dir,type,nr,size) (((dir)<<_IOC_DIRSHIFT)| \
                               ((type)<<_IOC_TYPESHIFT)| \
                               ((nr)<<_IOC_NRSHIFT)| \
                               ((size)<<_IOC_SIZESHIFT))
/* used to create numbers */
​
// 定义不带参数的 ioctl 命令
#define _IO(type,nr)   _IOC(_IOC_NONE,(type),(nr),0)
​
//定义带读参数的ioctl命令(copy_to_user) size为类型名
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
​
//定义带写参数的 ioctl 命令(copy_from_user) size为类型名
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
​
//定义带读写参数的 ioctl 命令 size为类型名
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
​
/* used to decode ioctl numbers */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)      (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
对ioctl的使用起始就是熟练使用这些宏定义,在最后奉上一个基本的ioctl实现

二、多个次设备的支持

每一个具体设备(次设备不一样的设备),必须有一个struct cdev来代表它

cdev_init

cdev.owner赋值

cdev_add

以上三个操作对每个具体设备都要进行

三、ioctl的基本实现

.h文件

#ifndef MY_CHAR_H
#define MY_CHAR_H

#include <asm/ioctl.h>

#define MY_CHAR_MAGIC 'a' //定义的幻数,可以使任意字母,用来做标识

#define MYCHAR_IOCTL_GET_MAXLEN _IOR(MY_CHAR_MAGIC,1,int *) //ioctl所实现的功能,获取我们定义的设备中一个数组的最大长度
#define MYCHAR_IOCTL_GET_CURLEN _IOR(MY_CHAR_MAGIC,2,int *)//获取这个数组当前的长度

#endif

.c文件:

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include"mychar.h"
#define BUF_LEN 100
int major = 11;//主设备号
int minor = 0; //次设备号
int mychar_num = 1;//设备数量

struct mychar_dev{
	struct cdev mydev;//自定义字符驱动结构体

	char mydev_buf[BUF_LEN];//自定义的数组
	int curlen ;/*被读取区域字节数量,也就是数组当前长度*/
}gmydev;

//驱动设备被打开后执行的程序
int mychar_open(struct inode *pnode,struct file *pfile){
	pfile->private_data = (void *)(container_of(pnode->i_cdev,struct mychar_dev,mydev));
	printk("open \n");
	return 0;
}

int mychar_close(struct inode *pnode,struct file *pfile){
	pfile->private_data = (void *)(container_of(pnode->i_cdev,struct mychar_dev,mydev));
	printk("close \n");
	return 0;
}

//字符驱动设备的读操作,在此程序中实现读取自定义的数组中的内容
ssize_t mychar_read(struct file *pfile, char __user *puser,size_t count,loff_t *p_pos){
	
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;
	if(pmydev->curlen < count)
		size = pmydev->curlen;
	else
		size = count;

	ret = copy_to_user(puser,pmydev->mydev_buf,size);
	if(ret){
		printk("copy_to_user failed\n");
		return -1;
	}
	memcpy(pmydev->mydev_buf,pmydev->mydev_buf + size,pmydev->curlen - size);

	pmydev->curlen -= size;
	return size;

}

//向自定义的数组中写数据
ssize_t mychar_write(struct file *pfile,const char __user *puser,size_t count,loff_t *p_pos){
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	if (count > BUF_LEN - pmydev->curlen)
		size = BUF_LEN - pmydev->curlen;
	else
		size = count;
	int ret = 0;
	ret = copy_from_user(pmydev->mydev_buf + pmydev->curlen,puser,size);
	if(ret){
		printk("copy_from_user failed\n");
		return -1;
	}
	pmydev->curlen += size;

	return size;

}

long mychar_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg){
	int __user *pret = (int *)arg;
	int maxlen = BUF_LEN;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	switch(cmd){
		case MYCHAR_IOCTL_GET_MAXLEN:
			ret = copy_to_user(pret,&maxlen,sizeof(int));
			if(ret){
				printk("copy_to_user failed\n");
				return -1;
			}
			break;
		case MYCHAR_IOCTL_GET_CURLEN:
			ret = copy_to_user(pret,&pmydev->curlen,sizeof(int));
			if(ret){
				printk("copy_to_user failed\n");
				return -1;
			}
			break;
		default:
			printk("cmd unknow\n");
			return -1;
	
	}

}

struct file_operations myops = {
	.owner = THIS_MODULE,//该结构体对象属于哪个内核模块
	.open = mychar_open,//打开设备
	.release = mychar_close,//关闭设备 
	.read = mychar_read,//读操作
	.write = mychar_write,//写操作
	.unlocked_ioctl = mychar_ioctl, //ioctl操作

};

int __init mychar_init(void){
	/* 将主设备号和次设备号组合成32位完整的设备号*/
	dev_t devno = MKDEV(major,minor);
	int ret = 0;
	//申请设备号,方式为手动申请
	ret = register_chrdev_region(devno,mychar_num,"mychar");
	//手动申请失败则使用动态申请
	if(ret != 0){
		ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
		if(ret != 0){
			printk("get chrdev failed\n");
			return -1;
		}
		major = MAJOR(devno);//自动申请的主设备号需要重新赋值

	}
	
		//给struct cdev对象制定操作函数集
		cdev_init(&gmydev.mydev,&myops);

		//将其添加到内核对应的数据结构里
		gmydev.mydev.owner = THIS_MODULE;
		
		//将指定的字符设备添加到内核中
		cdev_add(&gmydev.mydev,devno,mychar_num);

}

void __exit mychar_exit(void){
	//获取设备的32位设备号
	dev_t devno = MKDEV(major,minor);
	//移除对应的设备
	cdev_del(&gmydev.mydev);
	//将申请的设备号释放
	unregister_chrdev_region(devno,mychar_num);
	
}

module_init(mychar_init);
module_exit(mychar_exit);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GB28181设备接入侧录像查询和录像下载技术探究之实时录像

技术背景 我们在对接GB28181设备接入侧的时候&#xff0c;除了常规实时音视频按需上传外&#xff0c;还有个重要的功能&#xff0c;就是本地实时录像&#xff0c;录像后的数据&#xff0c;在执法记录仪等前端设备留底&#xff0c;然后&#xff0c;到工作站拷贝到专门的平台。 …

订单系统、报名、预约、表单系统 定制开发功能展示

安装教程环境说明&#xff1a;正常情况下PHP5.3-5.6、阿帕奇、mysql安装即可 安装说明&#xff1a; 1、上传源码压缩包到网站根目录&#xff08;这个请去问下空间商哪个是根目录&#xff0c;每家服务器商不一样&#xff0c;我们也不能确定&#xff0c;请确定是根目录再安装&am…

Java设计模式之行为型-解释器模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基础概念 解释器模式是指给定一个语言&#xff08;表达式&#xff09;&#xff0c;来表示它的文法&#xff0c;并定义一个解释器&#xff0c;使用该解释器来解释语言中的句子&#xff08;表达式&a…

Linux基础内容(25)—— 线程控制和线程结构

Linux基础内容&#xff08;24&#xff09; —— 线程概念_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131294692?spm1001.2014.3001.5501 目录 1.线程操作 1.线程创建问题 2.线程终止问题 1.exit退出 2.pthread_exit退出 3.直接退出 3…

只需一个提示词解除GPT-4的字符限制!

ChatGPT的内存有限,GPT-3.5-turbo的限制为4897个令牌,而GPT-4的最大限制为8192。如果您在使用GPT-4进行聊天时超过8192个令牌(约6827个单词),它就会开始遗忘。我想出了一种新的技巧,可以轻松将对话扩展10倍。 这种技巧不会将对话中的每个字都保存到内存中。当您去开会时,会有人…

0502事务原理-InnoDB引擎-MySQL-数据库

1 概述 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务特性 原子性&#xff08;Atomatic&#xff09;&#xff1a;事…

MiniGPT4系列之一部署篇:在RTX-3090 Ubuntu服务器部署步骤详解

MiniGPT4系列之一部署篇&#xff1a;在RTX-3090 Ubuntu服务器部署步骤详解_seaside2003的博客-CSDN博客 MiniGPT4系列之二推理篇命令行方式&#xff1a;在RTX-3090 Ubuntu服务器推理详解_seaside2003的博客-CSDN博客 MiniGPT4系列之三模型推理 (Web UI)&#xff1a;在RTX-309…

外包软件定制开发中关于沟通障碍及对应解决方案

引言 外包软件定制开发在当今的商业环境中越来越常见。它为公司提供了许多好处&#xff0c;包括降低成本、加速交付和专注于核心业务。然而&#xff0c;沟通障碍常常是外包软件定制开发中的一个重要挑战。由于外包团队和客户位于不同的地理位置、文化和语言差异&#xff0c;沟…

Python 列表 sort()函数使用详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 sort函数使用详解 1、升序降序2、sort()和sorted()的区别3、切片排序4、指定排序…

【C++】设计模式-单例模式

目录 一、单例模式 单例模式的三个要点 针对上述三要点的解决方案 常用的两类单例模式 二、懒汉模式实现 1.基本实现 2.锁静态成员析构单例 3.双层检查锁定优化 4.双层检查锁定智能指针 三、饿汉模式实现 1.基础实现 2.嵌套内部类解决内存泄漏 3.智能指针解决内存泄…

一种用于RBF神经网络的新型自适应内核研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

vuejs源码之虚拟dom中的vnode

在虚拟dom中&#xff0c;vnode是比较重要的。 什么是vnode 在vuejs中&#xff0c;有一个Vnode类 使用它可以实例不同类型的vnode实例&#xff0c;而不同类型的vnode实例各自表示不同类型的dom元素。 例如dom元素有文本节点&#xff0c;元素节点&#xff0c;注释节点等。 co…

Spring IoC及DI依赖注入

Spring 1.Spring的含义&#xff1a; Spring 可从狭义与广义两个角度看待 狭义的 Spring 是指 Spring 框架(Spring Fremework) 广义的 Spring 是指 Spring 生态体系 2.狭义的 Spring 框架 Spring 框架是企业开发复杂性的一站式解决方案 Spring 框架的核心是 IoC 容器和 AO…

数据库java中jdbcTemplate的事务问题

1.什么都不设置事务是默认提交的 两次获取的连接是不是一样的 参考文献(重磅): (542条消息) JdbcTemplate的事务控制_jdbctemplate transactionmanager_DayDayUp丶的博客-CSDN博客 PostMapping("/pinYin22")CrossOriginTransactionalpublic String pinYin22(HttpS…

【js实现语言国际化】使用json配置文件实现

需求&#xff1a;使用js让项目实现中文简体、繁体跟英文的切换&#xff0c;实现语言国际化 首先准备三种json配置文件&#xff1a; en.json {"textOne": "Today is Monday","textTwo": "Tomorrow is Tuesday","textThree"…

F#奇妙游(14):F#实现WPF的绑定

WPF中的绑定 绑定在UI开发中是一个非常重要的概念&#xff0c;它可以让我们的UI界面和数据模型之间建立起联系&#xff0c;当数据模型发生变化时&#xff0c;UI界面也会随之变化&#xff0c;反之亦然。这样的好处是显而易见的&#xff0c;我们不需要手动去更新UI界面&#xff…

金智教育IPO过会:计划募资约6亿元,郭超、史鸣杰为实控人

7月13日&#xff0c;深圳证券交易所披露的信息显示&#xff0c;江苏金智教育信息股份有限公司&#xff08;下称“金智教育”&#xff09;获得上市委会议通过。据贝多财经了解&#xff0c;金智教育于2022年6月30日递交上市申请材料&#xff0c;先后递交了6个版本的招股书&#x…

NDK OpenGL与OpenCV实现大眼萌特效

NDK​系列之OpenGL与OpenCV实现大眼萌特效&#xff0c;本节主要是在上一节OpenGL仿抖音极快极慢录制特效视频上增加大眼萌的特效。 OpenGL视频特效系列&#xff1a; NDK OpenGL渲染画面效果 NDK OpenGL离屏渲染与工程代码整合 NDK OpenGL仿抖音极快极慢录制特效视频 NDK O…

通讯录实现

普通版 需求 通讯录可以用来存储1000个人的信息&#xff0c;每个人的信息包括&#xff1a;姓名、性别、年龄、电话、住址 提供方法&#xff1a; 添加联系人信息删除指定联系人信息查找指定联系人信息修改指定联系人信息显示所有联系人信息清空所有联系人以名字排序所有联系…

【Linux后端服务器开发】UDP协议

目录 一、端口号 二、UDP报头格式 三、UDP的特点 四、UDP协议实现网络聊天群 一、端口号 端口号port标识了一个主机上进行通信的不同的应用程序。 0 ~ 1023&#xff1a;系统端口号&#xff0c;HTTP、FTP、SSH等这些广为使用的应用层协议&#xff0c;它们的端口号都是固定…