驅動的抽象層其實類似一個管理層,管理多種資源,使資源之間更加有序,高效的工作。使軟件的複用性和魯棒性更強。其核心思想是面向對象的編程思想,創建抽象的概念,增加抽象層之間的邏輯給更高層,屏蔽底層的“雜亂無章”。
理清芯片之間的關係和邏輯
硬件上8541e通過mipi和i2c連接到9288,9288通過gmsl信號線連接到96705編串器上,96705和0144是通過dvp接口連接,包含i2c。軟件上平臺8541e通過i2c訪問配置9288,通過9288的映射寄存器來訪問96705和0144,所以我們軟件實現的目標是在8541e上獲取0144的圖像數據,9288和96705是一種通信的編串和解串方式,不影響數據格式。
所以8541e看到的是關閉打開攝像頭,讀取攝像頭id,設置攝像頭格式,幀率等接口。不需要關心中間環節。這也是驅動抽象層給上層看到的接口。對於軟件的底層,我們可以把serdes抽象成橋,把傳感器抽象成sensor。不管什麼橋,不管什麼sensor都適用這套方法。
創建對象結構體
三個抽象對象:
- 面向平臺的camera操作方法和數據
- 面向brg的操作方法和數據
- 面向sensor的操作方法和數據
1和3的區別在於比如1的power on會包含2和3的power on。
靈活使用鏈表管理
鏈表用於管理已創建的數據對象,把相同的數據結構連起來,方便管理。比如在sensor的power中會用到brg的i2c_rw_0144的方法,就可以遍歷brg的鏈表,查找鏈表中名字爲“9288”的brg,當然如果註冊了多個brg,可以按照名字區分,也可以按照id來區分。
static LIST_HEAD(brg_list);
list_add_tail(&brg->list, &brg_list);
static inline struct serdbrg_device * sensor2brg(void)
{
struct serdbrg_device *ambrg;
list_for_each_entry(ambrg, &brg_list, list) {
if (ambrg->id == 0)
return ambrg;
}
printk("Can't find serdbrg in list\n");
return NULL;
}
創建邏輯層,抽象接口
當需要創建一個brg的設備驅動時候,我們需要調用一個註冊的接口:
int _register_serdes_brg(struct serdbrg_device *brg, struct serdbrg_ops *ops)
當需要創建一個sensor的設備驅動時候,我們需要調用一個註冊的接口:
int _register_serdes_brg_device(struct sensor_brg_device *sensor, struct sensor_brg_ops *ops)
調用這兩個接口之前,要完成對象結構體的創建,就是傳送到函數的參數。
這兩個函數所做的邏輯主要是:
- 獲取設備id信息,不成功則退出註冊,並提示用戶失敗。成功進入下一步。
- 加入設備到鏈表。
- 創建文件系統接口。
- 提供文件系統接口訪問邏輯,主要是camera抽象函數的實現,比如:
Camera的stream on方法,上層訪問文件系統接口,傳入參數POWERON,然後底層調用brg和sensor的方法,輸出圖像:
gbrg->ops->brg_power_on(gbrg,1) ;
gbrg->ops->brg_hw_init_pre(gbrg);
gbrg->ops->brg_set_clk_si(gbrg,0);
gsensor->ops->init_device(gsensor);
gbrg->ops->brg_vout_config(gbrg,1);
- 文件系統接口可以使用字符設備ioctl的方法也可以使用sys文件屬性的方法,都可以實現。
總結
雖然本例程沒有對多個sensor和brg做測試,但是原理上是可以很容易的兼容多個設備,通過id或者名字進行選擇操作方法。