1/* 2* Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved. 3* 4* Redistribution and use in source and binary forms, with or without 5* modification, are permitted provided that the following conditions are 6* met: 7* * Redistributions of source code must retain the above copyright 8* notice, this list of conditions and the following disclaimer. 9* * Redistributions in binary form must reproduce the above 10* copyright notice, this list of conditions and the following 11* disclaimer in the documentation and/or other materials provided 12* with the distribution. 13* * Neither the name of The Linux Foundation. nor the names of its 14* contributors may be used to endorse or promote products derived 15* from this software without specific prior written permission. 16* 17* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 18* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 20* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 21* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*/ 29 30#define DEBUG 0 31#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL) 32#include <log/log.h> 33#include <errno.h> 34#include <hardware/hdmi_cec.h> 35#include <utils/Trace.h> 36#include <utils/debug.h> 37#include <utils/sys.h> 38#include <vector> 39#include "qhdmi_cec.h" 40 41namespace qhdmicec { 42 43const int NUM_HDMI_PORTS = 1; 44const int MAX_SYSFS_DATA = 128; 45const int MAX_CEC_FRAME_SIZE = 20; 46const int MAX_SEND_MESSAGE_RETRIES = 1; 47 48const char* SYSFS_BASE = "/sys/devices/virtual/graphics/fb"; 49const char* UEVENT_SWITCH_HDMI = "change@/devices/virtual/switch/hdmi"; 50const char* FB_PATH = "/sys/devices/virtual/graphics/fb"; 51 52enum { 53 LOGICAL_ADDRESS_SET = 1, 54 LOGICAL_ADDRESS_UNSET = -1, 55}; 56 57// Offsets of members of struct hdmi_cec_msg 58// drivers/video/msm/mdss/mdss_hdmi_cec.c 59// XXX: Get this from a driver header 60enum { 61 CEC_OFFSET_SENDER_ID, 62 CEC_OFFSET_RECEIVER_ID, 63 CEC_OFFSET_OPCODE, 64 CEC_OFFSET_OPERAND, 65 CEC_OFFSET_FRAME_LENGTH = 17, 66 CEC_OFFSET_RETRANSMIT, 67}; 68 69//Forward declarations 70static void cec_close_context(cec_context_t* ctx __unused); 71static int cec_enable(cec_context_t *ctx, int enable); 72static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id); 73static void cec_monitor_deinit(cec_context_t* ctx); 74static void handle_cec_msg_event(cec_context_t* ctx, uint32_t node_event); 75 76void event_monitor(cec_context_t* ctx); // hdmi event monitor function 77static int get_event_value(const char *uevent_data, int length, const char *event_info); 78static int uevent_init(int *uevent_fd); 79static void handle_hdmihotplug_event(cec_context_t* ctx, uint32_t node_event); 80 81static int populate_event_data(cec_context_t* ctx, std::vector<eventData> *event_data_list); 82static int set_event_params(cec_context_t* ctx, uint32_t node_event, eventData *event_data); 83static void handle_exit_event(cec_context_t* ctx, uint32_t node_event); 84 85static ssize_t read_node(const char *path, char *data) 86{ 87 ssize_t err = 0; 88 FILE *fp = NULL; 89 err = access(path, R_OK); 90 if (!err) { 91 fp = fopen(path, "r"); 92 if (fp) { 93 err = fread(data, sizeof(char), MAX_SYSFS_DATA ,fp); 94 fclose(fp); 95 } 96 } 97 return err; 98} 99 100static ssize_t write_node(const char *path, const char *data, size_t len) 101{ 102 ssize_t err = 0; 103 int fd = -1; 104 err = access(path, W_OK); 105 if (!err) { 106 fd = open(path, O_WRONLY); 107 errno = 0; 108 err = write(fd, data, len); 109 if (err < 0) { 110 err = -errno; 111 } 112 close(fd); 113 } else { 114 ALOGE("%s: Failed to access path: %s error: %s", 115 __FUNCTION__, path, strerror(errno)); 116 err = -errno; 117 } 118 return err; 119} 120 121// Helper function to write integer values to the full sysfs path 122static ssize_t write_int_to_node(cec_context_t *ctx, 123 const char *path_postfix, 124 const int value) 125{ 126 std::string sysfs_full_path; 127 char sysfs_data[MAX_SYSFS_DATA]; 128 snprintf(sysfs_data, sizeof(sysfs_data), "%d",value); 129 sysfs_full_path = ctx->fb_sysfs_path + "/"; 130 sysfs_full_path.append(path_postfix); 131 ssize_t err = write_node(sysfs_full_path.c_str(), sysfs_data, strlen(sysfs_data)); 132 return err; 133} 134 135static void hex_to_string(const char *msg, ssize_t len, char *str) 136{ 137 //Functions assumes sufficient memory in str 138 char *ptr = str; 139 for(int i=0; i < len ; i++) { 140 ptr += snprintf(ptr, 3, "%02X", msg[i]); 141 // Overwrite null termination of snprintf in all except the last byte 142 if (i < len - 1) 143 *ptr = ':'; 144 ptr++; 145 } 146} 147 148static ssize_t cec_get_fb_node_number(cec_context_t *ctx) 149{ 150 //XXX: Do this from a common utility library across the display HALs 151 const int MAX_FB_DEVICES = 2; 152 ssize_t len = 0; 153 std::string fb_type_path; 154 char fb_type[MAX_SYSFS_DATA]; 155 const char *dtv_panel_str = "dtv panel"; 156 157 for(int num = 0; num < MAX_FB_DEVICES; num++) { 158 fb_type_path = SYSFS_BASE + std::to_string(ctx->fb_num) + "/msm_fb_type"; 159 len = read_node(fb_type_path.c_str(), fb_type); 160 ALOGD_IF(DEBUG, "%s: fb_type:%s", __FUNCTION__, fb_type); 161 if(len > 0 && (strncmp(fb_type, dtv_panel_str, strlen(dtv_panel_str)) == 0)){ 162 ALOGD_IF(DEBUG, "%s: Found DTV panel at fb%d", __FUNCTION__, num); 163 ctx->fb_num = num; 164 ctx->fb_sysfs_path = SYSFS_BASE + std::to_string(ctx->fb_num); 165 break; 166 } 167 } 168 if (len < 0) 169 return len; 170 else 171 return 0; 172} 173 174static int cec_add_logical_address(const struct hdmi_cec_device* dev, 175 cec_logical_address_t addr) 176{ 177 if (addr < CEC_ADDR_TV || addr > CEC_ADDR_BROADCAST) { 178 ALOGE("%s: Received invalid address: %d ", __FUNCTION__, addr); 179 return -EINVAL; 180 } 181 cec_context_t* ctx = (cec_context_t*)(dev); 182 ctx->logical_address[addr] = LOGICAL_ADDRESS_SET; 183 184 //XXX: We can get multiple logical addresses here but we can only send one 185 //to the driver. Store locally for now 186 ssize_t err = write_int_to_node(ctx, "cec/logical_addr", addr); 187 ALOGI("%s: Allocated logical address: %d ", __FUNCTION__, addr); 188 return (int) err; 189} 190 191static void cec_clear_logical_address(const struct hdmi_cec_device* dev) 192{ 193 cec_context_t* ctx = (cec_context_t*)(dev); 194 memset(ctx->logical_address, LOGICAL_ADDRESS_UNSET, 195 sizeof(ctx->logical_address)); 196 //XXX: Find logical_addr that needs to be reset 197 write_int_to_node(ctx, "cec/logical_addr", 15); 198 ALOGD_IF(DEBUG, "%s: Cleared logical addresses", __FUNCTION__); 199} 200 201static int cec_get_physical_address(const struct hdmi_cec_device* dev, 202 uint16_t* addr) 203{ 204 cec_context_t* ctx = (cec_context_t*)(dev); 205 std::string pa_path; 206 char pa_data[MAX_SYSFS_DATA]; 207 pa_path = ctx->fb_sysfs_path; 208 pa_path.append("/pa"); 209 int err = (int) read_node(pa_path.c_str(), pa_data); 210 *addr = (uint16_t) atoi(pa_data); 211 ALOGD_IF(DEBUG, "%s: Physical Address: 0x%x", __FUNCTION__, *addr); 212 if (err < 0) 213 return err; 214 else 215 return 0; 216} 217 218static int cec_send_message(const struct hdmi_cec_device* dev, 219 const cec_message_t* msg) 220{ 221 ATRACE_CALL(); 222 if(cec_is_connected(dev, 0) <= 0) 223 return HDMI_RESULT_FAIL; 224 225 cec_context_t* ctx = (cec_context_t*)(dev); 226 ALOGD_IF(DEBUG, "%s: initiator: %d destination: %d length: %u", 227 __FUNCTION__, msg->initiator, msg->destination, 228 (uint32_t) msg->length); 229 230 // Dump message received from framework 231 char dump[128]; 232 if(msg->length > 0) { 233 hex_to_string((char*)msg->body, msg->length, dump); 234 ALOGD_IF(DEBUG, "%s: message from framework: %s", __FUNCTION__, dump); 235 } 236 237 std::string write_msg_path; 238 char write_msg[MAX_CEC_FRAME_SIZE]; 239 memset(write_msg, 0, sizeof(write_msg)); 240 // See definition of struct hdmi_cec_msg in driver code 241 // drivers/video/msm/mdss/mdss_hdmi_cec.c 242 // Write header block 243 // XXX: Include this from header in kernel 244 write_msg[CEC_OFFSET_SENDER_ID] = msg->initiator; 245 write_msg[CEC_OFFSET_RECEIVER_ID] = msg->destination; 246 //Kernel splits opcode/operand, but Android sends it in one byte array 247 write_msg[CEC_OFFSET_OPCODE] = msg->body[0]; 248 if(msg->length > 1) { 249 memcpy(&write_msg[CEC_OFFSET_OPERAND], &msg->body[1], 250 sizeof(char)*(msg->length - 1)); 251 } 252 //msg length + initiator + destination 253 write_msg[CEC_OFFSET_FRAME_LENGTH] = (unsigned char) (msg->length + 1); 254 hex_to_string(write_msg, sizeof(write_msg), dump); 255 write_msg_path = ctx->fb_sysfs_path; 256 write_msg_path.append("/cec/wr_msg"); 257 int retry_count = 0; 258 ssize_t err = 0; 259 //HAL spec requires us to retry at least once. 260 while (true) { 261 err = write_node(write_msg_path.c_str(), write_msg, sizeof(write_msg)); 262 retry_count++; 263 if (err == -EAGAIN && retry_count <= MAX_SEND_MESSAGE_RETRIES) { 264 ALOGE("%s: CEC line busy, retrying", __FUNCTION__); 265 } else { 266 break; 267 } 268 } 269 270 if (err < 0) { 271 if (err == -ENXIO) { 272 ALOGI("%s: No device exists with the destination address", 273 __FUNCTION__); 274 return HDMI_RESULT_NACK; 275 } else if (err == -EAGAIN) { 276 ALOGE("%s: CEC line is busy, max retry count exceeded", 277 __FUNCTION__); 278 return HDMI_RESULT_BUSY; 279 } else { 280 return HDMI_RESULT_FAIL; 281 ALOGE("%s: Failed to send CEC message err: %zd - %s", 282 __FUNCTION__, err, strerror(int(-err))); 283 } 284 } else { 285 ALOGD_IF(DEBUG, "%s: Sent CEC message - %zd bytes written", 286 __FUNCTION__, err); 287 return HDMI_RESULT_SUCCESS; 288 } 289} 290 291void cec_receive_message(cec_context_t *ctx, char *msg, ssize_t len) 292{ 293 if(!ctx->system_control) 294 return; 295 296 char dump[128]; 297 if(len > 0) { 298 hex_to_string(msg, len, dump); 299 ALOGD_IF(DEBUG, "%s: Message from driver: %s", __FUNCTION__, dump); 300 } 301 302 hdmi_event_t event; 303 event.type = HDMI_EVENT_CEC_MESSAGE; 304 event.dev = (hdmi_cec_device *) ctx; 305 // Remove initiator/destination from this calculation 306 event.cec.length = msg[CEC_OFFSET_FRAME_LENGTH] - 1; 307 event.cec.initiator = (cec_logical_address_t) msg[CEC_OFFSET_SENDER_ID]; 308 event.cec.destination = (cec_logical_address_t) msg[CEC_OFFSET_RECEIVER_ID]; 309 //Copy opcode and operand 310 size_t copy_size = event.cec.length > sizeof(event.cec.body) ? 311 sizeof(event.cec.body) : event.cec.length; 312 memcpy(event.cec.body, &msg[CEC_OFFSET_OPCODE],copy_size); 313 hex_to_string((char *) event.cec.body, copy_size, dump); 314 ALOGD_IF(DEBUG, "%s: Message to framework: %s", __FUNCTION__, dump); 315 ctx->callback.callback_func(&event, ctx->callback.callback_arg); 316} 317 318void cec_hdmi_hotplug(cec_context_t *ctx, int connected) 319{ 320 //Ignore unplug events when system control is disabled 321 if(!ctx->system_control && connected == 0) 322 return; 323 hdmi_event_t event; 324 event.type = HDMI_EVENT_HOT_PLUG; 325 event.dev = (hdmi_cec_device *) ctx; 326 event.hotplug.connected = connected ? HDMI_CONNECTED : HDMI_NOT_CONNECTED; 327 ctx->callback.callback_func(&event, ctx->callback.callback_arg); 328} 329 330static void cec_register_event_callback(const struct hdmi_cec_device* dev, 331 event_callback_t callback, void* arg) 332{ 333 ALOGD_IF(DEBUG, "%s: Registering callback", __FUNCTION__); 334 cec_context_t* ctx = (cec_context_t*)(dev); 335 ctx->callback.callback_func = callback; 336 ctx->callback.callback_arg = arg; 337} 338 339static void cec_get_version(const struct hdmi_cec_device* dev, int* version) 340{ 341 cec_context_t* ctx = (cec_context_t*)(dev); 342 *version = ctx->version; 343 ALOGD_IF(DEBUG, "%s: version: %d", __FUNCTION__, *version); 344} 345 346static void cec_get_vendor_id(const struct hdmi_cec_device* dev, 347 uint32_t* vendor_id) 348{ 349 cec_context_t* ctx = (cec_context_t*)(dev); 350 *vendor_id = ctx->vendor_id; 351 ALOGD_IF(DEBUG, "%s: vendor id: %u", __FUNCTION__, *vendor_id); 352} 353 354static void cec_get_port_info(const struct hdmi_cec_device* dev, 355 struct hdmi_port_info* list[], int* total) 356{ 357 ALOGD_IF(DEBUG, "%s: Get port info", __FUNCTION__); 358 cec_context_t* ctx = (cec_context_t*)(dev); 359 *total = NUM_HDMI_PORTS; 360 *list = ctx->port_info; 361} 362 363static void cec_set_option(const struct hdmi_cec_device* dev, int flag, 364 int value) 365{ 366 cec_context_t* ctx = (cec_context_t*)(dev); 367 switch (flag) { 368 case HDMI_OPTION_WAKEUP: 369 ALOGD_IF(DEBUG, "%s: Wakeup: value: %d", __FUNCTION__, value); 370 //XXX 371 break; 372 case HDMI_OPTION_ENABLE_CEC: 373 ALOGD_IF(DEBUG, "%s: Enable CEC: value: %d", __FUNCTION__, value); 374 cec_enable(ctx, value? 1 : 0); 375 break; 376 case HDMI_OPTION_SYSTEM_CEC_CONTROL: 377 ALOGD_IF(DEBUG, "%s: system_control: value: %d", 378 __FUNCTION__, value); 379 ctx->system_control = !!value; 380 break; 381 } 382} 383 384static void cec_set_audio_return_channel(const struct hdmi_cec_device* dev, 385 int port, int flag) 386{ 387 cec_context_t* ctx = (cec_context_t*)(dev); 388 ctx->arc_enabled = flag ? true : false; 389 ALOGD_IF(DEBUG, "%s: ARC flag: %d port: %d", __FUNCTION__, flag, port); 390} 391 392static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id) 393{ 394 // Ignore port_id since we have only one port 395 int connected = 0; 396 cec_context_t* ctx = (cec_context_t*)(dev); 397 std::string connected_path; 398 char connected_data[MAX_SYSFS_DATA]; 399 connected_path = ctx->fb_sysfs_path; 400 connected_path.append("/connected"); 401 ssize_t err = read_node(connected_path.c_str(), connected_data); 402 connected = atoi(connected_data); 403 404 ALOGD_IF(DEBUG, "%s: HDMI at port %d is - %s", __FUNCTION__, port_id, 405 connected ? "connected":"disconnected"); 406 if (err < 0) 407 return (int) err; 408 else 409 return connected; 410} 411 412static int cec_device_close(struct hw_device_t *dev) 413{ 414 ALOGD_IF(DEBUG, "%s: Close CEC HAL ", __FUNCTION__); 415 if (!dev) { 416 ALOGE("%s: NULL device pointer", __FUNCTION__); 417 return -EINVAL; 418 } 419 cec_context_t* ctx = (cec_context_t*)(dev); 420 cec_close_context(ctx); 421 free(dev); 422 return 0; 423} 424 425static int cec_enable(cec_context_t *ctx, int enable) 426{ 427 ssize_t err; 428 // Enable CEC 429 int value = enable ? 0x3 : 0x0; 430 err = write_int_to_node(ctx, "cec/enable", value); 431 if(err < 0) { 432 ALOGE("%s: Failed to toggle CEC: enable: %d", 433 __FUNCTION__, enable); 434 return (int) err; 435 } 436 ctx->enabled = enable; 437 return 0; 438} 439 440static void cec_init_context(cec_context_t *ctx) 441{ 442 ALOGD_IF(DEBUG, "%s: Initializing context", __FUNCTION__); 443 int err = -EINVAL; 444 cec_get_fb_node_number(ctx); 445 446 //Initialize ports - We support only one output port 447 ctx->port_info = new hdmi_port_info[NUM_HDMI_PORTS]; 448 ctx->port_info[0].type = HDMI_OUTPUT; 449 ctx->port_info[0].port_id = 1; 450 ctx->port_info[0].cec_supported = 1; 451 //XXX: Enable ARC if supported 452 ctx->port_info[0].arc_supported = 0; 453 cec_get_physical_address((hdmi_cec_device *) ctx, 454 &ctx->port_info[0].physical_address ); 455 456 ctx->version = 0x4; 457 ctx->vendor_id = 0xA47733; 458 cec_clear_logical_address((hdmi_cec_device_t*)ctx); 459 460 //Enable CEC - framework expects it to be enabled by default 461 cec_enable(ctx, true); 462 463 ALOGD("%s: CEC enabled", __FUNCTION__); 464 465 ctx->node_list.push_back("cec_msg_event"); 466 ctx->node_list.push_back("hotplug_event"); 467 ctx->node_list.push_back("exit_event"); 468 469 err = populate_event_data(ctx, &ctx->event_data_list); 470 if (err < 0) { 471 ALOGE("Failed to populate poll parameters for monitoring HDMI CEC events. Exiting."); 472 cec_enable(ctx, false); 473 return; 474 } 475 476 ctx->hdmi_cec_monitor = std::thread(event_monitor, ctx); 477 478} 479 480static void cec_close_context(cec_context_t* ctx __unused) 481{ 482 ALOGD("%s: Closing context", __FUNCTION__); 483 484 uint64_t exit_value = 1; 485 long int write_size = write(ctx->exit_fd, &exit_value, sizeof(uint64_t)); 486 487 if (write_size != sizeof(uint64_t)) { 488 ALOGE("Error triggering exit_fd (%d). write size = %ld, error = %s", 489 ctx->exit_fd, write_size, strerror(errno)); 490 return; 491 } 492 493 if (ctx->hdmi_cec_monitor.joinable()) { 494 ctx->hdmi_cec_monitor.join(); 495 } 496} 497 498static int cec_device_open(const struct hw_module_t* module, 499 const char* name, 500 struct hw_device_t** device) 501{ 502 ALOGD_IF(DEBUG, "%s: name: %s", __FUNCTION__, name); 503 int status = -EINVAL; 504 if (!strcmp(name, HDMI_CEC_HARDWARE_INTERFACE )) { 505 struct cec_context_t *dev; 506 dev = (cec_context_t *) calloc (1, sizeof(*dev)); 507 if (dev) { 508 cec_init_context(dev); 509 //Setup CEC methods 510 dev->device.common.tag = HARDWARE_DEVICE_TAG; 511 dev->device.common.version = HDMI_CEC_DEVICE_API_VERSION_1_0; 512 dev->device.common.module = const_cast<hw_module_t* >(module); 513 dev->device.common.close = cec_device_close; 514 dev->device.add_logical_address = cec_add_logical_address; 515 dev->device.clear_logical_address = cec_clear_logical_address; 516 dev->device.get_physical_address = cec_get_physical_address; 517 dev->device.send_message = cec_send_message; 518 dev->device.register_event_callback = cec_register_event_callback; 519 dev->device.get_version = cec_get_version; 520 dev->device.get_vendor_id = cec_get_vendor_id; 521 dev->device.get_port_info = cec_get_port_info; 522 dev->device.set_option = cec_set_option; 523 dev->device.set_audio_return_channel = cec_set_audio_return_channel; 524 dev->device.is_connected = cec_is_connected; 525 526 *device = &dev->device.common; 527 status = 0; 528 } else { 529 status = -EINVAL; 530 } 531 } 532 return status; 533} 534 535void event_monitor(cec_context_t* ctx) { 536 ALOGD("%s IN", __FUNCTION__); 537 int err = -EINVAL; 538 539 prctl(PR_SET_NAME, "cec_monitor", 0, 0, 0); 540 setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); 541 542 while (!ctx->cec_exit_thread) { 543 err = poll(ctx->poll_fds.data(), (nfds_t)ctx->event_data_list.size(), -1); 544 if ( err <= 0 ) { 545 ALOGI("Failed to poll, Error %s", strerror(errno)); 546 continue; 547 } 548 549 for (uint32_t event = 0; event < ctx->event_data_list.size(); event++) { 550 pollfd &poll_fd = ctx->poll_fds[event]; 551 552 if (poll_fd.revents & POLLIN || poll_fd.revents & POLLPRI) { 553 ctx->event_data_list[event].event_parser(ctx, event); 554 } 555 } 556 } 557 558 cec_monitor_deinit(ctx); 559 ALOGD("%s OUT", __FUNCTION__); 560 return; 561} 562 563static int populate_event_data(cec_context_t* ctx, std::vector<eventData> *event_data_list) { 564 int err = -EINVAL; 565 ctx->poll_fds.resize(ctx->node_list.size()); 566 567 for (uint32_t event = 0; event < ctx->node_list.size(); event++) { 568 const char *event_name = ctx->node_list.at(event).c_str(); 569 eventData event_data; 570 event_data.event_name = event_name; 571 err = set_event_params(ctx, event, &event_data); 572 if (err < 0) { 573 ALOGE("Failed to set poll event parameters"); 574 return err; 575 } 576 577 event_data_list->push_back(event_data); 578 } 579 580 return 0; 581} 582 583static int set_event_params(cec_context_t* ctx, uint32_t node_event, eventData *event_data) { 584 pollfd poll_fd; 585 poll_fd.fd = -EINVAL; 586 587 if (!strncmp(event_data->event_name, "cec_msg_event", strlen("cec_msg_event"))) { 588 char node_path[MAX_STRING_LENGTH] = {0}; 589 590 snprintf(node_path, sizeof(node_path), "%s%d/%s", FB_PATH, ctx->fb_num, "cec/rd_msg"); 591 poll_fd.fd = open(node_path, O_RDONLY); 592 if (poll_fd.fd < 0) { 593 ALOGE("Node open failed for display %d event %s error %s", 594 ctx->fb_num, "cec/rd_msg", strerror(errno)); 595 return poll_fd.fd; 596 } 597 598 poll_fd.events |= POLLPRI | POLLERR; 599 // Read once on fd to clear the data 600 pread(poll_fd.fd, ctx->data, MAX_STRING_LENGTH, 0); 601 event_data->event_parser = &handle_cec_msg_event; 602 } else if (!strncmp(event_data->event_name, "hotplug_event", strlen("hotplug_event"))) { 603 if (!uevent_init(&poll_fd.fd)) { 604 ALOGE("Failed to register uevent for hotplug detection"); 605 return -1; 606 } 607 608 poll_fd.events |= POLLIN | POLLERR; 609 event_data->event_parser = &handle_hdmihotplug_event; 610 } else if (!strncmp(event_data->event_name, "exit_event", strlen("exit_event"))) { 611 poll_fd.fd = eventfd(0, 0); 612 poll_fd.events |= POLLIN; 613 event_data->event_parser = &handle_exit_event; 614 ctx->exit_fd = poll_fd.fd; 615 } 616 617 ctx->poll_fds[node_event] = poll_fd; 618 return 0; 619} 620 621static void handle_cec_msg_event(cec_context_t* ctx, uint32_t node_event) { 622 if ((ctx->poll_fds[node_event].revents & POLLPRI) && 623 (pread(ctx->poll_fds[node_event].fd, ctx->data, MAX_STRING_LENGTH, 0) > 0)) { 624 ALOGD_IF(DEBUG, "Handling CEC message %s", __FUNCTION__); 625 cec_receive_message(ctx, ctx->data, 0); 626 } 627 628 return; 629} 630 631static void handle_hdmihotplug_event(cec_context_t* ctx, uint32_t node_event) { 632 char uevent_data[PAGE_SIZE]; 633 int count = 0; 634 635 if (ctx->poll_fds[node_event].revents & POLLIN) { 636 count = static_cast<int> (recv(ctx->poll_fds[node_event].fd, uevent_data, 637 (INT32(sizeof(uevent_data))) - 2, 0)); 638 639 if ((count > 0) && (strcasestr(UEVENT_SWITCH_HDMI, uevent_data))) { 640 int connected = get_event_value(uevent_data, count, "SWITCH_STATE="); 641 ALOGD("HDMI CEC is %s", connected ? "connected" : "disconnected"); 642 cec_hdmi_hotplug(ctx, connected); 643 } 644 } 645 646 return; 647} 648 649static void handle_exit_event(cec_context_t* ctx, uint32_t node_event) { 650 ALOGD_IF(DEBUG, "Enter %s", __FUNCTION__); 651 652 if (ctx->poll_fds[node_event].revents & POLLIN) { 653 ctx->cec_exit_thread = true; 654 } 655 656 return; 657} 658 659static void cec_monitor_deinit(cec_context_t* ctx) { 660 for (uint32_t event = 0; event < ctx->poll_fds.size(); event++) { 661 close(ctx->poll_fds[event].fd); 662 ctx->poll_fds[event].fd = -1; 663 } 664} 665 666static int get_event_value(const char *uevent_data, int length, const char *event_info) { 667 const char *iterator_str = uevent_data; 668 while (((iterator_str - uevent_data) <= length) && (*iterator_str)) { 669 const char *pstr = strstr(iterator_str, event_info); 670 if (pstr != NULL) { 671 return (atoi(iterator_str + strlen(event_info))); 672 } 673 iterator_str += strlen(iterator_str) + 1; 674 } 675 return -1; 676} 677 678/* Returns 0 on failure, 1 on success */ 679static int uevent_init(int *uevent_fd) { 680 struct sockaddr_nl addr; 681 int sz = 64*1024; 682 int s; 683 684 memset(&addr, 0, sizeof(addr)); 685 addr.nl_family = AF_NETLINK; 686 addr.nl_pid = getpid(); 687 addr.nl_groups = 0xffffffff; 688 689 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 690 if (s < 0) 691 return 0; 692 693 setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); 694 695 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 696 close(s); 697 return 0; 698 } 699 700 *uevent_fd = s; 701 return (*uevent_fd > 0); 702} 703 704}; //namespace qhdmicec 705 706// Standard HAL module, should be outside qhdmicec namespace 707static struct hw_module_methods_t cec_module_methods = { 708 .open = qhdmicec::cec_device_open 709}; 710 711hdmi_module_t HAL_MODULE_INFO_SYM = { 712 .common = { 713 .tag = HARDWARE_MODULE_TAG, 714 .version_major = 1, 715 .version_minor = 0, 716 .id = HDMI_CEC_HARDWARE_MODULE_ID, 717 .name = "QTI HDMI CEC module", 718 .author = "The Linux Foundation", 719 .methods = &cec_module_methods, 720 } 721}; 722