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

2. 字符设备驱动

一、设备号

1.1. 什么是设备号

设备号是用来标记一类设备以及区分这类设备中具体个体的一组号码。

设备号由主设备号和次设备号组成。主设备号的作用为标记一类设备、用于标识设备驱动程序,而次设备号的作用是为了区分这类设备中的具体个体设备及用于标识同一驱动程序下的具体设备;通过该区分,系统可以准确的识别和操作每一个设备和对设备的有效管理,提高设备的利用率和可扩展性。

在Linux中设备号的定义通常使用dev_t,该类型需要包含头文件include/linux/types.h,查看其定义我们可以发现它其实就是一个32位的无符号整型变量;其中主设备号为高12位(同类型驱动个数为0~212−10~2^{12}-102121),次设备号为低20位(挂载设备为0~220−10~2^{20}-102201)。

Linux中为我们提供了相关宏用于处理设备号,这些宏位于include/linux/kdev_t.h中:

#define MINORBITS 20      /* 次设备号位数 */
#define MINORMASK ((1U << MINORBITS) - 1)/* 获取主设备号 */
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
/* 获取次设备号 */
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
/* 主+次组成设备号 */
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

1.2. 设备号的获取、释放和查看

1.2.1. 获取设备号

获取设备号的方式有两种:静态分配和动态分配,都是Linux内核提供的接口,该接口位于linux/fs.h中。

  • 静态分配register_chrdev_region
    函数原型:
/*** register_chrdev_region() - register a range of device numbers* @from: the first in the desired range of device numbers; must include the major number.* @count: the number of consecutive device numbers required* @name: the name of the device or driver.** Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);
  • 动态分配alloc_chrdev_region
    函数原型:
/*** alloc_chrdev_region() - register a range of char device numbers* @dev: output parameter for first assigned number* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: the name of the associated device or driver** Allocates a range of char device numbers. The major number will be* chosen dynamically, and returned (along with the first minor number)* in @dev. Returns zero or a negative error code.*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

1.2.2. 释放设备号

  • 释放设备号
    函数原型:
/*** unregister_chrdev_region() - return a range of device numbers* @from: the first in the range of numbers to unregister* @count: the number of device numbers to unregister** This function will unregister a range of @count device numbers,* starting with @from. The caller should normally be the one who* allocated those numbers in the first place...*/
void unregister_chrdev_region(dev_t from, unsigned count);

1.2.3. 查看设备号

  • 指令lsblk
    待补充
  • 查看文件/proc/devices
    通过cat /proc/devices将所有的设备号全部打印出来,如下:
Character devices: # 字符设备1 mem4 /dev/vc/04 tty4 ttyS
Block devices: # 块设备7 loop8 sd9 md
254 mdp
259 blkext

1.3. 栗子

驱动代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>int Isalloc = 0; /* 是否动态申请设备号:0-静态申请、非0-动态申请 */
int Major = 0;
int Minor = 0;module_param(Isalloc, int, S_IRUGO);
MODULE_PARM_DESC(Isalloc, "e.g.是否动态申请: 0-静态申请(需输入设备号),非0-动态申请(输入设备号无效).");
module_param(Major, int, S_IRUGO);
MODULE_PARM_DESC(Major, "e.g.主设备号");
module_param(Minor, int, S_IRUGO);
MODULE_PARM_DESC(Minor, "e.g.次设备号");dev_t chrdev_region = 0;static int chrdev_region_init(void)
{if (0 == Isalloc){/* 静态申请设备号 */chrdev_region = MKDEV(Major, Minor);/* zhf是驱动程序名称 */int i32Ret = register_chrdev_region(chrdev_region, 1, "zhf");if (0 > i32Ret){printk("register_chrdev_region err %d \n", i32Ret);}}else{/* 动态申请设备号 zhf是驱动程序名称 */int i32Ret = alloc_chrdev_region(&chrdev_region, 0, 1, "zhf");if (0 > i32Ret){printk("alloc_chrdev_region err %d \n", i32Ret);}}/* 设备号打印 */printk("chrdev region ok major:%d minor:%d\n", MAJOR(chrdev_region), MINOR(chrdev_region));return 0;
}static void chrdev_region_exit(void)
{/* 设备号销毁 */unregister_chrdev_region(&chrdev_region, 1);printk("unregister_chrdev_region ok \n");
}module_init(chrdev_region_init);
module_exit(chrdev_region_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhf");

二、字符设备驱动

2.1. 什么是字符设备

字符设备是一种用于传输字符或字节流的设备。其实就是传"acnah"这样的ASSIC码。常见的字符设备包括键盘、鼠标、串口等设备。字符设备以字节为单位进行读取和写入。

字符设备的交互方式:字符设备在系统中会被表示为设备文件,用户可通过打开设备文件与字符设备进行交互。

2.2. 字符设备的操作

2.2.1. 注册字符设备

定义字符类设备需要用到cd

http://www.xdnf.cn/news/16921.html

相关文章:

  • Docker环境离线安装指南
  • 计算机网络:如何将一个C类网络划分成两个子网
  • 35.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--数据缓存
  • Docker-03.快速入门-部署MySQL
  • 【大模型实战】向量数据库实战 - Chroma Milvus
  • Android 之 蓝牙通信(4.0 BLE)
  • CASAtomic 原子操作详解
  • 1.内核模块
  • 攻防世界-web-csaw-mfw
  • IO流-字节流-FileOutputStream
  • 复现YOLOV5+训练指定数据集
  • 关于Web前端安全防御之安全头配置
  • 最新Android Studio汉化教程--兼容插件包
  • Java Stream API 编程实战
  • MySQL 事务原理 + ACID笔记
  • 【C语言】结构体详解
  • 无人机集群协同三维路径规划,采用冠豪猪优化器(Crested Porcupine Optimizer, CPO)实现,Matlab代码
  • Jetpack Compose for XR:构建下一代空间UI的完整指南
  • C++引用:高效安全的别名机制详解
  • 途游Android面试题及参考答案
  • pytorch 安装
  • 机器翻译的分类:规则式、统计式、神经式MT的核心区别
  • 计算用户日活:从数据设计到可视化的全流程(高频场景题)
  • 深入掌握 ExcelJS:Node.js 中强大的 Excel 操作库
  • RAG 知识库实战指南:基于 Spring AI 构建 AI 知识问答应用
  • GaussDB case when的用法
  • 在win上安装最新的X-anylabeling以及sam2来帮助进行全自动追踪标注
  • 多模态后训练反常识:长思维链SFT和RL的协同困境
  • Git 常用命令指南:从入门到高效开发
  • 【Qt】QObject::startTimer: Timers cannot be started from another thread