【北京迅为】《STM32MP157开发板嵌入式开发指南》- 第三十九章 Linux Misc驱动

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等


  • 第三部分 Linux 驱动基础

三十九Linux Misc驱动

本章导读

Linux MISC驱动是最简单的字符设备驱动,学习MISC驱动将会为学习字符设备驱动奠定基础。

39.1 章节讲解了misc设备驱动的基本概念及函数使用方法

39.2 章节讲解了编写最简单的杂项设备驱动,并将其编译为驱动模块,在STM32MP157开发板上运行测试。

本章内容对应视频讲解链接(在线观看):

杂项设备驱动讲解 → https://www.bilibili.com/video/BV1Vy4y1B7ta?p=9

编写一个杂项设备驱动 → https://www.bilibili.com/video/BV1Vy4y1B7ta?p=10

程序源码在网盘资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\驱动程序例程\03-杂项设备驱动”路径下。

39.1 misc设备驱动简介

章节我们讲解杂项设备驱动,那么杂项设备驱动是属于我们linux三大设备驱动的哪一项呢?由于linux驱动倾向于分层设计,所以每个具体的设备都可以找到它归属的类型,从而可以套到它相应的架构里面去,我们只需要实现它最底层的那部分。但是也有部分字符设备,确实不知道它属于哪种类型,我们一般推荐大家采用miscdevice的框架结构。misc 的意思是混合的杂项的,所以 misc 设备驱动也叫做杂项设备驱动,当我们板子上的某个设备没有办法分类时,就可以用 misc 设备驱动。它的注册跟使用比较的简单,所以比较适用于功能简单的设备。正因为简单,所以它通常嵌套在 platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。杂项设备是字符设备的一种,杂项设备可以自动生成设备节点。

在学习misc设备驱动之前,先来了解几个基础概念。

概念1设备节点

我们可以启动我们的开发板,进入到dev目录下,dev目录下全部都是生成的设备节点,如下图所示:

我们的系统里面有很多杂项设备。我们可以输入以下命令来查看,如下图所示

cat /proc/misc

 

概念2 杂项设备的优点

杂项设备除了比字符设备代码简单,还有别的区别吗?所有的 misc 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。主设设备号相同就可以节省内核的资源在内核中大概可以找到200多处使用miscdevice框架结构的驱动。

概念3主设备号和次设备号的概念

设备号包含主设备号和次设备号,设备号是计算机识别设备的一种方式,主设备号相同的就被视为同一类设备,主设备号在Linux系统里面是唯一的,次设备号不一定唯一。主设备号可以比做成电话号码的区号。比如北京的区号是010,次设备号可以比作成电话号码。

主设备号可以通过以下命令来查看,前面的数字就是主设备号,如下图所示:

cat /proc/devices

 

misc 设备用 miscdevice 结构体表示,miscdevice结构体的定义在内核源码具体定义linux-5.4.31/include/linux/miscdevice.h中内容如下: 

struct miscdevice  {
    int minor; //次设备号
    const char *name; //设备节点的名字
    const struct file_operations *fops; //文件操作集
    struct list_head list;
    struct device *parent;
    struct device *this_device;
    const struct attribute_group **groups;
    const char *nodename;
    umode_t mode;
};

当我们创建一个 misc 设备的 miscdevice 结构体时,需要我们指定 minor、name 和 fops 这三个成员变量。minor 表示设备号,需要用户设置,在 Linux 内核中有一些预定义的 misc 设备的设备号,定义在linux-5.4.31/include/linux/miscdevice.h文件中,如下所示:

#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
 /*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
......
#define MISC_DYNAMIC_MINOR 255

我们设置子设备号时要注意不要重复使用其他设备的子设备号。可以从这些预定义的子设备号中选择

一个,也可以自定义。

name就是这个misc设备的名字,当设备注册成功后,会在/dev目录下自动生成一个名为name的设备文件。fops 就是这个 misc 设备的操作集合。

当创建好miscdevice 结构体后,使用 misc_register 函数向系统中注册一个misc设备,函数原型如下:

函数

int misc_register(struct miscdevice * misc)

参数 misc

之前创建好的 miscdevice 结构体

返回值

成功返回 0,失败返回负数。

在设备驱动的卸载函数中,使用 misc_deregister 函数来注销掉 misc 设备。函数原型如下 

函数

int misc_deregister(struct miscdevice *misc)

参数misc

要注销的 miscdevice 结构体。

返回值

 在miscdevice结构体的第四行,它指向了一个file_operation的结构体。file_operations文件操作集在定义在include/linux/fs.h下面,如下图所示

file_operations中的成员函数实际是由drivers/char/misc.c中misc驱动核心层的misc_fops成员函数间接调用的。file_operations结构体里面的结构体成员都对应一个调用。简单介绍一下其中比较常用的函数:

llseek()函数用来修改一个文件的当前的读写位置,并将新位置返回。

read()函数用来从设备中读取数据,成功时返回读取到的字节数,出错返回一个负值。

write()函数用来向设备发送数据,成功时返回该函数写入的字节数。

poll()函数用于查询设备是否可以进行非阻塞读写。

unlock_ioctl()函数提供设备相关控制命令的实现。

mmap()函数将设备内存映射到进程的虚拟地址空间中。

open()函数用于打开设备文件。

release()函数用于关闭设备文件。

注册杂项设备有一个通用的思路和方法,这里给大家总结为三个步骤:

  • 填充miscdevice这个结构体
  • 填充file_operations这个结构体
  • 注册杂项设备并生生成设备节点。

39.2 编写实验程序

通过39.1章节的学习,我们已经把杂项设备的基本概念搞懂了,在本实验中,使用 misc 设备驱动的方式来编写最简单的杂项设备的驱动。

39.2.1 编写驱动例程

首先我们回想一下注册杂项设备的三大流程,我们在Windows上面新建misc.c文件,并用sourceinsight打开。我们可以将上次编写的helloworld.c里面的代码拷贝到misc.c文件,并修改为如下图所示:

添加头文件

/*注册杂项设备头文件*/

#include <linux/miscdevice.h>

/*注册设备节点的文件结构体*/

#include <linux/fs.h>

填充miscdevice结构体 

struct miscdevice  misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "hello_misc",
	.fops = &misc_fops,
};

上述代码第2行的minor为MISC_DYNAMIC_MINOR,miscdevice核心层会自动找一个空闲的次设备号,否则用minor指定的次设备号。上述代码第3行name是设备的名称,我们自定义为"hello_misc"

填充file_operations结构体

struct file_operations misc_fops={
	.owner = THIS_MODULE
};

THIS_MODULE宏是什么意思呢?它在include/linux/module.h里的定义是

#define THIS_MODULE (&__this_module)

它是一个struct module变量,代表当前模块,可以通过THIS_MODULE宏来引用模块的struct module结构,比如使用THIS_MODULE->state可以获得当前模块的状态。这个owner指针指向的就是你的模块。

注册杂项设备并生成设备节点

在misc_init()函数中填充misc_register()函数注册杂项设备,并判断杂项设备是否注册成功。

static int misc_init(void){
    int ret;
    ret = misc_register(&misc_dev);             //注册杂项设备
    if(ret<0)                                   //判断杂项设备是否注册成功
    {
        printk("misc registe is error \n");     //打印杂项设备注册失败
    }
    printk("misc registe is succeed \n");       //打印杂项设备注册成功
    return 0;
}
在misc_exit()函数中填充misc_deregister()函数注销杂项设备。
 static void misc_exit(void){
    misc_deregister(&misc_dev);                 //注销杂项设备
    printk("misc gooodbye! \n");                //打印杂项设备注销成功
}

完整的代码如下图所示:

/*
 * @Descripttion: 最简单的杂项设备驱动
 * @version: 1.0
 * @Author: topeet
 */
#include <linux/init.h>              //初始化头文件
#include <linux/module.h>            //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h>        /*注册杂项设备头文件*/
#include <linux/fs.h>                /*注册设备节点的文件结构体*/
struct file_operations misc_fops = { //文件操作集
    .owner = THIS_MODULE};
struct miscdevice misc_dev = {
    //杂项设备结构体
    .minor = MISC_DYNAMIC_MINOR, //动态申请的次设备号
    .name = "hello_misc",        //杂项设备名字是hello_misc
    .fops = &misc_fops,          //文件操作集
};
static int misc_init(void)
{ //在初始化函数中注册杂项设备
    int ret;
    ret = misc_register(&misc_dev);
    if (ret < 0)
    {
        printk("misc registe is error \n");
    }
    printk("misc registe is succeed \n");
    return 0;
}
static void misc_exit(void)
{ //在卸载函数中注销杂项设备
    misc_deregister(&misc_dev);
    printk(" misc gooodbye! \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

现在最简单的杂项设备的驱动就写完了,那么接下来我们可以把这个驱动编译一下,然后放到我们的开发板上面运行。我们编译驱动,可以将它编译进内核里面,也可以将它编译成模块。

39.2.2 编译驱动程序

这里我们以stm32mp157开发板为例,将杂项设备驱动编译成模块,请参考本手册第三十七Linux内核模块。我们将misc.c文件拷贝到Ubuntu的/home/nfs/03目录下。将上次编译helloworld的Makefile文件拷贝到misc.c同级目录下,修改Makefile为:

文件如下图所示: 

驱动编译成功生成了ko文件,如下图所示: 

 

39.2.3 运行测试

启动STM32MP157开发板,我们通过nfs挂载共享文件目录,我们进入到共享目录,加载驱动模块如图所示:

insmod misc.ko

 

驱动加载成功后,输入以下命令,查看注册的设备节点是否存在,如下图所示,设备节点存在。

ls /dev/h*

 

我们输入以下命令拆卸驱动模块,如下图所示:

rmmod misc

 

那么,现在最简单的杂项设备已经完成了。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/890656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot下的智能健康推荐引擎

3系统分析 3.1可行性分析 通过对本基于智能推荐的卫生健康系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于智能推荐的卫生健康系统采用SSM框架&#…

24秋面试笔记

文章目录 一、专业技能1.1 具备扎实的Java基础&#xff0c;熟练掌握面向对象编码规范、集合、反射以及Java8特性等。1.1.1 Java基础1.1.2 集合1.1.3 Java8新特性 1.2 熟悉常用的数据结构(链表、栈、队列、二叉树等)&#xff0c;熟练使用排序、动态规划、DPS等算法。1.2.1 数据结…

CountUp.js 实现数字增长动画 Vue

效果&#xff1a; 官网介绍 1. 安装 npm install --save countup.js2. 基本使用 // template <span ref"number1Ref"></span>// script const number1Ref ref<HTMLElement>() onMounted(() > {new CountUp(number1Ref.value!, 9999999).sta…

Centos7 搭建单机elasticsearch

以下是在 CentOS 7 上安装 Elasticsearch 7.17.7 的完整步骤&#xff1a;&#xff08;数据默认保存在/var/lib/elasticsearch下&#xff0c;自行更改&#xff09; 一、装 Java 环境 Elasticsearch 是用 Java 编写的&#xff0c;所以需要先安装 Java 运行环境。 检查系统中是…

弘景光电:以创新为翼,翱翔光学科技新蓝海

在科技日新月异的今天&#xff0c;光学镜头及模组作为智能设备的核心组件&#xff0c;其重要性日益凸显。广东弘景光电科技股份有限公司&#xff08;以下简称“弘景光电”&#xff09;正是在这一领域中&#xff0c;凭借其卓越的研发实力和市场洞察力&#xff0c;即将在创业板上…

001 Qt_从零开始创建项目

文章目录 前言什么是QtQt的优点Qt的应用场景创建项目小结 前言 本文是Qt专栏的第一篇文章&#xff0c;该文将会向你介绍如何创建一个Qt项目 什么是Qt Qt 是⼀个 跨平台的 C 图形⽤⼾界⾯应⽤程序框架 。它为应⽤程序开发者提供了建⽴艺术级图形界⾯所需的所有功能。它是完全…

英特尔新旗舰 CPU 将运行更凉爽、更高效,适合 PC 游戏

英特尔终于解决了台式机 CPU 发热和耗电的问题。英特尔的新旗舰 Core Ultra 200S 系列处理器将于 10 月 24 日上市&#xff0c;该系列专注于每瓦性能&#xff0c;比之前的第 14 代芯片运行更凉爽、更高效。这些代号为 Arrow Lake S 的处理器也是英特尔首款内置 NPU&#xff08;…

Unity3D 观察者模式

Unity3D 泛型事件系统 观察者模式 观察者模式是一种行为设计模式&#xff0c;通过订阅机制&#xff0c;可以让对象触发事件时&#xff0c;通知多个其他对象。 在游戏逻辑中&#xff0c;UI 界面通常会监听一些事件&#xff0c;当数据层发生变化时&#xff0c;通过触发事件&am…

LabVIEW提高开发效率技巧----状态保存与恢复

在LabVIEW开发中&#xff0c;保存和恢复程序运行时的状态是一个关键技巧&#xff0c;特别是在涉及需要暂停或恢复操作的应用中。通过使用 Flatten To String 和 Unflatten From String 函数&#xff0c;开发人员可以将程序当前的状态转换为字符串并保存&#xff0c;再在需要时恢…

决策树随机森林-笔记

决策树 1. 什么是决策树&#xff1f; 决策树是一种基于树结构的监督学习算法&#xff0c;适用于分类和回归任务。 根据数据集构建一棵树&#xff08;二叉树或多叉树&#xff09;。 先选哪个属性作为向下分裂的依据&#xff08;越接近根节点越关键&#xff09;&#xff1f;…

人工智能和机器学习之线性代数(一)

人工智能和机器学习之线性代数&#xff08;一&#xff09; 人工智能和机器学习之线性代数一将介绍向量和矩阵的基础知识以及开源的机器学习框架PyTorch。 文章目录 人工智能和机器学习之线性代数&#xff08;一&#xff09;基本定义标量&#xff08;Scalar&#xff09;向量&a…

机器视觉AI场景为什么用Python比C++多?

好多开发者在讨论机在机器视觉人工智能领域的时候&#xff0c;纠结到底是用Python还是C&#xff0c;实际上&#xff0c;Python 和 C 都有广泛的应用&#xff0c;选择 Python而不是 C 可能有以下一些原因&#xff1a; 语言易学性和开发效率 语法简洁&#xff1a; Python 语法简…

软考系统分析师知识点十:软件工程

前言 今年报考了11月份的软考高级&#xff1a;系统分析师。 考试时间为&#xff1a;11月9日。 倒计时&#xff1a;27天。 目标&#xff1a;优先应试&#xff0c;其次学习&#xff0c;再次实践。 复习计划第一阶段&#xff1a;扫平基础知识点&#xff0c;仅抽取有用信息&am…

【消息队列】Kafka从入门到面试学习总结

国科大学习生活&#xff08;期末复习资料、课程大作业解析、大厂实习经验心得等&#xff09;: 文章专栏&#xff08;点击跳转&#xff09; 大数据开发学习文档&#xff08;分布式文件系统的实现&#xff0c;大数据生态圈学习文档等&#xff09;: 文章专栏&#xff08;点击跳转&…

【C】C语言常见概念~

C语言常见概念 转义字符 转义字符&#xff0c;顾名思义&#xff0c;转变原来意思的字符 比如 #include <stdio.h> int main() {printf("abcndef");return 0; }输出的结果为&#xff1a; 将代码修改一下&#xff1a; #include <stdio.h> int main(…

Web安全常用工具 (持续更新)

前言 本文虽然是讲web相关工具&#xff0c;但在在安全领域&#xff0c;没有人是先精通工具&#xff0c;再上手做事的。鉴于web领域繁杂戎多的知识点&#xff08;工具是学不完的&#xff0c;哭&#xff09;&#xff0c;如果你在本文的学习过程中遇到没有学过的知识点&#xff0…

《OpenCV计算机视觉》—— 人脸检测

文章目录 一、人脸检测流程介绍二、用于人脸检测的关键方法1.加载分类器&#xff08;cv2.CascadeClassifier()&#xff09;2.检测图像中的人脸&#xff08;cv2.CascadeClassifier.detectMultiscale()&#xff09; 三、代码实现 一、人脸检测流程介绍 下面是一张含有多个人脸的…

了解高可用架构之前——CAP

CAP定理(布鲁尔定理)&#xff0c;在2000年的ACM PODC上提出的猜想 &#x1f4d3;1 CAP理论 理论描述 第一版&#xff1a;any distributed system cannot guaranty C,A and P simultaneously 对于一个分布式计算系统&#xff0c;不可能同时满足一致性(Consistence)、可用性(Ava…

环境、能源主题会议,斯普林格/ IEEE 出版

&#x1f31f;第四届环境污染与治理国际学术会议 (ICEPG 2024) ✅收录率高&#xff0c;EI稳定检索 【往届见刊后1个月内完成检索】 ✅华北水利水电大学主办&#xff0c;院士、校长、杰青等大咖齐聚 ✔会议时间&#xff1a;2024年10月25-27日 ✔会议地点&#xff1a;郑州东站…

苹果AI科学家研究证明基于LLM的模型存在缺陷 因为它们无法推理

苹果公司人工智能科学家的一篇新论文发现&#xff0c;基于大型语言模型的引擎&#xff08;如 Meta 和 OpenAI 的引擎&#xff09;仍然缺乏基本的推理能力。该小组提出了一个新的基准–GSM-Symbolic&#xff0c;以帮助其他人衡量各种大型语言模型&#xff08;LLM&#xff09;的推…