-rw-r--r-- | Android.mk | 5 | ||||
-rw-r--r-- | hdmi_cec.c | 839 | ||||
-rw-r--r-- | hdmi_cec.cpp | 338 |
3 files changed, 341 insertions, 841 deletions
diff --git a/hdmi_cec.cpp b/hdmi_cec.cpp new file mode 100644 index 0000000..9436801 --- a/dev/null +++ b/hdmi_cec.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2014 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. + */ +/* + * Amlogic HDMITX CEC HAL + * Copyright (C) 2014 + * + * This implements a hdmi cec hardware library for the Android emulator. + * the following code should be built as a shared library that will be + * placed into /system/lib/hw/hdmi_cec.so + * + * It will be loaded by the code in hardware/libhardware/hardware.c + * which is itself called from + * frameworks/base/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp + */ + +#include <cutils/log.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <hardware/hdmi_cec.h> +#include <hardware/hardware.h> +#include <cutils/properties.h> +#include "hdmi_cec.h" +#include <jni.h> +#include <JNIHelp.h> + +#include <HdmiCecClient.h> + +#ifdef LOG_TAG +#undef LOG_TAG +#define LOG_TAG "CEC" +#else +#define LOG_TAG "CEC" +#endif + +/* Set to 1 to enable debug messages to the log */ +#define DEBUG 1 +#if DEBUG +# define D(format, args...) ALOGD("[%s]" format, __FUNCTION__, ##args) +#else +# define D(...) do{}while(0) +#endif + +#define E(format, args...) ALOGE("[%s]" format, __FUNCTION__, ##args) + +using namespace android; + +typedef struct aml_cec_hal { + hdmi_cec_device_t device; + sp<HdmiCecClient> client; + void *cb_data; + event_callback_t cb; + int fd; +} aml_cec_hal_t; + +struct aml_cec_hal *hal_info = NULL; + +class HdmiCecCallback : public HdmiCecEventListener { +public: + HdmiCecCallback(){} + ~HdmiCecCallback(){} + virtual void onEventUpdate(const hdmi_cec_event_t* event); +}; + +void HdmiCecCallback::onEventUpdate(const hdmi_cec_event_t* cecEvent) +{ + if (cecEvent == NULL) + return; + + int type = cecEvent->eventType; + + if ((cecEvent->eventType & HDMI_EVENT_CEC_MESSAGE) != 0 && hal_info->cb) { + D("send cec message, event type = %d", type); + hdmi_event_t event; + event.type = HDMI_EVENT_CEC_MESSAGE; + event.dev = &hal_info->device; + event.cec.initiator = cecEvent->cec.initiator; + event.cec.destination = cecEvent->cec.destination; + event.cec.length = cecEvent->cec.length; + memcpy(event.cec.body, cecEvent->cec.body, event.cec.length); + hal_info->cb(&event, hal_info->cb_data); + } else if ((cecEvent->eventType & HDMI_EVENT_HOT_PLUG) != 0 && hal_info->cb) { + D("cec hot plug, event type = %d", type); + hdmi_event_t event; + event.type = HDMI_EVENT_HOT_PLUG; + event.dev = &hal_info->device; + event.hotplug.connected = cecEvent->hotplug.connected; + event.hotplug.port_id = cecEvent->hotplug.port_id; + hal_info->cb(&event, hal_info->cb_data); + } +} + +/* + * (*add_logical_address)() passes the logical address that will be used + * in this system. + * + * HAL may use it to configure the hardware so that the CEC commands addressed + * the given logical address can be filtered in. This method can be called + * as many times as necessary in order to support multiple logical devices. + * addr should be in the range of valid logical addresses for the call + * to succeed. + * + * Returns 0 on success or -errno on error. + */ +static int cec_add_logical_address(const struct hdmi_cec_device* dev, cec_logical_address_t addr) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + return priv->client->addLogicalAddress(addr); +} + +/* + * (*clear_logical_address)() tells HAL to reset all the logical addresses. + * + * It is used when the system doesn't need to process CEC command any more, + * hence to tell HAL to stop receiving commands from the CEC bus, and change + * the state back to the beginning. + */ +static void cec_clear_logical_address(const struct hdmi_cec_device* dev) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + priv->client->clearLogicaladdress(); +} + +/* + * (*get_physical_address)() returns the CEC physical address. The + * address is written to addr. + * + * The physical address depends on the topology of the network formed + * by connected HDMI devices. It is therefore likely to change if the cable + * is plugged off and on again. It is advised to call get_physical_address + * to get the updated address when hot plug event takes place. + * + * Returns 0 on success or -errno on error. + */ +static int cec_get_physical_address(const struct hdmi_cec_device* dev, uint16_t* addr) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + return priv->client->getPhysicalAddress(addr); +} + +/* + * (*send_message)() transmits HDMI-CEC message to other HDMI device. + * + * The method should be designed to return in a certain amount of time not + * hanging forever, which can happen if CEC signal line is pulled low for + * some reason. HAL implementation should take the situation into account + * so as not to wait forever for the message to get sent out. + * + * It should try retransmission at least once as specified in the standard. + * + * Returns error code. See HDMI_RESULT_SUCCESS, HDMI_RESULT_NACK, and + * HDMI_RESULT_BUSY. + */ +static int cec_send_message(const struct hdmi_cec_device* dev, const cec_message_t* msg) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + return priv->client->sendMessage(msg, false); +} + +/* + * (*register_event_callback)() registers a callback that HDMI-CEC HAL + * can later use for incoming CEC messages or internal HDMI events. + * When calling from C++, use the argument arg to pass the calling object. + * It will be passed back when the callback is invoked so that the context + * can be retrieved. + */ +static void cec_register_event_callback(const struct hdmi_cec_device* dev, + event_callback_t callback, void* arg) +{ + if (!hal_info || hal_info->fd < 0) + return; + D("dev:%p, callback:%p, arg:%p\n", callback, arg, dev); + hal_info->cb = callback; + hal_info->cb_data = arg; +} + +/* + * (*get_version)() returns the CEC version supported by underlying hardware. + */ +static void cec_get_version(const struct hdmi_cec_device* dev, int* version) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + priv->client->getVersion(version); +} + +/* + * (*get_vendor_id)() returns the identifier of the vendor. It is + * the 24-bit unique company ID obtained from the IEEE Registration + * Authority Committee (RAC). + */ +static void cec_get_vendor_id(const struct hdmi_cec_device* dev, uint32_t* vendor_id) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + priv->client->getVendorId(vendor_id); +} + +/* + * (*get_port_info)() returns the hdmi port information of underlying hardware. + * info is the list of HDMI port information, and 'total' is the number of + * HDMI ports in the system. + */ +static void cec_get_port_info(const struct hdmi_cec_device* dev, + struct hdmi_port_info* list[], int* total) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + priv->client->getPortInfos(list, total); +} + +/* + * (*set_option)() passes flags controlling the way HDMI-CEC service works down + * to HAL implementation. Those flags will be used in case the feature needs + * update in HAL itself, firmware or microcontroller. + */ +static void cec_set_option(const struct hdmi_cec_device* dev, int flag, int value) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + priv->client->setOption(flag, value); +} + + +/* + * (*set_audio_return_channel)() configures ARC circuit in the hardware logic + * to start or stop the feature. Flag can be either 1 to start the feature + * or 0 to stop it. + * + * Returns 0 on success or -errno on error. + */ +static void cec_set_audio_return_channel(const struct hdmi_cec_device* dev, int port_id, int flag) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + priv->client->setAudioReturnChannel(port_id, flag); +} + +/* + * (*is_connected)() returns the connection status of the specified port. + * Returns HDMI_CONNECTED if a device is connected, otherwise HDMI_NOT_CONNECTED. + * The HAL should watch for +5V power signal to determine the status. + */ +static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id) +{ + aml_cec_hal_t *priv = (aml_cec_hal_t *)dev; + return priv->client->isConnected(port_id); +} + +/** Close the hdmi cec device */ +static int cec_close(struct hw_device_t *dev) +{ + if (hal_info != NULL) { + return hal_info->client->closeCecDevice(); + } + free(dev); + return -1; +} + +/** + * module methods + */ +static int open_cec( const struct hw_module_t* module, char const *name, + struct hw_device_t **device ) +{ + + if (strcmp(name, HDMI_CEC_HARDWARE_INTERFACE) != 0) { + D("cec strcmp fail !!!"); + return -EINVAL; + } + + if (device == NULL) { + D("NULL cec device on open"); + return -EINVAL; + } + + aml_cec_hal_t *dev = (aml_cec_hal_t*)malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->client = HdmiCecClient::connect(); + dev->client->setEventObserver(new HdmiCecCallback()); + dev->fd = dev->client->openCecDevice(); + + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = (struct hw_module_t*) module; + dev->device.common.close = cec_close; + + dev->device.add_logical_address = cec_add_logical_address; + dev->device.clear_logical_address = cec_clear_logical_address; + dev->device.get_physical_address = cec_get_physical_address; + dev->device.send_message = cec_send_message; + dev->device.register_event_callback = cec_register_event_callback; + dev->device.get_version = cec_get_version; + dev->device.get_vendor_id = cec_get_vendor_id; + dev->device.get_port_info = cec_get_port_info; + dev->device.set_option = cec_set_option; + dev->device.set_audio_return_channel = cec_set_audio_return_channel; + dev->device.is_connected = cec_is_connected; + + *device = &dev->device.common; + + hal_info = dev; + return 0; +} + +static struct hw_module_methods_t hdmi_cec_module_methods = { + .open = open_cec, +}; + +/* + * The hdmi cec Module + */ +struct hdmi_cec_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = HDMI_CEC_MODULE_API_VERSION_1_0, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = HDMI_CEC_HARDWARE_MODULE_ID, + .name = "Amlogic hdmi cec Module", + .author = "Amlogic Corp.", + .methods = &hdmi_cec_module_methods, + }, +}; + |