HAL(硬體抽象層)
Android的硬體抽象層,簡單來說,就是對Linux核心驅動程式的封裝,向上提供介面,向下遮蔽了具體的實現細節。也就是將Linux核心對硬體的支援分成了兩層,一層放在使用者空間(User Space),一層放在核心空間(Kernel Space),其中,硬體抽象層執行在使用者空間,對應具體的驅動實現細節,而Linux核心驅動程式執行在核心空間,只提供簡單的資料訪問邏輯。
那為什麼要將驅動的具體實現細節封裝到硬體抽象層呢?
Android系統使用的是Linux核心,Linux核心是基於GPL許可(GNU License),即對原始碼的修改都必須開源,如果將驅動的實現細節放在Linux核心層,那麼就意味著需要公開驅動程式的原始碼,這樣顯然是違背了硬體廠商的自身利益。
那為什麼將其抽象到硬體抽象層就可以規避這個問題呢?
除Linux核心外的Android原始碼使用的是ASL許可(Apache License),而ASL許可無須釋出原始碼,這樣Android就可以對該部分的程式碼不開源。
另外,HAL也可以遮蔽不同硬體裝置的差異,為Android提供了統一的訪問硬體裝置的介面。不同的硬體廠商遵循HAL標準來實現自己的硬體控制邏輯,這樣開發者不必關心不同硬體裝置的差異,只需要按照HAL提供的標準介面訪問硬體就可以了。
HAL 模組的實現
Android 系統的 HAL 層其實並不複雜,只要你能理解清楚下面這 3 個結構體的含義:
- hw_module_t:用來描述硬體模組
- hw_device_t:用來描述硬體裝置
- hw_module_methods_t:用來開啟硬體模組中包含硬體裝置,獲得指向硬體裝置結構體的指標
hw_module_t
/**
* 每個硬體模組中都要定義一個名字叫做 HAL_MODULE_INFO_SYM 結構體變數,
* 而這結構體變數中的第一個成員必須是 hw_module_t 型別。
*/
typedef struct hw_module_t {
/** 必須設定為 HARDWARE_MODULE_TAG */
uint32_t tag;
/** 表示硬體模組,用來區別於其他硬體模組 */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** methods給出了一個硬體抽象模組的操作方法列表 */
struct hw_module_methods_t* methods;
/**
*用來儲存載入硬體抽象模組後得到的控制程式碼值
*每一個硬體抽象模組都對應一個動態連結庫檔案
*dlopen函式開啟對應的動態連結庫檔案獲得這個控制程式碼
*/
void* dso;
} hw_module_t;
複製程式碼
從上面的註釋可以看到,對於具體的硬體模組,必須重新定義一個結構體變數,這個結構體變數的名字為HAL_MODULE_INFO_SYM,並且結構體的第一個成員變數必須為hw_module_t。
hw_module_methods_t
/**
* 該結構體只有一個名為open的函式指標變數
* 用來開啟硬體模組所管理的硬體裝置id值為 id 的硬體裝置,並將開啟的硬體裝置通過 device 返回
*/
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
複製程式碼
hw_device_t
/**
* 每個 HAL 層中硬體裝置對應的結構體中的第一個成員必須是 hw_device_t
* 然後接著的就是對應的方法和屬性
*/
typedef struct hw_device_t {
/** 必須初始化為 HARDWARE_DEVICE_TAG */
uint32_t tag;
uint32_t version;
/** 表示該硬體裝置屬於哪個硬體模組 */
struct hw_module_t* module;
/** 關閉硬體裝置 */
int (*close)(struct hw_device_t* device);
} hw_device_t;
複製程式碼
對於具體的硬體裝置,必須重新定義一個結構體變數,並且結構體的第一個成員變數必須為hw_device_t。
Audio HAL 模組的實現
Step 1:定義 struct audio_module 模組
具體的硬體模組要定義一個新的結構體並且這個結構體的第一個成員必須是 hw_module_t 型別
struct audio_module {
struct hw_module_t common;
};
複製程式碼
step 2:定義 struct audio_module 型別的
重新定義一個結構體變數,這個結構體變數的名字為HAL_MODULE_INFO_SYM,並且結構體的第一個成員變數必須為hw_module_t。
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "Generic audio HW HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
}
};
複製程式碼
step 3:定義 struct audio_hw_device 硬體裝置結構體
struct audio_hw_device {
// 第一個成員變數對應的是hw_module_t 型別
struct hw_device_t common;
......
// 後面對應的就是裝置操作方法
size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
const struct audio_config *config);
int (*open_output_stream)(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
audio_output_flags_t flags,
struct audio_config *config,
struct audio_stream_out **stream_out,
const char *address);
void (*close_output_stream)(struct audio_hw_device *dev,
struct audio_stream_out* stream_out);
int (*open_input_stream)(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
struct audio_config *config,
struct audio_stream_in **stream_in,
audio_input_flags_t flags,
const char *address,
audio_source_t source);
void (*close_input_stream)(struct audio_hw_device *dev,
struct audio_stream_in *stream_in);
......
};
複製程式碼
struct generic_audio_device {
struct audio_hw_device device;
pthread_mutex_t lock;
struct audio_stream_out *output;
struct audio_stream_in *input;
int fd;
bool mic_mute;
};
複製程式碼
step 4:定義 struct hw_module_methods_t 函式列表變數
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
};
複製程式碼
開啟硬體模組的硬體裝置
step 5:adev_open 函式的實現
static int adev_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
struct generic_audio_device *adev;
int fd;
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
return -EINVAL;
fd = open(AUDIO_DEVICE_NAME, O_RDWR);
if (fd < 0)
return -ENOSYS;
adev = calloc(1, sizeof(struct generic_audio_device));
adev->fd = fd;
adev->device.common.tag = HARDWARE_DEVICE_TAG;
adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
adev->device.common.module = (struct hw_module_t *) module;
adev->device.common.close = adev_close;
adev->device.init_check = adev_init_check;
adev->device.set_voice_volume = adev_set_voice_volume;
adev->device.set_master_volume = adev_set_master_volume;
adev->device.get_master_volume = adev_get_master_volume;
adev->device.set_master_mute = adev_set_master_mute;
adev->device.get_master_mute = adev_get_master_mute;
adev->device.set_mode = adev_set_mode;
adev->device.set_mic_mute = adev_set_mic_mute;
adev->device.get_mic_mute = adev_get_mic_mute;
adev->device.set_parameters = adev_set_parameters;
adev->device.get_parameters = adev_get_parameters;
adev->device.get_input_buffer_size = adev_get_input_buffer_size;
adev->device.open_output_stream = adev_open_output_stream;
adev->device.close_output_stream = adev_close_output_stream;
adev->device.open_input_stream = adev_open_input_stream;
adev->device.close_input_stream = adev_close_input_stream;
adev->device.dump = adev_dump;
*device = &adev->device.common;
return 0;
}
複製程式碼
我們可以從 adev_open 函式中的實現中看到,它裡面的主要工作就是做一些對 struct audio_hw_device 物件的初始化,將其定義的函式指標指向對應的已經實現好的函式中。
例如,這裡將struct audio_hw_device中定義的 open_output_stream 函式指標成員指向了 adev_open_output_stream 函式。這樣在 Framework 層呼叫的 struct audio_hw_device 物件的 open_output_stream 函式,其實最終呼叫的是 adev_open_output_stream函式。
參考文章:
https://blog.csdn.net/Luoshengyang/article/details/6567257
https://woshijpf.github.io/android/2017/03/25/Android-HAL%E5%B1%82%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.html
https://www.jianshu.com/p/21a6cc575ed3