HAL

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模块搜寻顺序

  1. 构造基本名字 name "<MODULE_ID>" 或 "<MODULE_ID>[.<inst>]"
  2. 查询是否有属性 property_get,判断是否存在,尝试加载
  3. 以上都不存在,则尝试加载default的

检测是否存在

依次检查以下各路径
PATH3/name.prop.so
PATH2/name.prop.so
PATH1/name.prop.so

加载流程

  1. 通过dlopen加载系统分区的库,或android_load_sphal_library 加载sphal命名空间的。

  2. 获取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 接口中用到的自定义类型
  • 接口实现部分

草稿

【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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容