驱动开发硬核特训 · Day 10 (理论上篇):设备模型 ≈ 运行时的适配器机制

news2025/4/16 1:44:06

🔍

B站相应的视屏教程
📌 内核:博文+视频 - 总线驱动模型实战全解析
敬请关注,记得标为原始粉丝。


在 Linux 驱动开发中,设备模型(Device Model)是理解驱动架构的核心。而从软件工程的角度看,它与**适配器模式(Adapter Pattern)**之间也存在着惊人的相似性。本文尝试从理论出发,构建“设备模型 ≈ 运行时的适配器机制”的认知,并通过逻辑图与结构分析,将这一理念讲透讲透。


一、适配器模式回顾:统一接口的桥梁

在这里插入图片描述

在软件设计模式中,**适配器模式(Adapter Pattern)**的作用是:将一个类的接口转换成客户端所期待的另一个接口

  • 目标接口(Target):客户端期待调用的接口
  • 源接口(Adaptee):已有的但不兼容的接口
  • 适配器(Adapter):中间桥梁,让客户端可以透明使用源接口

我们以 USB 转串口为例:

Client(PC) <--> USB接口
                ↓ 适配器
设备(串口芯片) <--> UART接口

在代码中,Adapter 就是包装 Adaptee 的对象,让它“看起来”满足了 Target 接口。


二、Linux 设备模型的运行时角色

Linux 的设备模型其实也完成了“统一接口”的桥接动作:

  • platform_device / i2c_client 等设备描述结构(相当于 Adaptee)
  • platform_driver / i2c_driver 等驱动结构(相当于 Adapter)
  • driver core(驱动核心)通过总线匹配机制连接设备与驱动(Adapter 注册给一个 Bus,用于查找匹配)

当你写下如下代码时:

platform_driver_register(&my_driver);

实际上就是在告诉内核:我有一个驱动,它能适配某些符合匹配条件的设备,请你在运行时自动“配对”他们。

这正是“运行时适配器机制”的体现!


三、匹配机制的本质:驱动适配器为谁服务?

1. 核心概念类比:

软件工程术语Linux 设备模型
Target接口驱动模型中的标准接口(probe等)
Adaptee类各种硬件设备(platform、I2C等)
Adapter适配器驱动结构体(platform_driver等)
适配者注册driver_register / i2c_add_driver
自动装配匹配逻辑总线匹配函数(of_match、id_table)

2. 匹配的流程图:

           ┌────────────┐
           │ 设备树Node │  ←──── 用户定义设备信息
           └────┬───────┘
                │
                ▼
         ┌──────────────┐
         │ platform_device │ ←─── 内核解析设备树,注册
         └────┬──────────┘
              │
              ▼
         ┌──────────────┐
         │  platform_bus │ ←─── 总线负责配对逻辑
         └────┬──────────┘
              │
      probe匹配机制
              │
              ▼
         ┌──────────────┐
         │ platform_driver │ ←── 驱动是适配器
         └──────────────┘

四、为什么说它是“运行时”的适配器?

适配器模式在传统软件设计中通常是编译期设计好适配关系,但在 Linux 驱动中,设备与驱动的绑定是:

  • 在设备注册(如 platform_device)后,在 driver core 的设备链表中查找驱动
  • 或者在驱动注册后,从已知设备中查找与之匹配的设备
  • 依据的是 名字匹配 / compatible 字符串

因此,这是 运行时动态匹配与绑定,它不是写死的关系,而是通过总线模型中的匹配函数灵活控制。


五、多个总线的统一模型:都是适配器

我们常见的 driver 类型:

  • platform_driver
  • i2c_driver
  • spi_driver
  • usb_driver
  • pci_driver

它们都继承了一个共同的父结构:

struct device_driver {
    const char *name;
    struct bus_type *bus;
    ...
};

它们都注册给对应的 bus_typebus_type 就是 Adapter 插口类型的定义者

换句话说,不同总线驱动之间的差异只是适配规则的差异,核心逻辑是一样的:我声明我能处理什么设备(通过 compatible 或 ID 表),然后内核会自动调用 probe 函数完成初始化。


六、设备模型中的三大核心角色

为了理解更深入,我们整理出设备模型三大核心结构体的适配关系:

设备模型角色软件设计模式对应Linux结构体功能描述
被适配者Adapteeplatform_device / i2c_client硬件描述
适配器Adapterplatform_driver / i2c_driver驱动逻辑
适配管理器AdapterManagerbus_type匹配规则、注册匹配与解绑回调

这三者构成了一个完整的“运行时适配”生态。


七、适配器模式的优势在驱动模型中的体现

适配器优势驱动模型的体现
将不兼容接口统一包装硬件种类繁多,统一由 driver core 适配
解耦:客户端与底层对象分离用户只关心 probe 中的行为,与具体设备解耦
支持运行时灵活绑定动态添加/删除驱动与设备都是天然支持的

八、真实场景举例:平台驱动模型

假设你有如下设备树片段:

lcdif1: lcd-controller@32e80000 {
    compatible = "fsl,imx8mp-lcdif1";
    reg = <0x32e80000 0x10000>;
    interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
    ...
};

你注册了如下 platform_driver:

static const struct of_device_id lcdif_match[] = {
    { .compatible = "fsl,imx8mp-lcdif1" },
    { /* sentinel */ }
};

static struct platform_driver lcdif_driver = {
    .probe = lcdif_probe,
    .remove = lcdif_remove,
    .driver = {
        .name = "lcdif",
        .of_match_table = lcdif_match,
    },
};

整个过程就是典型的 运行时适配器匹配:设备为 Adaptee,驱动为 Adapter,由 bus_type 来桥接匹配


九、总结与过渡

我们可以得出核心观点:

Linux 的驱动模型不是传统静态接口封装,而是构建了一个运行时的适配器机制系统,通过 bus / device / driver 三者的动态配对完成模块化的解耦架构。

  • 驱动 ≈ Adapter
  • 总线 ≈ AdapterManager(匹配策略)
  • 设备 ≈ Adaptee
  • probe 函数 ≈ Target 接口执行器

下一篇内容将全面深入 platform_driver、i2c_driver 结构与实际设备树如何映射为设备对象,驱动如何实现匹配,sysfs 如何生成等具体实现细节,并围绕 PCA9450 PMIC 进行实战代码分析。

敬请期待 Day 10 博文下篇:《设备模型 ≈ 运行时的适配器机制(下)——以 V4L2 摄像头驱动为例》


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

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

相关文章

flutter 打包mac程序 dmg教程

✅ 前提条件 ✅ 你已经在 macOS 上安装了 Android Studio Flutter SDK。 ✅ Flutter 支持 macOS 构建。 运行下面命令确认是否支持&#xff1a; Plain Text bash 复制编辑 flutter doctor ---## &#x1f9f1; 第一步&#xff1a;启用 macOS 支持如果是新项目&#xff0c;…

【数据结构与算法】——堆(补充)

前言 上一篇文章讲解了堆的概念和堆排序&#xff0c;本文是对堆的内容补充 主要包括&#xff1a;堆排序的时间复杂度、TOP 这里写目录标题 前言正文堆排序的时间复杂度TOP-K 正文 堆排序的时间复杂度 前文提到&#xff0c;利用堆的思想完成的堆排序的代码如下&#xff08;包…

atypica.AI:用「语言模型」为「主观世界」建模

人们不是在处理概率&#xff0c;而是在处理故事。 —— 丹尼尔卡尼曼 People dont choose between things, they choose between descriptions of things. —— Daniel Kahneman 商业研究是一门理解人类决策的学问。人并不只是根据纯粹理性做决策&#xff0c;而是受到叙事、情…

LLaMA-Factory双卡4090微调DeepSeek-R1-Distill-Qwen-14B医学领域

unsloth单卡4090微调DeepSeek-R1-Distill-Qwen-14B医学领域后&#xff0c;跑通一下多卡微调。 1&#xff0c;准备2卡RTX 4090 2&#xff0c;准备数据集 医学领域 pip install -U huggingface_hub export HF_ENDPOINThttps://hf-mirror.com huggingface-cli download --resum…

【WPF】自定义控件:ShellEditControl-同列单元格编辑支持文本框、下拉框和弹窗

需要实现表格同一列&#xff0c;单元格可以使用文本框直接输入编辑、下拉框选择和弹窗&#xff0c;文本框只能输入数字&#xff0c;弹窗中的数据是若干位的二进制值。 本文提供了两种实现单元格编辑状态下&#xff0c;不同编辑控件的方法&#xff1a; 1、DataTrigger控制控件的…

Seq2Seq - GRU补充讲解

nn.GRU 是 PyTorch 中实现门控循环单元&#xff08;Gated Recurrent Unit, GRU&#xff09;的模块。GRU 是一种循环神经网络&#xff08;RNN&#xff09;的变体&#xff0c;用于处理序列数据&#xff0c;能够更好地捕捉长距离依赖关系。 ⭐重点掌握输入输出部分输入张量&#…

从零开始学Python游戏编程19-游戏循环模式1

在《从零开始学Python游戏编程18-函数3》中提到&#xff0c;可以对游戏代码进行重构&#xff0c;把某些代码写入函数中&#xff0c;主程序再调用这些函数&#xff0c;这样使得代码程序更容易理解和维护。游戏循环模式实际上也是把代码写入到若干个函数中&#xff0c;通过循环的…

Java获取终端设备信息工具类

在很多场景中需要获取到终端设备的一些硬件信息等&#xff0c;获取的字段如下&#xff1a; 返回参数 参数含义备注systemName系统名称remoteIp公网iplocalIp本地ip取IPV4macmac地址去掉地址中的"-“或”:"进行记录cpuSerialcpu序列号hardSerial硬盘序列号drive盘符…

【Linux网络与网络编程】08.传输层协议 UDP

传输层协议负责将数据从发送端传输到接收端。 一、再谈端口号 端口号标识了一个主机上进行通信的不同的应用程序。在 TCP/IP 协议中&#xff0c;用 "源IP"&#xff0c;"源端口号"&#xff0c;"目的 IP"&#xff0c;"目的端口号"&…

没音响没耳机,把台式电脑声音播放到手机上

第一步&#xff0c;电脑端下载安装e2eSoft VSC虚拟声卡&#xff08;安装完成后关闭&#xff0c;不要点击和设置&#xff09; 第二步&#xff0c;电脑端下载安装&#xff08;SoundWire Server&#xff09;&#xff08;安装完成后不要关闭&#xff0c;保持默认配置&#xff09; 第…

XDocument和XmlDocument的区别及用法

因为这几天用到了不熟悉的xml统计数据&#xff0c;啃了网上的资料解决了问题&#xff0c;故总结下xml知识。 1.什么是XML?2.XDocument和XmlDocument的区别3.XDocument示例1示例2&#xff1a;示例3&#xff1a; 4.XmlDocument5.LINQ to XML6.XML序列化(Serialize)与反序列化(De…

Blender安装基础使用教程

本博客记录安装Blender和基础使用&#xff0c;可以按如下操作来绘制标靶场景、道路标识牌等。 目录 1.安装Blender 2.创建面板资源 步骤 1: 设置 Blender 场景 步骤 2: 创建一个平面 步骤 3: 将 PDF 转换为图像 步骤 4-方法1: 添加材质并贴图 步骤4-方法2&#xff1a;创…

【Git】从零开始使用git --- git 的基本使用

哪怕是野火焚烧&#xff0c;哪怕是冰霜覆盖&#xff0c; 依然是志向不改&#xff0c;依然是信念不衰。 --- 《悟空传》--- 从零开始使用git 了解 Gitgit创建本地仓库初步理解git结构版本回退 了解 Git 开发场景中&#xff0c;文档可能会经历若干版本的迭代。假如我们不进行…

Android 中支持旧版 API 的方法(API 30)

Android 中最新依赖库的版本支持 API 31 及以上版本&#xff0c;若要支持 API30&#xff0c;则对应的依赖库的版本就需要使用旧版本。 可通过修改模块级 build.gradle 文件来进行适配。 1、android 标签的 targetSdk 和 compileSdk 版本号 根据实际目标设备的 android 版本来…

[特殊字符] Hyperlane:Rust 高性能 HTTP 服务器库,开启 Web 服务新纪元!

&#x1f680; Hyperlane&#xff1a;Rust 高性能 HTTP 服务器库&#xff0c;开启 Web 服务新纪元&#xff01; &#x1f31f; 什么是 Hyperlane&#xff1f; Hyperlane 是一个基于 Rust 语言开发的轻量级、高性能 HTTP 服务器库&#xff0c;专为简化网络服务开发而设计。它支…

RIP V2路由协议配置实验CISCO

1.RIP V2简介&#xff1a; RIP V2&#xff08;Routing Information Protocol Version 2&#xff09;是 RIP 路由协议的第二版&#xff0c;属于距离矢量路由协议&#xff0c;主要用于中小型网络环境。相较于 RIP V1&#xff0c;RIP V2 在功能和性能上进行了多项改进&#xff0c…

《LNMP架构+Nextcloud私有云超维部署:量子级安全与跨域穿透实战》

项目实战-使用LNMP搭建私有云存储 准备工作 恢复快照&#xff0c;关闭安全软件 [rootserver ~]# setenforce 0[rootserver ~]# systemctl stop firewalld搭建LNMP环境 [rootserver ~]# yum install nginx mariadb-server php* -y# 并开启nginx服务并设置开机自启 [r…

3DMAX笔记-UV知识点和烘焙步骤

1. 在展UV时&#xff0c;如何点击模型&#xff0c;就能选中所有这个模型的uv 2. 分多张UV时&#xff0c;不同的UV的可以设置为不同的颜色&#xff0c;然后可以通过颜色进行筛选。 3. 烘焙步骤 摆放完UV后&#xff0c;要另存为一份文件&#xff0c;留作备份 将模型部件全部分成…

【新人系列】Golang 入门(十三):结构体 - 下

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12898955.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Golang 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…

Spring Boot 自定义商标(Logo)的完整示例及配置说明( banner.txt 文件和配置文件属性信息)

Spring Boot 自定义商标&#xff08;Logo&#xff09;的完整示例及配置说明 1. Spring Boot 商标&#xff08;Banner&#xff09;功能概述 Spring Boot 在启动时会显示一个 ASCII 艺术的商标 LOGO&#xff08;默认为 Spring 的标志&#xff09;。开发者可通过以下方式自定义&a…