1 /*
2  * Copyright (C) 2019 BayLibre, SAS.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "hdmi_cec"
18 
19 #include <stdint.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <pthread.h>
26 
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 
30 #include <poll.h>
31 #include <sys/socket.h>
32 #include <linux/netlink.h>
33 #include <linux/cec.h>
34 #include <sys/eventfd.h>
35 
36 #include <log/log.h>
37 #include <cutils/properties.h>
38 #include <hardware/hdmi_cec.h>
39 
40 typedef struct hdmicec_context
41 {
42     hdmi_cec_device_t device; /* must be first */
43     int cec_fd;
44     unsigned int vendor_id;
45     unsigned int type;
46     unsigned int version;
47     struct hdmi_port_info port_info;
48     event_callback_t p_event_cb;
49     void *cb_arg;
50     pthread_t thread;
51     int exit_fd;
52     pthread_mutex_t options_lock;
53     bool cec_enabled;
54     bool cec_control_enabled;
55 } hdmicec_context_t;
56 
hdmicec_add_logical_address(const struct hdmi_cec_device * dev,cec_logical_address_t addr)57 static int hdmicec_add_logical_address(const struct hdmi_cec_device *dev, cec_logical_address_t addr)
58 {
59     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
60     unsigned int la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED;
61     unsigned int all_dev_types = 0;
62     unsigned int prim_type = 0xff;
63     struct cec_log_addrs laddrs;
64     int ret;
65 
66     ALOGD("%s: addr:%x\n", __func__, addr);
67 
68     if (addr >= CEC_ADDR_BROADCAST)
69         return -1;
70 
71     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_LOG_ADDRS, &laddrs);
72     if (ret)
73         return ret;
74     memset(&laddrs, 0, sizeof(laddrs));
75 
76     laddrs.cec_version = ctx->version;
77     laddrs.vendor_id = ctx->vendor_id;
78 
79     switch (addr) {
80         case CEC_LOG_ADDR_TV:
81             prim_type = CEC_OP_PRIM_DEVTYPE_TV;
82             la_type = CEC_LOG_ADDR_TYPE_TV;
83             all_dev_types = CEC_OP_ALL_DEVTYPE_TV;
84             break;
85         case CEC_LOG_ADDR_RECORD_1:
86         case CEC_LOG_ADDR_RECORD_2:
87         case CEC_LOG_ADDR_RECORD_3:
88             prim_type = CEC_OP_PRIM_DEVTYPE_RECORD;
89             la_type = CEC_LOG_ADDR_TYPE_RECORD;
90             all_dev_types = CEC_OP_ALL_DEVTYPE_RECORD;
91             break;
92         case CEC_LOG_ADDR_TUNER_1:
93         case CEC_LOG_ADDR_TUNER_2:
94         case CEC_LOG_ADDR_TUNER_3:
95         case CEC_LOG_ADDR_TUNER_4:
96             prim_type = CEC_OP_PRIM_DEVTYPE_TUNER;
97             la_type = CEC_LOG_ADDR_TYPE_TUNER;
98             all_dev_types = CEC_OP_ALL_DEVTYPE_TUNER;
99             break;
100         case CEC_LOG_ADDR_PLAYBACK_1:
101         case CEC_LOG_ADDR_PLAYBACK_2:
102         case CEC_LOG_ADDR_PLAYBACK_3:
103             prim_type = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
104             la_type = CEC_LOG_ADDR_TYPE_PLAYBACK;
105             all_dev_types = CEC_OP_ALL_DEVTYPE_PLAYBACK;
106             laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
107             break;
108         case CEC_LOG_ADDR_AUDIOSYSTEM:
109             prim_type = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
110             la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
111             all_dev_types = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
112             break;
113         case CEC_LOG_ADDR_SPECIFIC:
114             prim_type = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
115             la_type = CEC_LOG_ADDR_TYPE_SPECIFIC;
116             all_dev_types = CEC_OP_ALL_DEVTYPE_SWITCH;
117             break;
118         case CEC_ADDR_RESERVED_1:
119         case CEC_ADDR_RESERVED_2:
120         case CEC_ADDR_UNREGISTERED:
121             laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
122             break;
123     }
124 
125     laddrs.num_log_addrs = 1;
126     laddrs.log_addr[0] = addr;
127     laddrs.log_addr_type[0] = la_type;
128     laddrs.primary_device_type[0] = prim_type;
129     laddrs.all_device_types[0] = all_dev_types;
130     laddrs.features[0][0] = 0;
131     laddrs.features[0][1] = 0;
132 
133     ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
134     if (ret) {
135         ALOGD("%s: %m\n", __func__);
136         return ret;
137     }
138 
139     ALOGD("%s: log_addr_mask=%x\n", __func__,  laddrs.log_addr_mask);
140 
141     return 0;
142 }
143 
hdmicec_clear_logical_address(const struct hdmi_cec_device * dev)144 static void hdmicec_clear_logical_address(const struct hdmi_cec_device *dev)
145 {
146     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
147     struct cec_log_addrs laddrs;
148     int ret;
149 
150     memset(&laddrs, 0, sizeof(laddrs));
151     ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
152     if (ret)
153         ALOGD("%s: %m\n", __func__);
154 }
155 
hdmicec_get_physical_address(const struct hdmi_cec_device * dev,uint16_t * addr)156 static int hdmicec_get_physical_address(const struct hdmi_cec_device *dev, uint16_t *addr)
157 {
158     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
159     int ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, addr);
160     if (ret)
161         ALOGD("%s: %m\n", __func__);
162 
163     return ret;
164 }
165 
hdmicec_send_message(const struct hdmi_cec_device * dev,const cec_message_t * msg)166 static int hdmicec_send_message(const struct hdmi_cec_device *dev, const cec_message_t *msg)
167 {
168     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
169     struct cec_msg cec_msg;
170     int ret;
171 
172     pthread_mutex_lock(&ctx->options_lock);
173     bool cec_enabled = ctx->cec_enabled;
174     pthread_mutex_unlock(&ctx->options_lock);
175     if (!cec_enabled) {
176         return HDMI_RESULT_FAIL;
177     }
178 
179     ALOGD("%s: len=%u\n", __func__, (unsigned int)msg->length);
180 
181     memset(&cec_msg, 0, sizeof(cec_msg));
182     cec_msg.msg[0] = (msg->initiator << 4) | msg->destination;
183 
184     memcpy(&cec_msg.msg[1], msg->body, msg->length);
185     cec_msg.len = msg->length + 1;
186 
187     ret = ioctl(ctx->cec_fd, CEC_TRANSMIT, &cec_msg);
188     if (ret) {
189         ALOGD("%s: %m\n", __func__);
190         return HDMI_RESULT_FAIL;
191     }
192 
193     if (cec_msg.tx_status != CEC_TX_STATUS_OK)
194         ALOGD("%s: tx_status=%d\n", __func__, cec_msg.tx_status);
195 
196     switch (cec_msg.tx_status) {
197         case CEC_TX_STATUS_OK:
198             return HDMI_RESULT_SUCCESS;
199         case CEC_TX_STATUS_ARB_LOST:
200             return HDMI_RESULT_BUSY;
201         case CEC_TX_STATUS_NACK:
202             return HDMI_RESULT_NACK;
203         default:
204             if (cec_msg.tx_status & CEC_TX_STATUS_NACK)
205                 return HDMI_RESULT_NACK;
206             return HDMI_RESULT_FAIL;
207     }
208 }
209 
hdmicec_register_event_callback(const struct hdmi_cec_device * dev,event_callback_t callback,void * arg)210 static void hdmicec_register_event_callback(const struct hdmi_cec_device *dev,
211         event_callback_t callback, void *arg)
212 {
213     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
214 
215     ctx->p_event_cb = callback;
216     ctx->cb_arg = arg;
217 }
218 
hdmicec_get_version(const struct hdmi_cec_device * dev,int * version)219 static void hdmicec_get_version(const struct hdmi_cec_device *dev, int *version)
220 {
221     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
222 
223     *version = ctx->version;
224 }
225 
hdmicec_get_vendor_id(const struct hdmi_cec_device * dev,uint32_t * vendor_id)226 static void hdmicec_get_vendor_id(const struct hdmi_cec_device *dev, uint32_t *vendor_id)
227 {
228     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
229 
230     *vendor_id = ctx->vendor_id;
231 }
232 
hdmicec_get_port_info(const struct hdmi_cec_device * dev,struct hdmi_port_info * list[],int * total)233 static void hdmicec_get_port_info(const struct hdmi_cec_device *dev,
234         struct hdmi_port_info *list[], int *total)
235 {
236     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
237     int ret;
238 
239     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, &ctx->port_info.physical_address);
240     if (ret)
241         ALOGD("%s: %m\n", __func__);
242 
243     ALOGD("type:%s, id:%d, cec support:%d, arc support:%d, physical address:%x",
244             ctx->port_info.type ? "output" : "input",
245             ctx->port_info.port_id,
246             ctx->port_info.cec_supported,
247             ctx->port_info.arc_supported,
248             ctx->port_info.physical_address);
249 
250     *list = &ctx->port_info;
251     *total = 1;
252 }
253 
hdmicec_set_option(const struct hdmi_cec_device * dev,int flag,int value)254 static void hdmicec_set_option(const struct hdmi_cec_device *dev, int flag, int value)
255 {
256     struct hdmicec_context* ctx = (struct hdmicec_context*)dev;
257     ALOGD("%s: flag=%d, value=%d", __func__, flag, value);
258     switch (flag) {
259         case HDMI_OPTION_ENABLE_CEC:
260             pthread_mutex_lock(&ctx->options_lock);
261             ctx->cec_enabled = (value == 1 ? true : false);
262             pthread_mutex_unlock(&ctx->options_lock);
263             break;
264         case HDMI_OPTION_WAKEUP:
265             // Not valid for playback devices
266             break;
267         case HDMI_OPTION_SYSTEM_CEC_CONTROL:
268             pthread_mutex_lock(&ctx->options_lock);
269             ctx->cec_control_enabled = (value == 1 ? true : false);
270             pthread_mutex_unlock(&ctx->options_lock);
271             break;
272     }
273 }
274 
hdmicec_is_connected(const struct hdmi_cec_device * dev,int port_id)275 static int hdmicec_is_connected(const struct hdmi_cec_device *dev, int port_id)
276 {
277     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
278     int ret;
279 
280     (void)port_id;
281 
282     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR,
283             &ctx->port_info.physical_address);
284     if (ret) {
285         ALOGD("%s: %m\n", __func__);
286         return ret;
287     }
288 
289     if (ctx->port_info.physical_address == CEC_PHYS_ADDR_INVALID)
290         return false;
291 
292     return true;
293 }
294 
get_opcode(struct cec_msg * message)295 static int get_opcode(struct cec_msg* message) {
296     return (((uint8_t)message->msg[1]) & 0xff);
297 }
298 
get_first_param(struct cec_msg * message)299 static int get_first_param(struct cec_msg* message) {
300     return (((uint8_t)message->msg[2]) & 0xff);
301 }
302 
is_power_ui_command(struct cec_msg * message)303 static bool is_power_ui_command(struct cec_msg* message) {
304     int ui_command = get_first_param(message);
305     switch (ui_command) {
306         case CEC_OP_UI_CMD_POWER:
307         case CEC_OP_UI_CMD_DEVICE_ROOT_MENU:
308         case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
309             return true;
310         default:
311             return false;
312     }
313 }
314 
is_transferable_in_sleep(struct cec_msg * message)315 static bool is_transferable_in_sleep(struct cec_msg* message) {
316     int opcode = get_opcode(message);
317     switch (opcode) {
318         case CEC_MESSAGE_ABORT:
319         case CEC_MESSAGE_DEVICE_VENDOR_ID:
320         case CEC_MESSAGE_GET_CEC_VERSION:
321         case CEC_MESSAGE_GET_MENU_LANGUAGE:
322         case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS:
323         case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID:
324         case CEC_MESSAGE_GIVE_OSD_NAME:
325         case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS:
326         case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS:
327         case CEC_MESSAGE_REPORT_POWER_STATUS:
328         case CEC_MESSAGE_SET_OSD_NAME:
329         case CEC_MESSAGE_DECK_CONTROL:
330         case CEC_MESSAGE_PLAY:
331             return true;
332         case CEC_MESSAGE_USER_CONTROL_PRESSED:
333             return is_power_ui_command(message);
334         default:
335             return false;
336     }
337 }
338 
event_thread(void * arg)339 static void *event_thread(void *arg)
340 {
341     struct hdmicec_context *ctx = (struct hdmicec_context *)arg;
342     int ret;
343     struct pollfd ufds[3] = {
344         { ctx->cec_fd, POLLIN, 0 },
345         { ctx->cec_fd, POLLERR, 0 },
346         { ctx->exit_fd, POLLIN, 0 },
347     };
348 
349     ALOGI("%s start!", __func__);
350 
351     while (1) {
352         ufds[0].revents = 0;
353         ufds[1].revents = 0;
354         ufds[2].revents = 0;
355 
356         ret = poll(ufds, 3, -1);
357 
358         if (ret <= 0)
359             continue;
360 
361         if (ufds[2].revents == POLLIN)   /* Exit */
362             break;
363 
364         if (ufds[1].revents == POLLERR) { /* CEC Event */
365             hdmi_event_t event = { };
366             struct cec_event ev;
367 
368             ret = ioctl(ctx->cec_fd, CEC_DQEVENT, &ev);
369             if (ret)
370                 continue;
371 
372             pthread_mutex_lock(&ctx->options_lock);
373             bool cec_enabled = ctx->cec_enabled;
374             pthread_mutex_unlock(&ctx->options_lock);
375             if (!cec_enabled) {
376                 continue;
377             }
378 
379             if (ev.event == CEC_EVENT_STATE_CHANGE) {
380                 event.type = HDMI_EVENT_HOT_PLUG;
381                 event.dev = &ctx->device;
382                 event.hotplug.port_id = 1;
383                 if (ev.state_change.phys_addr == CEC_PHYS_ADDR_INVALID)
384                     event.hotplug.connected = false;
385                 else
386                     event.hotplug.connected = true;
387 
388                 if (ctx->p_event_cb != NULL) {
389                     ctx->p_event_cb(&event, ctx->cb_arg);
390                 } else {
391                     ALOGE("no event callback for hotplug\n");
392                 }
393             }
394         }
395 
396         if (ufds[0].revents == POLLIN) { /* CEC Driver */
397             struct cec_msg msg = { };
398             hdmi_event_t event = { };
399 
400             ret = ioctl(ctx->cec_fd, CEC_RECEIVE, &msg);
401             if (ret) {
402                 ALOGE("%s: CEC_RECEIVE error (%m)\n", __func__);
403                 continue;
404             }
405 
406             if (msg.rx_status != CEC_RX_STATUS_OK) {
407                 ALOGD("%s: rx_status=%d\n", __func__, msg.rx_status);
408                 continue;
409             }
410 
411             pthread_mutex_lock(&ctx->options_lock);
412             bool cec_enabled = ctx->cec_enabled;
413             pthread_mutex_unlock(&ctx->options_lock);
414             if (!cec_enabled) {
415                 continue;
416             }
417 
418             pthread_mutex_lock(&ctx->options_lock);
419             bool cec_control_enabled = ctx->cec_control_enabled;
420             pthread_mutex_unlock(&ctx->options_lock);
421             if (!cec_control_enabled && !is_transferable_in_sleep(&msg)) {
422                 ALOGD("%s: filter message in standby mode\n", __func__);
423                 continue;
424             }
425 
426             if (ctx->p_event_cb != NULL) {
427                 event.type = HDMI_EVENT_CEC_MESSAGE;
428                 event.dev = &ctx->device;
429                 event.cec.initiator = msg.msg[0] >> 4;
430                 event.cec.destination = msg.msg[0] & 0xf;
431                 event.cec.length = msg.len - 1;
432                 memcpy(event.cec.body, &msg.msg[1], msg.len - 1);
433 
434                 ctx->p_event_cb(&event, ctx->cb_arg);
435             } else {
436                 ALOGE("no event callback for msg\n");
437             }
438         }
439     }
440 
441     ALOGI("%s exit!", __func__);
442     return NULL;
443 }
444 
hdmicec_set_arc(const struct hdmi_cec_device * dev,int port_id,int flag)445 static void hdmicec_set_arc(const struct hdmi_cec_device *dev, int port_id, int flag)
446 {
447     (void)dev;
448     (void)port_id;
449     (void)flag;
450     /* Not supported */
451 }
452 
hdmicec_close(struct hdmi_cec_device * dev)453 static int hdmicec_close(struct hdmi_cec_device *dev)
454 {
455     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
456     uint64_t tmp = 1;
457 
458     ALOGD("%s\n", __func__);
459 
460     if (ctx->exit_fd > 0) {
461         write(ctx->exit_fd, &tmp, sizeof(tmp));
462         pthread_join(ctx->thread, NULL);
463     }
464 
465     if (ctx->cec_fd > 0)
466         close(ctx->cec_fd);
467     if (ctx->exit_fd > 0)
468         close(ctx->exit_fd);
469     free(ctx);
470 
471     ctx->cec_enabled = false;
472     ctx->cec_control_enabled = false;
473     return 0;
474 }
475 
cec_init(struct hdmicec_context * ctx)476 static int cec_init(struct hdmicec_context *ctx)
477 {
478     struct cec_log_addrs laddrs = {};
479     struct cec_caps caps = {};
480     uint32_t mode;
481     int ret;
482 
483     // Ensure the CEC device supports required capabilities
484     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_CAPS, &caps);
485     if (ret)
486         return ret;
487 
488     if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS |
489                     CEC_CAP_TRANSMIT |
490                     CEC_CAP_PASSTHROUGH))) {
491         ALOGE("%s: wrong cec adapter capabilities %x\n",
492                 __func__, caps.capabilities);
493         return -1;
494     }
495 
496     // This is an exclusive follower, in addition put the CEC device into passthrough mode
497     mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
498     ret = ioctl(ctx->cec_fd, CEC_S_MODE, &mode);
499     if (ret)
500         return ret;
501 
502     ctx->type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
503 
504     ctx->vendor_id = property_get_int32("ro.hdmi.vendor_id",
505             0x000c03 /* HDMI LLC vendor ID */);
506 
507     ctx->version = property_get_bool("ro.hdmi.cec_version",
508             CEC_OP_CEC_VERSION_1_4);
509 
510     ctx->port_info.type = ctx->type == CEC_DEVICE_TV ? HDMI_INPUT : HDMI_OUTPUT;
511     ctx->port_info.port_id = 1;
512     ctx->port_info.cec_supported = 1;
513     ctx->port_info.arc_supported = 0;
514 
515     ALOGD("%s: type=%d\n", __func__, ctx->type);
516     ALOGD("%s: vendor_id=%04x\n", __func__, ctx->vendor_id);
517     ALOGD("%s: version=%d\n", __func__, ctx->version);
518 
519     memset(&laddrs, 0, sizeof(laddrs));
520     ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
521     if (ret)
522         return ret;
523 
524     pthread_mutex_init(&ctx->options_lock, NULL);
525 
526     ALOGD("%s: initialized CEC controller\n", __func__);
527 
528     return ret;
529 }
530 
open_hdmi_cec(const struct hw_module_t * module,const char * id,struct hw_device_t ** device)531 static int open_hdmi_cec(const struct hw_module_t *module, const char *id,
532         struct hw_device_t **device)
533 {
534     char *path = "/dev/cec0";
535     hdmicec_context_t *ctx;
536     int ret;
537 
538     ALOGD("%s: id=%s\n", __func__, id);
539 
540     ctx = malloc(sizeof(struct hdmicec_context));
541     if (!ctx)
542         return -ENOMEM;
543 
544     memset(ctx, 0, sizeof(*ctx));
545 
546     ctx->cec_fd = open(path, O_RDWR);
547     if (ctx->cec_fd < 0) {
548         ALOGE("faild to open %s, ret=%s\n", path, strerror(errno));
549         goto fail;
550     }
551 
552     ctx->exit_fd = eventfd(0, EFD_NONBLOCK);
553     if (ctx->exit_fd < 0) {
554         ALOGE("faild to open eventfd, ret = %d\n", errno);
555         goto fail;
556     }
557 
558     ctx->device.common.tag = HARDWARE_DEVICE_TAG;
559     ctx->device.common.version = HDMI_CEC_DEVICE_API_VERSION_1_0;
560     ctx->device.common.module = (struct hw_module_t *)module;
561     ctx->device.common.close = (int (*)(struct hw_device_t* device))hdmicec_close;
562 
563     ctx->device.add_logical_address = hdmicec_add_logical_address;
564     ctx->device.clear_logical_address = hdmicec_clear_logical_address;
565     ctx->device.get_physical_address = hdmicec_get_physical_address;
566     ctx->device.send_message = hdmicec_send_message;
567     ctx->device.register_event_callback = hdmicec_register_event_callback;
568     ctx->device.get_version = hdmicec_get_version;
569     ctx->device.get_vendor_id = hdmicec_get_vendor_id;
570     ctx->device.get_port_info = hdmicec_get_port_info;
571     ctx->device.set_option = hdmicec_set_option;
572     ctx->device.set_audio_return_channel = hdmicec_set_arc;
573     ctx->device.is_connected = hdmicec_is_connected;
574 
575     /* init status */
576     ret = cec_init(ctx);
577     if (ret)
578         goto fail;
579 
580     *device = &ctx->device.common;
581 
582     /* thread loop for receiving cec msg */
583     if (pthread_create(&ctx->thread, NULL, event_thread, ctx)) {
584         ALOGE("Can't create event thread: %s\n", strerror(errno));
585         goto fail;
586     }
587 
588     ctx->cec_enabled = true;
589     ctx->cec_control_enabled = true;
590     return 0;
591 
592 fail:
593     hdmicec_close((struct hdmi_cec_device *)ctx);
594     return -errno;
595 }
596 
597 /* module method */
598 static struct hw_module_methods_t hdmi_cec_module_methods = {
599     .open =  open_hdmi_cec,
600 };
601 
602 /* hdmi_cec module */
603 struct hw_module_t HAL_MODULE_INFO_SYM = {
604     .tag = HARDWARE_MODULE_TAG,
605     .version_major = 1,
606     .version_minor = 0,
607     .id = HDMI_CEC_HARDWARE_MODULE_ID,
608     .name = "YUKAWA HDMI CEC module",
609     .author = "The Android Open Source Project",
610     .methods = &hdmi_cec_module_methods,
611 };
612