【Linux】总线-设备-驱动模型

背景

前面,我们介绍了写驱动代码的一些常规步骤,并且也写了最基本的驱动代码,但是那些代码存在着问题,我们将硬件的信息都写进了驱动里了,如果我们在杂项设备驱动中控制led,那么会在硬件操作接口中包含硬件信息,如果引脚有变化,这个驱动代码就得重新修改,虽然修改也很简单,但是从框架的角度来看,这是不合理的,相当于驱动代码写死了。

于是,Linux引入了设备驱动模型分层的概念,将我们编写的驱动代码分为两块:设备和驱动。设备负责提供硬件资源,驱动代码负责去使用这些设备提供的硬件资源。并由总线将他们联系起来。

bus
device
driver

设备(device):挂载在某个总线的物理设备。

驱动(driver):与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式。

总线(bus):负责管理挂载对应总线的设备以及驱动。

类(class):对于具有相同功能的设备,归结到一种类别,进行分类管理。

重要目录

/sys/bus目录下的每个子目录都是注册好了的总线类型,每个总线类型下包含两个子目录(/sys/bus/devicessys/bus/drivers)。其中/sys/bus/devices下是该总线下的所有设备,而这些设备都是符号链接,他们分别指向真正的设备(/sys/devices目录下)。sys/bus/drivers目录下是所有注册在这个总线上的驱动,每个子目录下是一些可以观察和修改的driver参数。

/sys/devices目录下是全局设备结构体系,包含所有被发现的注册在各种总线上的各种物理设备。

/sys/dev目录下是所有的设备节点,但实际上都是些链接文件,同样指向了/sys/devices目录下的文件。

/sys/class目录下则是包含所有注册在kernel里面的设备类型,这是按照设备功能分类的设备模型。
在这里插入图片描述

框架原理

  • 总线管理着两个链表:设备链表和驱动链表。
  • 当我们向内核注册一个驱动时,便插入到总线的驱动链表。
  • 当我们向内核注册一个设备时,便插入到总线的设备链表。
  • 在插入的同时,总线会执行一个bus_type结构体中的match方法对新插入的设备或驱动进行匹配。
  • 匹配成功后,会调用驱动device_driver结构体中的probe方法。通常在probe方法中获取设备资源,具体的功能可自定义。
  • 移除设备或驱动时,会调用device_driver结构体中remove方法。

设备驱动模型

总线

总线是连接处理器和设备之间的桥梁,总线代表着同类设备需要共同遵守的工作时序。

在这里插入图片描述

bus_type结构体(内核源码/include/linux/device.h)struct bus_type {const char              *name;const struct attribute_group **bus_groups;const struct attribute_group **dev_groups;const struct attribute_group **drv_groups;int (*match)(struct device *dev, struct device_driver *drv);int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);int (*remove)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);const struct dev_pm_ops *pm;struct subsys_private *p;};
  • name :指定总线的名称,当新注册一种总线类型时,会在/sys/bus目录创建一个新的目录,目录名就是该参数的值;
  • drv_groups、dev_groups、bus_groups :分别表示驱动、设备以及总线的属性。这些属性可以是内部变量、字符串等等。通常会在对应的/sys目录下在以文件的形式存在,对于驱动而言,在目录/sys/bus//driver/存放了设备的默认属性;设备则在目录/sys/bus//devices/中。这些文件一般是可读写的,用户可以通过读写操作来获取和设置这些attribute的值。
  • match :当向总线注册一个新的设备或者是新的驱动时,会调用该回调函数。该回调函数主要负责判断是否有注册了的驱动适合新的设备,或者新的驱动能否驱动总线上已注册但没有驱动匹配的设备;
  • uevent :总线上的设备发生添加、移除或者其它动作时,就会调用该函数,来通知驱动做出相应的对策。
  • probe :当总线将设备以及驱动相匹配之后,执行该回调函数,最终会调用驱动提供的probe函数。
  • remove :当设备从总线移除时,调用该回调函数;
  • suspend、resume :电源管理的相关函数,当总线进入睡眠模式时,会调用suspend回调函数;而resume回调函数则是在唤醒总线的状态下执行;
  • pm :电源管理的结构体,存放了一系列跟总线电源管理有关的函数,与device_driver结构体中的pm_ops有关;
  • p :该结构体用于存放特定的私有数据,其成员klist_devices和klist_drivers记录了挂载在该总线的设备和驱动;

在实际编写linux驱动模块时,Linux内核已经为我们写好了大部分总线驱动,正常情况下我们一般不会去注册一个新的总线, 内核中提供了bus_register函数来注册总线,以及bus_unregister函数来注销总线。

注册/注销总线API(内核源码/drivers/base/bus.c)int bus_register(struct bus_type *bus);
void bus_unregister(struct bus_type *bus);

设备

device结构体(内核源码/include/linux/device.h)struct device {
const char *init_name;struct device           *parent;struct bus_type *bus;struct device_driver *driver;void            *platform_data;void            *driver_data;struct device_node      *of_node;dev_t                   devt;struct class            *class;
void (*release)(struct device *dev);const struct attribute_group **groups;  /* optional groups */
struct device_private   *p;
........
};
  • init_name :指定该设备的名称,总线匹配时,一般会根据比较名字,来进行配对;
  • parent :表示该设备的父对象,前面提到过,旧版本的设备之间没有任何关联,引入Linux设备模型之后,设备之间呈树状结构,便于管理各种设备;
  • bus :表示该设备依赖于哪个总线,当我们注册设备时,内核便会将该设备注册到对应的总线。
  • of_node :存放设备树中匹配的设备节点。当内核使能设备树,总线负责将驱动的of_match_table以及设备树的compatible属性进行比较之后,将匹配的节点保存到该变量。
  • platform_data :一个指针,用于保存具体的平台相关的数据。具体的driver模块,可以将一些私有的数据,暂存在这里,需要使用的时候,再拿出来,因此设备模型并不关心该指针得实际含义。
  • driver_data :同上,驱动层可通过dev_set/get_drvdata函数来获取该成员;
  • class :指向了该设备对应类,开篇我们提到的触摸,鼠标以及键盘等设备,对于计算机而言,他们都具有相同的功能,都归属于输入设备。我们可以在/sys/class目录下对应的类找到该设备,如input、leds、pwm等目录;
  • dev :dev_t类型变量,字符设备章节提及过,它是用于标识设备的设备号,该变量主要用于向/sys目录中导出对应的设备。
  • release :回调函数,当设备被注销时,会调用该函数。如果我们没定义该函数时,移除设备时,会提示“Device ‘xxxx’ does not have a release() function, it is broken and must be fixed”的错误。
  • group :指向struct attribute_group类型的指针,指定该设备的属性;
  • *p:是私有数据结构指针,该指针中会保存子设备链表、用于添加到bus/driver/prent等设备中的链表头等等。
内核也提供相关的API来注册和注销设备int device_register(struct device *dev);
void device_unregister(struct device *dev);

在讲解总线的时候,我们说过,当成功注册总线时,会在/sys/bus目录下创建对应总线的目录,该目录下有两个子目录,分别是drivers和devices, 我们使用device_register注册的设备从属于某个总线时,该总线的devices目录下便会存在该设备文件。

驱动

device_driver结构体(内核源码/include/linux/device.h)struct device_driver {const char              *name;struct bus_type         *bus;struct module           *owner;const char              *mod_name;      /* used for built-in modules */bool suppress_bind_attrs;       /* disables bind/unbind via sysfs */const struct of_device_id       *of_match_table;const struct acpi_device_id     *acpi_match_table;int (*probe) (struct device *dev);int (*remove) (struct device *dev);const struct attribute_group **groups;struct driver_private *p;};
  • name :指定驱动名称,总线进行匹配时,利用该成员与设备名进行比较;
  • bus :表示该驱动依赖于哪个总线,内核需要保证在驱动执行之前,对应的总线能够正常工作;
  • suppress_bind_attrs :布尔量,用于指定是否通过sysfs导出bind与unbind文件,bind与unbind文件是驱动用于绑定/解绑关联的设备。
  • owner :表示该驱动的拥有者,一般设置为THIS_MODULE;
  • of_match_table :指定该驱动支持的设备类型。当内核使能设备树时,会利用该成员与设备树中的compatible属性进行比较。
  • remove :当设备从操作系统中拔出或者是系统重启时,会调用该回调函数;
  • probe :当驱动以及设备匹配后,会执行该回调函数,对设备进行初始化。通常的代码,都是以main函数开始执行的,但是在内核的驱动代码,都是从probe函数开始的。
  • group :指向struct attribute_group类型的指针,指定该驱动的属性;

内核提供了driver_register函数以及driver_unregister函数来注册/注销驱动,成功注册的驱动会记录在/sys/bus//drivers目录

device_driver结构体(内核源码/include/linux/device.h)int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

示例

暂无。一般情况我们都会在系统已注册的总线上写驱动和设备。这篇文章主要了解这种架构的原理。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SQL实验】数据库、表、模式的SQL语句操作

完整代码在文章末尾 1、数据库的建立、删除和修改操作 (1)使用SQL语句创建数据库EDUC,并进行如下设置: 数据库文件和日志文件的逻辑名称分别为:Student_data和Student_log;数据文件的物理文件名为‘C:\DA…

基于Java语言的培训平台+学习平台+在线学习培训系统+教育平台+教育学习系统+课程学习平台

简述 企业培训平台企业考试系统培训平台考试系统企业大学企业视频网站视频学习平台 介绍 企业培训平台支持企业培训考试全流程,在线学习、在线考试,成熟的企业培训考试解决方案,充分满足企业培训需求。 独立部署,仅内部员工登录…

【热门】智慧果园管理系统解决方案

随着科技的进步,原有农业种植方式已经不能满足社会发展的需要,必须对传统的农业进行技术更新和改造。经过多年的实践,人们总结出一种新的种植方法——温室农业,即“用人工设施控制环境因素,使作物获得最适宜的生长条件,从而延长生产季节,获得最佳的产出”。这种农业生产方式…

03.顺序表实现

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删改查。一般见到的顺序表都是在结构体中定义的数组,只是比普通数组多了增删改查等一些其他功能函数。 上节已经介绍了顺序表有…

【android studio】Gradle和Gradle插件版本关系/配置/常见ERR示例

参考链接: Android之Gradle和Gradle插件区别及联系 grdle 的安装与配置 、gradle和jdk版本对应关系 Android Gradle Plugin与Gradle版本、JDK版本对应关系 配置示例 常见err 主要原因 1.编译版本未设定 2.有多个module而且gradle 版本设置不一致 修改如下&#xf…

虚幻闪烁灯光材质

创建一个材质 材质域改成光照函数 , Time让材质动起来 参数B用来控制速度 , Sine 让灯光闪烁 , Frac 增加了闪烁细节 把材质放到灯光材质上 效果还是挺不错的! 可以用于一些恐怖游戏~

OpenCV高级图形用户界面(8)在指定的窗口中显示一幅图像函数imshow()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在指定的窗口中显示一幅图像。 函数 imshow 在指定的窗口中显示一幅图像。如果窗口是以 cv::WINDOW_AUTOSIZE 标志创建的,图像将以原…

仕考网:国考笔试没进面还有机会吗?

在国家公务员考试及各省公务员考试中,除了常规的招录程序之外,还有调剂、递补和补录多重机会。 调剂:于笔试结束之后至面试启动之前.针对因报名人数不足未达到预定面试比例的岗位,将开放公开调剂。只要考生的笔试成绩超越了国考设…

如何查看默认网关地址:详细步骤

在日常的网络配置与故障排查中,了解并正确查看默认网关地址是一项基础且至关重要的技能。默认网关是连接本地网络与外部网络(如互联网)的关键节点,它扮演着数据包转发的重要角色。无论是家庭网络、办公室网络还是更复杂的网络环境…

一些简单的编程题(Java与C语言)

引言: 这篇文章呢,小编将会举一些简单的编程题用来帮助大家理解一下Java代码,并且与C语言做个对比,不过这篇文章所出现的题目小编不会向随缘解题系列里面那样详细的讲解每一到题,本篇文章的主要目的是帮助小编和读者们…

vcenter的使用

1 配置 1.1 时间配置 报错原因:ESXI主机没有配置DNS无法解析NTP服务器网址。 解决办法:配置ESXI主机DNS,操作如下图。 点击【配置】、【服务】 【编辑启动策略】 我们可以看到当前的【NTP服务状态】处于 已停止的状态 点击【配置】、【时间…

基于直播美颜SDK的实时美颜平台开发指南

随着直播平台的快速发展,用户对视频质量的要求越来越高,尤其是对于美颜效果的需求。为满足这一市场需求,基于直播美颜SDK的实时美颜平台应运而生。本文将探讨如何开发这样一个平台,助力开发者在激烈的竞争中脱颖而出。 一、理解美…

C#学习笔记(三)

C#学习笔记(三) 第 二 章 命名空间和类、数据类型、变量和代码规范二、类的组成和使用分析1. 基本概念2. 类的内容组成3. 方法的初步理解 第 二 章 命名空间和类、数据类型、变量和代码规范 二、类的组成和使用分析 1. 基本概念 类是程序的基本单元&a…

OpenCV高级图形用户界面(6)获取指定窗口中图像的矩形区域函数getWindowImageRect()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 提供窗口中图像的矩形区域。 该函数 getWindowImageRect 返回图像渲染区域的客户端屏幕坐标、宽度和高度。 函数原型 Rect cv::getWindowImage…

STM32_实验1_建立新工程

1、使用STM32CubeIDE建立一个新工程 1.1选择时钟源为外部晶振时钟。 1.2选择调试方式为 serial wire(串行线)。 1.3配置时钟树. 1.4选择以 c 和 h 文件型式管理工程文件。 1.5生成 hex 可执行文件。(完成后点击锤子) 2.串口输出调…

Windows环境NodeJS下载配置安装运行

Windows环境NodeJS下载配置安装运行 (1)下载 Node.js — Run JavaScript Everywhere 安装文件。 一路傻瓜式安装。 如果安装正常,输入命令可显示版本号: (2)可以查询nodejs默认的后续依赖安装包位置及缓存…

地方门户分类信息网站源码系统 用户可以自由发帖 PHP+MySQL组合开发 带完整的安装代码包以及搭建部署教程

系统概述 地方门户分类信息网站源码系统是一个基于PHP和MySQL开发的强大平台,旨在帮助用户轻松搭建地方性的分类信息网站。该系统集成了众多实用功能,支持用户自由发帖、浏览和搜索各类信息,如二手交易、求职招聘、房屋租售、生活服务、商家…

Winform和WPF的技术对比

WinForms(Windows Forms)和WPF(Windows Presentation Foundation)是用于创建桌面应用程序的两种技术。尽管两者都可以用于开发功能强大的Windows应用程序,但它们的设计理念、功能和开发体验都有显著区别。在本文中&…

三、ElementPlus下拉搜索加弹窗组件的封装

近期产品提出了一个需求,要求一个form的表单里面的一个组件既可以下拉模糊搜索,又可以弹窗搜索,我就为这个封装了一个组件,下面看效果图。 效果大家看到了,下面就看组件封装和实现方法 第一步,组件封装&…

Scratch图形化编程等级考试4级真题试卷(202409)

2024年9月Scratch图形化编程等级考试四级真题试卷 题目总数:24 总分数:100 选择题 第 1 题 单选题 Scratch运行下列程序,依次输入8、2、6后,角色说出的内容是?( ) A.26862 B.62862 …