summaryrefslogtreecommitdiff
path: root/hdmi_cec.c (plain)
blob: 98d43f04f77ad21aaecb39fdd2dcf8caca68ef00
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->device_type == DEV_TYPE_PLAYBACK) {
391 addr = hal_info->addr_bitmap;
392 addr &= 0x7fff;
393 for (i = 0; i < 15; i++) {
394 if (addr & 1) {
395 break;
396 }
397 addr >>= 1;
398 }
399 msg.initiator = i;
400 } else {
401 msg.initiator = 0; /* root for TV */
402 }
403 msg.destination = dest;
404 msg.length = len;
405 memcpy(msg.body, buffer, len);
406 return cec_send_message(NULL, &msg);
407}
408
409/*
410 * (*register_event_callback)() registers a callback that HDMI-CEC HAL
411 * can later use for incoming CEC messages or internal HDMI events.
412 * When calling from C++, use the argument arg to pass the calling object.
413 * It will be passed back when the callback is invoked so that the context
414 * can be retrieved.
415 */
416static void cec_register_event_callback(const struct hdmi_cec_device* dev,
417 event_callback_t callback, void* arg)
418{
419 if (!hal_info || hal_info->fd < 0)
420 return ;
421 D("dev:%p, callback:%p, arg:%p\n", callback, arg, dev);
422 hal_info->cb = callback;
423 hal_info->cb_data = arg;
424}
425
426/*
427 * (*get_version)() returns the CEC version supported by underlying hardware.
428 */
429static void cec_get_version(const struct hdmi_cec_device* dev, int* version)
430{
431 if (!hal_info || hal_info->fd < 0)
432 return ;
433 ioctl(hal_info->fd, CEC_IOC_GET_VERSION, version);
434 D("dev:%p, version:%x\n", dev, *version);
435}
436
437/*
438 * (*get_vendor_id)() returns the identifier of the vendor. It is
439 * the 24-bit unique company ID obtained from the IEEE Registration
440 * Authority Committee (RAC).
441 */
442static void cec_get_vendor_id(const struct hdmi_cec_device* dev, uint32_t* vendor_id)
443{
444 if (!hal_info || hal_info->fd < 0)
445 return ;
446 ioctl(hal_info->fd, CEC_IOC_GET_VENDOR_ID, vendor_id);
447 D("dev:%p, vendor_id:%x\n", dev, *vendor_id);
448}
449
450/*
451 * (*get_port_info)() returns the hdmi port information of underlying hardware.
452 * info is the list of HDMI port information, and 'total' is the number of
453 * HDMI ports in the system.
454 */
455static void cec_get_port_info(const struct hdmi_cec_device* dev,
456 struct hdmi_port_info* list[], int* total)
457{
458 int i;
459
460 if (!hal_info || hal_info->fd < 0)
461 return ;
462
463 ioctl(hal_info->fd, CEC_IOC_GET_PORT_NUM, total);
464 D("dev:%p, total port:%d\n", dev, *total);
465 if (*total > MAX_PORT)
466 *total = MAX_PORT;
467 hal_info->port_data = malloc(sizeof(struct hdmi_port_info) * (*total));
468 if (!hal_info->port_data) {
469 E("alloc port_data failed\n");
470 *total = 0;
471 return ;
472 }
473 ioctl(hal_info->fd, CEC_IOC_GET_PORT_INFO, hal_info->port_data);
474 for (i = 0; i < *total; i++) {
475 D("port %d, type:%s, id:%d, cec support:%d, arc support:%d, physical address:%x\n",
476 i, hal_info->port_data[i].type ? "output" : "input",
477 hal_info->port_data[i].port_id,
478 hal_info->port_data[i].cec_supported,
479 hal_info->port_data[i].arc_supported,
480 hal_info->port_data[i].physical_address);
481 }
482 *list = hal_info->port_data;
483 hal_info->total_port = *total;
484}
485
486/*
487 * (*set_option)() passes flags controlling the way HDMI-CEC service works down
488 * to HAL implementation. Those flags will be used in case the feature needs
489 * update in HAL itself, firmware or microcontroller.
490 */
491static void cec_set_option(const struct hdmi_cec_device* dev, int flag, int value)
492{
493 int ret;
494
495 if (!hal_info || hal_info->fd < 0)
496 return ;
497 switch (flag) {
498 case HDMI_OPTION_ENABLE_CEC:
499 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_ENALBE_CEC, value);
500 break;
501
502 case HDMI_OPTION_WAKEUP:
503 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_WAKEUP, value);
504 break;
505
506 case HDMI_OPTION_SYSTEM_CEC_CONTROL:
507 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_SYS_CTRL, value);
508 if (value)
509 hal_info->flag |= (1 << HDMI_OPTION_SYSTEM_CEC_CONTROL);
510 else
511 hal_info->flag &= ~(1 << HDMI_OPTION_SYSTEM_CEC_CONTROL);
512 break;
513
514 case HDMI_OPTION_SET_LANG:
515 ret = ioctl(hal_info->fd, CEC_IOC_SET_OPTION_SET_LANG, value);
516 break;
517
518 default:
519 break;
520 }
521 D("dev:%p, flag:%x, value:%x, ret:%d, hal_flag:%x\n",
522 dev, flag, value, ret, hal_info->flag);
523}
524
525/*
526 * (*set_audio_return_channel)() configures ARC circuit in the hardware logic
527 * to start or stop the feature. Flag can be either 1 to start the feature
528 * or 0 to stop it.
529 *
530 * Returns 0 on success or -errno on error.
531 */
532static void cec_set_audio_return_channel(const struct hdmi_cec_device* dev, int port_id, int flag)
533{
534 int ret;
535 if (!hal_info || hal_info->fd < 0)
536 return ;
537 ret = ioctl(hal_info->fd, CEC_IOC_SET_ARC_ENABLE, flag);
538 D("dev:%p, port id:%d, flag:%x, ret:%d\n", dev, port_id, flag, ret);
539}
540
541/*
542 * (*is_connected)() returns the connection status of the specified port.
543 * Returns HDMI_CONNECTED if a device is connected, otherwise HDMI_NOT_CONNECTED.
544 * The HAL should watch for +5V power signal to determine the status.
545 */
546static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id)
547{
548 int status = -1, ret;
549
550 if (!hal_info || hal_info->fd < 0)
551 return HDMI_NOT_CONNECTED;
552
553 /* use status pass port id */
554 status = port_id;
555 ret = ioctl(hal_info->fd, CEC_IOC_GET_CONNECT_STATUS, &status);
556 if (ret)
557 return HDMI_NOT_CONNECTED;
558 D("dev:%p, port:%d, connected:%s\n", dev, port_id, status ? "yes" : "no");
559 return (status) ? HDMI_CONNECTED : HDMI_NOT_CONNECTED;
560}
561
562/** Close the hdmi cec device */
563static int cec_close(struct hw_device_t *dev)
564{
565 if (!hal_info)
566 return -EINVAL;
567
568 hal_info->run = 0;
569 while (!hal_info->exited) {
570 usleep(100 * 1000);
571 }
572 free(dev);
573 close(hal_info->fd);
574 free(hal_info->port_data);
575 free(hal_info);
576 D("closed ok\n");
577 return 0;
578}
579
580/**
581 * module methods
582 */
583static int open_cec( const struct hw_module_t* module, char const *name,
584 struct hw_device_t **device )
585{
586 char value[PROPERTY_VALUE_MAX] = {};
587 int dev_type;
588 int ret;
589
590 hal_info = malloc(sizeof(*hal_info));
591 if (!hal_info) {
592 D("%s, alloc memory failed\n", __func__);
593 return -EINVAL;
594 }
595 memset(hal_info, 0, sizeof(*hal_info));
596 if (strcmp(name, HDMI_CEC_HARDWARE_INTERFACE) != 0) {
597 D("cec strcmp fail !!!");
598 return -EINVAL;
599 }
600 if (device == NULL) {
601 D("NULL cec device on open");
602 return -EINVAL;
603 }
604 property_get("ro.hdmi.device_type", value, "4");
605 D("ro.hdmi.device_type:%s\n", value);
606 ret = sscanf(value, "%d", &dev_type);
607 if (ret == 1 &&
608 (dev_type >= DEV_TYPE_TV && dev_type <= DEV_TYPE_VIDEO_PROCESSOR)) {
609 hal_info->device_type = dev_type;
610 } else {
611 hal_info->device_type = DEV_TYPE_PLAYBACK;
612 }
613 memset(value, 0, sizeof(value));
614 property_get("persist.sys.hdmi.keep_awake", value, "true");
615 if (!strcmp(value, "false")) {
616 hal_info->ext_control = 1;
617 } else {
618 hal_info->ext_control = 0;
619 }
620 D("ext control:%d, %s", hal_info->ext_control, value);
621
622 hdmi_cec_device_t *dev = malloc(sizeof(hdmi_cec_device_t));
623 memset(dev, 0, sizeof(hdmi_cec_device_t));
624
625 dev->common.tag = HARDWARE_DEVICE_TAG;
626 dev->common.version = 0;
627 dev->common.module = (struct hw_module_t*) module;
628 dev->common.close = cec_close;
629
630 dev->add_logical_address = cec_add_logical_address;
631 dev->clear_logical_address = cec_clear_logical_address;
632 dev->get_physical_address = cec_get_physical_address;
633 dev->send_message = cec_send_message;
634 dev->register_event_callback = cec_register_event_callback;
635 dev->get_version = cec_get_version;
636 dev->get_vendor_id = cec_get_vendor_id;
637 dev->get_port_info = cec_get_port_info;
638 dev->set_option = cec_set_option;
639 dev->set_audio_return_channel = cec_set_audio_return_channel;
640 dev->is_connected = cec_is_connected;
641
642 *device = (hw_device_t*) dev;
643
644 hal_info->run = 1;
645 hal_info->exited = 0;
646 hal_info->ThreadId = 0;
647 hal_info->dev = dev;
648 hal_info->total_port = 0;
649 hal_info->con_status = 0;
650 hal_info->port_data = NULL;
651 hal_info->addr_bitmap = (1 << CEC_ADDR_BROADCAST);
652 hal_info->cb_data = NULL;
653 hal_info->cb = NULL;
654 hal_info->fd = open(CEC_FILE, O_RDWR);
655 hal_info->javavm = NULL;
656 hal_info->obj = NULL;
657 hal_info->flag = 0;
658 if (hal_info->fd < 0) {
659 E("can't open %s\n", CEC_FILE);
660 return -EINVAL;
661 }
662 ret = ioctl(hal_info->fd, CEC_IOC_SET_DEV_TYPE, hal_info->device_type);
663 pthread_create(&hal_info->ThreadId, NULL, cec_rx_loop, hal_info);
664 pthread_setname_np(hal_info->ThreadId, "cec_rx_loop");
665
666 return 0;
667}
668
669static struct hw_module_methods_t hdmi_cec_module_methods = {
670 .open = open_cec,
671};
672
673/*
674 * The hdmi cec Module
675 */
676struct hdmi_cec_module HAL_MODULE_INFO_SYM = {
677 .common = {
678 .tag = HARDWARE_MODULE_TAG,
679 .module_api_version = HDMI_CEC_MODULE_API_VERSION_1_0,
680 .hal_api_version = HARDWARE_HAL_API_VERSION,
681 .id = HDMI_CEC_HARDWARE_MODULE_ID,
682 .name = "Amlogic hdmi cec Module",
683 .author = "Amlogic Corp.",
684 .methods = &hdmi_cec_module_methods,
685 },
686};
687
688JNIEXPORT int JNICALL
689Java_com_droidlogic_app_HdmiCecExtend_nativeSendCecMessage(JNIEnv *env, jobject thiz, jint dest, jbyteArray body)
690{
691 jbyte *ba = (*env)->GetByteArrayElements(env, body, JNI_FALSE);
692 jsize len = (*env)->GetArrayLength(env, body);
693
694 return cec_send_message_ext(dest, len, ba);
695}
696
697JNIEXPORT void JNICALL
698Java_com_droidlogic_app_HdmiCecExtend_nativeInit(JNIEnv *env, jobject thiz, jobject obj)
699{
700 jobject obj1;
701 if (hal_info != NULL) {
702 obj1 = (*env)->NewGlobalRef(env, obj);
703 hal_info->obj = obj1;
704 }
705}
706
707JNIEXPORT jint JNICALL
708Java_com_droidlogic_app_HdmiCecExtend_nativeGetPhysicalAddr(JNIEnv *env, jobject thiz)
709{
710 unsigned short addr = -1;
711 if (cec_get_physical_address(hal_info->dev, &addr) < 0) {
712 return -1;
713 }
714
715 return addr;
716}
717
718JNIEXPORT jint JNICALL
719Java_com_droidlogic_app_HdmiCecExtend_nativeGetVendorId(JNIEnv *env, jobject thiz)
720{
721 unsigned int id = 0;
722
723 cec_get_vendor_id(hal_info->dev, &id);
724 return id;
725}
726
727JNIEXPORT jint JNICALL
728Java_com_droidlogic_app_HdmiCecExtend_nativeGetCecVersion(JNIEnv *env, jobject thiz)
729{
730 int version = 0;
731
732 cec_get_version(hal_info->dev, &version);
733 return version;
734}
735
736static JNINativeMethod hdmiExtend_method[] = {
737 {"nativeSendCecMessage", "(I[B)I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeSendCecMessage},
738 {"nativeInit", "(Lcom/droidlogic/HdmiCecExtend;)V", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeInit},
739 {"nativeGetPhysicalAddr", "()I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeGetPhysicalAddr},
740 {"nativeGetVendorId", "()I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeGetVendorId},
741 {"nativeGetCecVersion", "()I", (void *)Java_com_droidlogic_app_HdmiCecExtend_nativeGetCecVersion},
742};
743
744static int registerNativeMethods(JNIEnv* env, const char* className,
745 const JNINativeMethod* methods, int numMethods)
746{
747 int rc;
748 jclass clazz;
749 clazz = (*env)->FindClass(env, className);
750
751 if (clazz == NULL) {
752 D("NULL clazz\n");
753 return -1;
754 }
755
756 if ((rc = ((*env)->RegisterNatives(env, clazz, methods, numMethods))) < 0) {
757 return -1;
758 }
759
760 if (hal_info != NULL) {
761 hal_info->onCecMessageRx = (*env)->GetMethodID(env, clazz, "onCecMessageRx", "([B)V");
762 if (hal_info->onCecMessageRx == NULL) {
763 D("can't found method onCecMessageRx");
764 } else {
765 D("got method onCecMessageRx, env:%p", env);
766 }
767 hal_info->onAddAddress = (*env)->GetMethodID(env, clazz, "onAddAddress", "(I)V");
768 if (hal_info->onAddAddress == NULL) {
769 D("can't found method onAddAddress");
770 } else {
771 D("got method onAddAddress, env:%p", env);
772 }
773 }
774
775 return 0;
776}
777
778
779JNIEXPORT jint
780JNI_OnLoad(JavaVM* vm, void* reserved)
781{
782 JNIEnv* env = NULL;
783 jint result = -1;
784
785 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
786 D("GetEnv failed!\n");
787 return result;
788 }
789 if (registerNativeMethods(env,
790 "com/droidlogic/HdmiCecExtend",
791 hdmiExtend_method,
792 NELEM(hdmiExtend_method)) < 0) {
793 D("registerNativeMethods failed\n");
794 }
795 if (hal_info) {
796 hal_info->javavm = vm;
797 }
798
799 return JNI_VERSION_1_4;
800}
801
802