androidHAL
架构
源码解析
以Android 9.0 TV CEC 相关解析,主要是整个框架
hal层关键定义
- Pie\hardware\libhardware\include\hardware\hardware.h
- Pie\hardware\libhardware\hardware.c
struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;
hw_module_t
描述抽象硬件,关键成员methods
typedef struct hw_module_t {
...
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods;
...
}hw_module_t;
hw_module_methods_t
设定打开硬件方法,或的一个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
描述抽象设备
typedef struct hw_device_t {
...
/** reference to the module this device belongs to */
struct hw_module_t* module;
/** Close this device */
int (*close)(struct hw_device_t* device);
...
} hw_device_t;
hw_get_module、hw_get_module_by_class
加载硬件模块,获取hw_module_t
int hw_get_module(const char *id, const struct hw_module_t **module); //根据id
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module); //根据class_id和实例
一些模块关联多个interface,如
audio.primary.<variant>.so
audio.a2dp.<variant>.so
hal模块加载路径
path:
PATH1:"/system/lib[64]/hw"
PATH2:"/vendor/lib[64]/hw"
PATH3:"/odm/lib[64]/hw"
hal模块名构造
name.prop.so:
name:"<MODULE_ID>" 或 "<MODULE_ID>[.<inst>]"
prop:即variant
variant 来自系统配置属性值,通过getprop xxx 可以获取
"ro.hardware.<name>",
"ro.hardware",
"ro.product.board",
"ro.board.platform",
"ro.arch"
以上值都为空则为默认值 "default"
hal模块搜寻顺序
- 构造基本名字 name "<MODULE_ID>" 或 "<MODULE_ID>[.<inst>]"
- 查询是否有属性 property_get,判断是否存在,尝试加载
- 以上都不存在,则尝试加载default的
检测是否存在
依次检查以下各路径
PATH3/name.prop.so
PATH2/name.prop.so
PATH1/name.prop.so
加载流程
通过
dlopen
加载系统分区的库,或android_load_sphal_library
加载sphal命名空间的。-
获取hw_module_t结构
const char *sym = HAL_MODULE_INFO_SYM_AS_STR; //"HMI" hmi = (struct hw_module_t *)dlsym(handle, sym);
CEC 整体架构实现
Linux驱动层
Pie\common\drivers\amlogic\cec
创建/dev/cec
字符设备,后续通过访问该设备访问CEC功能。
驱动封装层
Pie\vendor\amlogic\common\frameworks\services\hdmicec\libhdmi_cec
打开/dev/cec驱动,封装CEC命令操作,
hal模块层
Pie\hardware\libhardware\include\hardware\hdmi_cec.h
定义模块及设备
#define HDMI_CEC_HARDWARE_MODULE_ID "hdmi_cec"
typedef struct hdmi_cec_module {
struct hw_module_t common;
} hdmi_module_t;
typedef struct hdmi_cec_device {
struct hw_device_t common;
int (*add_logical_address)(const struct hdmi_cec_device* dev,
cec_logical_address_t addr);
void (*clear_logical_address)(const struct hdmi_cec_device* dev);
int (*get_physical_address)(const struct hdmi_cec_device* dev, uint16_t* addr);
int (*send_message)(const struct hdmi_cec_device* dev, const cec_message_t*);
void (*register_event_callback)(const struct hdmi_cec_device* dev,
event_callback_t callback, void* arg);
void (*get_version)(const struct hdmi_cec_device* dev, int* version);
void (*get_vendor_id)(const struct hdmi_cec_device* dev, uint32_t* vendor_id);
void (*get_port_info)(const struct hdmi_cec_device* dev,
struct hdmi_port_info* list[], int* total);
void (*set_option)(const struct hdmi_cec_device* dev, int flag, int value);
void (*set_audio_return_channel)(const struct hdmi_cec_device* dev, int port_id,
int flag);
int (*is_connected)(const struct hdmi_cec_device* dev, int port_id);
void* reserved[16 - 11];
} hdmi_cec_device_t;
// 定义打开和关闭操作
static inline int hdmi_cec_open(const struct hw_module_t* module,
struct hdmi_cec_device** device) {
return module->methods->open(module,
HDMI_CEC_HARDWARE_INTERFACE, TO_HW_DEVICE_T_OPEN(device));
}
static inline int hdmi_cec_close(struct hdmi_cec_device* device) {
return device->common.close(&device->common);
}
// 定义HAL回调函数,驱动可以回调到上层
typedef void (*event_callback_t)(const hdmi_event_t* event, void* arg);
service 层
service 启动
-
直通式
int main() { return defaultPassthroughServiceImplementation<IHdmiCec>(); }
-
绑定式
int main(int argc __unused, char** argv __unused) { bool treble = property_get_bool("persist.vendor.hdmi_cec.treble", true); if (treble) { android::ProcessState::initWithDriver("/dev/vndbinder"); } ALOGI("hdmi_cec starting in %s mode", treble?"treble":"normal"); configureRpcThreadpool(5, false); sp<ProcessState> proc(ProcessState::self()); if (treble) { sp<IHdmiCec> hdmicec = new HdmiCec(); if (hdmicec == nullptr) { ALOGE("Cannot create HdmiCec HAL service"); } else if (hdmicec->registerAsService() != android::OK) { ALOGE("Cannot register HdmiCec HAL service."); } else { ALOGI("Treble HdmiCec HAL service created."); } } else{ } IPCThreadState::self()->joinThreadPool(); return 1; }
启动服务,通过启动脚本启动
service vendor.cec-hal-1-0 /vendor/bin/hw/android.hardware.tv.cec@1.0-service class hal user system group system
service实现
class HdmiCec : public IHdmiCec, public hidl_death_recipient {
... // 声明HAL 接口
...
virtual void serviceDied(uint64_t /*cookie*/,
const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
... //服务死亡处理
}
private:
static sp<IHdmiCecCallback> mCallback;
const hdmi_cec_device_t* mDevice;
}
hal层
关键文件路径
Pie\hardware\interfaces\tv\cec\1.0 // HIDL 接口定义
Pie\hardware\interfaces\tv\cec\1.0\default // HIDL 接口默认实现
-
接口定义部分
-
Android.bp
构建hidl interface脚本
-
-
IHdmiCec.hal
IHdmiCecCallback.hal
定义接口及回调接口-
type.hal
接口中用到的自定义类型
-
-
接口实现部分
-
Android.bp
构建库和服务 -
HdmiCec.h
HdmiCec.cpp
实现接口 -
service.cpp
android.hardware.tv.cec@1.0-service.rc
服务实现,及启动脚本
-
草稿
【CEC Linux驱动】
common\drivers\amlogic\cec
可选 AMLOGIC_M8B_CEC AMLOGIC_AO_CEC 两种类型
/sys/class/hdmirx/hdmirx0/cec
【CEC 驱动封装层】
Pie\vendor\amlogic\common\frameworks\services\hdmicec\libhdmi_cec
生成 libhdmi_cec_static.so,打开/dev/cec驱动,封装CEC命令操作
hdmi_device_t 代表一个hdmi设备,包括设备类型,逻辑地址,CEC相关设置等,作用是记录CEC的状态信息
①
class HdmiCecControl : public HdmiCecBase
{
init <== 初始化
openCecDevice <== 打开 /dev/cec,ioctl 设置驱动,记录CEC设备信息
closeCecDevice <== 关闭 /dev/cec 驱动
HdmiCecControl::cec_process_func <== 通过 ioctl 设置参数,执行动作
threadLoop <== readMessage (从CEC驱动中读取消息==> 通过CEC驱动read消息),
messageValidateAndHandle(消费消息,
post到MsgHandler::handleMessage ==> HdmiCecControl::send 或 HdmiCecControl::cec_process_func 处理掉),
post给 HdmiCecEventListener
sendMessage\sendExtMessage ==> HdmiCecControl::send ==> 通过CEC驱动write发送消息
setEventObserver === 将 HdmiCecEventListener 设置
}
【CEC 服务层】
Pie\vendor\amlogic\common\frameworks\services\hdmicec\server
生成 hdmicecd 可执行文件,hdmicecd.rc 启动文件,启动/vendor/bin/hdmicecd
①
main_hdmicec.cpp
main === 创建 DroidHdmiCec ,注册服务 DroidHdmiCec::registerAsService()
②
class DroidHdmiCec : public IDroidHdmiCEC, public HdmiCecEventListener
{
构造函数 === 创建 HdmiCecControl,cecControl->setEventObserver(this HdmiCecEventListener)
setCallback === 把 IDroidHdmiCecCallback 设置给 mClients
openCecDevice === cecControl->openCecDevice
closeCecDevice === cecControl->closeCecDevice
cec_process_func == cecControl->cec_process_func
sendMessage === cecControl->sendMessage
onEventUpdate == 将 hdmi_cec_event_t 通知给Client。mClients[i]->notifyCallback
HdmiCecControl* cecControl
IDroidHdmiCecCallback mClients
}
【CEC 客户端层】
Pie\vendor\amlogic\common\frameworks\services\hdmicec\binder
生成 libhdmicec.so
①
HdmiCecBase.h 定义了 HdmiCecBase 和 HdmiCecEventListener
HdmiCecHidlClient.h 定义了 HdmiCecHidlClient HdmiCecHidlCallback
②
HdmiCecHidlClient
{
connect === 构造 HdmiCecHidlClient
构造 === 通过 getHdmiCecService 获取 hdmicec [hdmicec.setCallback(callback)]
getHdmiCecService === IDroidHdmiCEC::tryGetService
openCecDevice === hdmicec->openCecDevice
closeCecDevice === hdmicec->closeCecDevice
cec_process_func === hdmicec->cec_process_func
sendMessage === hdmicec->sendMessage
setEventObserver == 设置 mEventListener
HdmiCecHidlCallback* callback
IDroidHdmiCEC* hdmicec
HdmiCecEventListener * mEventListener
}
③
class HdmiCecHidlCallback : public IDroidHdmiCecCallback
{
构造 === 传入 HdmiCecHidlClient
notifyCallback === 传入 CecEvent, mEventListener ==> HdmiCecEventListener
HdmiCecHidlClient *cecClient
}
④
HdmiCecEventListener
{
onEventUpdate(hdmi_cec_event_t)
}
【CEC jni层】
Pie\vendor\amlogic\common\frameworks\core\jni\hdmi_cec
生成 libhdmicec_jni.so
【CEC java层】
Pie\vendor\amlogic\common\frameworks\core\java\com\droidlogic
通过 /sys/class/xxx 文件访问CEC驱动
【】
Pie\hardware\amlogic\hdmi_cec
生成 hdmi_cec.amlogic.so,使用HdmiCecHidlClient 访问cec服务
aml_cec_hal_t{ hdmi_cec_device_t 入口参数,上层传递
HdmiCecHidlClient,
event_callback_t,
fd}
struct hdmi_cec_module <= .common.methods <= struct hw_module_methods_t.open <= open_cec()
open_cec() ==do==> (HdmiCecHidlClient <==create== HdmiCecHidlClient::connect),
(HdmiCecHidlClient <==set== HdmiCecCallback,
(fd <==set== HdmiCecHidlClient::openCecDevice),
(hdmi_cec_device_t.funcxxx <==set== cec_process_func)
cec_close ==do==> (HdmiCecHidlClient::closeCecDevice)
cec_process_func ==do==> (HdmiCecHidlClient::xxx_process_func)
class HdmiCecCallback : public HdmiCecEventListener
{
onEventUpdate(hdmi_cec_event_t) <== 执行event_callback_t()
}
【设置应用】
Pie\vendor\amlogic\common\apps\DroidTvSettings\src\com\droidlogic\tv\settings
Pie\vendor\amlogic\common\frameworks\core\java\com\droidlogic\app
【CEC语言设置流程】
HdmiCecControl::init-->mCecDevice.isPlaybackDeviceType = true
HdmiCecControl::openCecDevice--> mCecDevice.mTvOsdName = 0; GET_MENU_LANGUAGE-->驱动send CEC_MESSAGE_GET_MENU_LANGUAGE
--> readMessage-->CEC_MESSAGE_SET_MENU_LANGUAGE-->persist.vendor.sys.cec.set_menu_language-->handleSetMenuLanguage
--> mEventListener->onEventUpdate
./vendor/amlogic/common/frameworks/core/res/src/com/droidlogic/BootComplete.java:44://import com.droidlogic.HdmiCecExtend;
Pie/bionic/libc/include\dlfcn.h
Pie\hardware\libhardware\include\hardware\hardware.h
Pie\hardware\libhardware\hardware.c
Pie\frameworks\base\services\core\java\com\android\server\hdmi
Pie\hardware\interfaces\tv\cec\1.0
Pie\hardware\libhardware\include\hardware\hdmi_cec.h
Pie/frameworks/base/services/java/com/android/server/SystemServer.java
Pie\frameworks\base\services\core\jni\com_android_server_hdmi_HdmiCecController.cpp