summaryrefslogtreecommitdiff
path: root/hdmi_cec.c (plain)
blob: 04dafedd440ff8a81b04bf6876943b6a8b9021ca
1/*
2 * Copyright (C) 2014 The Android Open Source Project
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 * Amlogic HDMITX CEC HAL
18 * Copyright (C) 2014
19 *
20 * This implements a hdmi cec hardware library for the Android emulator.
21 * the following code should be built as a shared library that will be
22 * placed into /system/lib/hw/hdmi_cec.so
23 *
24 * It will be loaded by the code in hardware/libhardware/hardware.c
25 * which is itself called from
26 * frameworks/base/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
27 */
28
29#include <cutils/log.h>
30#include <stdint.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <pthread.h>
37#include <sys/ioctl.h>
38#include <sys/types.h>
39#include <hardware/hdmi_cec.h>
40#include <hardware/hardware.h>
41#include <cutils/properties.h>
42#include "hdmi_cec.h"
43#include <jni.h>
44#include <JNIHelp.h>
45
46#ifdef LOG_TAG
47#undef LOG_TAG
48#define LOG_TAG "CEC"
49#else
50#define LOG_TAG "CEC"
51#endif
52
53/* Set to 1 to enable debug messages to the log */
54#define DEBUG 1
55#if DEBUG
56# define D(format, args...) ALOGD("[%s]"format, __func__, ##args)
57#else
58# define D(...) do{}while(0)
59#endif
60
61#define E(format, args...) ALOGE("[%s]"format, __func__, ##args)
62
63#define CEC_FILE "/dev/cec"
64#define MAX_PORT 32
65
66#define MESSAGE_SET_MENU_LANGUAGE 0x32
67
68/*
69 * structures for platform cec implement
70 * @device_type : indentify type of cec device, such as tv or mbox
71 * @run : run flag for rx poll thread
72 * @exit : if rx poll thread is exited
73 * @addr_bitmap : bit maps for each valid logical address
74 * @fd : file descriptor for global read/write
75 * @ThreadId : pthread for poll cec rx message
76 * @cb_data : data pointer for cec message RX call back
77 * @cb : event call back for cec message RX
78 * @dev : for hdmi_cec_device type
79 * @port_data : array for port data
80 * @onCecMessageRx : for JNI call if got message
81 * @onAddAddress : for event to extend process
82 * @env : saved JNI enverioment
83 * @javavm : java vm for jni
84 */
85struct aml_cec_hal {
86 int device_type;
87 int run;
88 int exited;
89 int addr_bitmap;
90 int fd;
91 int total_port;
92 int ext_control;
93 int flag;
94 unsigned int con_status;
95 pthread_t ThreadId;
96 void *cb_data;
97 event_callback_t cb;
98 struct hdmi_cec_device *dev;
99 struct hdmi_port_info *port_data;
100 jmethodID onCecMessageRx;
101 jmethodID onAddAddress;
102 jobject obj;
103 JavaVM *javavm;
104};
105
106struct aml_cec_hal *hal_info = NULL;
107
108static int cec_rx_read_msg(unsigned char *buf, int msg_cnt)
109{
110 int i;
111 char *path = CEC_FILE;
112
113 if (msg_cnt <= 0 || !buf) {
114 return 0;
115 }
116 /* maybe blocked at driver */
117 i = read(hal_info->fd, buf, msg_cnt);
118 if (i < 0) {
119 E("read :%s failed, ret:%d\n", path, i);
120 return -1;
121 }
122 return i;
123}
124
125static void check_connect_status(struct aml_cec_hal *hal)
126{
127 unsigned int prev_status, bit;
128 int i, port, ret;
129 hdmi_event_t event;
130
131 prev_status = hal->con_status;
132 for (i = 0; i < hal->total_port && hal->port_data != NULL; i++) {
133 port = hal->port_data[i].port_id;
134 ret = ioctl(hal_info->fd, CEC_IOC_GET_CONNECT_STATUS, &port);
135 if (ret) {
136 D("get port %d connected status failed, ret:%d\n", hal->port_data[i].port_id, ret);
137 continue;
138 }
139 bit = prev_status & (1 << i);
140 if (bit ^ ((port ? 1 : 0) << i)) {
141 D("port:%d, connect status changed, now:%d, prev_status:%x\n",
142 hal->port_data[i].port_id, port, prev_status);
143 event.type = HDMI_EVENT_HOT_PLUG;
144 event.dev = hal->dev;
145 event.hotplug.connected = port;
146 event.hotplug.port_id = hal->port_data[i].port_id;
147 if (hal->cb && (hal->flag & (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL))) {
148 hal->cb(&event, hal_info->cb_data);
149 }
150 prev_status &= ~(bit);
151 prev_status |= ((port ? 1 : 0) << i);
152 D("now mask:%x\n", prev_status);
153 }
154 }
155 hal->con_status = prev_status;
156}
157
158static void *cec_rx_loop(void *data)
159{
160 struct aml_cec_hal *hal = (struct aml_cec_hal *)data;
161 hdmi_event_t event;
162 unsigned char msg_buf[CEC_MESSAGE_BODY_MAX_LENGTH];
163 int r;
164#if DEBUG
165 char buf[64] = {};
166 int size = 0, i;
167#endif
168 JNIEnv *env;
169 JavaVM *Vm;
170 int ret;
171
172 D("start\n");
173 while (hal_info->fd < 0) {
174 usleep(1000 * 1000);
175 hal_info->fd = open(CEC_FILE, O_RDWR);
176 }
177 D("file open ok\n");
178 while (hal && hal->run) {
179 check_connect_status(hal);
180 memset(&event, 0, sizeof(event));
181 memset(msg_buf, 0, sizeof(msg_buf));
182
183 /* try to got a message from dev */
184 r = cec_rx_read_msg(msg_buf, CEC_MESSAGE_BODY_MAX_LENGTH);
185 if (r <= 1) { /* ignore received ping messages */
186 continue;
187 }
188 #if DEBUG
189 size = 0;
190 memset(buf, 0, sizeof(buf));
191 for (i = 0; i < r; i++) {
192 size += sprintf(buf + size, "%02x ", msg_buf[i]);
193 }
194 D("msg:%s", buf);
195 #endif
196 memcpy(event.cec.body, msg_buf + 1, r - 1);
197 event.type = HDMI_EVENT_CEC_MESSAGE;
198 event.dev = hal->dev;
199 event.cec.initiator = (msg_buf[0] >> 4) & 0xf;
200 event.cec.destination = (msg_buf[0] >> 0) & 0xf;
201 event.cec.length = r - 1;
202 if (hal->device_type == DEV_TYPE_PLAYBACK &&
203 msg_buf[1] == 0x32 &&
204 hal->ext_control) {
205 D("ignore menu language change for tx\n");
206 } else {
207 if (hal->cb && (hal->flag & (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL))) {
208 hal->cb(&event, hal_info->cb_data);
209 }
210 }
211 /* call java method to process cec message for ext control */
212 if ((hal->ext_control == 0x03) &&
213 (hal->device_type == DEV_TYPE_PLAYBACK) &&
214 (hal->flag & (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL)) &&
215 (hal->javavm)) {
216 Vm = hal->javavm;
217 ret = (*Vm)->GetEnv(Vm, (void**)&env, JNI_VERSION_1_4);
218 if (ret < 0) {
219 ret = (*Vm)->AttachCurrentThread(Vm, &env, NULL);
220 if (ret < 0) {
221 D("can't attach Vm");
222 continue;
223 }
224 }
225 jbyteArray array = (*env)->NewByteArray(env, r);
226 (*env)->SetByteArrayRegion(env, array, 0, r, msg_buf);
227 (*env)->CallVoidMethod(env, hal->obj, hal->onCecMessageRx, array);
228 (*env)->DeleteLocalRef(env, array);
229 }
230 }
231 D("end\n");
232 hal->exited = 1;
233 return 0;
234}
235
236/*
237 * (*add_logical_address)() passes the logical address that will be used
238 * in this system.
239 *
240 * HAL may use it to configure the hardware so that the CEC commands addressed
241 * the given logical address can be filtered in. This method can be called
242 * as many times as necessary in order to support multiple logical devices.
243 * addr should be in the range of valid logical addresses for the call
244 * to succeed.
245 *
246 * Returns 0 on success or -errno on error.
247 */
248static int cec_add_logical_address(const struct hdmi_cec_device* dev, cec_logical_address_t addr)
249{
250 JNIEnv *env;
251 JavaVM *Vm;
252 int ret;
253
254 if (!hal_info || hal_info->fd < 0)
255 return -EINVAL;
256 if (addr < CEC_ADDR_BROADCAST)
257 hal_info->addr_bitmap |= (1 << addr);
258
259 if (hal_info->ext_control) {
260 hal_info->ext_control |= (0x02);
261 if ((hal_info->device_type == DEV_TYPE_PLAYBACK) &&
262 (hal_info->obj != NULL)) {
263 Vm = hal_info->javavm;
264 ret = (*Vm)->GetEnv(Vm, (void**)&env, JNI_VERSION_1_4);
265 if (ret < 0) {
266 ret = (*Vm)->AttachCurrentThread(Vm, &env, NULL);
267 if (ret < 0) {
268 D("can't attach Vm");
269 }
270 } else if (hal_info->onAddAddress) {
271 (*env)->CallVoidMethod(env, hal_info->obj, hal_info->onAddAddress, addr);
272 }
273 }
274 }
275 D("dev:%p, addr:%x, bitmap:%x\n", dev, addr, hal_info->addr_bitmap);
276 return ioctl(hal_info->fd, CEC_IOC_ADD_LOGICAL_ADDR, addr);
277}
278
279/*
280 * (*clear_logical_address)() tells HAL to reset all the logical addresses.
281 *
282 * It is used when the system doesn't need to process CEC command any more,
283 * hence to tell HAL to stop receiving commands from the CEC bus, and change
284 * the state back to the beginning.
285 */
286static void cec_clear_logical_address(const struct hdmi_cec_device* dev)
287{
288 if (!hal_info || hal_info->fd < 0)
289 return ;
290 hal_info->addr_bitmap = (1 << CEC_ADDR_BROADCAST);
291 D("dev:%p, bitmap:%x\n", dev, hal_info->addr_bitmap);
292 if (hal_info->ext_control) {
293 hal_info->ext_control &= ~(0x02);
294 }
295 ioctl(hal_info->fd, CEC_IOC_CLR_LOGICAL_ADDR, 0);
296}
297
298/*
299 * (*get_physical_address)() returns the CEC physical address. The
300 * address is written to addr.
301 *
302 * The physical address depends on the topology of the network formed
303 * by connected HDMI devices. It is therefore likely to change if the cable
304 * is plugged off and on again. It is advised to call get_physical_address
305 * to get the updated address when hot plug event takes place.
306 *
307 * Returns 0 on success or -errno on error.
308 */
309static int cec_get_physical_address(const struct hdmi_cec_device* dev, uint16_t* addr)
310{
311 int ret;
312 if (!hal_info || hal_info->fd < 0)
313 return -EINVAL;
314 ret = ioctl(hal_info->fd, CEC_IOC_GET_PHYSICAL_ADDR, addr);
315 D("dev:%p, physical addr:%x, ret:%d\n", dev, *addr, ret);
316 return ret;
317}
318
319static char *get_send_result(int r)
320{
321 switch (r) {
322 case HDMI_RESULT_SUCCESS:
323 return "success";
324 case HDMI_RESULT_NACK:
325 return "no ack";
326 case HDMI_RESULT_BUSY:
327 return "busy";
328 case HDMI_RESULT_FAIL:
329 return "fail other";
330 default:
331 return "unknown fail code";
332 }
333}
334
335/*
336 * (*send_message)() transmits HDMI-CEC message to other HDMI device.
337 *
338 * The method should be designed to return in a certain amount of time not
339 * hanging forever, which can happen if CEC signal line is pulled low for
340 * some reason. HAL implementation should take the situation into account
341 * so as not to wait forever for the message to get sent out.
342 *
343 * It should try retransmission at least once as specified in the standard.
344 *
345 * Returns error code. See HDMI_RESULT_SUCCESS, HDMI_RESULT_NACK, and
346 * HDMI_RESULT_BUSY.
347 */
348static int cec_send_message(const struct hdmi_cec_device* dev, const cec_message_t* msg)
349{
350 int i, ret;
351 unsigned char msg_buf[CEC_MESSAGE_BODY_MAX_LENGTH] = {};
352#if DEBUG
353 char buf[64] = {};
354 int size = 0;
355#endif
356
357 if (!hal_info || hal_info->fd < 0)
358 return HDMI_RESULT_FAIL;
359
360 /* don't send message if controled by extend */
361 if (hal_info->ext_control == 0x03 && dev) {
362 return HDMI_RESULT_SUCCESS;
363 }
364
365 memset(msg_buf, 0, sizeof(msg_buf));
366 msg_buf[0] = ((msg->initiator & 0xf) << 4) | (msg->destination & 0xf);
367 memcpy(msg_buf + 1, msg->body, msg->length);
368 ret = write(hal_info->fd, msg_buf, msg->length + 1);
369#if DEBUG
370 memset(buf, 0, sizeof(buf));
371 for (i = 0; i < (int)msg->length; i++) {
372 size += sprintf(buf + size, "%02x ", msg->body[i]);
373 }
374 D("[%x -> %x]len:%d, body:%s, result:%s\n",
375 msg->initiator, msg->destination, msg->length,
376 buf, get_send_result(ret));
377#endif
378 return ret;
379}
380
381/*
382 * export cec send message API for other usage
383 */
384int cec_send_message_ext(int dest, int len, unsigned char *buffer)
385{
386 int addr;
387 int i;
388 cec_message_t msg;
389
390 if (!hal_info)
391 return -1;
392
393 if (hal_info->device_type == DEV_TYPE_PLAYBACK) {
394 addr = hal_info->addr_bitmap;
395 addr &= 0x7fff;
396 for (i = 0; i < 15; i++) {
397 if (addr & 1) {
398 break;
399 }
400 addr >>= 1;
401 }
402 msg.initiator = i;
403 } else {
404 msg.initiator = 0; /* root for TV */
405 }
406 msg.destination = dest;
407 msg.length = len;
408 memcpy(msg.body, buffer, len);
409 return cec_send_message(NULL, &msg);
410}
411
412/*
413 * (*register_event_callback)() registers a callback that HDMI-CEC HAL
414 * can later use for incoming CEC messages or internal HDMI events.
415 * When calling from C++, use the argument arg to pass the calling object.
416 * It will be passed back when the callback is invoked so that the context
417 * can be retrieved.
418 */
419static void cec_register_event_callback(const struct hdmi_cec_device* dev,
420 event_callback_t callback, void* arg)
421{
422 if (!hal_info || hal_info->fd < 0)
423 return ;
424 D("dev:%p, callback:%p, arg:%p\n", callback, arg, dev);
425 hal_info->cb = callback;
426 hal_info->cb_data = arg;
427}
428
429/*
430 * (*get_version)() returns the CEC version supported by underlying hardware.
431 */
432static void cec_get_version(const struct hdmi_cec_device* dev, int* version)
433{
434 if (!hal_info || hal_info->fd < 0)
435 return ;
436 ioctl(hal_info->fd, CEC_IOC_GET_VERSION, version);
437 D("dev:%p, version:%x\n", dev, *version);
438}
439
440/*
441 * (*get_vendor_id)() returns the identifier of the vendor. It is
442 * the 24-bit unique company ID obtained from the IEEE Registration
443 * Authority Committee (RAC).
444 */
445static void cec_get_vendor_id(const struct hdmi_cec_device* dev, uint32_t* vendor_id)
446{
447 if (!hal_info || hal_info->fd < 0)
448 return ;
449 ioctl(hal_info->fd, CEC_IOC_GET_VENDOR_ID, vendor_id);
450 D("dev:%p, vendor_id:%x\n", dev, *vendor_id);
451}
452
453/*
454 * (*get_port_info)() returns the hdmi port information of underlying hardware.
455 * info is the list of HDMI port information, and 'total' is the number of
456 * HDMI ports in the system.
457 */
458static void cec_get_port_info(const struct hdmi_cec_device* dev,
459 struct hdmi_port_info* list[], int* total)
460{
461 int i;
462
463 if (!hal_info || hal_info->fd < 0)
464 return ;
465
466 ioctl(hal_info->fd, CEC_IOC_GET_PORT_NUM, total);
467 D("dev:%p, total port:%d\n", dev, *total);
468 if (*total > MAX_PORT)
469 *total = MAX_PORT;
470 hal_info->port_data = malloc(sizeof(struct hdmi_port_info) * (*total));
471 if (!hal_info->port_data) {
472 E("alloc port_data failed\n");
473 *total = 0;
474 return ;
475 }
476 ioctl(hal_info->fd, CEC_IOC_GET_PORT_INFO, hal_info->port_data);
477 for (i = 0; i < *total; i++) {
478 D("port %d, type:%s, id:%d, cec support:%d, arc support:%d, physical address:%x\n",
479 i, hal_info->port_data[i].type ? "output" : "input",
480 hal_info->port_data[i].port_id,
481 hal_info->port_data[i].cec_supported,
482 hal_info->port_data[i].arc_supported,
483 hal_info->port_data[i].physical_address);
484 }
485 *list = hal_info->port_data;
486 hal_info->total_port = *total;
487}
488
489/*
490 * (*set_option)() passes flags controlling the way HDMI-CEC service works down
491 * to HAL implementation. Those flags will be used in case the feature needs
492 * update in HAL itself, firmware or microcontroller.
493 */
494static void cec_set_option(const struct hdmi_cec_device* dev, int flag, int value)
495{
496 int ret;
497
498 if (!hal_info || hal_info->fd < 0)
499 return ;
500 switch (flag) {
501 case HDMI_OPTION_ENABLE_CEC:
502 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_ENALBE_CEC, value);
503 break;
504
505 case HDMI_OPTION_WAKEUP:
506 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_WAKEUP, value);
507 break;
508
509 case HDMI_OPTION_SYSTEM_CEC_CONTROL:
510 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_SYS_CTRL, value);
511 if (value)
512 hal_info->flag |= (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL);
513 else
514 hal_info->flag &= ~(1 << HDMI_OPTION_SYSTEM_CEC_CONTROL);
515 break;
516
517 /* set device auto-power off by uboot */
518 case HDMI_OPTION_CEC_AUTO_DEVICE_OFF:
519 ret = ioctl(hal_info->fd, CEC_IOC_SET_AUTO_DEVICE_OFF, value);
520 break;
521
522 case HDMI_OPTION_SET_LANG:
523 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_SET_LANG, value);
524 break;
525
526 default:
527 break;
528 }
529 D("dev:%p, flag:%x, value:%x, ret:%d, hal_flag:%x\n",
530 dev, flag, value, ret, hal_info->flag);
531}
532
533/*
534 * get connect status when boot
535 */
536static void get_boot_connect_status(struct aml_cec_hal *hal_info)
537{
538 unsigned int total;
539 int i, port, ret;
540
541 ret = ioctl(hal_info->fd, CEC_IOC_GET_PORT_NUM, &total);
542 D("total port:%d, ret:%d\n", total, ret);
543 if (ret < 0)
544 return ;
545
546 if (total > MAX_PORT)
547 total = MAX_PORT;
548 hal_info->con_status = 0;
549 for (i = 0; i < total; i++) {
550 port = i + 1;
551 ret = ioctl(hal_info->fd, CEC_IOC_GET_CONNECT_STATUS, &port);
552 if (ret) {
553 D("get port %d connected status failed, ret:%d\n", i, ret);
554 continue;
555 }
556 hal_info->con_status |= ((port ? 1 : 0) << i);
557 }
558 D("con_status:%x\n", hal_info->con_status);
559}
560
561/*
562 * (*set_audio_return_channel)() configures ARC circuit in the hardware logic
563 * to start or stop the feature. Flag can be either 1 to start the feature
564 * or 0 to stop it.
565 *
566 * Returns 0 on success or -errno on error.
567 */
568static void cec_set_audio_return_channel(const struct hdmi_cec_device* dev, int port_id, int flag)
569{
570 int ret;
571 if (!hal_info || hal_info->fd < 0)
572 return ;
573 ret = ioctl(hal_info->fd, CEC_IOC_SET_ARC_ENABLE, flag);
574 D("dev:%p, port id:%d, flag:%x, ret:%d\n", dev, port_id, flag, ret);
575}
576
577/*
578 * (*is_connected)() returns the connection status of the specified port.
579 * Returns HDMI_CONNECTED if a device is connected, otherwise HDMI_NOT_CONNECTED.
580 * The HAL should watch for +5V power signal to determine the status.
581 */
582static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id)
583{
584 int status = -1, ret;
585
586 if (!hal_info || hal_info->fd < 0)
587 return HDMI_NOT_CONNECTED;
588
589 /* use status pass port id */
590 status = port_id;
591 ret = ioctl(hal_info->fd, CEC_IOC_GET_CONNECT_STATUS, &status);
592 if (ret)
593 return HDMI_NOT_CONNECTED;
594 D("dev:%p, port:%d, connected:%s\n", dev, port_id, status ? "yes" : "no");
595 return (status) ? HDMI_CONNECTED : HDMI_NOT_CONNECTED;
596}
597
598/** Close the hdmi cec device */
599static int cec_close(struct hw_device_t *dev)
600{
601 if (!hal_info)
602 return -EINVAL;
603
604 hal_info->run = 0;
605 while (!hal_info->exited) {
606 usleep(100 * 1000);
607 }
608 free(dev);
609 close(hal_info->fd);
610 free(hal_info->port_data);
611 free(hal_info);
612 D("closed ok\n");
613 return 0;
614}
615
616/**
617 * module methods
618 */
619static int open_cec( const struct hw_module_t* module, char const *name,
620 struct hw_device_t **device )
621{
622 char value[PROPERTY_VALUE_MAX] = {};
623 int dev_type;
624 int ret;
625
626 hal_info = malloc(sizeof(*hal_info));
627 if (!hal_info) {
628 D("%s, alloc memory failed\n", __func__);
629 return -EINVAL;
630 }
631 memset(hal_info, 0, sizeof(*hal_info));
632 if (strcmp(name, HDMI_CEC_HARDWARE_INTERFACE) != 0) {
633 D("cec strcmp fail !!!");
634 return -EINVAL;
635 }
636 if (device == NULL) {
637 D("NULL cec device on open");
638 return -EINVAL;
639 }
640 property_get("ro.hdmi.device_type", value, "4");
641 D("ro.hdmi.device_type:%s\n", value);
642 ret = sscanf(value, "%d", &dev_type);
643 if (ret == 1 &&
644 (dev_type >= DEV_TYPE_TV && dev_type <= DEV_TYPE_VIDEO_PROCESSOR)) {
645 hal_info->device_type = dev_type;
646 } else {
647 hal_info->device_type = DEV_TYPE_PLAYBACK;
648 }
649 memset(value, 0, sizeof(value));
650 property_get("persist.sys.hdmi.keep_awake", value, "true");
651 if (!strcmp(value, "false")) {
652 hal_info->ext_control = 1;
653 } else {
654 hal_info->ext_control = 0;
655 }
656 D("ext control:%d, %s", hal_info->ext_control, value);
657
658 hdmi_cec_device_t *dev = malloc(sizeof(hdmi_cec_device_t));
659 memset(dev, 0, sizeof(hdmi_cec_device_t));
660
661 dev->common.tag = HARDWARE_DEVICE_TAG;
662 dev->common.version = 0;
663 dev->common.module = (struct hw_module_t*) module;
664 dev->common.close = cec_close;
665
666 dev->add_logical_address = cec_add_logical_address;
667 dev->clear_logical_address = cec_clear_logical_address;
668 dev->get_physical_address = cec_get_physical_address;
669 dev->send_message = cec_send_message;
670 dev->register_event_callback = cec_register_event_callback;
671 dev->get_version = cec_get_version;
672 dev->get_vendor_id = cec_get_vendor_id;
673 dev->get_port_info = cec_get_port_info;
674 dev->set_option = cec_set_option;
675 dev->set_audio_return_channel = cec_set_audio_return_channel;
676 dev->is_connected = cec_is_connected;
677
678 *device = (hw_device_t*) dev;
679
680 hal_info->run = 1;
681 hal_info->exited = 0;
682 hal_info->ThreadId = 0;
683 hal_info->dev = dev;
684 hal_info->total_port = 0;
685 hal_info->con_status = 0;
686 hal_info->port_data = NULL;
687 hal_info->addr_bitmap = (1 << CEC_ADDR_BROADCAST);
688 hal_info->cb_data = NULL;
689 hal_info->cb = NULL;
690 hal_info->fd = open(CEC_FILE, O_RDWR);
691 hal_info->javavm = NULL;
692 hal_info->obj = NULL;
693 hal_info->flag = 0;
694 if (hal_info->fd < 0) {
695 E("can't open %s\n", CEC_FILE);
696 return -EINVAL;
697 }
698 ret = ioctl(hal_info->fd, CEC_IOC_SET_DEV_TYPE, hal_info->device_type);
699 get_boot_connect_status(hal_info);
700 pthread_create(&hal_info->ThreadId, NULL, cec_rx_loop, hal_info);
701 pthread_setname_np(hal_info->ThreadId, "cec_rx_loop");
702
703 return 0;
704}
705
706static struct hw_module_methods_t hdmi_cec_module_methods = {
707 .open = open_cec,
708};
709
710/*
711 * The hdmi cec Module
712 */
713struct hdmi_cec_module HAL_MODULE_INFO_SYM = {
714 .common = {
715 .tag = HARDWARE_MODULE_TAG,
716 .module_api_version = HDMI_CEC_MODULE_API_VERSION_1_0,
717 .hal_api_version = HARDWARE_HAL_API_VERSION,
718 .id = HDMI_CEC_HARDWARE_MODULE_ID,
719 .name = "Amlogic hdmi cec Module",
720 .author = "Amlogic Corp.",
721 .methods = &hdmi_cec_module_methods,
722 },
723};
724
725JNIEXPORT int JNICALL
726Java_com_droidlogic_app_HdmiCecExtend_nativeSendCecMessage(JNIEnv *env, jobject thiz, jint dest, jbyteArray body)
727{
728 jbyte *ba = (*env)->GetByteArrayElements(env, body, JNI_FALSE);
729 jsize len = (*env)->GetArrayLength(env, body);
730
731 return cec_send_message_ext(dest, len, ba);
732}
733
734JNIEXPORT void JNICALL
735Java_com_droidlogic_app_HdmiCecExtend_nativeInit(JNIEnv *env, jobject thiz, jobject obj)
736{
737 jobject obj1;
738 if (hal_info != NULL) {
739 obj1 = (*env)->NewGlobalRef(env, obj);
740 hal_info->obj = obj1;
741 }
742}
743
744JNIEXPORT jint JNICALL
745Java_com_droidlogic_app_HdmiCecExtend_nativeGetPhysicalAddr(JNIEnv *env, jobject thiz)
746{
747 unsigned short addr = -1;
748 if (hal_info && cec_get_physical_address(hal_info->dev, &addr) < 0) {
749 return -1;
750 }
751
752 return addr;
753}
754
755JNIEXPORT jint JNICALL
756Java_com_droidlogic_app_HdmiCecExtend_nativeGetVendorId(JNIEnv *env, jobject thiz)
757{
758 unsigned int id = 0;
759
760 if (hal_info)
761 cec_get_vendor_id(hal_info->dev, &id);
762 return id;
763}
764
765JNIEXPORT jint JNICALL
766Java_com_droidlogic_app_HdmiCecExtend_nativeGetCecVersion(JNIEnv *env, jobject thiz)
767{
768 int version = 0;
769
770 cec_get_version(hal_info->dev, &version);
771 return version;
772}
773
774static JNINativeMethod hdmiExtend_method[] = {
775 {"nativeSendCecMessage", "(I[B)I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeSendCecMessage},
776 {"nativeInit", "(Lcom/droidlogic/HdmiCecExtend;)V", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeInit},
777 {"nativeGetPhysicalAddr", "()I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeGetPhysicalAddr},
778 {"nativeGetVendorId", "()I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeGetVendorId},
779 {"nativeGetCecVersion", "()I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeGetCecVersion},
780};
781
782static int registerNativeMethods(JNIEnv* env, const char* className,
783 const JNINativeMethod* methods, int numMethods)
784{
785 int rc;
786 jclass clazz;
787 clazz = (*env)->FindClass(env, className);
788
789 if (clazz == NULL) {
790 D("NULL clazz\n");
791 return -1;
792 }
793
794 if ((rc = ((*env)->RegisterNatives(env, clazz, methods, numMethods))) < 0) {
795 return -1;
796 }
797
798 if (hal_info != NULL) {
799 hal_info->onCecMessageRx = (*env)->GetMethodID(env, clazz, "onCecMessageRx", "([B)V");
800 if (hal_info->onCecMessageRx == NULL) {
801 D("can't found method onCecMessageRx");
802 } else {
803 D("got method onCecMessageRx, env:%p", env);
804 }
805 hal_info->onAddAddress = (*env)->GetMethodID(env, clazz, "onAddAddress", "(I)V");
806 if (hal_info->onAddAddress == NULL) {
807 D("can't found method onAddAddress");
808 } else {
809 D("got method onAddAddress, env:%p", env);
810 }
811 }
812
813 return 0;
814}
815
816
817JNIEXPORT jint
818JNI_OnLoad(JavaVM* vm, void* reserved)
819{
820 JNIEnv* env = NULL;
821 jint result = -1;
822
823 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
824 D("GetEnv failed!\n");
825 return result;
826 }
827 if (registerNativeMethods(env,
828 "com/droidlogic/HdmiCecExtend",
829 hdmiExtend_method,
830 NELEM(hdmiExtend_method)) < 0) {
831 D("registerNativeMethods failed\n");
832 }
833 if (hal_info) {
834 hal_info->javavm = vm;
835 }
836
837 return JNI_VERSION_1_4;
838}
839
840