当前位置: 首页 > news >正文

驱动开发之新字符设备驱动开发

1.前言

register_chrdev unregister_chrdev 这两个函数是老版本驱动使用的函数,现在新的
字符设备驱动已经不再使用这两个函数,而是使用 Linux 内核推荐的新字符设备驱动 API 函数。
旧版本的接口使用,感兴趣可以看下面这个博客:
驱动开发之字符设备开发-CSDN博客

 2.剧情前透

在旧版本开发过程中,不难发现两个比较烦人的问题:

(1)设备号是静态分配的,需要我们去cat /proc/devices命令查看设备占用的设备ID号,看看哪些空闲的,而且还要看看 Documentation/devices.txt文档里面有没有占用的风险

(2)设备需要自己去创建,如:mknod /dev/chrdevbase c 200 0 创建设备节点。假如是动态分配的设备号,还得每一次通过cat /proc/devices命令查看设备占用的设备ID号,再通过mknod命令创建节点给应用层使用。

基于以上两个问题:本章就要解决这两个问题,自动分配管理设备号,自动创建设备文件。同时使用新的字符设备接口创建字符设备。

3.分配和释放设备号

3.1.没有指定设备号的申请函数

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

参数如:

dev:保存申请到的设备号。

baseminor:次设备号起始地址,

count:要申请的设备号数量。

name:设备名字。

使用注意:在调用之前,得通过devid = MKDEV(major, minor);生成设备ID。

3.2.给定了设备的主设备号和次设备号 的申请函数

int register_chrdev_region(dev_t from, unsigned count, const char *name)
参数如:
from 是要申请的起始设备号,也就是给定的设备号
count 是要申请的数量,一 般都是一个;
name 是设备名字。

3.3.释放设备号函数

void unregister_chrdev_region(dev_t from, unsigned count)
参数如:
from 是要申请的起始设备号,也就是给定的设备号
count 是要申请的数量,一 般都是一个;

3.4.申请函数的使用模板

int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */if (major) { /* 定义了主设备号 */devid = MKDEV(major, minor); /* 大部分驱动次设备号都选择 0 minor可以直接给0 */register_chrdev_region(devid, 1, "test");} else { /* 没有定义设备号 */alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */major = MAJOR(devid); /* 获取分配号的主设备号 */minor = MINOR(devid); /* 获取分配号的次设备号 */}

 4.自动创建设备

4.1.自动创建设备介绍

Linux 下通过 udev 来实现设备文件的创建与删除, udev 可以检 测系统中硬件设备状态,可以根据系统中硬件设备状态来创建或者删除设备文件。比如使用 modprobe 命令成功加载驱动模块以后就自动在 /dev 目录下创建对应的设备节点文件 , 使用 rmmod 命令卸载驱动模块以后就删除掉 /dev 目录下的设备节点文件。使用 busybox 构建根文件 系统的时候,busybox 会创建一个 udev 的简化版本— mdev。

4.2.创建和删除类

4.2.1.创建类

struct class *class_create (struct module *owner, const char *name)

参数:

owner 一般为 THIS_MODULE
name 是类名字
返回值:创建的类对象

使用说明:

自动创建设备节点的工作是在驱动程序的入口函数中完成的,一般在 cdev_add 函数后面添
加自动创建设备节点相关代码。cdev_add 函数在后面有介绍。

4.2.1.删除类

void class_destroy(struct class *cls);
cls 就是要删除的类对象

4.3.创建和删除设备

4.3.1.创建设备

struct device *device_create(
                struct class *class,
                struct device *parent,
                dev_t devt,
                void *drvdata,
                const char *fmt, ...)
device_create 是个可变参数函数
参数 class 就是设备要创建哪个类下面;
参数 parent 是父 设备,一般为 NULL ,也就是没有父设备;
参数 devt 是设备号;
参数 drvdata 是设备可能会使用 的一些数据,一般为 NULL
参数 fmt 是设备名字,如果设置 fmt=xxx 的话,就会生成 /dev/xxx 这个设备文件。
返回值就是创建好的设备。

4.3.2.删除设备

void device_destroy(struct class *class, dev_t devt)

参数 class 是要删除的设备所处的类

参数 devt 是要删除的设备号

5.新型字符设备接口

5.1.字符设备结构

Linux 中使用 cdev 结构体表示一个字符设备, cdev 结构体在 include/linux/cdev.h 文件中
的定义如下:
 struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;};
cdev 中有两个重要的成员变量: ops dev ,这两个就是字符设备文件操作函数集合
file_operations 以及设备号 dev_t

5.2.cdev_init 函数

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数:
cdev 就是要初始化的 cdev 结构体变量,
fops 就是字符设备文件操作函数集合
作用:初始化设备,绑定字符设备的操作函数。
问题Q1:不调这个函数,直接将 fops 对象给 cdev对象赋值会怎么样?

5.3.cdev_add 函数

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数:

  p 指向要添加的字符设备 (cdev 结构体变量 )
dev 就是设备所使用的设备号,
count 是要添加的设备数量

作用:cdev_add 函数用于向 Linux 系统添加字符设备(cdev 结构体变量)

5.4.cdev_del 函数

void cdev_del(struct cdev *p)

参数:

  p 指向要添加的字符设备 (cdev 结构体变量 )

作用:cdev_del 函数从 Linux 内核中删除相应的字符设备

6.测试例子

6.1.驱动代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define newchr_CNT			1		  	/* 设备号个数 */
#define newchr_NAME			"newchr"	/* 名newchr *//* newchr设备结构体 */
struct newchr_dev{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;		/* 类 		*/struct device *device;	/* 设备 	 */int major;				/* 主设备号	  */int minor;				/* 次设备号   */
};struct newchr_dev newchr;	/* newchr设备 *//** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int newchr_open(struct inode *inode, struct file *filp)
{filp->private_data = &newchr; /* 设置私有数据 */return 0;
}/** @description		: 从设备读取数据 * @param - filp 	: 要打开的设备文件(文件描述符)* @param - buf 	: 返回给用户空间的数据缓冲区* @param - cnt 	: 要读取的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 读取的字节数,如果为负值,表示读取失败*/
static ssize_t newchr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{printk("newchr_read \r\n");return 0;
}/** @description		: 向设备写数据 * @param - filp 	: 设备文件,表示打开的文件描述符* @param - buf 	: 要写给设备写入的数据* @param - cnt 	: 要写入的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 写入的字节数,如果为负值,表示写入失败*/
static ssize_t newchr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char newchrstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write fainewchr!\r\n");return -EFAULT;}newchrstat = databuf[0];		/* 获取状态值 */printk("newchr_write, %x\r\n", newchrstat);return 0;
}/** @description		: 关闭/释放设备* @param - filp 	: 要关闭的设备文件(文件描述符)* @return 			: 0 成功;其他 失败*/
static int newchr_release(struct inode *inode, struct file *filp)
{printk("newchr_release \r\n");return 0;
}/* 设备操作函数 */
static struct file_operations newchr_fops = {.owner = THIS_MODULE,.open = newchr_open,.read = newchr_read,.write = newchr_write,.release = 	newchr_release,
};/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static int __init newchr_init(void)
{/* 注册字符设备驱动 *//* 1、创建设备号 */if (newchr.major) {		/*  定义了设备号 */newchr.devid = MKDEV(newchr.major, 0);register_chrdev_region(newchr.devid, newchr_CNT, newchr_NAME);} else {						/* 没有定义设备号 */alloc_chrdev_region(&newchr.devid, 0, newchr_CNT, newchr_NAME);	/* 申请设备号 */newchr.major = MAJOR(newchr.devid);	/* 获取分配号的主设备号 */newchr.minor = MINOR(newchr.devid);	/* 获取分配号的次设备号 */}printk("newchenewchr major=%d,minor=%d\r\n",newchr.major, newchr.minor);	/* 2、初始化cdev */newchr.cdev.owner = THIS_MODULE;cdev_init(&newchr.cdev, &newchr_fops);/* 3、添加一个cdev */cdev_add(&newchr.cdev, newchr.devid, newchr_CNT);/* 4、创建类 */newchr.class = class_create(THIS_MODULE, newchr_NAME);if (IS_ERR(newchr.class)) {return PTR_ERR(newchr.class);}/* 5、创建设备 */newchr.device = device_create(newchr.class, NULL, newchr.devid, NULL, newchr_NAME);if (IS_ERR(newchr.device)) {return PTR_ERR(newchr.device);}return 0;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit newchr_exit(void)
{/* 注销字符设备驱动 */cdev_del(&newchr.cdev);/*  删除cdev */unregister_chrdev_region(newchr.devid, newchr_CNT); /* 注销设备号 */device_destroy(newchr.class, newchr.devid);class_destroy(newchr.class);
}module_init(newchr_init);
module_exit(newchr_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("seven");

6.2.应用层测试代码

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开驱动的文件 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 *//* 向drvier写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("drvier Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

6.3.测试结果

加载驱动之后,会自动分配设备号,并且在/dev/下创建了设备节点newchr该节点。应用层就可以使用了。

应用层操作/dev/newchr节点,符号预期效果。

OK,到目前为止,简单字符设备已经创建完毕,下一篇,就要点灯了,点灯大师再现了。

相关文章:

驱动开发之新字符设备驱动开发

1.前言 register_chrdev 和 unregister_chrdev 这两个函数是老版本驱动使用的函数&#xff0c;现在新的 字符设备驱动已经不再使用这两个函数&#xff0c;而是使用 Linux 内核推荐的新字符设备驱动 API 函数。 旧版本的接口使用&#xff0c;感兴趣可以看下面这个博客&#…...

【JMU】21编译原理期末笔记

本拖延症晚期患者不知不觉已经有半年没写博客了&#xff0c;天天不知道在忙什么。 乘着期末周前赶紧先把编译原理上传了&#xff0c;我记得我这科是86分&#xff0c;有点小遗憾没上90&#xff0c;但是总体不错。 链接&#xff1a;https://pan.baidu.com/s/1gO8pT7paHv1lkM_ZpkI…...

就业信息|基于SprinBoot+vue的就业信息管理系统(源码+数据库+文档)

就业信息管理系统 目录 基于SprinBootvue的就业信息管理系统 一、前言 二、系统设计 三、系统功能设计 1前台功能模块 2后台功能模块 4.2.1管理员功能 4.2.2学生功能 4.2.3企业功能 4.2.4导师功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设…...

一.架构设计

架构采用 ddd 架构&#xff0c;不同于传统简单的三层的架构&#xff0c;其分层的思想对于大家日后都是很有好处的&#xff0c;会给大家的思想层级&#xff0c;提高很多。 传统的项目 现有的架构 采取ddd架构&#xff0c;给大家在复杂基础上简化保留精髓&#xff0c;一步步进行…...

<学习笔记>从零开始自学Python-之-实用库篇(一)-pyscript

由Anaconda创建的PyScript是一项实验性的但很有前途的新技术&#xff0c;它使python运转时在支撑WebAssembly的浏览器中作为一种脚本言语运用。 每个现代常用的浏览器现在都支撑WebAssembly&#xff0c;这是许多言语&#xff08;如C、C和Rust&#xff09;能够编译的高速运转时…...

Vue项目中npm run build 卡住不执行的几种情况(实战版)

方法一 一&#xff1a;比较常见是镜像导致的原因 我们可以找到build/check-versions文件 将这段代码注释,重新运行就可以解决这个问题 if (shell.which(npm)) {versionRequirements.push({name: npm,currentVersion: exec(npm --version),versionRequirement: packageConfig.en…...

《Python源码剖析》之pyc文件

前言 前面我们主要围绕pyObject和pyTypeObject聊完了python的内建对象部分&#xff0c;现在我们将开启新的篇章—python虚拟机&#xff0c;将聚焦在python的执行部分&#xff0c;搞懂从“代码”到“执行”的过程。开启新的篇章之前&#xff0c;你也许会有一个疑惑&#xff1a;我…...

Python零基础-中【详细】

接上篇继续&#xff1a; Python零基础-上【详细】-CSDN博客 目录 十、函数式编程 1、匿名函数lambda表达式 &#xff08;1&#xff09;匿名函数理解 &#xff08;2&#xff09;lambda表达式的基本格式 &#xff08;3&#xff09;lambda表达式的使用场景 &#xff08;4&…...

回溯 leetcode

22. 括号生成 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()","()(())"…...

Android firebase消息推送集成 FCM消息处理

FirebaseMessagingService 是 Firebase Cloud Messaging (FCM) 提供的一个服务&#xff0c;用于处理来自 Firebase 服务器的消息。它有几个关键的方法&#xff0c;你提到的 onMessageReceived、doRemoteMessage 和 handleIntent 各有不同的用途。下面逐一解释这些方法的作用和用…...

react中怎么为props设置默认值

在React中&#xff0c;你可以使用ES6的类属性&#xff08;class properties&#xff09;或者函数组件中的默认参数&#xff08;default parameters&#xff09;来定义props的默认值。 1.类组件中定义默认props 对于类组件&#xff0c;你可以在组件内部使用defaultProps属性来…...

企业如何做好 SQL 质量管理?

研发人员写 SQL 操作数据库想必一定是一类基础且常见的工作内容。如何避免 “问题” SQL 流转到生产环境&#xff0c;保证数据质量&#xff1f;这值得被研发/DBA/运维所重视。 什么是 SQL 问题&#xff1f; 对于研发人员来说&#xff0c;在日常工作中&#xff0c;大部分都需要…...

半年不在csdn写博客,总结一下这半年的学习经历,coderfun的一些碎碎念.

前言 自从自己建站一来&#xff0c;就不在csdn写博客了&#xff0c;但是后来自己的网站因为资金问题不能继续维护下去&#xff0c;所以便放弃了自建博客网站来写博客&#xff0c;等到以后找到稳定&#xff0c;打算满意的工作再来做自己的博客网站。此篇博客用来记录自己在csdn…...

c++中的命名空间与缺省参数

一、命名空间 1、概念&#xff1a;在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存 在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c; 以避免命名冲突或…...

SpringBoot整合WebSocket实现聊天室

1.简单的实现了聊天室功能&#xff0c;注意页面刷新后聊天记录不会保存&#xff0c;后端没有做消息的持久化 2.后端用户的识别只简单使用Session用户的身份 0.依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-…...

llama-factory学习个人记录

框架、模型、数据集准备 1.llama-factory部署 # 克隆仓库 git clone https://github.com/hiyouga/LLaMA-Factory.git # 创建虚拟环境 conda create --name llama_factory python3.10 # 激活虚拟环境 conda activate llama_factory # 安装依赖 cd LLaMA-Factory pip install -…...

VLC播放器(全称VideoLAN Client)

一、简介 VLC播放器&#xff08;全称VideoLAN Client&#xff09;是一款开源的多媒体播放器&#xff0c;由VideoLAN项目团队开发。它支持多种音视频格式&#xff0c;并能够在多种操作系统上运行&#xff0c;如Windows、Mac OS X、Linux、Android和iOS等。VLC播放器具备播放文件…...

跟小伙伴们说一下

因为很忙&#xff0c;有一段时间没有更新了&#xff0c;这次先把菜鸟教程停更一下&#xff0c;因为自己要查缺补漏一些细节问题&#xff0c;而且为了方便大家0基础也想学C语言&#xff0c;这里打算给大家开一个免费专栏&#xff0c;这里大家就可以好好学习啦&#xff0c;哪怕0基…...

学 C/C++ 具体能干什么?

学习 C 和 C 后&#xff0c;你可以从事许多不同的工作和项目&#xff0c;这两种语言以其高性能和低级控制而闻名&#xff0c;特别适合以下几个领域&#xff1a; 1. 系统编程 C 和 C 是系统编程的首选语言&#xff0c;适用于操作系统、驱动程序和嵌入式系统开发。 操作系统开发…...

Django之Ajax实战笔记--城市级联操作

1. 项目架构搭建 1.1 创建项目tpdemo,创建应用myapp # 创建项目框架tpdemo$ django-admin startproject tpdemo$ cd tpdemo# 在项目中创建一个myapp应用$ python manage.py startapp myapp# 创建模板目录$ mkdir templates$ mkdir templates/myapp$ cd ..$ tree tpdemotpdemo…...

2026年上海AI Agent智能体开发公司全景解析:从技术底座到产业落地的能力坐标

引言&#xff1a;先把结论放在这里。2026年的上海&#xff0c;AI Agent智能体早已不是概念展厅里的抽象模型&#xff0c;而是直接进入业务流程、改写生产力公式的现实工具。面对“上海AI Agent智能体开发公司哪家好”或者“上海智能体软件开发公司推荐”这类问题&#xff0c;很…...

Windows屏幕录制全栈实现:Graphics Capture+FFmpeg零拷贝编码

1. 这不是“调个API就完事”的录制功能&#xff0c;而是要亲手把屏幕变成可编程的视频流管道很多人看到“FFmpeg屏幕录制”第一反应是&#xff1a;网上一搜&#xff0c;几十个C#封装库&#xff0c;NuGet install一下&#xff0c;几行代码start()就完事。我去年也这么想——直到…...

机器学习赋能银河系考古:CatBoost模型高精度预测恒星年龄

1. 项目概述&#xff1a;用机器学习为银河系“测龄”在银河系考古学这个领域&#xff0c;我们就像是在研究一部没有文字记载的古老家族史。恒星&#xff0c;作为这部历史书中的“化石”&#xff0c;它们的年龄是解读银河系过去130亿年里如何诞生、成长和演化的最关键线索。然而…...

为什么选择Vueify?探索Vue单文件组件的Browserify终极解决方案 [特殊字符]

为什么选择Vueify&#xff1f;探索Vue单文件组件的Browserify终极解决方案 &#x1f680; 【免费下载链接】vueify Browserify transform for single-file Vue components 项目地址: https://gitcode.com/gh_mirrors/vu/vueify 在前端开发的世界中&#xff0c;Vue.js以其…...

Fiddler HTTPS抓包失败原因与证书信任机制详解

1. 为什么HTTPS抓包总在“证书这关”卡死&#xff1f;——不是Fiddler不行&#xff0c;是系统和APP联手设防Fiddler HTTPS抓包避坑指南&#xff1a;从证书安装失败到APP抓包不全的完整解决方案——这个标题里藏着太多人反复踩坑却始终没想通的真相。我带过三届移动测试团队&…...

美国RTP全系列材料:全面解析高性能导电塑料产品服务

宏裕塑胶代理美国RTP全系列材料&#xff0c;专注于为制造业企业提供高性价比、稳定可控的工程塑料原料供应及全流程技术支持&#xff0c;凭借源头直采优势与专业技术服务&#xff0c;助力客户降低采购成本、提升生产效率&#xff0c;适用于塑胶制品厂、精密注塑厂、汽车零部件厂…...

30+平台文档一键免费下载:浏览器文档下载工具的终极解决方案

30平台文档一键免费下载&#xff1a;浏览器文档下载工具的终极解决方案 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档&#xff0c;但是相关网站浏览体验不好各种广告&#xff0c;各种登录验证&#xff0c;需要很多步骤才能下载文档&#xff0c;该脚本就是…...

[开源] 病历自举报系统:面向临床质控的电子病历智能预审工具,用大模型扮演质疑者角色发现逻辑矛盾与缺项问题

本项目是一个专为中文电子病历&#xff08;EMR&#xff09;设计的轻量级质控辅助工具&#xff0c;核心目标是让医生在提交病历前&#xff0c;就能快速识别出文本中潜藏的逻辑矛盾、信息缺项、时间线错乱、数值异常和主观夸大等典型质量问题。我们不替代人工质控&#xff0c;也不…...

深度剖析Claude Code实操逻辑,解锁AI编程高效开发方式

文章目录前言一、我用Claude Code的翻车现场&#xff0c;能写一本《程序员血泪史》二、Claude Code的核心设计思想&#xff1a;你以为它是保姆&#xff0c;其实它是保安三、普通模式vs规划模式&#xff1a;一个是临时工&#xff0c;一个是项目经理四、两条核心指令&#xff0c;…...

机器学习驱动热光伏电池设计:从材料筛选到器件优化

1. 项目概述&#xff1a;当机器学习遇见热光伏电池设计在能源技术领域&#xff0c;热光伏&#xff08;TPV&#xff09;技术一直是一个充满魅力但又颇具挑战的方向。简单来说&#xff0c;它就像一个“热能收割机”&#xff0c;能将高温热源&#xff08;比如工业废热、聚光太阳能…...