Linux字符设备驱动中同类型多设备节点的创建---一个驱动程序支持多个同类型设备

文章目录

  • 前言
  • 1 代码解析
    • 1.1 驱动层
    • 1.2 应用层
  • 2 运行结果
  • 总结


前言

本期分享的内容相对比较简单,那就是同时注册多个同类型的字符设备驱动,那么这样我们就可以同时支持多个同类型的设备了!下面来带大家看一下:


1 代码解析

1.1 驱动层

//本驱动程序支持主设备号major = 11,次设备号为0,1,2的三个设备
表明驱动程序支持三个同类型的设备,在使用时需要创建真实的设备节点
mknod /dev/mydev0 c 11 0
mknod /dev/mydev1 c 11 1
mknod /dev/mydev2 c 11 2
编写一个驱动程序,可以分别驱动三个设备节点

#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
#define MYCHAR_DEV_CNT 	3
int major = 11; 		//主设备号
int minor = 0; 			//次设备号
int mychar_num = MYCHAR_DEV_CNT; 	//设备数量

struct mychar_dev
{
	struct cdev mydev; 			//每一类设备都有一个cdev结构体

	char mydev_buf[BUF_LEN];  	//内核空间
	int curlen; 				//有效数字从零开始
};

//创建MYCHAR_DEV_CNT个设备结构体
struct mychar_dev gmydev_arr[MYCHAR_DEV_CNT];

int mychar_open(struct inode *pnode, struct file *pfile)
{
	pfile->private_data = (void *)container_of(pnode->i_cdev, struct mychar_dev, mydev);


	printk("mychar open is called!!!\n");
	return 0;
}

ssize_t mychar_read(struct file *pfile, char __user *pbuf, size_t count, loff_t *ppos)
{
	int size = 0;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	if (count > pmydev->curlen)
	{
		size = pmydev->curlen;
	}
	else
	{
		size = count;
	}

	ret = copy_to_user(pbuf, 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);   //把在mydev_buf中剩下有效数据存放在以mydev_buf的首地址中
	pmydev->curlen -= size; 			//读走的字节要被减去

	return size;
}

ssize_t mychar_write(struct file *pfile, const char __user *pbuf, size_t count, loff_t *ppos)
{
	int size = 0;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	if (count < BUF_LEN - pmydev->curlen)
	{
		size = count;
	}
	else
	{
		size = BUF_LEN - pmydev->curlen;
	}

	ret = copy_from_user(pmydev->mydev_buf + pmydev->curlen, pbuf, 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("fail to copy_to_user!\n");
				return -1;
			}
			break;
		case MYCHAR_IOCTL_GET_CURLEN:
			ret = copy_to_user(pret, &pmydev->curlen, sizeof(int));
			if (ret)
			{
				printk("fail to copy_from_user!\n");
				return -1;
			}
			break;
		default:
			printk("the cmd is unknow!\n");
			return -1;
	}

	return 0;
}

int mychar_close(struct inode *pnode, struct file *pfile)
{

	printk("mychar clsoe is called!!!\n");
	return 0;
}

/* 对字符设备的操作函数 */
struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.write = mychar_write,
	.read = mychar_read,
	.unlocked_ioctl = mychar_ioctl,	
	.release = mychar_close,
};

int __init mychar_init(void)
{
	int ret = 0;
	int i = 0;

	dev_t devno = MKDEV(major, minor); 										//组合设备号

	ret = register_chrdev_region(devno, mychar_num, "mychar"); 				//手动申请设备号
	if (ret) 																//返回值为0表示申请成功
	{
		ret = alloc_chrdev_region(&devno, 0, mychar_num, "mychar"); 	//申请失败则系统自动分配
		if (ret)
		{
			printk("get devno failed!\n");
			return -1;
		}
		major = MAJOR(devno); 												//从系统分配的设备号中取出主设备号
		minor = MINOR(devno); 												//从系统分配的设备号中取出次设备号
		devno = MKDEV(major, minor); 										//组合设备号
	}

	for (i = 0;i < MYCHAR_DEV_CNT;++i)
	{
		devno = MKDEV(major, minor + i); 										//组合设备号
		/* 使得设备具有myops中的函数操作方法 */
		cdev_init(&gmydev_arr[i].mydev, &myops);
		gmydev_arr[i].mydev.owner = THIS_MODULE;

		/* 将设备号为devno的这个设备(mydev)添加到内核(内核hash链表中) */
		cdev_add(&gmydev_arr[i].mydev, devno, 1);
	}

	printk("hello world!\n");
	return 0;
}

void __exit mychar_exit(void)
{
	int i = 0;
	dev_t devno = MKDEV(major, minor); 						//组合设备号
	
	for (i = 0;i < MYCHAR_DEV_CNT;++i)
	{
		/* 从内核中删除mydev这个设备 */
		cdev_del(&gmydev_arr[i].mydev);
	}
	unregister_chrdev_region(devno, mychar_num); 		//注销设备号
	printk("bye bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);

1.2 应用层

应用层的代码没有任何修改!

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

int main(int argc, const char *argv[])
{
	int fd = -1;
	char buf[6];
	int max = 0;
	int cur = 0;

	if (argc < 2)
	{
		printf("the arguement is too few!\n");
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("fail to open %s\n", argv[1]);
		return -1;
	}

	ioctl(fd, MYCHAR_IOCTL_GET_MAXLEN, &max);
	printf("max = %d\n", max);

	write(fd, "hello", 6);

	printf("max = %d\n", max);
	ioctl(fd, MYCHAR_IOCTL_GET_CURLEN, &cur);
	printf("cur = %d\n", cur);

	read(fd, buf, 6);

	printf("buf = %s\n", buf);

	close(fd);
	fd = -1;
	return 0;
}

2 运行结果

在这里插入图片描述
每一个设备文件都支持这样的操作,因此运行后的结果是完全一致的!


总结

本期的分享相对来讲比较简单,就是需要将以前的设备修改为数组,那么在驱动程序的入口和出口函数中都需要进行着重修改,也就是循环创建和删除!
最后,各位小伙伴们如果有收获,可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Flink精讲】Flink性能调优:内存调优

内存调优 内存模型 JVM 特定内存 JVM 本身使用的内存&#xff0c;包含 JVM 的 metaspace 和 over-head 1&#xff09; JVM metaspace&#xff1a; JVM 元空间 taskmanager.memory.jvm-metaspace.size&#xff0c;默认 256mb 2&#xff09; JVM over-head 执行开销&#xff1…

深入探讨基于大语言模型的数据标注

文章地址&#xff1a; https://arxiv.org/pdf/2402.13446 数据标注是将原始数据用相关信息进行标注&#xff0c;对于提高机器学习模型的效果至关重要。然而&#xff0c;这一过程往往需要大量人力和资金支持。先进大语言模型&#xff08;LLMs&#xff09;的出现&#xff0c;例如…

小程序--事件处理

一、事件对象 给小程序的事件传递参数&#xff0c;有以下两种方法&#xff1a; 1、自定义属性 <view class"item" wx:for"{{ 5 }}" wx:key"*this" data-index"{{index}}" bind:tap"onClick"></view> Page({o…

spss常用检验方法

spss常用检验方法 1 数据是否符合正态分布1.1符合正态分布1.1.1怎样的数据符合正态分布1.1.2 spss怎么统计正态分布1.1.3 方差齐性检验1.1.4 具体统计学分析 1.2 不符合正态分布1.2.1 Mann-Whitney U检验1.2.2 Wilcoxon符号秩检验1.2.3 Kruskal-Wallis H检验1.2.4 Friedman检验…

CCF-CSP: 因子化简(100分)

第一次提交的时候90分&#xff0c;显示的超时&#xff0c;第一反应是难道有死循环? 检查一遍发现并没有&#xff0c;那就是真的超时了&#xff0c;然后翻阅blog,发现不需要去做判断是否是素数这一步&#xff0c;原因是任意一个非素数都是素数乘积构成&#xff0c;比如说&#…

提高移动应用的安全性:策略与实践

提高移动应用的安全性&#xff1a;策略与实践 随着移动应用的普及&#xff0c;安全性问题变得日益重要。用户数据保护、应用逻辑安全、以及防止恶意攻击都是开发者必须关注的重点。本文将探讨如何通过一系列策略和实践来提高移动应用的安全性。 1. 数据加密与保护 敏感数据加…

ABAP - Function ALV 05 添加选择框列、全选、取消全选

很多用户不习惯原生GRID的选择模式&#xff0c;所以业务需要用到自定义的选择框来进行数据的操作&#xff0c;显示效果如图所示&#xff0c;增加一条选择列&#xff0c;且配置全选和全选全选的按钮功能&#xff0c;如下图所示。 实现这种功能需要用到Fieldcat的参数控制以及GUI…

c#高级-正则表达式

正则表达式是由普通字符和元字符&#xff08;特殊符号&#xff09;组成的文字形式 应用场景 1.用于验证输入的邮箱是否合法。 2.用于验证输入的电话号码是否合法。 3.用于验证输入的身份证号码是否合法。等等 正则表达式常用的限定符总结&#xff1a; 几种常用的正则简写表达式…

力扣--动态规划1027.最长等差数列

思路分析&#xff1a; 使用动态规划的思想&#xff0c;定义二维数组dp&#xff0c;其中dp[i][j]表示以nums[i]为结尾&#xff0c;公差为(j-1000)的等差数列长度。为了适应负数的情况&#xff0c;将公差的范围设为[-1000, 1000]&#xff0c;并且加上1000作为数组索引。 初始化r…

javaApI(Application Programming Interface)应用程序编程接口

ApI概念 Apl:指的是官方给开发人员提供的说明文档&#xff0c;对语言中有哪些类&#xff0c;类中有哪些方法进行说明 Objict 类 java.lang.Object 是java类体系结构中最顶层的类 Object可以表示java中任意的类 Object类中的方法 toString() 输出一个对象&#xff0c;但是…

Python服务器监测测试策略与工具:确保应用的高可用性!

在构建高可用性的应用程序时&#xff0c;服务器监测测试是至关重要的一环。Python作为一种强大的编程语言&#xff0c;提供了丰富的工具和库来帮助我们进行服务器监测测试。本文将介绍一些关键的策略和工具&#xff0c;帮助你确保应用的高可用性。 1. 监测策略的制定&#xff…

matlab|基于DistFlow潮流的配电网故障重构(输入任意线路)

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 程序采用适用于辐射状网络的DistFlow潮流模型&#xff0c;可输入任意故障线路编号&#xff0c;得到优化重构结果。这个程序是配电网故障重构可视化matlabyalmip的升级版&#xff0c;原来的程序是以电压质量作…

介绍 PIL+IPython.display+mtcnn for 音视频读取、标注

1. nn.NLLLoss是如何计算误差的? nn.NLLLoss是负对数似然损失函数&#xff0c;用于多分类问题中。它的计算方式如下&#xff1a;首先&#xff0c;对于每个样本&#xff0c;我们需要将其预测结果通过softmax函数转换为概率分布。softmax函数可以将一个向量映射为一个概率分布&…

第四套CCF信息学奥赛c++ CSP-J认证初级组 中小学信奥赛入门组初赛考前模拟冲刺题(阅读程序题)

第四套中小学信息学奥赛CSP-J考前冲刺题 二、阅读程序题 (程序输入不超过数组或字符串定义的范围&#xff0c;判断题正确填√错误填X;除特殊说明外&#xff0c;判断题 1.5分&#xff0c;选择题3分&#xff0c;共计40分) 第一题 归并排序 1 #include <iostream> 2 usi…

【Java程序设计】【C00285】基于Springboot的游戏分享网站(有论文)

基于Springboot的游戏分享网站&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的游戏分享网站 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xff1a;在网站首页可以查看首页、游戏…

Linux环境基础开发工具使用篇(三) git 与 gdb

一、版本控制器-git 1.简单理解: ①git既是服务端&#xff0c;又是客户端 ②git会记录版本的变化 ③git是一个去中心化的分布式软件 git/gitee 是基于git仓库搭建的网站&#xff0c;让版本管理可视化 2.git 三板斧提交代码 查看安装的git版本 git--version 命令行提交代…

SpringMVC 学习(四)之获取请求参数

目录 1 通过 HttpServletRequest 获取请求参数 2 通过控制器方法的形参获取请求参数 3 通过 POJO 获取请求参数&#xff08;重点&#xff09; 1 通过 HttpServletRequest 获取请求参数 public String handler1(HttpServletRequest request) <form action"${pageCont…

Linux 文件操作

目录 C语言下的文件操作 Linux下的文件操作 文件描述符的前因后果 文件描述符的概念 文件描述符的分配规则 理解C语言的FILE结构体 Linux重定向 文件缓冲区 文件系统 文件系统的概念 ext2文件系统 对ext2的补充 虚拟文件系统的概念 软硬链接 C语言下的文件操作 …

Java零基础 - 关键字 instanceof

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

【深入理解设计模式】代理设计模式

代理设计模式&#xff1a; 代理设计模式是一种结构型设计模式&#xff0c;它允许你提供一个替代物或占位符来控制对其他对象的访问。在代理模式中&#xff0c;一个类代表另一个类的功能。这种类型的设计模式属于结构型模式&#xff0c;因为该模式涉及类和对象的组合。 概述 …
最新文章