Add Yukawa HDMI CEC support

- Add CEC HAL
- Add hdmi_control service
- Set /dev/cec0 permission
- Add missing CEC key layout

Change-Id: I959b5b141d7bf81f59bcb7e81edf64b788b947d7
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/Generic.kl b/Generic.kl
new file mode 100644
index 0000000..1fb4290
--- /dev/null
+++ b/Generic.kl
@@ -0,0 +1,449 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Generic key layout file for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad range of keyboards.
+# Do not edit the generic key layout to support a specific keyboard; instead, create
+# a new key layout file with the required keyboard configuration.
+#
+
+key 1     ESCAPE
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 13    EQUALS
+key 14    DEL
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 28    ENTER
+key 29    CTRL_LEFT
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 41    GRAVE
+key 42    SHIFT_LEFT
+key 43    BACKSLASH
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+key 55    NUMPAD_MULTIPLY
+key 56    ALT_LEFT
+key 57    SPACE
+key 58    CAPS_LOCK
+key 59    F1
+key 60    F2
+key 61    F3
+key 62    F4
+key 63    F5
+key 64    F6
+key 65    F7
+key 66    F8
+key 67    F9
+key 68    F10
+key 69    NUM_LOCK
+key 70    SCROLL_LOCK
+key 71    NUMPAD_7
+key 72    NUMPAD_8
+key 73    NUMPAD_9
+key 74    NUMPAD_SUBTRACT
+key 75    NUMPAD_4
+key 76    NUMPAD_5
+key 77    NUMPAD_6
+key 78    NUMPAD_ADD
+key 79    NUMPAD_1
+key 80    NUMPAD_2
+key 81    NUMPAD_3
+key 82    NUMPAD_0
+key 83    NUMPAD_DOT
+# key 84 (undefined)
+key 85    ZENKAKU_HANKAKU
+key 86    BACKSLASH
+key 87    F11
+key 88    F12
+key 89    RO
+# key 90 "KEY_KATAKANA"
+# key 91 "KEY_HIRAGANA"
+key 92    HENKAN
+key 93    KATAKANA_HIRAGANA
+key 94    MUHENKAN
+key 95    NUMPAD_COMMA
+key 96    NUMPAD_ENTER
+key 97    CTRL_RIGHT
+key 98    NUMPAD_DIVIDE
+key 99    SYSRQ
+key 100   ALT_RIGHT
+# key 101 "KEY_LINEFEED"
+key 102   MOVE_HOME
+key 103   DPAD_UP
+key 104   PAGE_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 107   MOVE_END
+key 108   DPAD_DOWN
+key 109   PAGE_DOWN
+key 110   INSERT
+key 111   FORWARD_DEL
+# key 112 "KEY_MACRO"
+key 113   VOLUME_MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+key 116   POWER
+key 117   NUMPAD_EQUALS
+# key 118 "KEY_KPPLUSMINUS"
+key 119   BREAK
+# key 120 (undefined)
+key 121   NUMPAD_COMMA
+key 122   KANA
+key 123   EISU
+key 124   YEN
+key 125   META_LEFT
+key 126   META_RIGHT
+key 127   MENU
+key 128   MEDIA_STOP
+# key 129 "KEY_AGAIN"
+# key 130 "KEY_PROPS"
+# key 131 "KEY_UNDO"
+# key 132 "KEY_FRONT"
+key 133   COPY
+# key 134 "KEY_OPEN"
+key 135   PASTE
+# key 136 "KEY_FIND"
+key 137   CUT
+# key 138 "KEY_HELP"
+key 139   MENU
+key 140   CALCULATOR
+# key 141 "KEY_SETUP"
+key 142   SLEEP
+key 143   WAKEUP
+# key 144 "KEY_FILE"
+# key 145 "KEY_SENDFILE"
+# key 146 "KEY_DELETEFILE"
+# key 147 "KEY_XFER"
+# key 148 "KEY_PROG1"
+# key 149 "KEY_PROG2"
+key 150   EXPLORER
+# key 151 "KEY_MSDOS"
+key 152   POWER
+# key 153 "KEY_DIRECTION"
+# key 154 "KEY_CYCLEWINDOWS"
+key 155   ENVELOPE
+key 156   BOOKMARK
+# key 157 "KEY_COMPUTER"
+key 158   BACK
+key 159   FORWARD
+key 160   MEDIA_CLOSE
+key 161   MEDIA_EJECT
+key 162   MEDIA_EJECT
+key 163   MEDIA_NEXT
+key 164   MEDIA_PLAY_PAUSE
+key 165   MEDIA_PREVIOUS
+key 166   MEDIA_STOP
+key 167   MEDIA_RECORD
+key 168   MEDIA_REWIND
+key 169   CALL
+# key 170 "KEY_ISO"
+key 171   MUSIC
+key 172   HOME
+key 173   REFRESH
+# key 174 "KEY_EXIT"
+# key 175 "KEY_MOVE"
+# key 176 "KEY_EDIT"
+key 177   PAGE_UP
+key 178   PAGE_DOWN
+key 179   NUMPAD_LEFT_PAREN
+key 180   NUMPAD_RIGHT_PAREN
+# key 181 "KEY_NEW"
+# key 182 "KEY_REDO"
+# key 183   F13
+# key 184   F14
+# key 185   F15
+# key 186   F16
+# key 187   F17
+# key 188   F18
+# key 189   F19
+# key 190   F20
+# key 191   F21
+# key 192   F22
+# key 193   F23
+# key 194   F24
+# key 195 (undefined)
+# key 196 (undefined)
+# key 197 (undefined)
+# key 198 (undefined)
+# key 199 (undefined)
+key 200   MEDIA_PLAY
+key 201   MEDIA_PAUSE
+# key 202 "KEY_PROG3"
+# key 203 "KEY_PROG4"
+# key 204 (undefined)
+# key 205 "KEY_SUSPEND"
+# key 206 "KEY_CLOSE"
+key 207   MEDIA_PLAY
+key 208   MEDIA_FAST_FORWARD
+# key 209 "KEY_BASSBOOST"
+# key 210 "KEY_PRINT"
+# key 211 "KEY_HP"
+key 212   CAMERA
+key 213   MUSIC
+# key 214 "KEY_QUESTION"
+key 215   ENVELOPE
+# key 216 "KEY_CHAT"
+key 217   SEARCH
+# key 218 "KEY_CONNECT"
+# key 219 "KEY_FINANCE"
+# key 220 "KEY_SPORT"
+# key 221 "KEY_SHOP"
+# key 222 "KEY_ALTERASE"
+# key 223 "KEY_CANCEL"
+key 224   BRIGHTNESS_DOWN
+key 225   BRIGHTNESS_UP
+key 226   HEADSETHOOK
+
+key 256   BUTTON_1
+key 257   BUTTON_2
+key 258   BUTTON_3
+key 259   BUTTON_4
+key 260   BUTTON_5
+key 261   BUTTON_6
+key 262   BUTTON_7
+key 263   BUTTON_8
+key 264   BUTTON_9
+key 265   BUTTON_10
+key 266   BUTTON_11
+key 267   BUTTON_12
+key 268   BUTTON_13
+key 269   BUTTON_14
+key 270   BUTTON_15
+key 271   BUTTON_16
+
+key 288   BUTTON_1
+key 289   BUTTON_2
+key 290   BUTTON_3
+key 291   BUTTON_4
+key 292   BUTTON_5
+key 293   BUTTON_6
+key 294   BUTTON_7
+key 295   BUTTON_8
+key 296   BUTTON_9
+key 297   BUTTON_10
+key 298   BUTTON_11
+key 299   BUTTON_12
+key 300   BUTTON_13
+key 301   BUTTON_14
+key 302   BUTTON_15
+key 303   BUTTON_16
+
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 306   BUTTON_C
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 309   BUTTON_Z
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+key 312   BUTTON_L2
+key 313   BUTTON_R2
+key 314   BUTTON_SELECT
+key 315   BUTTON_START
+key 316   BUTTON_MODE
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+
+# key 352 "KEY_OK"
+key 353   DPAD_CENTER
+# key 354 "KEY_GOTO"
+# key 355 "KEY_CLEAR"
+# key 356 "KEY_POWER2"
+# key 357 "KEY_OPTION"
+# key 358 "KEY_INFO"
+# key 359 "KEY_TIME"
+# key 360 "KEY_VENDOR"
+# key 361 "KEY_ARCHIVE"
+key 362   GUIDE
+# key 363 "KEY_CHANNEL"
+# key 364 "KEY_FAVORITES"
+# key 365 "KEY_EPG"
+key 366   DVR
+# key 367 "KEY_MHP"
+# key 368 "KEY_LANGUAGE"
+# key 369 "KEY_TITLE"
+# key 370 "KEY_SUBTITLE"
+# key 371 "KEY_ANGLE"
+# key 372 "KEY_ZOOM"
+# key 373 "KEY_MODE"
+# key 374 "KEY_KEYBOARD"
+# key 375 "KEY_SCREEN"
+# key 376 "KEY_PC"
+key 377   TV
+# key 378 "KEY_TV2"
+# key 379 "KEY_VCR"
+# key 380 "KEY_VCR2"
+# key 381 "KEY_SAT"
+# key 382 "KEY_SAT2"
+# key 383 "KEY_CD"
+# key 384 "KEY_TAPE"
+# key 385 "KEY_RADIO"
+# key 386 "KEY_TUNER"
+# key 387 "KEY_PLAYER"
+# key 388 "KEY_TEXT"
+# key 389 "KEY_DVD"
+# key 390 "KEY_AUX"
+# key 391 "KEY_MP3"
+# key 392 "KEY_AUDIO"
+# key 393 "KEY_VIDEO"
+# key 394 "KEY_DIRECTORY"
+# key 395 "KEY_LIST"
+# key 396 "KEY_MEMO"
+key 397   CALENDAR
+# key 398 "KEY_RED"
+# key 399 "KEY_GREEN"
+# key 400 "KEY_YELLOW"
+# key 401 "KEY_BLUE"
+key 402   CHANNEL_UP
+key 403   CHANNEL_DOWN
+# key 404 "KEY_FIRST"
+# key 405 "KEY_LAST"
+# key 406 "KEY_AB"
+# key 407 "KEY_NEXT"
+# key 408 "KEY_RESTART"
+# key 409 "KEY_SLOW"
+# key 410 "KEY_SHUFFLE"
+# key 411 "KEY_BREAK"
+# key 412 "KEY_PREVIOUS"
+# key 413 "KEY_DIGITS"
+# key 414 "KEY_TEEN"
+# key 415 "KEY_TWEN"
+
+key 429   CONTACTS
+
+# key 448 "KEY_DEL_EOL"
+# key 449 "KEY_DEL_EOS"
+# key 450 "KEY_INS_LINE"
+# key 451 "KEY_DEL_LINE"
+
+
+key 464   FUNCTION
+key 465   ESCAPE            FUNCTION
+key 466   F1                FUNCTION
+key 467   F2                FUNCTION
+key 468   F3                FUNCTION
+key 469   F4                FUNCTION
+key 470   F5                FUNCTION
+key 471   F6                FUNCTION
+key 472   F7                FUNCTION
+key 473   F8                FUNCTION
+key 474   F9                FUNCTION
+key 475   F10               FUNCTION
+key 476   F11               FUNCTION
+key 477   F12               FUNCTION
+key 478   1                 FUNCTION
+key 479   2                 FUNCTION
+key 480   D                 FUNCTION
+key 481   E                 FUNCTION
+key 482   F                 FUNCTION
+key 483   S                 FUNCTION
+key 484   B                 FUNCTION
+
+
+# key 497 KEY_BRL_DOT1
+# key 498 KEY_BRL_DOT2
+# key 499 KEY_BRL_DOT3
+# key 500 KEY_BRL_DOT4
+# key 501 KEY_BRL_DOT5
+# key 502 KEY_BRL_DOT6
+# key 503 KEY_BRL_DOT7
+# key 504 KEY_BRL_DOT8
+
+key 522   STAR
+key 523   POUND
+key 580   APP_SWITCH
+key 582   VOICE_ASSIST
+
+# Keys defined by HID usages
+key usage 0x0c006F BRIGHTNESS_UP
+key usage 0x0c0070 BRIGHTNESS_DOWN
+
+# Joystick and game controller axes.
+# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x03 RX
+axis 0x04 RY
+axis 0x05 RZ
+axis 0x06 THROTTLE
+axis 0x07 RUDDER
+axis 0x08 WHEEL
+axis 0x09 GAS
+axis 0x0a BRAKE
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# LEDs
+led 0x00 NUM_LOCK
+led 0x01 CAPS_LOCK
+led 0x02 SCROLL_LOCK
+led 0x03 COMPOSE
+led 0x04 KANA
+led 0x05 SLEEP
+led 0x06 SUSPEND
+led 0x07 MUTE
+led 0x08 MISC
+led 0x09 MAIL
+led 0x0a CHARGING
+
+# CEC
+key 352   ENTER
+key 618   HOME
+key 141   POWER
+key 174   BACK
diff --git a/device.mk b/device.mk
index 41be28e..591933c 100644
--- a/device.mk
+++ b/device.mk
@@ -39,7 +39,7 @@
     $(LOCAL_PATH)/p2p_supplicant_overlay.conf:$(TARGET_COPY_OUT_VENDOR)/etc/wifi/p2p_supplicant_overlay.conf \
     $(LOCAL_PATH)/bt-wifi-firmware/BCM.hcd:$(TARGET_COPY_OUT_VENDOR)/firmware/brcm/BCM4359C0.hcd \
     $(LOCAL_PATH)/bt-wifi-firmware/fw_bcm4359c0_ag.bin:$(TARGET_COPY_OUT_VENDOR)/firmware/brcm/fw_bcm4359c0_ag.bin \
-    $(LOCAL_PATH)/bt-wifi-firmware/nvram.txt:$(TARGET_COPY_OUT_VENDOR)/firmware/brcm/nvram.txt 
+    $(LOCAL_PATH)/bt-wifi-firmware/nvram.txt:$(TARGET_COPY_OUT_VENDOR)/firmware/brcm/nvram.txt \
 
 # TV Specific Packages
 PRODUCT_PACKAGES += \
@@ -112,6 +112,18 @@
     android.hardware.drm@1.0-impl \
     android.hardware.drm@1.0-service \
 
+# CEC
+PRODUCT_PACKAGES += \
+    android.hardware.tv.cec@1.0-impl \
+    android.hardware.tv.cec@1.0-service \
+    hdmi_cec.yukawa
+
+PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=4
+
+PRODUCT_COPY_FILES += \
+    $(LOCAL_PATH)/Generic.kl:$(TARGET_COPY_OUT_VENDOR)/usr/keylayout/Generic.kl \
+    frameworks/native/data/etc/android.hardware.hdmi.cec.xml:system/etc/permissions/android.hardware.hdmi.cec.xml
+
 # Memtrack
 PRODUCT_PACKAGES += memtrack.default \
     android.hardware.memtrack@1.0-service \
diff --git a/hdmicec/Android.mk b/hdmicec/Android.mk
new file mode 100644
index 0000000..621a59e
--- /dev/null
+++ b/hdmicec/Android.mk
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2019 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK.  Modules must be uniquely
+# named (liblights.tuna), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+# HAL module implementation, not prelinked and stored in
+# hw/<COPYPIX_HARDWARE_MODULE_ID>.<ro.board.platform>.so
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := hdmi_cec.c
+LOCAL_CFLAGS := -Werror
+
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+
+LOCAL_MODULE := hdmi_cec.$(TARGET_BOARD_PLATFORM)
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+########################################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := hdmi_cec_test_hal.c
+LOCAL_SRC_FILES += hdmi_cec.c
+LOCAL_CFLAGS := -Wno-error -Wno-unused-parameter
+
+LOCAL_MODULE_PATH := $(TARGET_OUT)/bin
+
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+
+LOCAL_MODULE := hdmicec_test
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/hdmicec/hdmi_cec.c b/hdmicec/hdmi_cec.c
new file mode 100644
index 0000000..85920cd
--- /dev/null
+++ b/hdmicec/hdmi_cec.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2019 BayLibre, SAS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hdmi_cec"
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <poll.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/cec.h>
+#include <sys/eventfd.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware/hdmi_cec.h>
+
+typedef struct hdmicec_context
+{
+    hdmi_cec_device_t device; /* must be first */
+    int cec_fd;
+    unsigned int vendor_id;
+    unsigned int type;
+    unsigned int version;
+    struct hdmi_port_info port_info;
+    event_callback_t p_event_cb;
+    void *cb_arg;
+    pthread_t thread;
+    int exit_fd;
+} hdmicec_context_t;
+
+static int hdmicec_add_logical_address(const struct hdmi_cec_device *dev, cec_logical_address_t addr)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    unsigned int la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+    unsigned int all_dev_types = 0;
+    unsigned int prim_type = 0xff;
+    struct cec_log_addrs laddrs;
+    int ret;
+
+    ALOGD("%s: addr:%x\n", __func__, addr);
+
+    if (addr >= CEC_ADDR_BROADCAST)
+        return -1;
+
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_G_LOG_ADDRS, &laddrs);
+    if (ret)
+        return ret;
+    memset(&laddrs, 0, sizeof(laddrs));
+
+    laddrs.cec_version = ctx->version;
+    laddrs.vendor_id = ctx->vendor_id;
+
+    switch (addr) {
+        case CEC_LOG_ADDR_TV:
+            prim_type = CEC_OP_PRIM_DEVTYPE_TV;
+            la_type = CEC_LOG_ADDR_TYPE_TV;
+            all_dev_types = CEC_OP_ALL_DEVTYPE_TV;
+            break;
+        case CEC_LOG_ADDR_RECORD_1:
+        case CEC_LOG_ADDR_RECORD_2:
+        case CEC_LOG_ADDR_RECORD_3:
+            prim_type = CEC_OP_PRIM_DEVTYPE_RECORD;
+            la_type = CEC_LOG_ADDR_TYPE_RECORD;
+            all_dev_types = CEC_OP_ALL_DEVTYPE_RECORD;
+            break;
+        case CEC_LOG_ADDR_TUNER_1:
+        case CEC_LOG_ADDR_TUNER_2:
+        case CEC_LOG_ADDR_TUNER_3:
+        case CEC_LOG_ADDR_TUNER_4:
+            prim_type = CEC_OP_PRIM_DEVTYPE_TUNER;
+            la_type = CEC_LOG_ADDR_TYPE_TUNER;
+            all_dev_types = CEC_OP_ALL_DEVTYPE_TUNER;
+            break;
+        case CEC_LOG_ADDR_PLAYBACK_1:
+        case CEC_LOG_ADDR_PLAYBACK_2:
+        case CEC_LOG_ADDR_PLAYBACK_3:
+            prim_type = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
+            la_type = CEC_LOG_ADDR_TYPE_PLAYBACK;
+            all_dev_types = CEC_OP_ALL_DEVTYPE_PLAYBACK;
+            laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
+            break;
+        case CEC_LOG_ADDR_AUDIOSYSTEM:
+            prim_type = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
+            la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
+            all_dev_types = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
+            break;
+        case CEC_LOG_ADDR_SPECIFIC:
+            prim_type = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
+            la_type = CEC_LOG_ADDR_TYPE_SPECIFIC;
+            all_dev_types = CEC_OP_ALL_DEVTYPE_SWITCH;
+            break;
+        case CEC_ADDR_RESERVED_1:
+        case CEC_ADDR_RESERVED_2:
+        case CEC_ADDR_UNREGISTERED:
+            laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
+            break;
+    }
+
+    laddrs.num_log_addrs = 1;
+    laddrs.log_addr[0] = addr;
+    laddrs.log_addr_type[0] = la_type;
+    laddrs.primary_device_type[0] = prim_type;
+    laddrs.all_device_types[0] = all_dev_types;
+    laddrs.features[0][0] = 0;
+    laddrs.features[0][1] = 0;
+
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
+    if (ret) {
+        ALOGD("%s: %m\n", __func__);
+        return ret;
+    }
+
+    ALOGD("%s: log_addr_mask=%x\n", __func__,  laddrs.log_addr_mask);
+
+    return 0;
+}
+
+static void hdmicec_clear_logical_address(const struct hdmi_cec_device *dev)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    struct cec_log_addrs laddrs;
+    int ret;
+
+    memset(&laddrs, 0, sizeof(laddrs));
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
+    if (ret)
+        ALOGD("%s: %m\n", __func__);
+}
+
+static int hdmicec_get_physical_address(const struct hdmi_cec_device *dev, uint16_t *addr)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    int ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, addr);
+    if (ret)
+        ALOGD("%s: %m\n", __func__);
+
+    return ret;
+}
+
+static int hdmicec_send_message(const struct hdmi_cec_device *dev, const cec_message_t *msg)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    struct cec_msg cec_msg;
+    int ret;
+
+    ALOGD("%s: len=%u\n", __func__, (unsigned int)msg->length);
+
+    memset(&cec_msg, 0, sizeof(cec_msg));
+    cec_msg.msg[0] = (msg->initiator << 4) | msg->destination;
+
+    memcpy(&cec_msg.msg[1], msg->body, msg->length);
+    cec_msg.len = msg->length + 1;
+
+    ret = ioctl(ctx->cec_fd, CEC_TRANSMIT, &cec_msg);
+    if (ret) {
+        ALOGD("%s: %m\n", __func__);
+        return HDMI_RESULT_FAIL;
+    }
+
+    if (cec_msg.tx_status != CEC_TX_STATUS_OK)
+        ALOGD("%s: tx_status=%d\n", __func__, cec_msg.tx_status);
+
+    switch (cec_msg.tx_status) {
+        case CEC_TX_STATUS_OK:
+            return HDMI_RESULT_SUCCESS;
+        case CEC_TX_STATUS_ARB_LOST:
+            return HDMI_RESULT_BUSY;
+        case CEC_TX_STATUS_NACK:
+            return HDMI_RESULT_NACK;
+        default:
+            return HDMI_RESULT_FAIL;
+    }
+}
+
+static void hdmicec_register_event_callback(const struct hdmi_cec_device *dev,
+        event_callback_t callback, void *arg)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+
+    ctx->p_event_cb = callback;
+    ctx->cb_arg = arg;
+}
+
+static void hdmicec_get_version(const struct hdmi_cec_device *dev, int *version)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+
+    *version = ctx->version;
+}
+
+static void hdmicec_get_vendor_id(const struct hdmi_cec_device *dev, uint32_t *vendor_id)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+
+    *vendor_id = ctx->vendor_id;
+}
+
+static void hdmicec_get_port_info(const struct hdmi_cec_device *dev,
+        struct hdmi_port_info *list[], int *total)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    int ret;
+
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, &ctx->port_info.physical_address);
+    if (ret)
+        ALOGD("%s: %m\n", __func__);
+
+    ALOGD("type:%s, id:%d, cec support:%d, arc support:%d, physical address:%x",
+            ctx->port_info.type ? "output" : "input",
+            ctx->port_info.port_id,
+            ctx->port_info.cec_supported,
+            ctx->port_info.arc_supported,
+            ctx->port_info.physical_address);
+
+    *list = &ctx->port_info;
+    *total = 1;
+}
+
+static void hdmicec_set_option(const struct hdmi_cec_device *dev, int flag, int value)
+{
+    (void)dev;
+    ALOGD("%s: flag=%d, value=%d", __func__, flag, value);
+    switch (flag) {
+        case HDMI_OPTION_ENABLE_CEC:
+        case HDMI_OPTION_WAKEUP:
+        case HDMI_OPTION_SYSTEM_CEC_CONTROL:
+            /* TOFIX */
+            break;
+    }
+}
+
+static int hdmicec_is_connected(const struct hdmi_cec_device *dev, int port_id)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    int ret;
+
+    (void)port_id;
+
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR,
+            &ctx->port_info.physical_address);
+    if (ret) {
+        ALOGD("%s: %m\n", __func__);
+        return ret;
+    }
+
+    if (ctx->port_info.physical_address == CEC_PHYS_ADDR_INVALID)
+        return false;
+
+    return true;
+}
+
+static void *event_thread(void *arg)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)arg;
+    int ret;
+    struct pollfd ufds[3] = {
+        { ctx->cec_fd, POLLIN, 0 },
+        { ctx->cec_fd, POLLERR, 0 },
+        { ctx->exit_fd, POLLIN, 0 },
+    };
+
+    ALOGI("%s start!", __func__);
+
+    while (1) {
+        ufds[0].revents = 0;
+        ufds[1].revents = 0;
+        ufds[2].revents = 0;
+
+        ret = poll(ufds, 3, -1);
+
+        if (ret <= 0)
+            continue;
+
+        if (ufds[2].revents == POLLIN)   /* Exit */
+            break;
+
+        if (ufds[1].revents == POLLERR) { /* CEC Event */
+            hdmi_event_t event = { };
+            struct cec_event ev;
+
+            ret = ioctl(ctx->cec_fd, CEC_DQEVENT, &ev);
+            if (ret)
+                continue;
+
+            if (ev.event == CEC_EVENT_STATE_CHANGE) {
+                event.type = HDMI_EVENT_HOT_PLUG;
+                event.dev = &ctx->device;
+                event.hotplug.port_id = 1;
+                if (ev.state_change.phys_addr == CEC_PHYS_ADDR_INVALID)
+                    event.hotplug.connected = false;
+                else
+                    event.hotplug.connected = true;
+
+                if (ctx->p_event_cb != NULL) {
+                    ctx->p_event_cb(&event, ctx->cb_arg);
+                } else {
+                    ALOGE("no event callback for hotplug\n");
+                }
+            }
+        }
+
+        if (ufds[0].revents == POLLIN) { /* CEC Driver */
+            struct cec_msg msg = { };
+            hdmi_event_t event = { };
+
+            ret = ioctl(ctx->cec_fd, CEC_RECEIVE, &msg);
+            if (ret) {
+                ALOGE("%s: CEC_RECEIVE error (%m)\n", __func__);
+                continue;
+            }
+
+            if (msg.rx_status != CEC_RX_STATUS_OK) {
+                ALOGD("%s: rx_status=%d\n", __func__, msg.rx_status);
+                continue;
+            }
+
+            if (ctx->p_event_cb != NULL) {
+                event.type = HDMI_EVENT_CEC_MESSAGE;
+                event.dev = &ctx->device;
+                event.cec.initiator = msg.msg[0] >> 4;
+                event.cec.destination = msg.msg[0] & 0xf;
+                event.cec.length = msg.len;
+                memcpy(event.cec.body, &msg.msg[1], msg.len - 1);
+
+                ctx->p_event_cb(&event, ctx->cb_arg);
+            } else {
+                ALOGE("no event callback for msg\n");
+            }
+        }
+    }
+
+    ALOGI("%s exit!", __func__);
+    return NULL;
+}
+
+static void hdmicec_set_arc(const struct hdmi_cec_device *dev, int port_id, int flag)
+{
+    (void)dev;
+    (void)port_id;
+    (void)flag;
+    /* Not supported */
+}
+
+static int hdmicec_close(struct hdmi_cec_device *dev)
+{
+    struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
+    uint64_t tmp = 1;
+
+    ALOGD("%s\n", __func__);
+
+    if (ctx->exit_fd > 0) {
+        write(ctx->exit_fd, &tmp, sizeof(tmp));
+        pthread_join(ctx->thread, NULL);
+    }
+
+    if (ctx->cec_fd > 0)
+        close(ctx->cec_fd);
+    if (ctx->exit_fd > 0)
+        close(ctx->exit_fd);
+    free(ctx);
+
+    return 0;
+}
+
+static int cec_init(struct hdmicec_context *ctx)
+{
+    struct cec_log_addrs laddrs = {};
+    struct cec_caps caps = {};
+    uint32_t mode;
+    int ret;
+
+    // Ensure the CEC device supports required capabilities
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_G_CAPS, &caps);
+    if (ret)
+        return ret;
+
+    if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS |
+                    CEC_CAP_TRANSMIT |
+                    CEC_CAP_PASSTHROUGH))) {
+        ALOGE("%s: wrong cec adapter capabilities %x\n",
+                __func__, caps.capabilities);
+        return -1;
+    }
+
+    // This is an exclusive follower, in addition put the CEC device into passthrough mode
+    mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+    ret = ioctl(ctx->cec_fd, CEC_S_MODE, &mode);
+    if (ret)
+        return ret;
+
+    ctx->type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
+
+    ctx->vendor_id = property_get_int32("ro.hdmi.vendor_id",
+            0x000c03 /* HDMI LLC vendor ID */);
+
+    ctx->version = property_get_bool("ro.hdmi.cec_version",
+            CEC_OP_CEC_VERSION_1_4);
+
+    ctx->port_info.type = ctx->type == CEC_DEVICE_TV ? HDMI_INPUT : HDMI_OUTPUT;
+    ctx->port_info.port_id = 1;
+    ctx->port_info.cec_supported = 1;
+    ctx->port_info.arc_supported = 0;
+
+    ALOGD("%s: type=%d\n", __func__, ctx->type);
+    ALOGD("%s: vendor_id=%04x\n", __func__, ctx->vendor_id);
+    ALOGD("%s: version=%d\n", __func__, ctx->version);
+
+    memset(&laddrs, 0, sizeof(laddrs));
+    ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
+    if (ret)
+        return ret;
+
+    ALOGD("%s: initialized CEC controller\n", __func__);
+
+    return ret;
+}
+
+static int open_hdmi_cec(const struct hw_module_t *module, const char *id,
+        struct hw_device_t **device)
+{
+    char *path = "/dev/cec0";
+    hdmicec_context_t *ctx;
+    int ret;
+
+    ALOGD("%s: id=%s\n", __func__, id);
+
+    ctx = malloc(sizeof(struct hdmicec_context));
+    if (!ctx)
+        return -ENOMEM;
+
+    memset(ctx, 0, sizeof(*ctx));
+
+    ctx->cec_fd = open(path, O_RDWR);
+    if (ctx->cec_fd < 0) {
+        ALOGE("faild to open %s, ret=%s\n", path, strerror(errno));
+        goto fail;
+    }
+
+    ctx->exit_fd = eventfd(0, EFD_NONBLOCK);
+    if (ctx->exit_fd < 0) {
+        ALOGE("faild to open eventfd, ret = %d\n", errno);
+        goto fail;
+    }
+
+    ctx->device.common.tag = HARDWARE_DEVICE_TAG;
+    ctx->device.common.version = HDMI_CEC_DEVICE_API_VERSION_1_0;
+    ctx->device.common.module = (struct hw_module_t *)module;
+    ctx->device.common.close = (int (*)(struct hw_device_t* device))hdmicec_close;
+
+    ctx->device.add_logical_address = hdmicec_add_logical_address;
+    ctx->device.clear_logical_address = hdmicec_clear_logical_address;
+    ctx->device.get_physical_address = hdmicec_get_physical_address;
+    ctx->device.send_message = hdmicec_send_message;
+    ctx->device.register_event_callback = hdmicec_register_event_callback;
+    ctx->device.get_version = hdmicec_get_version;
+    ctx->device.get_vendor_id = hdmicec_get_vendor_id;
+    ctx->device.get_port_info = hdmicec_get_port_info;
+    ctx->device.set_option = hdmicec_set_option;
+    ctx->device.set_audio_return_channel = hdmicec_set_arc;
+    ctx->device.is_connected = hdmicec_is_connected;
+
+    /* init status */
+    ret = cec_init(ctx);
+    if (ret)
+        goto fail;
+
+    *device = &ctx->device.common;
+
+    /* thread loop for receiving cec msg */
+    if (pthread_create(&ctx->thread, NULL, event_thread, ctx)) {
+        ALOGE("Can't create event thread: %s\n", strerror(errno));
+        goto fail;
+    }
+    return 0;
+
+fail:
+    hdmicec_close((struct hdmi_cec_device *)ctx);
+    return -errno;
+}
+
+/* module method */
+static struct hw_module_methods_t hdmi_cec_module_methods = {
+    .open =  open_hdmi_cec,
+};
+
+/* hdmi_cec module */
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+    .tag = HARDWARE_MODULE_TAG,
+    .version_major = 1,
+    .version_minor = 0,
+    .id = HDMI_CEC_HARDWARE_MODULE_ID,
+    .name = "ATOM HDMI CEC module",
+    .author = "The Android Open Source Project",
+    .methods = &hdmi_cec_module_methods,
+};
diff --git a/hdmicec/hdmi_cec_test_hal.c b/hdmicec/hdmi_cec_test_hal.c
new file mode 100644
index 0000000..a3da9df
--- /dev/null
+++ b/hdmicec/hdmi_cec_test_hal.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 ASUSTek COMPUTER INC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+#include <hardware/hdmi_cec.h>
+
+extern struct hw_module_t HAL_MODULE_INFO_SYM;
+static int (*hdmicec_open)(const struct hw_module_t *, const char *, struct hw_device_t **);
+
+static struct hw_module_t *device;
+static struct hw_module_t **device2 = &device;
+
+int main(int argc, char* argv[])
+{
+    struct hdmi_cec_device *cec_device;
+    int err, type = -1;
+
+    if (argc > 1)
+        type = atoi(argv[1]);
+
+    ALOGI("open hdmicec");
+    hdmicec_open = HAL_MODULE_INFO_SYM.methods->open;
+    err = (*hdmicec_open)(&HAL_MODULE_INFO_SYM, NULL, (struct hw_device_t **)device2);
+    if (!err) {
+        ALOGI("open success");
+    } else {
+        ALOGE("open fail");
+    }
+
+    cec_device = (struct hdmi_cec_device *)(*device2);
+#if 0
+    if (type != -1) {
+        int fd;
+
+        ALOGI("0. test type change: %d\n", type);
+        fd = open("/dev/cec0", O_RDWR);
+        if (fd < 0) {
+            ALOGE("fail to open CEC device\n");
+        } else {
+            err = ioctl(fd, CEC_IOC_SET_DEV_TYPE, type);
+            if (!err) {
+                ALOGI("type change success\n");
+            } else {
+                ALOGE("type change fail\n");
+            }
+            close(fd);
+        }
+    }
+#endif
+
+    ALOGI("1. test add logical address\n");
+    err = cec_device->add_logical_address(cec_device, CEC_ADDR_PLAYBACK_1);
+    if (!err) {
+        ALOGI("add logical address success\n");
+    } else {
+        ALOGE("add logical address fail\n");
+    }
+
+    ALOGI("2. test send cec message\n");
+    cec_message_t send_msg;
+
+    ALOGI("2.a header\n");
+    send_msg.initiator = CEC_ADDR_PLAYBACK_1;
+    send_msg.destination = CEC_ADDR_PLAYBACK_1;
+    send_msg.length = 0;
+
+    err = cec_device->send_message(cec_device, &send_msg);
+    ALOGI("send_message result= %d", err);
+
+    ALOGI("2.b header + opcode\n");
+    send_msg.initiator = CEC_ADDR_PLAYBACK_1;
+    send_msg.destination = CEC_ADDR_TV;
+    send_msg.length = 1;
+    send_msg.body[0] = CEC_MESSAGE_IMAGE_VIEW_ON;
+
+    err = cec_device->send_message(cec_device, &send_msg);
+    ALOGI("send_message result= %d", err);
+
+    ALOGI("2.c header + opcode + operands\n");
+    send_msg.initiator = CEC_ADDR_PLAYBACK_1;
+    send_msg.destination = CEC_ADDR_TV;
+    send_msg.length = 15; //max opcode + operands should be 15
+    send_msg.body[0] = CEC_MESSAGE_VENDOR_COMMAND;
+    for (size_t i = 1; i < send_msg.length; i++)
+        send_msg.body[i] = i;
+
+    err = cec_device->send_message(cec_device, &send_msg);
+    ALOGI("send_message result= %d", err);
+
+    ALOGI("3 check hdmi connect status\n");
+    err = cec_device->is_connected(cec_device, 1);
+    ALOGI("is connected= %d", err);
+
+    ALOGI("4 get hdmi physical address\n");
+    uint16_t addr;
+    err = cec_device->get_physical_address(cec_device, &addr);
+    ALOGI("addr= %d, err= %d", addr, err);
+
+    sleep(3);
+    ALOGI("close hdmicec");
+    cec_device->common.close((struct hw_device_t *)cec_device);
+
+    return 0;
+}
diff --git a/manifest.xml b/manifest.xml
index def252e..5497335 100755
--- a/manifest.xml
+++ b/manifest.xml
@@ -40,6 +40,15 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal>
+        <name>android.hardware.tv.cec</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IHdmiCec</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl">
         <name>android.hardware.bluetooth</name>
         <transport>hwbinder</transport>
diff --git a/ueventd.rc b/ueventd.rc
index 862118d..ecee833 100644
--- a/ueventd.rc
+++ b/ueventd.rc
@@ -4,3 +4,4 @@
 /dev/mali0       0666 system graphics
 /dev/ion	 0666 system graphics
 /dev/graphics/fb0       0666 system graphics
+/dev/cec0	0666 system graphics