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

news2025/1/19 14:36:27

文章目录

  • 前言
  • 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.coloradmin.cn/o/1469969.html

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

相关文章

python 线程笔记二 (概念+示例代码)

1. 线程介绍 1. 在前面了解了进程的概念&#xff0c;简单来说进程就是在内存中申请了一块内存空间&#xff0c;其实还有一个线程的概念&#xff0c; 线程包含在进程之中&#xff0c;是代码真正的执行者。也就是说进程其实是一个资源单位&#xff0c;而线程是执行单位。 2. 线程…

瑞_Redis_初识Redis(含安装教程)

文章目录 1 初识Redis1.1 认识NoSQL1.1.1 结构化与非结构化1.1.2 关联和非关联1.1.3 查询方式1.1.4 事务1.1.5 总结 1.2 认识Redis1.2.1 介绍1.2.2 特征1.2.3 优势 1.3 安装Redis ★★★1.3.1 Linux安装Redis1.3.1.0 资源准备1.3.1.1 安装Redis依赖1.3.1.2 上传安装包并解压1.3…

vim恢复.swp [BJDCTF2020]Cookie is so stable1

打开题目 扫描目录得到 关于 .swp 文件 .swp 文件一般是 vim 编辑器在编辑文件时产生的&#xff0c;当用 vim 编辑器编辑文件时就会产生&#xff0c;正常退出时 .swp 文件被删除&#xff0c;但是如果直接叉掉&#xff08;非正常退出&#xff09;&#xff0c;那么 .swp 文件就会…

windows 中, bash: conda: command not found(已解决)

git bash 中运行conda命令&#xff0c;出现这种错误&#xff0c;原因是你没有在git bash中 配置conda&#xff0c;导致git bash无法找到conda 那就配置一下&#xff0c;找到你的conda的安装位置下的bash.sh文件&#xff0c;一般在安装位置&#xff08;我的安装在C盘的自定义路径…

在Linux操作系统的ECS实例上安装Hive

目录 1. 完成hadoop安装配置2. 安装配置MySql安装配置 3. 安装Hive4. 配置元数据到MySQL5. hiveserver2服务配置文件测试 1. 完成hadoop安装配置 在Linux操作系统的ECS实例上安装hadoop 以上已安装并配置完jdk、hadoop也搭建了伪分布集群 2. 安装配置MySql 安装 下下一步…

Unity中URP实现水体效果(泡沫)

文章目录 前言一、给水上色1、我们在属性面板定义两个颜色2、在常量缓冲区申明这两个颜色3、在片元着色器中&#xff0c;使用深度图对这两个颜色进行线性插值&#xff0c;实现渐变的效果 二、实现泡沫效果1、采样 泡沫使用的噪波纹理2、控制噪波效果强弱3、定义_FoamRange来控制…

算法-计算机基础知识

1&#xff0c;坐标系与数学不同&#xff0c;x轴向下&#xff0c;y轴向右 2.案例&#xff1a;螺旋矩阵 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 class Solution {public List<Integer> spiralOrder(int[][] matrix) { List<Integer&…

解锁苏宁电商数据新纪元:关键字搜索API接口引领业务升级

苏宁关键字搜索API接口&#xff1a;电商数据探索的新篇章 一、引言 在电商领域&#xff0c;数据的重要性不言而喻。为了帮助开发者更高效地获取和利用电商数据&#xff0c;苏宁开放平台提供了关键字搜索API接口。本文将带你深入了解这一接口的技术细节&#xff0c;让你在电商…

【电子通识】为什么单片机芯片上会有多组VDD电源?

在单片机芯片规格书中&#xff0c;我们经常能看到多个组VDD的设计&#xff0c;如下红框所示管脚都是VDD管脚。 为什么需要这样设计&#xff1f;只设置一个VDD管脚&#xff0c;把其他的VDD管脚让出来多做几个IO或是其他复用功能不好吗&#xff1f;接下来我们从单片机内部的电路结…

Jenkins自动化部署构建说明(8)

Jenkins构建说明 - 20211012 什么是Jenkins? Jenkins 是一款流行的开源持续集成&#xff08;Continuous Integration&#xff09;工具&#xff0c;广泛用于项目开发&#xff0c;具有自动化构建、测试和部署等功能。它是一个自动化的周期性的集成测试过程&#xff0c;从检出代…

安装 Ubuntu 22.04.3 和 docker

文章目录 一、安装 Ubuntu 22.04.31. 简介2. 下载地址3. 系统安装4. 系统配置 二、安装 Docker1. 安装 docker2. 安装 docker compose3. 配置 docker 一、安装 Ubuntu 22.04.3 1. 简介 Ubuntu 22.04.3 是Linux操作系统的一个版本。LTS 版本支持周期到2032年。 系统要求双核 C…

自定义神经网络四之编写自定义神经网络

文章目录 前言神经网络组件代码整体的项目结构Tensor张量Layers层NeuralNet神经网络Loss损失函数Optim优化器data数据处理train训练 神经网络解决实际问题实际问题训练和推理代码 总结 前言 自定义神经网络一之Tensor和神经网络 自定义神经网络二之模型训练推理 自定义神经网络…

线程计数器(CountDownLatch)

&#x1f96d;线程计数器&#xff08;CountDownLatch&#xff09; CountDownLatch也属于共享锁&#xff0c;其内部有一个int类型的属性表示可以同时并发并行的线程的数量 同时等待N个任务执行结束 举例说明&#xff1a; 比如跑步比赛&#xff0c;必须等所有运动员通过终点才…

值类型和引用类型详解(C#)

可能你对值类型和引用类型还不太了解。 值类型和引用类型&#xff0c;是c#比较基础&#xff0c;也必须掌握的知识点&#xff0c;但是也不是那么轻易就能掌握&#xff0c;今天跟着我一起来看看吧。 典型类型 首先我们看看这两种不同的类型有哪些比较典型的代表。 典型值类型…

java面试题之mysql篇

1、数据库索引 ​​​​​​​ 索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她&#xff0c;则与在表中搜索所有的行相比&#xff0c;索引有助于更快地获取信息。 索引的一个主要…

H5多用途的产品介绍展示单页HTML5静态网页模板

H5多用途的产品介绍展示单页HTML5静态网页模板 源码介绍&#xff1a;一款H5自适应多用途的产品介绍展示单页HTML静态网页模板&#xff0c;可用于团队官网、产品官网。 下载地址&#xff1a; https://www.changyouzuhao.cn/13534.html

26.java-单元测试xml注解

单元测试&xml&注解 单元测试 单元测试就是针对最小的功能单元编写测试代码&#xff0c;Java程序最小的功能单元是方法&#xff0c;因此&#xff0c;单元测试就是针对 Java 方法的测试&#xff0c;进而检查方法的正确性。 简单理解 : 就是一个测试代码的工具 目前测试…

iPhone数据恢复软件有哪些?11 款 iPhone 数据恢复软件

随着技术的出现&#xff0c;我们对智能手机的依赖程度超出了我们的想象。从保存珍贵的相册、电话簿、日记到重要文件&#xff0c;应有尽有。 但我们也意识到&#xff0c;技术给我们带来的东西也可能被夺走。一次错误的触摸或点击可能会删除手机上的所有数据&#xff1b;您可能…

Linux第66步_linux字符设备驱动_挂载和卸载

1、了解linux中的驱动类型: 1)、字符设备驱动 字符设备是limnux驱动中最基本的一类设备驱动&#xff0c;字符设备就是一个一个字节&#xff0c;按照字节流进行读写操作的设备&#xff0c;读写数据是分先后顺序的。如&#xff1a;GPIO输入输出、UART、I2C、SPI、USB、LCD、音频…

Apache Doris 发展历程、技术特性及云原生时代的未来规划

文章目录 每日一句正能量前言作者介绍Apache Doris 特性极简架构高效自运维高并发场景支持MPP 执行引擎明细与聚合模型的统一便捷数据接入Apache Doris 极速 1.0 时代极速列式内存布局向量化的计算框架Cache 亲和度虚函数调用SIMD 指令集 稳定多源基于云原生向量数据库Milvus 的…