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

嵌入式linux驱动学习-用cdev代替register_chrdev()

​上回说到字符设备驱动程序的注册与销毁register_chrdev()和unregister_chrdev()这是有缺陷的。

嵌入式lnux驱动学习-2.一个驱动程序的流程

现在用另外一个更好的方法代替,我们先来看看register_chrdev()实际上是调用了

__register_chrdev(major, 0, 256, name, fops);

static inline int register_chrdev(unsigned int major, const char *name,          const struct file_operations *fops){  return __register_chrdev(major, 0, 256, name, fops);}

这个256其实就是申请的次设备号个数。

还记得怎么创建设备节点吗,无论是手动创建还是自动创建都是用主设备号加上次设备号,一个驱动程序可以有很多不同的设备节点

一个驱动程序有自己对应的file_operations结构体,A驱动对应A_fop结构体,用register_chrdev()后,A驱动的256个设备节点都对应A_fop结构体。

用另外一种方法,可以指定次设备号个数,举例:

用register_chrdev()后,A驱动主设备号254,B驱动主设备号就不能用254,不然就冲突了。

而另外一种方法,A驱动主设备号254,次设备号申请0-2,3个,B驱动主设备号仍然可以用254,次设备号只要不用0-3就行,主设备号相同也不会产生冲突。

实际上另外一种方法就是把register_chrdev()展开:

1.分配主次设备号

#define LED_MAJOR 0#define DEVICE_NUM 1static int major = LED_MAJOR;static int __init myled_init(void){  int ret;  dev_t devno = MKDEV (major,0);  if (major)    ret = register_chrdev_region(devno, DEVICE_NUM, "myled");  else {    ret = alloc_chrdev_region(&devno, 0, DEVICE_NUM, "myled") ;    major = MAJOR(devno); }

dev_t devno定义了完整设备号, 为 32 位, 其中 12 位为主设备号, 20 位为次设备号。 

使用如下宏可以从 dev_t 获得主设备号和次设备号:

MAJOR (dev_t dev)
MINOR (dev_t dev)

使用如下宏从主、次设备号获得完整的设备号

MKDEV (major,minor)

register_chrdev_region()函数用于已知起始设备的设备号的情况, 而alloc_chrdev_region() 用于设备号未知, 向系统动态申请未被占用的设备号的情况,可以自动避开设备号重复的冲突。

DEVICE_NUM为我们要申请的次设备号个数,这里设置了1个。

2.初始化 cdev 结构体

在 Linux 内核中, 使用 cdev 结构体描述一个字符设备, cdev 结构体的定义如下:

struct cdev {  struct kobject kobj; /* 内嵌的 kobject */  struct module *owner; /* 所属模块 */  struct file_operations *ops; /* 文件操作结构体 */  struct list_head list;  dev_t dev; /* 设备号 */  unsigned int count;};

cdev 结构体里有一个重要成员 file_operations 定义了字符设备驱动提供给虚拟文件系统的接口函数。

绑定file_operations结构体在cdev结构体初始化中完成

为了精简就写个什么都没有的open函数。

static int led_open (struct inode *node, struct file *filp){    return 0;}static struct file_operations myled_oprs = {  .owner = THIS_MODULE,  .open  = led_open,};static int __init myled_init(void){  int ret;  dev_t devno = MKDEV (major,0);  if (major)    ret = register_chrdev_region(devno, DEVICE_NUM, "myled");  else {    ret = alloc_chrdev_region(&devno, 0, DEVICE_NUM, "myled") ;    major = MAJOR(devno);  }    cdev_init(&cdev_myled, &myled_oprs);//初始化  ...... }

3.添加驱动

很简单,就是在初始化后加一句

cdev_add (&cdev_myled, devno, DEVICE_NUM);

4.删除驱动和设备号

在驱动出口使用

  cdev_del(&cdev_myled);  unregister_chrdev_region(MKDEV (major, 0), DEVICE_NUM);

5.完整测试

自动创建设备节点的方式是一样的,我们只申请一个次设备号0,但是用次设备号0和1,创建两个设备节点myled0,myled1。

然后写一个简单的应用程序,功能只是打开设备节点,如果是一个myled0能打开,myled1打不开即正常。

驱动:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/cdev.h>#include <linux/device.h>#define LED_MAJOR 0#define DEVICE_NUM 1static int major = LED_MAJOR;static struct class *led_class;static struct cdev cdev_myled;static int led_open (struct inode *node, struct file *filp){    return 0;}static struct file_operations myled_oprs = {  .owner = THIS_MODULE,  .open  = led_open,};static int __init myled_init(void){  int ret;  dev_t devno = MKDEV (major,0);  if (major)    ret = register_chrdev_region(devno, DEVICE_NUM, "myled");  else {    ret = alloc_chrdev_region(&devno, 0, DEVICE_NUM, "myled") ;    major = MAJOR(devno);  }    cdev_init(&cdev_myled, &myled_oprs);  cdev_add (&cdev_myled, devno, DEVICE_NUM);  led_class = class_create(THIS_MODULE, "myled");  device_create(led_class, NULL, MKDEV(major, 0),NULL,"myled0");     device_create(led_class, NULL, MKDEV(major, 1),NULL,"myled1");   return 0;}static void __exit myled_exit(void){  cdev_del(&cdev_myled);  unregister_chrdev_region(MKDEV (major, 0), DEVICE_NUM);  device_destroy(led_class, MKDEV(major, 0));  device_destroy(led_class, MKDEV(major, 1));    class_destroy(led_class);}module_init(myled_init);module_exit(myled_exit);MODULE_LICENSE("GPL");

应用:

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(int argc,char **argv){  int fd;  if(argc != 2) {    printf("usage:%s num",argv[0]);    return 0;  }  fd = open(argv[1],O_RDWR);  if(fd < 0) {    printf("can't open!\n");  } else    printf("can open\n");  return 0;}

makefile:

KERN_DIR = /usr/src/linux-headers-4.8.0-36-genericall:  make -C $(KERN_DIR) M=`pwd` modules  gcc -o led_test led_test.cclean:  make -C $(KERN_DIR) M=`pwd` modules clean  rm -rf modules.order  rm -f led_nothingobj-m += led_nothing.o

文件传入linux系统,make命令编译,insmod命令加载驱动:

结果:

测试成功

6.测试2

两个驱动用同一个主设备号,看看系统能不能识别

如下代码,第一个主设备号由系统自动分配后,第二个主设备号就用和第一个一样的,两个驱动对应不同的open函数,分别生成两个设备,对应设备成功打开时,用printk输出信息。

如果打开不同节点时,输出信息不同,说明成功。

应用程序和上文完全相同

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/cdev.h>#include <linux/device.h>#define LED_MAJOR 0#define DEVICE_NUM 2static int major = LED_MAJOR;static struct class *led_class;static struct cdev cdev_myled;static struct cdev cdev_myled2;static int led_open (struct inode *node, struct file *filp){    printk("myled0/1 open\n");  return 0;}static struct file_operations myled_oprs = {  .owner = THIS_MODULE,  .open  = led_open,};static int led_open2 (struct inode *node, struct file *filp){    printk("myled2/3 open\n");  return 0;}static struct file_operations myled_oprs2 = {  .owner = THIS_MODULE,  .open  = led_open2,};static int __init myled_init(void){  int ret;  dev_t devno = MKDEV (major,0);  if (major)    ret = register_chrdev_region(devno, DEVICE_NUM, "myled");  else {    ret = alloc_chrdev_region(&devno, 0, DEVICE_NUM, "myled") ;    major = MAJOR(devno);  }  cdev_init(&cdev_myled, &myled_oprs);  cdev_add (&cdev_myled, devno, DEVICE_NUM);    register_chrdev_region(MKDEV (major,2), DEVICE_NUM, "myled2");    cdev_init(&cdev_myled2, &myled_oprs2);  cdev_add (&cdev_myled2, MKDEV (major,2), DEVICE_NUM);  led_class = class_create(THIS_MODULE, "myled");  device_create(led_class, NULL, MKDEV(major, 0),NULL,"myled0");   device_create(led_class, NULL, MKDEV(major, 1),NULL,"myled1");     device_create(led_class, NULL, MKDEV(major, 2),NULL,"myled2");   device_create(led_class, NULL, MKDEV(major, 3),NULL,"myled3");   return 0;}static void __exit myled_exit(void){  cdev_del(&cdev_myled);  unregister_chrdev_region(MKDEV (major, 0), DEVICE_NUM);  device_destroy(led_class, MKDEV(major, 0));  device_destroy(led_class, MKDEV(major, 1));    cdev_del(&cdev_myled2);  unregister_chrdev_region(MKDEV (major, 2), DEVICE_NUM);  device_destroy(led_class, MKDEV(major, 2));  device_destroy(led_class, MKDEV(major, 3));    class_destroy(led_class);}module_init(myled_init);module_exit(myled_exit);MODULE_LICENSE("GPL");

编译后insmod装载驱动,cat /proc/devices查看一下

两个主设备号相同的驱动程序出现了

四个设备节点也都打开成功,用dmseg命令查看内核打印信息

成功。可见cdev方法虽然多了几步,但是更加灵活。在今后的讲解中为了精简代码,还是用register_chrdev()。

更多内容与参考资料:大叔的嵌入式小站:
嵌入式linux驱动学习-3.用cdev代替register_chrrdev

相关文章:

嵌入式linux驱动学习-用cdev代替register_chrdev()

​上回说到字符设备驱动程序的注册与销毁register_chrdev()和unregister_chrdev()这是有缺陷的。 嵌入式lnux驱动学习-2.一个驱动程序的流程 现在用另外一个更好的方法代替&#xff0c;我们先来看看register_chrdev()实际上是调用了 __register_chrdev(major, 0, 256, name,…...

技术更新!10个MySQL性能调优技巧

MySQL是世界上使用最广泛的开源数据库&#xff0c;它在业界的受欢迎程度让其他数据库望尘莫及。它是一个关系型数据库管理系统&#xff0c;多年来依然是应用程序的核心。在过去几年里&#xff0c;MySQL有一些重要发展。因此&#xff0c;整理更新10个MySQL性能调优技巧。 模式设…...

ICLR 2023|VLDet:从图像-文本对中学习区域-词语对齐的开放词汇式目标检测

原文链接&#xff1a;https://www.techbeat.net/article-info?id4614&isPreview1 作者&#xff1a;林闯 目标检测任务在AI工业界具有非常广泛的应用&#xff0c;但由于数据获取和标注的昂贵&#xff0c;检测的目标一直被限制在预先设定好的有限类别上。而在学术界&#xf…...

如何效率搭建企业流程系统?试试低代码平台吧

编者按&#xff1a;本文介绍了一款可私有化部署的低代码平台&#xff0c;可用于搭建团队流程管理体系&#xff0c;并详细介绍了该平台可实现的流程管理功能。关键词:可视化设计&#xff0c;集成能力&#xff0c;流程审批&#xff0c;流程调试天翎是国内最早从事快速开发平台研发…...

嵌入式开发:C++在深度嵌入式系统中的应用

深度嵌入式系统通常在C语言中实现。为什么会这样?这样的系统是否也能从C中获益?嵌入式开发人员在将广泛、高效的深度嵌入式代码库从C转换为C方面的实践经验的贡献。嵌入式和深度嵌入式系统通常用C而不是C实现。软件开发人员必须放弃C作为强类型系统、模板元编程(TMP)和面向对…...

快鲸scrm发布快递行业私域运营解决方案

现如今&#xff0c;快递行业竞争格局日益激烈&#xff0c;前有“四通一达”等传统快递企业&#xff0c;后有自带互联网基因、绑定电商流量新贵快递企业&#xff0c;如菜鸟、京东等。在这一背景下&#xff0c;很多快递企业开启了增长破局之旅&#xff0c;他们纷纷搭建起私域运营…...

【蓝桥杯集训·每日一题】AcWing 1497. 树的遍历

文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴递归一、题目 1、原题链接 1497. 树的遍历 2、题目描述 一个二叉树&#xff0c;树中每个节点的权值互不相同。 现在给出它的后序遍历和中序遍历&#xff0c;请你输出它的 …...

详解matplotlib的color配置

详解matplotlib的color配置 Matplotlib可识别的color格式 格式举例RGB或RGBA&#xff0c;由[0, 1]之间的浮点数组成的元组&#xff0c;分别代表红色、绿色、蓝色和透明度(0.1, 0.2, 0.5), (0.1, 0.2, 0.5, 0.3不区分大小写的十六进制RGB或RGBA字符串。‘#0f0f0f’, ‘#0f0f0f…...

Oracle删除表数据的三种方式

简介 oracle数据库mysql数据库都是如此 drop命令>truncate命令>delete命令&#xff0c;它们的执行方式、效率和结果各有不同。还是万年的student 学生表 自己可以建个尝试这玩一下。 drop命令 语句: drop table 表名&#xff1b; 理由&#xff1a;1、用drop删除表数据&…...

第 16 章_多版本并发控制

第 16 章_多版本并发控制 1. 什么是MVCC MVCC &#xff08;Multiversion Concurrency Control&#xff09;&#xff0c;多版本并发控制。顾名思义&#xff0c;MVCC 是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作…...

五种 IO 模型

文章目录操作系统和内存内核空间和用户空间应用程序的内核态和用户态网络 IO 和磁盘 IO简易的网络通信流程阻塞和非阻塞阻塞 IO 模型非阻塞 IO 模型IO 复用模型SelectPollEpoll小结信号驱动 IO 模型异步 IO 模型五种 IO 模型的对比IO 模型里的同步和异步5种 IO 模型分别是&…...

34-Golang中的结构体!!!

Golang中的结构体结构体和结构体变量(实例)的区别和联系结构体变量(实例)在内存中的布局如何声明结构体字段/属性注意事项和细节说明创建结构体实例的四种方式结构体使用细节结构体和结构体变量(实例)的区别和联系 1.结构体是自定义的数据类型&#xff0c;代表一类事物2.结构体…...

这6个视频剪辑素材库,你一定要知道~

推荐5个免费商用视频素材网站&#xff0c;建议收藏哦&#xff01; 1、菜鸟图库 视频素材下载_mp4视频大全 - 菜鸟图库 网站素材量很大&#xff0c;有设计、图片、音频、视频等超多素材&#xff0c;大部分都能免费下载。视频素材都很高清&#xff0c;有自然、人物、科技、农业…...

RocketMQ WIN11 搭建

去官方下载 https://rocketmq.apache.org/zh/download/ 下载&#xff0c;博主下载的是 4.6.0 的版本&#xff0c;选择Binary版本 拓展 Source 下载&#xff1a;需要编译 Binary 下载&#xff1a;不需要编译 解压缩&#xff0c;运行 先解压缩环境变量中添加rocketMQ文件夹路…...

iPhone更换电池和屏幕后提醒非原厂配件的操作办法

---开局一张图&#xff0c;内容全靠编系列&#xff01; 【图】 自从在iPhone系统iOS13开始支持原厂配件检测后&#xff0c;可以说苹果也动起了维修站商家利益的这块蛋糕。道理自然简单&#xff0c;卷嘛&#xff01;全球汽车行业也不是靠卖新车才赚钱的&#xff0c;各种交通事故…...

chatGPT发布记录

发行说明&#xff08;2 月 13 日&#xff09;我们对 ChatGPT 进行了多项更新&#xff01;这是新功能&#xff1a;我们更新了免费计划中 ChatGPT 模型的性能&#xff0c;以便为更多用户提供服务。根据用户反馈&#xff0c;我们现在默认让 Plus 用户使用更快的 ChatGPT 版本&…...

DataX及DataX-Web

大数据Hadoop之——数据同步工具DataX数据采集工具-DataX datax详细介绍及使用 一、概述 DataX 是阿里云DataWorks数据集成的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、…...

数据结构与算法系列之kmp算法

什么是kmp算法 1.kmp算法是一种改进的字符串算法&#xff0c;其核心是利用匹配失败后的信息&#xff0c;尽量减少模式串与主串的匹配次数已达到快速匹配的目的。 它主要实现作用的是 在 &#xff08;主串&#xff09;中找到 &#xff08;匹配&#xff09;字符串。 例 BF算法与k…...

算法分析详解

自古老的公元前1世纪开始&#xff0c;《周髀算经》就作为中国最古老的天文学和数学著作。 《周髀算经》采用最简便可行的方法确定天文历法&#xff0c;揭示日月星辰的运行规律&#xff0c;包括四季更替&#xff0c;气候变化&#xff0c;南北有极&#xff0c;昼夜相推的道理。为…...

东南大学自然辩证法概论期末总结

写在前面 作者&#xff1a;夏日 博客地址&#xff1a;https://blog.csdn.net/zss192 本文为2022年东南大学自然辩证法概论期末总结&#xff0c;内容为根据老师所发题纲综合多个资料总结得来 考试形式&#xff1a;从老师所发题纲&#xff0c;10个题目中选出4个&#xff0c;题…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG

TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码&#xff1a;HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…...

简约商务通用宣传年终总结12套PPT模版分享

IOS风格企业宣传PPT模版&#xff0c;年终工作总结PPT模版&#xff0c;简约精致扁平化商务通用动画PPT模版&#xff0c;素雅商务PPT模版 简约商务通用宣传年终总结12套PPT模版分享:商务通用年终总结类PPT模版https://pan.quark.cn/s/ece1e252d7df...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战

🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...