HAL Stub框架

HAL stub的框架比較簡單,三個結構體、兩個常量、一個函數,簡稱321架構,它的定義在:@hardware/libhardware/include/hardware/hardware.h

@hardware/libhardware/hardware.c


1. /*  

2. 每一個硬件都通過hw_module_t來描述,我們稱之爲一個硬件對象。你可以去繼承這個hw_module_t,然後擴展自己的屬性,硬件對象必須定義爲一個固定的名字:HMI,即:Hardware Module Information的簡寫,每一個硬件對象裏都封裝了一個函數指針open用於打開該硬件,我們理解爲硬件對象的open方法,open調用後返回這個硬件對應的Operation interface 

3. */  

4.  struct hw_module_t  

5.     uint32_t tag;           // 該值必須聲明爲HARDWARE_MODULE_TAG  

6.     uint16_t version_major; // 主版本號  

7.     uint16_t version_minor;     // 次版本號  

8.     const char *id;         //硬件id名,唯一標識module  

9.     const char *name;       // 硬件module名字  

10.     const char * author;        // 作者  

11.     struct hw_module_methods_t* methods;    //指向封裝有open函數指針的結構體  

12.     void* dso;              // module’s dso  

13.     uint32_t reserved[32-7];    // 128字節補齊  

14. ;  

15.   

16./*  

17.硬件對象的open方法描述結構體,它裏面只有一個元素:open函數指針 

18.*/  

19. struct hw_module_methods_t{  

20.     // 只封裝了open函數指針  

21.     int (*open)(const struct hw_module_t* module, const char * id,  

22.         struct hw_device_t** device);  

23. };  

24.   

25./* 

26.硬件對象hw_module_topen方法返回該硬件的Operation interface,它由hw_device_t結構體來描述,我們稱之爲:該硬件的操作接口 

27.*/  

28. struct hw_device_t{  

29.     uint32_t tag;               // 必須賦值爲HARDWARE_DEVICE_TAG  

30.     uint32_t version;               // 版本號  

31.     struct hw_module_t* module; // 該設備操作屬於哪個硬件對象,可以看成硬件操作接口與硬件對象的聯繫  

32.     uint32_t reserved[12];          // 字節補齊  

33.     int (*close)(struct hw_device_t* device);   // 該設備的關閉函數指針,可以看做硬件的close方法  

34. };  


上述三個結構之間關係緊密,每個硬件對象由一個hw_module_t來描述,只要我們拿到了這個硬件對象,就可以調用它的open方法,返回這個硬件對象的硬件操作接口,然後就可以通過這些硬件操作接口來間接操作硬件了。只不過,open方法被struct hw_module_methods_t結構封裝了一次,硬件操作接口被hw_device_t封裝了一次而已。

那用戶程序如何才能拿到硬件對象呢?

答案是通過硬件id名來拿。

我們來看下321架構裏的:兩個符號常量和一個函數:


1. // 這個就是HAL Stub對象固定的名字  

2. #define HAL_MODULE_INFO_SYM             HMI  

3. // 這是字符串形式的名字  

4. #define HAL_MODULE_INFO_SYM_AS_STR      "HMI"  

5. //這個函數是通過硬件名來獲得硬件HAL Stub對象  

6. int hw_get_module(const char *id, const struct hw_module_t **module);  


當用戶調用hw_get_module函數時,第一個參數傳硬件id名,那麼這個函數會從當前系統註冊的硬件對象裏查找傳遞過來的id名對應的硬件對象,然後返回之。

從調用者的角度,我們基本上沒有什麼障礙了,那如何註冊一個硬件對象呢?

很簡單,只需要聲明一個結構體即可,看下面這個Led Stub註冊的例子:


1. const struct led_module_t HAL_MODULE_INFO_SYM = {  

2.     common: {   // 初始化父結構hw_module_t成員  

3.         tag: HARDWARE_MODULE_TAG,  

4.         version_major: 1,  

5.         version_minor: 0,  

6.         id: LED_HARDWARE_MODULE_ID,  

7.         name: "led HAL Stub",  

8.         author: "farsight",  

9.         methods: &led_module_methods,  

10.     },   

11.     // 擴展屬性放在這兒  

12. };  


對,就這麼簡單,我們只需要聲明一個結構體led_moduel_t,起名叫HAL_MODULE_INFO_SYM,也就是固定的名字:HMI,然後將這個結構體填充好就行了。led_module_t又是什麼結構體類型啊?前面分析hw_modult_t類型時說過,我們可以“繼承”hw_module_t類型,創建自己的硬件對象,然後自己再擴展特有屬性,這裏的led_module_t就是“繼承”的hw_module_t類型。注意,繼承加上了雙引號,因爲在C語言裏沒有繼承這個概念:


1. struct led_module_t {  

2.     struct hw_module_t common;  

3. };  

結構體led_module_t封裝了hw_module_t結構體,也就是說led_module_t這個新(子)結構體包含了舊(父)結構體,在新結構體裏可以再擴展一些新的成員。結構體本身就具有封裝特性,這不就是面向對象的封裝和繼承嗎!爲了顯得專業點,我們用UML描述一下:

                                                          


在上面的類圖裏,把hw_module_methods_t裏封裝的open函數指針指針寫成open方法。

open方法既:methods,自然也被子結構體給“繼承”下來,我們將它初始化爲led_module_methods的地址,該結構是hw_module_methods_t類型的,其聲明代碼如下:


1. static struct hw_module_methods_t led_module_methods = {  

2.     open: led_device_open    

3. };  

它裏面僅有的open成員是個函數指針,它被指向led_device_open函數:


1. static int led_device_open(const struct hw_module_t* module, const char* name,  

2.     struct hw_device_t** device)  

3. {  

4.     struct led_device_t *led_device;  

5.     LOGI("%s E ", __func__);  

6.     led_device = (struct led_device_t *)malloc(sizeof(*led_device));  

7.     memset(led_device, 0, sizeof(*led_device));   

8.   

9.     // init hw_device_t  

10.     led_device->common.tag= HARDWARE_DEVICE_TAG;  

11.     led_device->common.version = 0;  

12.     led_device->common.module= module;  

13.     led_device->common.close = led_device_close;   

14.   

15.     // init operation interface  

16.     led_device->set_on= led_set_on;  

17.     led_device->set_off= led_set_off;  

18.     led_device->get_led_count = led_getcount;  

19.     *device= (struct hw_device_t *)led_device;  

20.   

21.     if((fd=open("/dev/leds",O_RDWR))==-1)  

22.     {  

23.         LOGI("open error");  

24.         return -1;  

25.     }else  

26.     LOGI("open ok\n");  

27.   

28.     return 0;  

29. }  


led_device_open函數的功能:

Ø 分配硬件設備操作結構體led_device_t,該結構體描述硬件操作行爲

Ø 初始化led_device_t的父結構體hw_device_t成員

Ø 初始化led_device_t中擴展的操作接口

Ø 打開設備,將led_device_t結構體以父結構體類型返回(面向對象裏的多態)


hw_module_thw_module_methods_t及硬件open函數的關係如下:


我們來看下led_device_t和其父結構體hw_device_t的關係:


1. struct led_device_t {  

2.     struct hw_device_t common;   // led_devict_t的父結構,它裏面只封裝了close方法  

3.     // 下面三個函數指針是子結構led_device_t對父結構hw_device_t的擴展,可以理解爲子類擴展了父類增加了三個方法  

4.     int (*getcount_led)(struct led_device_t *dev);  

5.     int (*set_on)(struct led_device_t *dev);  

6.     int (*set_off)(struct led_device_t *dev);  

7. };  

UML類圖來表示:


由類圖可知,led_device_t擴展了三個接口:seton() setoff()get_led_count()

那麼剩下的工作就是實現子結構中新擴展的三個接口了:


1. static int led_getcount(struct led_control_device_t*dev)  {  

2.          LOGI("led_getcount");  

3.          return 4;  

4. }  

5.    

6.  static int led_set_on(struct led_control_device_t *dev)  {     

7.          LOGI("led_set_on");  

8.          ioctl(fd,GPG3DAT2_ON,NULL);  

9.          return 0;  

10. }  

11.    

12. static int led_set_off(struct led_control_device_t*dev)  {  

13.          LOGI("led_set_off");  

14.          ioctl(fd,GPG3DAT2_OFF,NULL);  

15.          return 0;  

16. }  


這三個接口函數直接和底層驅動打交道去控制硬件,具體驅動部分我們不去講,那是另外一個體繫了。

總結一下:

        我們有一個硬件id名,通過這個id調用hw_get_module(char*id,struct hw_module_t **module),這個函數查找註冊在當前系統中與id對應的硬件對象並返回之,硬件對象裏有個通過hw_module_methods_t結構封裝的open函數指針,回調這個open函數,它返回封裝有硬件操作接口的led_device_t結構體,這樣我們可以通過這個硬件接口去間接的訪問硬件了。在這個過程中hw_get_module返回的是子結構體類型led_module_t,雖然函數的第二個參數類型爲hw_module_t的父類型,這裏用到了面向對象裏的多態的概念。


發佈了203 篇原創文章 · 獲贊 71 · 訪問量 54萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章