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
繁體地址,請注明出處:http://hk.pswp.cn/web/82457.shtml
英文地址,請注明出處:http://en.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; …