在閱讀 Linux 核心原始碼中關於 OV2655 影像感測器的驅動程式時,許多開發者會發現一個疑點:驅動碼中並未直接呼叫 i2c_add_driver() 來註冊 I2C 驅動,而是定義了一個 v4l2_i2c_driver_data 結構體。本文將深入解析 V4L2 子系統如何透過統一的介面完成 I2C 驅動的初始化與註冊。
在 OV2655 驅動原始碼中,通常會看到類似以下的定義:
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = S5K4BA_DRIVER_NAME,
.probe = ov2655_probe,
.remove = __devexit_p(ov2655_remove),
.id_table = ov2655_id,
};
這裡並沒有顯式的 module_init() 呼叫 i2c_add_driver(),那麼驅動是如何被載入並註冊到 I2C 總線子系統的呢?
答案藏在 <media/v4l2-i2c-drv.h> 這個標頭檔中。所有基於 V4L2 的 I2C 影像感測器驅動都會包含此檔案。該檔案提供了一個通用的 I2C 驅動模板 v4l2_i2c_driver,並實作了標準的模組初始化與清理函式:
static int __init v4l2_i2c_drv_init(void) {
v4l2_i2c_driver.driver.name = v4l2_i2c_data.name;
v4l2_i2c_driver.command = v4l2_i2c_data.command;
v4l2_i2c_driver.probe = v4l2_i2c_data.probe;
v4l2_i2c_driver.remove = v4l2_i2c_data.remove;
v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend;
v4l2_i2c_driver.resume = v4l2_i2c_data.resume;
v4l2_i2c_driver.id_table = v4l2_i2c_data.id_table;
return i2c_add_driver(&v4l2_i2c_driver);
}
static void __exit v4l2_i2c_drv_cleanup(void) {
i2c_del_driver(&v4l2_i2c_driver);
}
module_init(v4l2_i2c_drv_init);
module_exit(v4l2_i2c_drv_cleanup);
v4l2_i2c_driver_data 結構體,提供驅動名稱、Probe/Remove 回呼函式及裝置 ID 表。v4l2-i2c-drv.h 中的通用邏輯,在模組載入時 (module_init) 將上述資料複製到通用的 v4l2_i2c_driver 結構體中。i2c_add_driver() 向 I2C 核心註冊,完成驅動的掛載。這種設計實現了「關注點分離」:驅動開發者專注於硬體邏輯,而 I2C 總線註冊、裝置匹配、電源管理等通用樣板程式碼由 V4L2 核心層統一維護,減少重複造輪子,也降低出錯機率。