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 } hdmicec_context_t;
53 
hdmicec_add_logical_address(const struct hdmi_cec_device * dev,cec_logical_address_t addr)54 static int hdmicec_add_logical_address(const struct hdmi_cec_device *dev, cec_logical_address_t addr)
55 {
56     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
57     unsigned int la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED;
58     unsigned int all_dev_types = 0;
59     unsigned int prim_type = 0xff;
60     struct cec_log_addrs laddrs;
61     int ret;
62 
63     ALOGD("%s: addr:%x\n", __func__, addr);
64 
65     if (addr >= CEC_ADDR_BROADCAST)
66         return -1;
67 
68     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_LOG_ADDRS, &laddrs);
69     if (ret)
70         return ret;
71     memset(&laddrs, 0, sizeof(laddrs));
72 
73     laddrs.cec_version = ctx->version;
74     laddrs.vendor_id = ctx->vendor_id;
75 
76     switch (addr) {
77         case CEC_LOG_ADDR_TV:
78             prim_type = CEC_OP_PRIM_DEVTYPE_TV;
79             la_type = CEC_LOG_ADDR_TYPE_TV;
80             all_dev_types = CEC_OP_ALL_DEVTYPE_TV;
81             break;
82         case CEC_LOG_ADDR_RECORD_1:
83         case CEC_LOG_ADDR_RECORD_2:
84         case CEC_LOG_ADDR_RECORD_3:
85             prim_type = CEC_OP_PRIM_DEVTYPE_RECORD;
86             la_type = CEC_LOG_ADDR_TYPE_RECORD;
87             all_dev_types = CEC_OP_ALL_DEVTYPE_RECORD;
88             break;
89         case CEC_LOG_ADDR_TUNER_1:
90         case CEC_LOG_ADDR_TUNER_2:
91         case CEC_LOG_ADDR_TUNER_3:
92         case CEC_LOG_ADDR_TUNER_4:
93             prim_type = CEC_OP_PRIM_DEVTYPE_TUNER;
94             la_type = CEC_LOG_ADDR_TYPE_TUNER;
95             all_dev_types = CEC_OP_ALL_DEVTYPE_TUNER;
96             break;
97         case CEC_LOG_ADDR_PLAYBACK_1:
98         case CEC_LOG_ADDR_PLAYBACK_2:
99         case CEC_LOG_ADDR_PLAYBACK_3:
100             prim_type = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
101             la_type = CEC_LOG_ADDR_TYPE_PLAYBACK;
102             all_dev_types = CEC_OP_ALL_DEVTYPE_PLAYBACK;
103             laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
104             break;
105         case CEC_LOG_ADDR_AUDIOSYSTEM:
106             prim_type = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
107             la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
108             all_dev_types = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
109             break;
110         case CEC_LOG_ADDR_SPECIFIC:
111             prim_type = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
112             la_type = CEC_LOG_ADDR_TYPE_SPECIFIC;
113             all_dev_types = CEC_OP_ALL_DEVTYPE_SWITCH;
114             break;
115         case CEC_ADDR_RESERVED_1:
116         case CEC_ADDR_RESERVED_2:
117         case CEC_ADDR_UNREGISTERED:
118             laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
119             break;
120     }
121 
122     laddrs.num_log_addrs = 1;
123     laddrs.log_addr[0] = addr;
124     laddrs.log_addr_type[0] = la_type;
125     laddrs.primary_device_type[0] = prim_type;
126     laddrs.all_device_types[0] = all_dev_types;
127     laddrs.features[0][0] = 0;
128     laddrs.features[0][1] = 0;
129 
130     ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
131     if (ret) {
132         ALOGD("%s: %m\n", __func__);
133         return ret;
134     }
135 
136     ALOGD("%s: log_addr_mask=%x\n", __func__,  laddrs.log_addr_mask);
137 
138     return 0;
139 }
140 
hdmicec_clear_logical_address(const struct hdmi_cec_device * dev)141 static void hdmicec_clear_logical_address(const struct hdmi_cec_device *dev)
142 {
143     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
144     struct cec_log_addrs laddrs;
145     int ret;
146 
147     memset(&laddrs, 0, sizeof(laddrs));
148     ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
149     if (ret)
150         ALOGD("%s: %m\n", __func__);
151 }
152 
hdmicec_get_physical_address(const struct hdmi_cec_device * dev,uint16_t * addr)153 static int hdmicec_get_physical_address(const struct hdmi_cec_device *dev, uint16_t *addr)
154 {
155     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
156     int ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, addr);
157     if (ret)
158         ALOGD("%s: %m\n", __func__);
159 
160     return ret;
161 }
162 
hdmicec_send_message(const struct hdmi_cec_device * dev,const cec_message_t * msg)163 static int hdmicec_send_message(const struct hdmi_cec_device *dev, const cec_message_t *msg)
164 {
165     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
166     struct cec_msg cec_msg;
167     int ret;
168 
169     ALOGD("%s: len=%u\n", __func__, (unsigned int)msg->length);
170 
171     memset(&cec_msg, 0, sizeof(cec_msg));
172     cec_msg.msg[0] = (msg->initiator << 4) | msg->destination;
173 
174     memcpy(&cec_msg.msg[1], msg->body, msg->length);
175     cec_msg.len = msg->length + 1;
176 
177     ret = ioctl(ctx->cec_fd, CEC_TRANSMIT, &cec_msg);
178     if (ret) {
179         ALOGD("%s: %m\n", __func__);
180         return HDMI_RESULT_FAIL;
181     }
182 
183     if (cec_msg.tx_status != CEC_TX_STATUS_OK)
184         ALOGD("%s: tx_status=%d\n", __func__, cec_msg.tx_status);
185 
186     switch (cec_msg.tx_status) {
187         case CEC_TX_STATUS_OK:
188             return HDMI_RESULT_SUCCESS;
189         case CEC_TX_STATUS_ARB_LOST:
190             return HDMI_RESULT_BUSY;
191         case CEC_TX_STATUS_NACK:
192             return HDMI_RESULT_NACK;
193         default:
194             return HDMI_RESULT_FAIL;
195     }
196 }
197 
hdmicec_register_event_callback(const struct hdmi_cec_device * dev,event_callback_t callback,void * arg)198 static void hdmicec_register_event_callback(const struct hdmi_cec_device *dev,
199         event_callback_t callback, void *arg)
200 {
201     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
202 
203     ctx->p_event_cb = callback;
204     ctx->cb_arg = arg;
205 }
206 
hdmicec_get_version(const struct hdmi_cec_device * dev,int * version)207 static void hdmicec_get_version(const struct hdmi_cec_device *dev, int *version)
208 {
209     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
210 
211     *version = ctx->version;
212 }
213 
hdmicec_get_vendor_id(const struct hdmi_cec_device * dev,uint32_t * vendor_id)214 static void hdmicec_get_vendor_id(const struct hdmi_cec_device *dev, uint32_t *vendor_id)
215 {
216     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
217 
218     *vendor_id = ctx->vendor_id;
219 }
220 
hdmicec_get_port_info(const struct hdmi_cec_device * dev,struct hdmi_port_info * list[],int * total)221 static void hdmicec_get_port_info(const struct hdmi_cec_device *dev,
222         struct hdmi_port_info *list[], int *total)
223 {
224     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
225     int ret;
226 
227     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, &ctx->port_info.physical_address);
228     if (ret)
229         ALOGD("%s: %m\n", __func__);
230 
231     ALOGD("type:%s, id:%d, cec support:%d, arc support:%d, physical address:%x",
232             ctx->port_info.type ? "output" : "input",
233             ctx->port_info.port_id,
234             ctx->port_info.cec_supported,
235             ctx->port_info.arc_supported,
236             ctx->port_info.physical_address);
237 
238     *list = &ctx->port_info;
239     *total = 1;
240 }
241 
hdmicec_set_option(const struct hdmi_cec_device * dev,int flag,int value)242 static void hdmicec_set_option(const struct hdmi_cec_device *dev, int flag, int value)
243 {
244     (void)dev;
245     ALOGD("%s: flag=%d, value=%d", __func__, flag, value);
246     switch (flag) {
247         case HDMI_OPTION_ENABLE_CEC:
248         case HDMI_OPTION_WAKEUP:
249         case HDMI_OPTION_SYSTEM_CEC_CONTROL:
250             /* TOFIX */
251             break;
252     }
253 }
254 
hdmicec_is_connected(const struct hdmi_cec_device * dev,int port_id)255 static int hdmicec_is_connected(const struct hdmi_cec_device *dev, int port_id)
256 {
257     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
258     int ret;
259 
260     (void)port_id;
261 
262     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR,
263             &ctx->port_info.physical_address);
264     if (ret) {
265         ALOGD("%s: %m\n", __func__);
266         return ret;
267     }
268 
269     if (ctx->port_info.physical_address == CEC_PHYS_ADDR_INVALID)
270         return false;
271 
272     return true;
273 }
274 
event_thread(void * arg)275 static void *event_thread(void *arg)
276 {
277     struct hdmicec_context *ctx = (struct hdmicec_context *)arg;
278     int ret;
279     struct pollfd ufds[3] = {
280         { ctx->cec_fd, POLLIN, 0 },
281         { ctx->cec_fd, POLLERR, 0 },
282         { ctx->exit_fd, POLLIN, 0 },
283     };
284 
285     ALOGI("%s start!", __func__);
286 
287     while (1) {
288         ufds[0].revents = 0;
289         ufds[1].revents = 0;
290         ufds[2].revents = 0;
291 
292         ret = poll(ufds, 3, -1);
293 
294         if (ret <= 0)
295             continue;
296 
297         if (ufds[2].revents == POLLIN)   /* Exit */
298             break;
299 
300         if (ufds[1].revents == POLLERR) { /* CEC Event */
301             hdmi_event_t event = { };
302             struct cec_event ev;
303 
304             ret = ioctl(ctx->cec_fd, CEC_DQEVENT, &ev);
305             if (ret)
306                 continue;
307 
308             if (ev.event == CEC_EVENT_STATE_CHANGE) {
309                 event.type = HDMI_EVENT_HOT_PLUG;
310                 event.dev = &ctx->device;
311                 event.hotplug.port_id = 1;
312                 if (ev.state_change.phys_addr == CEC_PHYS_ADDR_INVALID)
313                     event.hotplug.connected = false;
314                 else
315                     event.hotplug.connected = true;
316 
317                 if (ctx->p_event_cb != NULL) {
318                     ctx->p_event_cb(&event, ctx->cb_arg);
319                 } else {
320                     ALOGE("no event callback for hotplug\n");
321                 }
322             }
323         }
324 
325         if (ufds[0].revents == POLLIN) { /* CEC Driver */
326             struct cec_msg msg = { };
327             hdmi_event_t event = { };
328 
329             ret = ioctl(ctx->cec_fd, CEC_RECEIVE, &msg);
330             if (ret) {
331                 ALOGE("%s: CEC_RECEIVE error (%m)\n", __func__);
332                 continue;
333             }
334 
335             if (msg.rx_status != CEC_RX_STATUS_OK) {
336                 ALOGD("%s: rx_status=%d\n", __func__, msg.rx_status);
337                 continue;
338             }
339 
340             if (ctx->p_event_cb != NULL) {
341                 event.type = HDMI_EVENT_CEC_MESSAGE;
342                 event.dev = &ctx->device;
343                 event.cec.initiator = msg.msg[0] >> 4;
344                 event.cec.destination = msg.msg[0] & 0xf;
345                 event.cec.length = msg.len - 1;
346                 memcpy(event.cec.body, &msg.msg[1], msg.len - 1);
347 
348                 ctx->p_event_cb(&event, ctx->cb_arg);
349             } else {
350                 ALOGE("no event callback for msg\n");
351             }
352         }
353     }
354 
355     ALOGI("%s exit!", __func__);
356     return NULL;
357 }
358 
hdmicec_set_arc(const struct hdmi_cec_device * dev,int port_id,int flag)359 static void hdmicec_set_arc(const struct hdmi_cec_device *dev, int port_id, int flag)
360 {
361     (void)dev;
362     (void)port_id;
363     (void)flag;
364     /* Not supported */
365 }
366 
hdmicec_close(struct hdmi_cec_device * dev)367 static int hdmicec_close(struct hdmi_cec_device *dev)
368 {
369     struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
370     uint64_t tmp = 1;
371 
372     ALOGD("%s\n", __func__);
373 
374     if (ctx->exit_fd > 0) {
375         write(ctx->exit_fd, &tmp, sizeof(tmp));
376         pthread_join(ctx->thread, NULL);
377     }
378 
379     if (ctx->cec_fd > 0)
380         close(ctx->cec_fd);
381     if (ctx->exit_fd > 0)
382         close(ctx->exit_fd);
383     free(ctx);
384 
385     return 0;
386 }
387 
cec_init(struct hdmicec_context * ctx)388 static int cec_init(struct hdmicec_context *ctx)
389 {
390     struct cec_log_addrs laddrs = {};
391     struct cec_caps caps = {};
392     uint32_t mode;
393     int ret;
394 
395     // Ensure the CEC device supports required capabilities
396     ret = ioctl(ctx->cec_fd, CEC_ADAP_G_CAPS, &caps);
397     if (ret)
398         return ret;
399 
400     if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS |
401                     CEC_CAP_TRANSMIT |
402                     CEC_CAP_PASSTHROUGH))) {
403         ALOGE("%s: wrong cec adapter capabilities %x\n",
404                 __func__, caps.capabilities);
405         return -1;
406     }
407 
408     // This is an exclusive follower, in addition put the CEC device into passthrough mode
409     mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
410     ret = ioctl(ctx->cec_fd, CEC_S_MODE, &mode);
411     if (ret)
412         return ret;
413 
414     ctx->type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
415 
416     ctx->vendor_id = property_get_int32("ro.hdmi.vendor_id",
417             0x000c03 /* HDMI LLC vendor ID */);
418 
419     ctx->version = property_get_bool("ro.hdmi.cec_version",
420             CEC_OP_CEC_VERSION_1_4);
421 
422     ctx->port_info.type = ctx->type == CEC_DEVICE_TV ? HDMI_INPUT : HDMI_OUTPUT;
423     ctx->port_info.port_id = 1;
424     ctx->port_info.cec_supported = 1;
425     ctx->port_info.arc_supported = 0;
426 
427     ALOGD("%s: type=%d\n", __func__, ctx->type);
428     ALOGD("%s: vendor_id=%04x\n", __func__, ctx->vendor_id);
429     ALOGD("%s: version=%d\n", __func__, ctx->version);
430 
431     memset(&laddrs, 0, sizeof(laddrs));
432     ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
433     if (ret)
434         return ret;
435 
436     ALOGD("%s: initialized CEC controller\n", __func__);
437 
438     return ret;
439 }
440 
open_hdmi_cec(const struct hw_module_t * module,const char * id,struct hw_device_t ** device)441 static int open_hdmi_cec(const struct hw_module_t *module, const char *id,
442         struct hw_device_t **device)
443 {
444     char *path = "/dev/cec0";
445     hdmicec_context_t *ctx;
446     int ret;
447 
448     ALOGD("%s: id=%s\n", __func__, id);
449 
450     ctx = malloc(sizeof(struct hdmicec_context));
451     if (!ctx)
452         return -ENOMEM;
453 
454     memset(ctx, 0, sizeof(*ctx));
455 
456     ctx->cec_fd = open(path, O_RDWR);
457     if (ctx->cec_fd < 0) {
458         ALOGE("faild to open %s, ret=%s\n", path, strerror(errno));
459         goto fail;
460     }
461 
462     ctx->exit_fd = eventfd(0, EFD_NONBLOCK);
463     if (ctx->exit_fd < 0) {
464         ALOGE("faild to open eventfd, ret = %d\n", errno);
465         goto fail;
466     }
467 
468     ctx->device.common.tag = HARDWARE_DEVICE_TAG;
469     ctx->device.common.version = HDMI_CEC_DEVICE_API_VERSION_1_0;
470     ctx->device.common.module = (struct hw_module_t *)module;
471     ctx->device.common.close = (int (*)(struct hw_device_t* device))hdmicec_close;
472 
473     ctx->device.add_logical_address = hdmicec_add_logical_address;
474     ctx->device.clear_logical_address = hdmicec_clear_logical_address;
475     ctx->device.get_physical_address = hdmicec_get_physical_address;
476     ctx->device.send_message = hdmicec_send_message;
477     ctx->device.register_event_callback = hdmicec_register_event_callback;
478     ctx->device.get_version = hdmicec_get_version;
479     ctx->device.get_vendor_id = hdmicec_get_vendor_id;
480     ctx->device.get_port_info = hdmicec_get_port_info;
481     ctx->device.set_option = hdmicec_set_option;
482     ctx->device.set_audio_return_channel = hdmicec_set_arc;
483     ctx->device.is_connected = hdmicec_is_connected;
484 
485     /* init status */
486     ret = cec_init(ctx);
487     if (ret)
488         goto fail;
489 
490     *device = &ctx->device.common;
491 
492     /* thread loop for receiving cec msg */
493     if (pthread_create(&ctx->thread, NULL, event_thread, ctx)) {
494         ALOGE("Can't create event thread: %s\n", strerror(errno));
495         goto fail;
496     }
497     return 0;
498 
499 fail:
500     hdmicec_close((struct hdmi_cec_device *)ctx);
501     return -errno;
502 }
503 
504 /* module method */
505 static struct hw_module_methods_t hdmi_cec_module_methods = {
506     .open =  open_hdmi_cec,
507 };
508 
509 /* hdmi_cec module */
510 struct hw_module_t HAL_MODULE_INFO_SYM = {
511     .tag = HARDWARE_MODULE_TAG,
512     .version_major = 1,
513     .version_minor = 0,
514     .id = HDMI_CEC_HARDWARE_MODULE_ID,
515     .name = "YUKAWA HDMI CEC module",
516     .author = "The Android Open Source Project",
517     .methods = &hdmi_cec_module_methods,
518 };
519