@@ -1,17 +0,0 @@
|
|||||||
// Copyright (C) 2019 The Android Open-Source Project
|
|
||||||
// Copyright (C) 2021-2022 KonstaKANG
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
cc_library_shared {
|
|
||||||
name: "hdmi_cec.rpi",
|
|
||||||
relative_install_path: "hw",
|
|
||||||
proprietary: true,
|
|
||||||
srcs: ["hdmi_cec.c"],
|
|
||||||
cflags: ["-Werror"],
|
|
||||||
shared_libs: [
|
|
||||||
"liblog",
|
|
||||||
"libcutils",
|
|
||||||
"libhardware",
|
|
||||||
],
|
|
||||||
}
|
|
616
cec/hdmi_cec.c
616
cec/hdmi_cec.c
@@ -1,616 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019 BayLibre, SAS.
|
|
||||||
* Copyright (C) 2021-2022 KonstaKANG
|
|
||||||
*
|
|
||||||
* 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 <log/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;
|
|
||||||
pthread_mutex_t options_lock;
|
|
||||||
bool cec_enabled;
|
|
||||||
bool cec_control_enabled;
|
|
||||||
} 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;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&ctx->options_lock);
|
|
||||||
bool cec_enabled = ctx->cec_enabled;
|
|
||||||
pthread_mutex_unlock(&ctx->options_lock);
|
|
||||||
if (!cec_enabled) {
|
|
||||||
return HDMI_RESULT_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
|
||||||
if (cec_msg.tx_status & CEC_TX_STATUS_NACK)
|
|
||||||
return HDMI_RESULT_NACK;
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (ctx->port_info.physical_address != CEC_PHYS_ADDR_INVALID) {
|
|
||||||
*list = &ctx->port_info;
|
|
||||||
*total = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hdmicec_set_option(const struct hdmi_cec_device *dev, int flag, int value)
|
|
||||||
{
|
|
||||||
struct hdmicec_context* ctx = (struct hdmicec_context*)dev;
|
|
||||||
ALOGD("%s: flag=%d, value=%d", __func__, flag, value);
|
|
||||||
switch (flag) {
|
|
||||||
case HDMI_OPTION_ENABLE_CEC:
|
|
||||||
pthread_mutex_lock(&ctx->options_lock);
|
|
||||||
ctx->cec_enabled = (value == 1 ? true : false);
|
|
||||||
pthread_mutex_unlock(&ctx->options_lock);
|
|
||||||
break;
|
|
||||||
case HDMI_OPTION_WAKEUP:
|
|
||||||
// Not valid for playback devices
|
|
||||||
break;
|
|
||||||
case HDMI_OPTION_SYSTEM_CEC_CONTROL:
|
|
||||||
pthread_mutex_lock(&ctx->options_lock);
|
|
||||||
ctx->cec_control_enabled = (value == 1 ? true : false);
|
|
||||||
pthread_mutex_unlock(&ctx->options_lock);
|
|
||||||
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 int get_opcode(struct cec_msg* message) {
|
|
||||||
return (((uint8_t)message->msg[1]) & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_first_param(struct cec_msg* message) {
|
|
||||||
return (((uint8_t)message->msg[2]) & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_power_ui_command(struct cec_msg* message) {
|
|
||||||
int ui_command = get_first_param(message);
|
|
||||||
switch (ui_command) {
|
|
||||||
case CEC_OP_UI_CMD_POWER:
|
|
||||||
case CEC_OP_UI_CMD_DEVICE_ROOT_MENU:
|
|
||||||
case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_transferable_in_sleep(struct cec_msg* message) {
|
|
||||||
int opcode = get_opcode(message);
|
|
||||||
switch (opcode) {
|
|
||||||
case CEC_MESSAGE_ABORT:
|
|
||||||
case CEC_MESSAGE_DEVICE_VENDOR_ID:
|
|
||||||
case CEC_MESSAGE_GET_CEC_VERSION:
|
|
||||||
case CEC_MESSAGE_GET_MENU_LANGUAGE:
|
|
||||||
case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS:
|
|
||||||
case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID:
|
|
||||||
case CEC_MESSAGE_GIVE_OSD_NAME:
|
|
||||||
case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS:
|
|
||||||
case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS:
|
|
||||||
case CEC_MESSAGE_REPORT_POWER_STATUS:
|
|
||||||
case CEC_MESSAGE_SET_OSD_NAME:
|
|
||||||
case CEC_MESSAGE_DECK_CONTROL:
|
|
||||||
case CEC_MESSAGE_PLAY:
|
|
||||||
return true;
|
|
||||||
case CEC_MESSAGE_USER_CONTROL_PRESSED:
|
|
||||||
return is_power_ui_command(message);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&ctx->options_lock);
|
|
||||||
bool cec_enabled = ctx->cec_enabled;
|
|
||||||
pthread_mutex_unlock(&ctx->options_lock);
|
|
||||||
if (!cec_enabled) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&ctx->options_lock);
|
|
||||||
bool cec_enabled = ctx->cec_enabled;
|
|
||||||
pthread_mutex_unlock(&ctx->options_lock);
|
|
||||||
if (!cec_enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&ctx->options_lock);
|
|
||||||
bool cec_control_enabled = ctx->cec_control_enabled;
|
|
||||||
pthread_mutex_unlock(&ctx->options_lock);
|
|
||||||
if (!cec_control_enabled && !is_transferable_in_sleep(&msg)) {
|
|
||||||
ALOGD("%s: filter message in standby mode\n", __func__);
|
|
||||||
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 - 1;
|
|
||||||
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));
|
|
||||||
if (ctx->thread)
|
|
||||||
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;
|
|
||||||
|
|
||||||
pthread_mutex_init(&ctx->options_lock, NULL);
|
|
||||||
|
|
||||||
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[32];
|
|
||||||
char prop[PROPERTY_VALUE_MAX];
|
|
||||||
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));
|
|
||||||
|
|
||||||
property_get("ro.hdmi.cec_device", prop, "cec0");
|
|
||||||
snprintf(path, sizeof(path), "/dev/%s", prop);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->cec_enabled = true;
|
|
||||||
ctx->cec_control_enabled = true;
|
|
||||||
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 = "Raspberry Pi HDMI CEC HAL",
|
|
||||||
.author = "The Android Open Source Project",
|
|
||||||
.methods = &hdmi_cec_module_methods,
|
|
||||||
};
|
|
@@ -134,15 +134,6 @@ PRODUCT_COPY_FILES += \
|
|||||||
PRODUCT_COPY_FILES += \
|
PRODUCT_COPY_FILES += \
|
||||||
$(DEVICE_PATH)/camera/media_profiles_V1_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml
|
$(DEVICE_PATH)/camera/media_profiles_V1_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml
|
||||||
|
|
||||||
# CEC
|
|
||||||
PRODUCT_PACKAGES += \
|
|
||||||
android.hardware.tv.cec@1.0-impl \
|
|
||||||
android.hardware.tv.cec@1.0-service \
|
|
||||||
hdmi_cec.rpi
|
|
||||||
|
|
||||||
PRODUCT_COPY_FILES += \
|
|
||||||
frameworks/native/data/etc/android.hardware.hdmi.cec.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.hdmi.cec.xml
|
|
||||||
|
|
||||||
# Debugfs
|
# Debugfs
|
||||||
PRODUCT_SET_DEBUGFS_RESTRICTIONS := false
|
PRODUCT_SET_DEBUGFS_RESTRICTIONS := false
|
||||||
|
|
||||||
|
@@ -430,12 +430,6 @@ key 657 MACRO_2
|
|||||||
key 658 MACRO_3
|
key 658 MACRO_3
|
||||||
key 659 MACRO_4
|
key 659 MACRO_4
|
||||||
|
|
||||||
# CEC
|
|
||||||
key 352 ENTER
|
|
||||||
key 618 HOME
|
|
||||||
key 141 POWER
|
|
||||||
key 174 BACK
|
|
||||||
|
|
||||||
# Keys defined by HID usages
|
# Keys defined by HID usages
|
||||||
key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING
|
key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING
|
||||||
key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING
|
key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING
|
||||||
|
@@ -34,13 +34,4 @@
|
|||||||
<instance>legacy/0</instance>
|
<instance>legacy/0</instance>
|
||||||
</interface>
|
</interface>
|
||||||
</hal>
|
</hal>
|
||||||
<hal format="hidl">
|
|
||||||
<name>android.hardware.tv.cec</name>
|
|
||||||
<transport>hwbinder</transport>
|
|
||||||
<version>1.0</version>
|
|
||||||
<interface>
|
|
||||||
<name>IHdmiCec</name>
|
|
||||||
<instance>default</instance>
|
|
||||||
</interface>
|
|
||||||
</hal>
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@@ -117,8 +117,4 @@
|
|||||||
-->
|
-->
|
||||||
<integer name="config_defaultNightMode">2</integer>
|
<integer name="config_defaultNightMode">2</integer>
|
||||||
|
|
||||||
<!-- CEC Configuration -->
|
|
||||||
<bool name="config_cecTvSendStandbyOnSleepEnabled_default">false</bool>
|
|
||||||
<bool name="config_cecTvSendStandbyOnSleepDisabled_default">true</bool>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -55,8 +55,4 @@
|
|||||||
-->
|
-->
|
||||||
<integer name="config_longPressOnPowerBehavior">1</integer>
|
<integer name="config_longPressOnPowerBehavior">1</integer>
|
||||||
|
|
||||||
<!-- CEC Configuration -->
|
|
||||||
<bool name="config_cecTvSendStandbyOnSleepEnabled_default">false</bool>
|
|
||||||
<bool name="config_cecTvSendStandbyOnSleepDisabled_default">true</bool>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -6,10 +6,6 @@
|
|||||||
/dev/v4l-subdev* 0660 system camera
|
/dev/v4l-subdev* 0660 system camera
|
||||||
/dev/video* 0660 system camera
|
/dev/video* 0660 system camera
|
||||||
|
|
||||||
# CEC
|
|
||||||
/dev/cec0 0660 system graphics
|
|
||||||
/dev/cec1 0660 system graphics
|
|
||||||
|
|
||||||
# DMA
|
# DMA
|
||||||
/dev/dma_heap/linux,cma 0666 system graphics
|
/dev/dma_heap/linux,cma 0666 system graphics
|
||||||
|
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
type cec_device, dev_type;
|
|
@@ -2,10 +2,6 @@
|
|||||||
/sys/class/rfkill/rfkill[0-9]/state u:object_r:sysfs_bluetooth_writable:s0
|
/sys/class/rfkill/rfkill[0-9]/state u:object_r:sysfs_bluetooth_writable:s0
|
||||||
/vendor/bin/hw/android\.hardware\.bluetooth-service\.rpi u:object_r:hal_bluetooth_default_exec:s0
|
/vendor/bin/hw/android\.hardware\.bluetooth-service\.rpi u:object_r:hal_bluetooth_default_exec:s0
|
||||||
|
|
||||||
# CEC
|
|
||||||
/dev/cec0 u:object_r:cec_device:s0
|
|
||||||
/dev/cec1 u:object_r:cec_device:s0
|
|
||||||
|
|
||||||
# DRM
|
# DRM
|
||||||
/vendor/bin/hw/android\.hardware\.drm-service\.clearkey u:object_r:hal_drm_clearkey_exec:s0
|
/vendor/bin/hw/android\.hardware\.drm-service\.clearkey u:object_r:hal_drm_clearkey_exec:s0
|
||||||
/vendor/bin/hw/android\.hardware\.drm-service\.widevine(.*)? u:object_r:hal_drm_widevine_exec:s0
|
/vendor/bin/hw/android\.hardware\.drm-service\.widevine(.*)? u:object_r:hal_drm_widevine_exec:s0
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
allow hal_tv_cec_default cec_device:chr_file rw_file_perms;
|
|
@@ -36,10 +36,6 @@ persist.bluetooth.a2dp_aac.vbr_supported=true
|
|||||||
media.settings.xml=/vendor/etc/media_profiles_V1_0.xml
|
media.settings.xml=/vendor/etc/media_profiles_V1_0.xml
|
||||||
ro.hardware.camera=libcamera
|
ro.hardware.camera=libcamera
|
||||||
|
|
||||||
# CEC
|
|
||||||
ro.hdmi.cec_device=cec0
|
|
||||||
ro.hdmi.device_type=4
|
|
||||||
|
|
||||||
# Display
|
# Display
|
||||||
debug.drm.mode.force=1920x1080
|
debug.drm.mode.force=1920x1080
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user