Linux I2C 子系统全解:结构、机制与工程实战

Linux I2C 子系统全解:结构、机制与工程实战


前言

I2C(Inter-Integrated Circuit)作为嵌入式系统和各种电子产品中最常用的串行通信总线之一,在 Linux 内核中的地位极其重要。然而,Linux I2C 子系统的分层结构、对象模型、驱动编写与平台适配,常常让初学者和有经验的驱动工程师都感觉“玄而又玄”:

  • i2c-core 到底是什么?它是结构体还是框架代码?
  • i2c_adapter/i2c_client/i2c_driver 这三者的真正职责和关系是什么?
  • 设备树、ACPI 如何描述和管理 I2C 设备?
  • 用户空间如何安全、优雅地操作 I2C 设备?
  • 为什么有些 I2C 设备驱动放在 drivers/i2c/chips,有些则在各自子系统?
  • 适配器和外设驱动之间的数据流和调用栈到底长什么样?

本文将用“站在一线工程师视角”的方式,结合内核主线代码,系统剖析 Linux I2C 子系统的设计精髓,厘清常见痛点,助你真正吃透 I2C 驱动开发的门道。


在这里插入图片描述

一、I2C 子系统的整体分层架构

1.1 为什么要分层?

在内核设计里,“分层”几乎是所有复杂子系统必须采取的手段。一方面解耦硬件和上层协议,另一方面便于移植和扩展。I2C 子系统的分层非常经典,既和 PCI、USB 等总线有相通之处,也有其独特的地方。

1.2 四层结构一览

层级主要职责典型源码文件关键结构体(接口)
用户空间接口层向用户空间提供 /dev/i2c-x 访问接口drivers/i2c/i2c-dev.cstruct file_operations
设备驱动层各类 I2C 外设(如 EEPROM、Sensor)协议驱动drivers/misc/eeprom/at24.c
drivers/i2c/chips/*.c
struct i2c_driver
struct i2c_client
适配器驱动层适配各类 I2C 控制器(主控/硬件)drivers/i2c/busses/i2c-xxx.cstruct i2c_adapter
struct i2c_algorithm
I2C 核心层管理适配器、设备、驱动的注册、匹配与调度drivers/i2c/i2c-core-base.c
drivers/i2c/i2c-core-of.c
struct i2c_adapter
struct i2c_client
struct i2c_driver
struct bus_type(i2c_bus_type)

分层的本质是:每一层只关心本层的职责,通过清晰的接口与上下层交互,彼此低耦合、高内聚。


二、I2C 子系统核心对象和关系

2.1 三大核心结构体

  1. struct i2c_adapter
    代表 I2C 控制器(主机端),由适配器驱动(adapter driver)实现。例如 i.MX6/8、Designware、Synopsys 等硬件厂商的控制器驱动都会定义并注册自己的 adapter。

    struct i2c_adapter {struct module *owner;unsigned int class;const struct i2c_algorithm *algo;void *algo_data;struct device dev;int nr; // 适配器编号// ... 省略其余成员
    };
    
  2. struct i2c_client
    代表 I2C 总线上的从设备(外设)。每个外设在系统里注册为一个 i2c_client。

    struct i2c_client {unsigned short addr;struct i2c_adapter *adapter;struct device dev;// ... 省略其余成员
    };
    
  3. struct i2c_driver
    代表外设驱动程序,由驱动开发者实现。会指定可以支持哪些 i2c_client。

    struct i2c_driver {int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);const struct i2c_device_id *id_table;struct device_driver driver;// ... 省略其余成员
    };
    

2.2 i2c-core 的角色

i2c-core 是整个 I2C 框架的“中枢神经”
它管理着 adapter、client、driver 的注册与注销;在总线匹配到合适的 client-driver 对时,自动调用 probe/remove 等回调;它还定义和注册了 i2c_bus_type,将 I2C 适配器和设备纳入内核统一的设备模型(/sys/bus/i2c)。

易混淆点:

  • i2c-core 并不是结构体对象,而是一组实现适配器/设备/驱动管理与调度的代码集合。
  • i2c-core 定义的结构体(adapter、client、driver)都是面向外部的对象,i2c-core 本身则是幕后操盘手。

2.3 各对象的注册和生命周期

  • 适配器注册:i2c_add_adapter() / 注销:i2c_del_adapter()
  • 驱动注册:i2c_add_driver() / 注销:i2c_del_driver()
  • 设备注册:通常由核心层自动创建设备(client),也可用 i2c_new_client_device() 手动注册
典型适配器注册片段(i2c-imx.c)
static int imx_i2c_probe(struct platform_device *pdev)
{// ... 硬件初始化、资源分配struct i2c_adapter *adap = &i2c_imx->adapter;adap->owner = THIS_MODULE;adap->algo = &imx_i2c_algo;// ...i2c_add_adapter(adap); // 注册到 i2c-core// ...
}
典型设备驱动注册片段(at24.c)
static struct i2c_driver at24_driver = {.driver = {.name = "at24",.of_match_table = at24_of_match,},.probe_new = at24_probe,.remove = at24_remove,.id_table = at24_ids,
};static int __init at24_init(void)
{return i2c_add_driver(&at24_driver); // 注册到 i2c-core
}
module_init(at24_init);

三、I2C 设备树/ACPI与自动识别

3.1 设备树下的 I2C 设备描述

现代 SoC 平台上,I2C 设备和控制器大多通过 device tree 描述。例如:

&i2c1 {status = "okay";clock-frequency = <100000>;sensor@48 {compatible = "ti,tmp102";reg = <0x48>;};
};
  • &i2c1:引用已声明的 i2c1 适配器
  • sensor@48:代表挂在 i2c1 上、地址为 0x48 的设备
  • compatible:决定使用哪个驱动
  • reg:I2C 地址

i2c-core 会在启动时扫描各 bus 下的节点,根据 compatible 字符串自动和 i2c_driver 的 of_match_table 匹配,自动创建设备并调用 probe。

3.2 ACPI 下的 I2C 设备识别

X86/服务器平台常用 ACPI 描述 I2C 设备,匹配原理与 DT 类似,匹配 driver 的 acpi_match_table。


四、数据流与调用流程解析

4.1 从用户空间到硬件的数据流

  1. 用户空间调用 open/read/write/ioctl 操作 /dev/i2c-x
  2. 内核 i2c-dev.c 通过 file_operations 调用 i2c-core 的消息传输接口
  3. i2c-core 调用 i2c_adapter 提供的 master_xfer/algo 方法
  4. 适配器驱动操作硬件控制器,完成 I2C 物理通信

真实代码流程片段(i2c-dev.c):

static ssize_t i2cdev_write(struct file *file, const char __user *buf,size_t count, loff_t *offset)
{struct i2c_client *client = file->private_data;struct i2c_adapter *adap = client->adapter;// ...ret = i2c_transfer(adap, msgs, num);// ...
}

i2c_transfer 又会调用适配器驱动里定义的 algorithm(如 master_xfer):

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{return adap->algo->master_xfer(adap, msgs, num);
}

五、工程痛点与常见疑问

5.1 i2c-core 到底是结构体还是代码?

答:i2c-core 不是结构体,是整个 I2C 子系统的核心实现代码模块,提供对象注册/调度/统一接口。它通过 struct bus_type 将 I2C 融入内核设备模型。

5.2 适配器驱动和设备驱动的关系到底怎么理解?

  • 适配器驱动负责让“硬件能动起来”,对上暴露 i2c_adapter 对象,提供收发能力。
  • 设备驱动(如 at24、tmp102)不直接操作硬件,而是通过 i2c-core 提供的抽象接口,调用 i2c_transfer 等 API 间接与硬件通信。
  • 两者通过 i2c-core 桥接,对彼此无感知、相互独立,极大增强了代码复用和平台兼容性。

5.3 “I2C 总线”在内核里到底有没有独立的 struct?

I2C 总线没有像 PCI/USB 那样单独的 struct i2c_bus。Linux 用 adapter(控制器)+ client(设备)+ bus_type(总线类型)三者结合,实现了虚拟总线模型。所有挂在同一个 adapter 下的设备,即被认为挂在同一条逻辑 I2C 总线。

5.4 为什么 I2C 设备驱动分散在 chips、misc、media 等目录?

这是历史和分层带来的必然产物。芯片类通用驱动(如 EEPROM、RTC、温度传感器)有的放在 i2c/chips,有的归属于自己子系统(如摄像头 sensor、音频 codec 等),但它们的入口都是统一的 i2c_driver/i2c_client,只是组织目录不同。

5.5 设备树下 I2C 设备无法自动 probe 的常见坑

  • compatible 属性写错或驱动里未配置 of_match_table
  • i2c_adapter 未正常注册(如 status = “disabled”)
  • I2C 地址(reg)冲突或写错
  • 驱动的 id_table 未包含正确的名称

六、典型实战代码分析:at24 EEPROM 驱动

以主线 drivers/misc/eeprom/at24.c 为例,展示 i2c_driver 的专业实现范式。

核心结构注册:

static struct i2c_driver at24_driver = {.driver = {.name = "at24",.of_match_table = at24_of_match,},.probe_new = at24_probe,.remove = at24_remove,.id_table = at24_ids,
};module_i2c_driver(at24_driver);

probe 实现要点:

  • 获取设备树/ACPI/ID Table 数据,解析页大小、容量等属性
  • 初始化 nvmem 子系统,导出用户空间访问接口
  • 管理电源、runtime PM、regulator
  • 多地址芯片通过 devm_i2c_new_dummy_device 注册所有地址

与 i2c-core 交互流程

  • probe 时,i2c-core 根据设备树/of_match 匹配,自动调用 at24_probe
  • 数据读写最终下沉到 i2c_transfer,自动适配底层控制器

七、调试与开发建议

7.1 如何排查 I2C 驱动无法 probe?

  • 检查 dmesg 是否有 adapter 注册日志
  • 检查设备树 compatible 是否和驱动匹配
  • i2cdetect 等工具确认设备地址能扫描到
  • i2cdump 验证数据可读写
  • 查看 /sys/bus/i2c/devices/ 是否有设备节点

7.2 推荐阅读代码入口

  • drivers/i2c/i2c-core-base.c:框架主干、对象注册、调度实现
  • drivers/i2c/busses/:主流适配器驱动实现
  • 具体外设驱动(如 at24.c、tmp102.c、lm75.c)

八、架构精髓与面试高频考点

8.1 核心理念

  • 统一分层、模块解耦:适配器与设备驱动完全分离
  • 设备模型集成:总线类型、自动绑定、生命周期管理
  • 跨平台支持:一套驱动代码,适配 N 多硬件平台
  • 面向对象抽象:每类对象自管理,只暴露接口而不泄露实现细节

8.2 高频面试问题

  • 解释 i2c_adapter、i2c_client、i2c_driver 的职责和关系
  • 描述 i2c-core 如何实现驱动和设备的自动绑定
  • 如何用设备树描述 I2C 设备?驱动如何匹配?
  • 用户空间如何访问 I2C 设备?其背后工作原理是什么?
  • 如何调试 I2C 驱动 probe 不上的问题?

九、总结与延伸阅读

Linux I2C 子系统虽然代码量不大,但蕴含了大量内核设计哲学和工程经验。理解其分层架构和对象模型,不仅能提升驱动开发能力,还能加深对 Linux 设备模型、总线机制的认知。希望本文的梳理和实例,能帮你建立起系统性思维框架,写出健壮、可维护的 I2C 驱动。


附录:常用内核 API/结构体速查

  • i2c_add_adapter() / i2c_del_adapter()
  • i2c_add_driver() / i2c_del_driver()
  • i2c_new_client_device() / i2c_unregister_device()
  • i2c_transfer() / i2c_smbus_xfer()
  • struct i2c_adapter
  • struct i2c_algorithm
  • struct i2c_client
  • struct i2c_driver
  • struct bus_type

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

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

相关文章

多线程编程技术解析及示例:pthread_cond_timedwait、pthread_mutex_lock 和 pthread_mutex_trylock

多线程编程技术解析及示例&#xff1a;pthread_cond_timedwait、pthread_mutex_lock 和 pthread_mutex_trylock 摘要 本文深入解析了多线程编程中 pthread_cond_timedwait、pthread_mutex_lock 和 pthread_mutex_trylock 三个函数的功能、使用场景及注意事项&#xff0c;并通…

元胞自动机(Cellular Automata, CA)

一、什么是元胞自动机&#xff08;Cellular Automata, CA&#xff09; 元胞自动机&#xff08;CA&#xff09; 是一种基于离散时间、离散空间与规则驱动演化的动力系统&#xff0c;由 冯诺依曼&#xff08;John von Neumann&#xff09; 于1940年代首次提出&#xff0c;用于模…

Flutter面试题

Flutter架构解析 1. Flutter 是什么?它与其他移动开发框架有什么不同? Flutter 是 Google 开发的开源移动应用开发框架,可用于快速构建高性能、高保真的移动应用(iOS 和 Android),也支持 Web、桌面和嵌入式设备。。它与其他移动开发框架(如 React Native、Xamarin、原…

MySQL 如何判断某个表中是否存在某个字段

在MySQL中&#xff0c;判断某个表中是否存在某个字段&#xff0c;可以通过查询系统数据库 INFORMATION_SCHEMA.COLUMNS 实现。以下是详细步骤和示例&#xff1a; 方法&#xff1a;使用 INFORMATION_SCHEMA.COLUMNS 通过查询系统元数据表 COLUMNS&#xff0c;检查目标字段是否存…

golang 实现基于redis的并行流量控制(计数锁)

在业务开发中&#xff0c;有时需要对某个操作在整个集群中限制并发度&#xff0c;例如限制大模型对话的并行数。基于redis zset实现计数锁&#xff0c;做个笔记。 关键词&#xff1a;并行流量控制、计数锁 package redisutilimport ("context""fmt""…

从线性方程组角度理解公式 s=n−r(3E−A)

从线性方程组角度理解公式 sn−r(3E−A) 这个公式本质上是 ​齐次线性方程组解空间维度 的直接体现。下面通过三个关键步骤解释其在线性方程组中的含义&#xff1a; 1. ​公式对应的线性方程组 考虑矩阵方程&#xff1a; (3E−A)x0 其中&#xff1a; x 是 n 维未知向量3E−…

Docker 在 AI 开发中的实践:GPU 支持与深度学习环境的容器化

人工智能(AI)和机器学习(ML),特别是深度学习,正以前所未有的速度发展。然而,AI 模型的开发和部署并非易事。开发者常常面临复杂的依赖管理(如 Python 版本、TensorFlow/PyTorch 版本、CUDA、cuDNN)、异构硬件(CPU 和 GPU)支持以及环境复现困难等痛点。这些挑战严重阻…

解决CSDN等网站访问不了的问题

原文网址&#xff1a;解决CSDN等网站访问不了的问题-CSDN博客 简介 本文介绍解决CSDN等网站访问不了的方法。 问题描述 CSDN访问不了了&#xff0c;页面是空的。 问题解决 方案1&#xff1a;修改DNS 可能是dns的问题&#xff0c;需要重新配置。 国内常用的dns是&#x…

使用tortoisegit连接远程仓库进行克隆、拉取、获取、提交、推送、新建/切换分支、重命名、删除的一套流程(附带巨全面的git命令)

1.整备好tortoisegit工具。 2.新建一个文件夹&#xff0c;并进入这个文件夹后鼠标右击&#xff08;选择克隆&#xff09;&#xff1a; 3.先去项目中拿到https地址&#xff0c;再填入&#xff1a; 4.新建分支&#xff0c;右击克隆到本地的项目文件&#xff1a; 5.推送到远程&am…

ArcGIS Pro 3.4 二次开发 - 地图创作 1

环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 ArcGIS Pro 3.4 二次开发 - 地图创作 11 样式管理1.1 如何通过名称获取项目中的样式1.2 如何创建新样式1.3 如何向项目添加样式1.4 如何从项目中移除样式1.5 如何向样式添加样式项1.6 如何从样式中移除样式项1.7 如何判断样式是否可…

Express 集成Sequelize+Sqlite3 默认开启WAL 进程间通信 Conf 打包成可执行 exe 文件

代码&#xff1a;express-exe: 将Express开发的js打包成exe服务丢给客户端使用 实现目标 Express 集成 Sequelize 操作 Sqlite3 数据库&#xff1b; 启动 Sqlite3 时默认开启 WAL 模式&#xff0c;避免读写互锁&#xff0c;支持并发读&#xff1b; 利用 Conf 实现主进程与 Ex…

.Net Framework 4/C# 初识 C#

一、C# 专栏 由于博主原先是做的Linux C/C 嵌入式领域&#xff0c;因此对 C# 也较为懵懂&#xff0c;C# 是典型的 OOP 编程&#xff0c;这一点与 C 类似&#xff0c;但是在语法上&#xff0c;C# 移除了对指针的运用以及内存管理&#xff0c;所以既不用考虑指针的复杂运用也不用…

Python趣学篇:Pygame实现粒子烟花绽放效果

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《Python星球日记》🪐 目录 一、项目亮点与效果预览1. 核心特色功能2. 技术学习价值二、技术原理深度解析1. 向量运算:烟花运动的数学基…

NiceGUI 是一个基于 Python 的现代 Web 应用框架

NiceGUI 是一个基于 Python 的现代 Web 应用框架&#xff0c;它允许开发者直接使用 Python 构建交互式 Web 界面&#xff0c;而无需编写前端代码。以下是 NiceGUI 的主要功能和特点&#xff1a; 核心功能 1.简单易用的 UI 组件 提供按钮、文本框、下拉菜单、滑块、图表等常见…

Linux中的mysql逻辑备份与恢复

一、安装mysql社区服务 二、数据库的介绍 三、备份类型和备份工具 一、安装mysql社区服务 这是小编自己写的&#xff0c;没有安装的去看看 Linux换源以及yum安装nginx和mysql-CSDN博客 二、数据库的介绍 2.1 数据库的组成 数据库是一堆物理文件的集合&#xff0c;主要包括…

鸿蒙UI开发——组件的自适应拉伸

1、概 述 针对常见的开发场景&#xff0c;ArkUI开发框架提供了非常多的自适应布局能力&#xff0c;这些布局可以独立使用&#xff0c;也可多种布局叠加使用。本文针对ArkUI提供的拉伸能力做简单讨论。 拉伸能力是指容器组件尺寸发生变化时&#xff0c;增加或减小的空间全部分…

K 值选对,准确率翻倍:KNN 算法调参的黄金法则

目录 一、背景介绍 二、KNN 算法原理 2.1 核心思想 2.2 距离度量方法 2.3 算法流程 2.4算法结构&#xff1a; 三、KNN 算法代码实现 3.1 基于 Scikit-learn 的简单实现 3.2 手动实现 KNN&#xff08;自定义代码&#xff09; 四、K 值选择与可视化分析 4.1 K 值对分类…

Azure DevOps Server 2022.2 补丁(Patch 5)

微软Azure DevOps Server的产品组在4月8日发布了2022.2 的第5个补丁。下载路径为&#xff1a;https://aka.ms/devops2022.2patch5 这个补丁的主要功能是修改了代理(Agent)二进制安装文件的下载路径&#xff1b;之前&#xff0c;微软使用这个CND(域名为vstsagentpackage.azuree…

PHP7+MySQL5.6 查立得轻量级公交查询系统

# PHP7MySQL5.6 查立得轻量级公交查询系统 ## 系统简介 本系统是一个基于PHP7和MySQL5.6的轻量级公交查询系统(40KB级)&#xff0c;支持线路查询、站点查询和换乘查询功能。系统采用原生PHPMySQL开发&#xff0c;无需第三方框架&#xff0c;适合手机端访问。 首发版本&#x…

Vue-Cropper:全面掌握图片裁剪组件

Vue-Cropper 完全学习指南&#xff1a;Vue图片裁剪组件 &#x1f3af; 什么是 Vue-Cropper&#xff1f; Vue-Cropper 是一个简单易用的Vue图片裁剪组件&#xff0c;支持Vue2和Vue3。它提供了丰富的配置选项和回调方法&#xff0c;可以满足各种图片裁剪需求。 &#x1f31f; …