嵌入式Linux 开发经验:注册一个 misc 设备

news/2024/10/31 1:35:26/

前言

  • 之前买过好几本Linux 设备驱动的书,不过对设备驱动一知半解,什么叫设备,什么又叫驱动?最近工作需要,从源码级别深入的研究了一下 Linux 下的设备与驱动的概念,略有所收获

  • 一般提起驱动开发,都是面向硬件的,至少是底层开发,依赖具体的平台,Linux 作为一个通用的操作系统内核,当前不可能顾及所以的具体外设驱动,只能抽取出驱动的共性,抽象出一个设备驱动模型(框架)出来,这个设备模型从上层看,无论设备多么简单或者复杂,把共性的设备操作,如:注册、反注册、打开、关闭、控制、读写等 封装成 总线、驱动、设备,与设备树配合起来,为上层提供通用的设备操作接口,如文件接口、socket 接口,为底层设备驱动开发提供便利,降低驱动开发的难度。

  • 其实就是抽象与分层,让驱动开发就像是【填空题】,照着模板填空补充,就可以开发具体的设备驱动了。设备驱动的目的,就是驱使设备工作起来,可以让上层应用操作。

测试环境搭建

  • ubuntu 20.04

  • VMware Workstation Pro 16

  • 基于qemu(模拟器),vexpress-a9 平台

  • Linux 6.0.10 (当前最新版本)

  • 注册一个简单的misc 设备,掌握misc 设备注册的方法

misc 设备是什么设备

  • 大家经常提到的是 三大类设备:【字符设备】、【块设备】、【网络设备】。

  • misc 设备是什么呢?为何使用 misc 设备?

  • 如今Linux 设备驱动非常的庞大,所以当前接触的一些外设,都有类似的驱动模型,misc (杂类设备)属于 char 字符设备。

  • 使用 misc 设备的好处就是 Linux 提供了完善的 misc 设备管理,使用 misc 设备提供的API,就可以方便的注册管理 一个 misc 具体设备,使用 misc 设备最核心的 一般是使用 open、close、ioctl 接口,这些接口,可以让用户太的应用操作设备。

  • 注册了一个 misc 设备,如 led0,用户态程序通过 open("/dev/led0", O_RDWR),就可以打开内核驱动misc 设备,通过 ioctl 就可以控制 内核驱动 misc 设备。

  • 也就有一些设备,不是直接读写的,大部分操作都是控制命令,如空调的控制,一般有打开空调、关闭空调、调节空调的温度、模式等操作,可以把空调作为misc 设备来控制,打开关闭使用 open close,调节温度、模式等使用 ioctl。

注册 misc 设备

  • 这里就注意一点: 驱动与设备的概念,这里 misc 属于设备。

  • 这里为了方便,注册一个简单的设备,与上一篇 嵌入式Linux 开发经验:platform_driver_register 的使用方法 平台驱动 配合起来,当 平台驱动匹配设备树节点成功后,再初始化 misc 设备。

  • 新建 linux-6.0.10/drivers/led_control/led_misc.c

#include "led_misc.h"#define LED_MISC_DEVICE_NAME        "led_misc"struct led_misc_dev
{struct miscdevice misc;
};struct led_misc_dev *led_miscdev;/* 打开设备,用户态执行 open 命令,就会走到这里 */
static int led_misc_open(struct inode *inode, struct file *filp)
{printk(KERN_INFO "%s : enter\n", __func__);return 0;
}/* 打开设备,用户态执行 close 命令,就会走到这里 */
static int led_misc_close(struct inode *inode, struct file *filp)
{printk(KERN_INFO "%s : enter\n", __func__);return 0;
}/* 内存映射,大部分功能都差不多 */
static int led_misc_mmap(struct file *filp, struct vm_area_struct *vma)
{int ret = 0;if (filp == NULL){printk(KERN_ERR "invalid file!");return -EFAULT;}if (vma == NULL){printk(KERN_ERR "invalid vma area");return -EFAULT;}ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,vma->vm_end - vma->vm_start, vma->vm_page_prot);printk(KERN_INFO "%s : ret = %d\n", __func__, ret);return ret;
}/* 设备控制类,用户态执行 ioctl 命令,就会走到这里 */
static long led_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{printk(KERN_INFO "%s : enter\n", __func__);return 0;
}/* 设备的操作,用户态 应用通过【文件】操作接口操作 */
static const struct file_operations led_misc_fops =
{.owner  = THIS_MODULE,.llseek = no_llseek,.unlocked_ioctl = led_misc_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl = led_misc_ioctl,
#endif.mmap = led_misc_mmap,.open = led_misc_open,.release = led_misc_close,
};/* 注意这个 初始化不是自动初始化,放在 平台驱动 probe 函数  */
int led_miscdev_init(void)
{int ret;led_miscdev = kzalloc(sizeof(*led_miscdev), GFP_KERNEL);if (!led_miscdev)return -ENOMEM;led_miscdev->misc.minor = MISC_DYNAMIC_MINOR;led_miscdev->misc.fops = &led_misc_fops;led_miscdev->misc.name = LED_MISC_DEVICE_NAME;led_miscdev->misc.nodename = LED_MISC_DEVICE_NAME;ret = misc_register(&led_miscdev->misc);if (ret < 0){printk(KERN_INFO "%s : error\n", __func__);}else{printk(KERN_INFO "%s : ok\n", __func__);}return ret;
}/* 可以放在 平台驱动 remove 函数  */
void led_miscdev_exit(void)
{misc_deregister(&led_miscdev->misc);printk(KERN_INFO "%s : ok\n", __func__);
}
  • 新建 linux-6.0.10/drivers/led_control/led_misc.h
#ifndef __LED_MISC_H__
#define __LED_MISC_H__#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>int led_miscdev_init(void);
void led_miscdev_exit(void);#endif

其他修改

  • 上面是 注册 misc 设备的实现,但是需要调用才能执行,配合 平台驱动, led_miscdev_init 放在 平台驱动的 probe 函数中,led_miscdev_exit 可以放在 平台驱动 remove 函数,也可以放在 平台驱动 module_exit 的执行函数中

  • 修改 linux-6.0.10/drivers/led_control/led_control.c

static int led_control_probe(struct platform_device *pdev)
{printk(KERN_INFO "%s : enter\n", __func__);led_miscdev_init();  /* 设备树节点匹配后,调用 */return 0;
}static int led_control_remove(struct platform_device *pdev)
{printk(KERN_INFO "%s : enter\n", __func__);//led_miscdev_exit();  /* 移除设备驱动时,释放 misc 设备  */return 0;
}static void __exit led_control_driver_exit(void)
{printk(KERN_INFO "%s : enter\n", __func__);led_miscdev_exit();  /* 移除设备驱动时,释放 misc 设备  */platform_driver_unregister(&led_control_driver);
}
  • 修改 linux-6.0.10/drivers/led_control/led_control.h,添加 #include "led_misc.h"
#ifndef __LED_CONTROL_H__
#define __LED_CONTROL_H__#include <linux/of.h>
#include <linux/platform_device.h>#include "led_misc.h"#endif
  • 修改 linux-6.0.10/drivers/led_control/Makefile,增加
obj-$(CONFIG_LED_CONTROL) += led_control.o
obj-$(CONFIG_LED_CONTROL) += led_misc.o

编译与运行

  • 编译与 qemu 运行方法参考上篇 嵌入式Linux 开发经验:platform_driver_register 的使用方法 平台驱动的注册中提到的方法

  • 【小技巧】,这里把 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 编译命令 做成一个 shell 脚本

  • vim mk.sh

#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- $1 $2 $3
  • chmod +x mk.sh 增加执行权限

  • 编译时: ./mk.sh -j4 就可以编译了

在这里插入图片描述

  • 更新 zImge Linux 内核编译的产物,开启 qemu 查看注册的 misc 设备

  • 启动 qemu 的信息包括 如下:

led_control_driver_init : enter
led_control_probe : enter
led_miscdev_init : ok

在这里插入图片描述

  • 说明设备树节点匹配后,正确调用了 misc 设备的初始化函数 led_miscdev_init

  • Linux shell 查看 注册的 misc 设备

ls /sys/class/misc/ -la, 可以查看 ,注意在 /sys/class/misc/ 目录下

在这里插入图片描述

  • 注册 misc 设备成功了,接下来可以编写 用户态的应用,通过文件操作接口,如 open close ioctl 来控制这个 内核 misc 设备了

小结

  • 本篇与上一篇 平台驱动配合,记录了一下 平台驱动+ misc 设备的操作流程,部分简单的设备,可以利用Linux 设备驱动框架提供的便利,想填空题一样快速开发构建自己的实际的设备驱动。

  • 使用 Linux 设备种类大概有三种,不过细分, misc 属于 char 字符设备,当前还有各种形形色色的功能不同的设备。 Misc 设备属于比较常用的简单的控制类设备


http://www.ppmy.cn/news/1088.html

相关文章

车载测试好就业?车载测试就业薪资和前景怎么样?

什么是车载智能系统测试&#xff1f; 车载测试&#xff0c;又叫车载智能系统测试&#xff0c;是汽车智能化重要的组成部分&#xff0c;由旧有的车载资通讯系统结合联网汽车技术所演进而来&#xff0c;随着软硬件技术的不断进步&#xff0c; 让车载智能系统拥有强大的运算能力及…

SpringBoot: Controller层的优雅实现

目录1. 实现目标2. 统一状态码3. 统一响应体4. 统一异常5. 统一入参校验6. 统一返回结果7. 统一异常处理8. 验证1. 实现目标 优雅校验接口入参响应体格式统一处理异常统一处理 2. 统一状态码 创建状态码接口&#xff0c;所有状态码必须实现这个接口&#xff0c;统一标准 pa…

【YOLO系列改进NO.45】首发最新特征融合技术RepGFPN(DAMO-YOLO)

文章目录前言一、解决问题二、基本原理三、​添加方法四、总结前言 作为当前先进的深度学习目标检测算法YOLOv7&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列…

【Python百日进阶-数据分析】Day121 - Plotly Figure参数: 散点图(三)marker 标记

文章目录marker 标记marker 标记 代码&#xff1a; fig.update_traces(markerdict(…), selectordict(type‘scatter’)) 类型&#xff1a;包含下面列出的一个或多个键的字典。 autocolorscale 自动色标 代码&#xff1a; fig.update_traces(marker_autocolorscale, selectord…

Nexus私服(三)

(一) maven中snapshots和releases snapshots快照在maven指的是开发阶段的版本,会频繁的更新,常指在开发测试阶段,一般在pom的版本中声明1.0-SNAPSHOTsnapshots快照的引入是为了解决开发阶段依赖的问题。当我们的版本号中不显示声明SNAPSHOT,maven会默认你的是releases稳定版本。…

Yolo算法检测之Anchor Boxes原理详解

刚开始yolo系列的目标检测算法&#xff0c;在一个网格中只能检测一个对象&#xff0c;但是我们在实验中发现&#xff0c;一个网格中很多时候存在不仅一个目标&#xff0c;可能存在多个目标&#xff0c;类似如下图所示&#xff0c;下面中间的网格中就存在人和车辆两个目标的中心…

十一、【React-Router6】Hooks 汇总

文章目录 1.useRoutes() 2. useNavigate() 3. useParams() 4. useSearchParams() 5. useLocation() 6. useMatch() 7. useInRouterContext() 8. useNavigationType() 9. useOutlet() 10. useResolvedPath() 1、useRoutes() 根据路由表&#xff0c;动态创建 <Routes> 和…

switch case与while语句练习

switch case 选择 假设用1&#xff0c;2。。。。7分别表示星期一。。。。星期天&#xff0c;现输入一个数字&#xff0c;输出对应的星期几。比如&#xff1a;输入3&#xff0c;则输出“星期三” #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() {int d…