summaryrefslogtreecommitdiff
path: root/src/hdmi_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hdmi_service.c')
-rw-r--r--src/hdmi_service.c1104
1 files changed, 1104 insertions, 0 deletions
diff --git a/src/hdmi_service.c b/src/hdmi_service.c
new file mode 100644
index 0000000..2542566
--- /dev/null
+++ b/src/hdmi_service.c
@@ -0,0 +1,1104 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ * License terms: <FOSS license>.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <errno.h> /* Errors */
+#include <stdarg.h>
+#include <stdio.h> /* Input/Output */
+#include <stdlib.h> /* General Utilities */
+#include <pthread.h> /* POSIX Threads */
+#include <string.h> /* String handling */
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include "linux/fb.h"
+#include <sys/socket.h>
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+pthread_t thread_main;
+pthread_t thread_kevent;
+pthread_t thread_socklisten;
+pthread_mutex_t event_mutex;
+pthread_mutex_t fb_state_mutex;
+pthread_mutex_t cmd_mutex;
+pthread_cond_t event_cond;
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+void (*hdmi_callback_fn)(int cmd, int data_length, __u8 *data) = NULL;
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+int hdmi_events;
+enum hdmi_fb_state hdmi_fb_state;
+enum hdmi_plug_state hdmi_plug_state;
+struct cmd_data *cmd_data;
+int cmd_id_ind;
+
+const __u8 plugdetdis_val[] = {0x00, 0x00, 0x00};/* 00: disable, 00:ontime,
+ 00: offtime*/
+const __u8 plugdeten_val[] = {0x01, 0x05, 0x02};/* 01: enable, 05:ontime,
+ 02: offtime*/
+
+int get_new_cmd_id_ind(void)
+{
+ cmd_id_ind++;
+ return cmd_id_ind;
+}
+
+/* Sets the format to be used in sysfs files */
+static int storeastext(int as_text)
+{
+ int storeastext;
+ int result = RESULT_OK;
+ int wr_res;
+ char *str;
+
+ if (as_text)
+ str = STOREASTEXT_STR;
+ else
+ str = STOREASBIN_STR;
+
+ /* Set file format in sysfs files; hextext or binary */
+ storeastext = open(STOREASTEXT_FILE, O_WRONLY);
+ if (storeastext < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", STOREASTEXT_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto storeastext_end;
+ }
+ wr_res = write(storeastext, str, strlen(str));
+ close(storeastext);
+ if (wr_res != (int)strlen(str))
+ result = STOREAS_FAIL;
+
+storeastext_end:
+ return result;
+}
+
+/* Trigger event in kernel event file */
+static int hdmievwakeupfile_wr(void)
+{
+ int evwakeup;
+ int res;
+ __u8 val = 1;
+
+ evwakeup = open(EVWAKEUP_FILE, O_WRONLY);
+ if (evwakeup < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", EVWAKEUP_FILE);
+ return -1;
+ }
+ res = write(evwakeup, &val, 1);
+ close(evwakeup);
+ if (res != 1) {
+ LOGHDMILIB("***** Failed to write %s *****", EVWAKEUP_FILE);
+ return -2;
+ }
+ return 0;
+}
+
+/* Set hw power */
+int poweronoff(__u8 onoff)
+{
+ int pwrfd;
+
+ pwrfd = open(POWERONOFF_FILE, O_WRONLY);
+ if (pwrfd < 0) {
+ LOGHDMILIB(" failed to open %s", POWERONOFF_FILE);
+ return -1;
+ }
+ write(pwrfd, &onoff, 1);
+ close(pwrfd);
+
+ return 0;
+}
+
+/* Get hw power */
+static int powerstate_get(enum hdmi_power_state *power_state)
+{
+ int pwrfd;
+ int res;
+ __u8 onoff;
+
+ *power_state = HDMI_POWERUNDEF;
+ pwrfd = open(POWERONOFF_FILE, O_RDONLY);
+ if (pwrfd < 0) {
+ LOGHDMILIB(" failed to open %s", POWERONOFF_FILE);
+ return -1;
+ }
+ res = read(pwrfd, &onoff, 1);
+ close(pwrfd);
+ if (res != 1)
+ return -1;
+ if (onoff)
+ *power_state = HDMI_POWERON;
+ else
+ *power_state = HDMI_POWEROFF;
+ return 0;
+}
+
+/* Set local plug state */
+static int plugstate_set(enum hdmi_plug_state plug_state)
+{
+ hdmi_plug_state = plug_state;
+ return 0;
+}
+
+/* Get local plug state */
+static int plugstate_get(enum hdmi_plug_state *plug_state)
+{
+ *plug_state = hdmi_plug_state;
+ return 0;
+}
+
+/* Select HDMI or DVI mode */
+static int hdmi_format_set(enum hdmi_format format)
+{
+ int fd;
+
+ fd = open(HDMIFORMAT_FILE, O_WRONLY);
+ if (fd < 0) {
+ LOGHDMILIB(" failed to open %s", HDMIFORMAT_FILE);
+ return -1;
+ }
+ write(fd, &format, 1);
+ close(fd);
+
+ return 0;
+}
+
+/* Send illegal state message on client socket */
+static int illegalstate_send(__u32 cmd, __u32 cmd_id)
+{
+ int val;
+ __u8 buf[16];
+ int sock;
+
+ val = cmd;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ memcpy(&buf[CMDID_OFFSET], &cmd_id, 4);
+ val = 0;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+
+ /* Send on socket */
+ return clientsocket_send(buf, CMDBUF_OFFSET + val);
+}
+
+/* Subscribe for plug events */
+int hdmiplug_subscribe(void)
+{
+ int plugdetenfd;
+ int res;
+
+ plugdetenfd = open(PLUGDETEN_FILE, O_WRONLY);
+ if (plugdetenfd < 0) {
+ LOGHDMILIB(" failed to open %s", PLUGDETEN_FILE);
+ goto hdmiplug_subscribe_err2;
+ }
+
+ /* Unsubscribe */
+ res = write(plugdetenfd, plugdetdis_val, sizeof(plugdetdis_val));
+ if (res != sizeof(plugdetdis_val))
+ goto hdmiplug_subscribe_err1;
+
+ /* Subscribe */
+ res = write(plugdetenfd, plugdeten_val, sizeof(plugdeten_val));
+ if (res != sizeof(plugdeten_val))
+ goto hdmiplug_subscribe_err1;
+
+ close(plugdetenfd);
+ return 0;
+
+hdmiplug_subscribe_err1:
+ close(plugdetenfd);
+hdmiplug_subscribe_err2:
+ return -1;
+}
+
+/* Allow-Avoid Early suspend */
+static int stayalive(__u8 enable)
+{
+ int stayalivefd;
+ int cnt = 0;
+ int res;
+
+ stayalivefd = open(STAYALIVE_FILE, O_WRONLY);
+ while ((stayalivefd < 0) && (cnt++ < 30)) {
+ usleep(200000);
+ stayalivefd = open(STAYALIVE_FILE, O_WRONLY);
+ }
+ LOGHDMILIB("cnt:%d", cnt);
+
+ if (stayalivefd < 0) {
+ LOGHDMILIB(" failed to open %s", STAYALIVE_FILE);
+ goto stayalive_err2;
+ }
+ res = write(stayalivefd, &enable, 1);
+ if (res != 1)
+ goto stayalive_err1;
+
+ close(stayalivefd);
+ return 0;
+
+stayalive_err1:
+ close(stayalivefd);
+stayalive_err2:
+ return -1;
+}
+
+/* Handling of plug events */
+static int hdmiplugged_handle(int *basic_audio_support)
+{
+ __u8 data[128];
+ int nr_formats;
+ __u8 cea;
+ __u8 vesaceanr;
+ int disponoff;
+ char req_str[7];
+ int wr_res;
+ int fd;
+ char fbname[128];
+ char buf[128];
+ int read_res;
+ struct video_format *formats;
+ __u8 extension;
+ int cnt = 0;
+ struct edid_latency edid_latency = {-1, -1, -1, -1};
+ int res;
+ int ret = 0;
+
+ LOGHDMILIB("%s", "HDMIEVENT_HDMIPLUGGED");
+
+ plugstate_set(HDMI_PLUGGED);
+ *basic_audio_support = 0;
+ video_formats_clear();
+
+ /* Behaviour at early suspend */
+ stayalive(HDMI_SERVICE_STAY_ALIVE_DURING_SUSPEND);
+
+ /* Set hdmi fb state */
+ hdmi_fb_state = HDMI_FB_OPENED;
+
+ /* Get HW supported formats */
+ video_formats_supported_hw();
+ nr_formats = nr_formats_get();
+ formats = video_formats_get();
+
+ cnt = 0;
+ /* Read and parse EDID */
+ res = -1;
+ while (res && (cnt < 3)) {
+ res = edid_read(0, data);
+ if (res == 0)
+ res = edid_parse0(data + 1, &extension, formats, nr_formats);
+ if (res && (cnt < 2))
+ usleep(EDIDREAD_WAITTIME);
+ cnt++;
+ }
+ if (res) {
+ ret = -1;
+ goto hdmiplugged_handle_end;
+ }
+
+ if (extension) {
+ /* Extension data exists */
+ if (edid_read(1, data) != 0) {
+ ret = -2;
+ goto hdmiplugged_handle_end;
+ }
+
+ edid_parse1(data + 1, formats, nr_formats,
+ basic_audio_support, &edid_latency);
+
+ /* Set hdmi format to hdmi */
+ hdmi_format_set(HDMI_FORMAT_HDMI);
+ cea = 1;
+ } else {
+ /* Set hdmi format to dvi */
+ hdmi_format_set(HDMI_FORMAT_DVI);
+ cea = 0;
+ }
+
+ LOGHDMILIB("Basic audio support: %d", *basic_audio_support);
+ LOGHDMILIB("Latency: video:%d audio:%d",
+ edid_latency.video_latency,
+ edid_latency.audio_latency);
+ LOGHDMILIB("Interlaced latency: video:%d audio:%d",
+ edid_latency.intlcd_video_latency,
+ edid_latency.intlcd_audio_latency);
+
+ set_vesacea_prio_all();
+ get_best_videoformat(&cea, &vesaceanr);
+
+ /* Check if fb is created */
+ /* Get fb dev name */
+ disponoff = open(DISPONOFF_FILE, O_RDWR);
+ if (disponoff < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", DISPONOFF_FILE);
+ ret = -3;
+ goto hdmiplugged_handle_end;
+ }
+ read_res = read(disponoff, buf, sizeof(buf));
+ if (read_res > 0) {
+ LOGHDMILIB("fbname:%s", buf);
+ } else {
+ /* Create frame buffer with best resolution */
+ lseek(disponoff, 0, SEEK_SET);
+ sprintf(req_str, "%02x%02x%02x", 1, cea, vesaceanr);
+ LOGHDMILIB("req_str:%s", req_str);
+
+ wr_res = write(disponoff, req_str, strlen(req_str));
+ if (wr_res != (int)strlen(req_str)) {
+ LOGHDMILIB("***** Failed to write %s *****",
+ DISPONOFF_FILE);
+ close(disponoff);
+ ret = -4;
+ goto hdmiplugged_handle_end;
+ }
+
+ /* Check that fb was created */
+ /* Get fb dev name */
+ lseek(disponoff, 0, SEEK_SET);
+ read_res = read(disponoff, buf, sizeof(buf));
+ if (read_res <= 0) {
+ LOGHDMILIB("***** Failed to read %s *****", DISPONOFF_FILE);
+ close(disponoff);
+ ret = -5;
+ goto hdmiplugged_handle_end;
+ }
+
+ LOGHDMILIB("fbname:%s", buf);
+ }
+ close(disponoff);
+
+ /* Change resolution to be sure to have correct freq */
+ hdmi_fb_chres(cea, vesaceanr);
+
+hdmiplugged_handle_end:
+ LOGHDMILIB("%s end:%d", __func__, ret);
+ return ret;
+}
+
+static int hdmiunplugged_handle(void)
+{
+ enum hdmi_power_state power_state;
+
+ LOGHDMILIB("%s", "HDMIEVENT_HDMIUNPLUGGED");
+ plugstate_set(HDMI_UNPLUGGED);
+
+ /* Allow early suspend */
+ stayalive(0);
+ return 0;
+}
+
+static int hdmiunknownhandle(int event)
+{
+ LOGHDMILIB("HDMIEVENT_EVENTUNKNOWN: %d", event);
+ return 0;
+}
+
+/* Close frame buffer */
+static int hdmi_fb_close(void)
+{
+ int disponoff;
+ char req_str[7];
+ int wr_res;
+
+ LOGHDMILIB("%s begin", __func__);
+ LOGHDMILIB("hdmi_fb_state:%d", hdmi_fb_state);
+
+ if (hdmi_fb_state == HDMI_FB_CLOSED) {
+ LOGHDMILIB("%s", "FB already closed");
+ return 0;
+ }
+
+ /* Destroy frame buffer */
+ disponoff = open(DISPONOFF_FILE, O_WRONLY);
+ if (disponoff < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", DISPONOFF_FILE);
+ } else {
+ sprintf(req_str, "%02x%02x%02x", 0, 0, 0);
+ LOGHDMILIB("req_str:%s", req_str);
+
+ wr_res = write(disponoff, req_str, strlen(req_str));
+ close(disponoff);
+ if (wr_res != (int)strlen(req_str))
+ LOGHDMILIB("***** Failed to write %s *****",
+ DISPONOFF_FILE);
+ }
+
+ hdmievclr(EVENTMASK_ALL);
+
+ hdmi_fb_state = HDMI_FB_CLOSED;
+ LOGHDMILIB("%s end", __func__);
+ return 0;
+}
+
+/* Send Infoframe */
+static int infofr_send(__u8 type, __u8 ver, __u8 crc, __u8 len, __u8 *data)
+{
+ int infofrfd;
+ char buf[128];
+ int res = 0;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ buf[0] = type;
+ buf[1] = ver;
+ buf[2] = crc;
+ buf[3] = len;
+ memcpy(&buf[4], data, len);
+
+ infofrfd = open(INFOFRSEND_FILE, O_WRONLY);
+ if (infofrfd <= 0) {
+ LOGHDMILIB("***** Failed to open %s *****\n", INFOFRSEND_FILE);
+ res = -1;
+ goto infofr_send_end;
+ }
+
+ res = write(infofrfd, buf, len + 4);
+ if (res != len + 4) {
+ LOGHDMILIB("***** infofrsend failed %d *****\n", res);
+ res = -1;
+ goto infofr_send_end;
+ }
+
+ if (infofrfd > 0)
+ close(infofrfd);
+
+infofr_send_end:
+ LOGHDMILIB("%s end:%d", __func__, res);
+ return res;
+}
+
+/* Send plug event message on client socket */
+static int plugevent_send(__u32 cmd, int audio_support, int nr, struct vesacea vesacea[])
+{
+ int res = 0;
+ int val;
+ __u8 buf[128];
+ int sock;
+ __u32 cmd_id;
+ int cnt;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ LOGHDMILIB("audio_support:%d", audio_support);
+ LOGHDMILIB("nr video supp:%d", nr);
+
+ cmd_id = get_new_cmd_id_ind();
+
+ val = cmd;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ memcpy(&buf[CMDID_OFFSET], &cmd_id, 4);
+ val = 2 + nr * 2;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ buf[CMDLEN_OFFSET] = audio_support;
+ buf[CMDLEN_OFFSET + 1] = nr;
+ for (cnt = 0; cnt < nr; cnt++) {
+ buf[CMDLEN_OFFSET + 2 + cnt * 2] = vesacea[cnt].cea;
+ buf[CMDLEN_OFFSET + 3 + cnt * 2] = vesacea[cnt].nr;
+ }
+
+ /* Send on socket */
+ res = clientsocket_send(buf, CMDBUF_OFFSET + val);
+ LOGHDMILIB("%s end", __func__);
+ return res;
+}
+
+/* Add command to list */
+int cmd_add(struct cmd_data *cmd)
+{
+ struct cmd_data **cmd_obj;
+ struct cmd_data *cmd_new;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ cmd_new = malloc(sizeof(struct cmd_data));
+ if (cmd_new)
+ memcpy(cmd_new, cmd, sizeof(struct cmd_data));
+ else
+ return -1;
+
+ /* Add to list */
+ pthread_mutex_lock(&cmd_mutex);
+ cmd_obj = &cmd_data;
+ while (*cmd_obj != NULL)
+ cmd_obj = &((*cmd_obj)->next);
+ *cmd_obj = cmd_new;
+ pthread_mutex_unlock(&cmd_mutex);
+
+ LOGHDMILIB("%s end", __func__);
+ return 0;
+}
+
+/* Delete command list */
+static int cmd_del_all(void)
+{
+ struct cmd_data **cmd_obj;
+ struct cmd_data *cmd_del;
+
+ LOGHDMILIB("%s begin", __func__);
+ pthread_mutex_lock(&cmd_mutex);
+ cmd_obj = &cmd_data;
+ while (*cmd_obj != NULL) {
+ cmd_del = *cmd_obj;
+ cmd_obj = &((*cmd_obj)->next);
+ free(cmd_del);
+ }
+ pthread_mutex_unlock(&cmd_mutex);
+ LOGHDMILIB("%s end", __func__);
+ return 0;
+}
+
+/* Signal an event to main thread */
+int hdmi_event(int event)
+{
+ pthread_mutex_lock(&event_mutex);
+ hdmi_events |= event;
+ if (hdmi_events)
+ pthread_cond_signal(&event_cond);
+ pthread_mutex_unlock(&event_mutex);
+ return 0;
+}
+
+/* Handling of received command */
+static int hdmi_eventcmd(void)
+{
+ struct cmd_data *cmd_obj = NULL;
+ struct cmd_data *del_obj;
+ int res = 0;
+ int ret = -1;
+ enum hdmi_power_state power_state;
+ enum hdmi_plug_state plug_state;
+ int handlecmd;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ pthread_mutex_lock(&cmd_mutex);
+ if (cmd_data) {
+ cmd_obj = cmd_data;
+ cmd_data = cmd_data->next;
+ }
+ pthread_mutex_unlock(&cmd_mutex);
+
+ /* Handle all mesages in list */
+ while (cmd_obj) {
+ res = -1;
+ handlecmd = 1;
+ ret = cmd_obj->cmd;
+
+ /* Check power and plug state */
+ switch (cmd_obj->cmd) {
+ case HDMI_ENABLE:
+ case HDMI_DISABLE:
+ case HDMI_EXIT:
+ case HDMI_CECSEND:
+ default:
+ break;
+
+ case HDMI_EDIDREQ:
+ case HDMI_FB_RES_SET:
+ case HDMI_HDCP_INIT:
+ case HDMI_VESACEAPRIO_SET:
+ case HDMI_INFOFR:
+ handlecmd = 0;
+ powerstate_get(&power_state);
+ plugstate_get(&plug_state);
+ if (power_state != HDMI_POWERON)
+ illegalstate_send(HDMI_ILLSTATE_UNPOWERED,
+ cmd_obj->cmd_id);
+ else if (plug_state != HDMI_PLUGGED)
+ illegalstate_send(HDMI_ILLSTATE_UNPLUGGED,
+ cmd_obj->cmd_id);
+ else
+ handlecmd = 1;
+ break;
+
+ case HDMI_FB_RELEASE:
+ handlecmd = 0;
+ powerstate_get(&power_state);
+ plugstate_get(&plug_state);
+ if ((power_state == HDMI_POWERON) &&
+ (plug_state == HDMI_PLUGGED))
+ illegalstate_send(HDMI_ILLSTATE_PWRON_PLUGGED,
+ cmd_obj->cmd_id);
+ else
+ handlecmd = 1;
+ break;
+ }
+
+ if (handlecmd == 0)
+ break;
+
+ /* Handle cmd */
+ switch (cmd_obj->cmd) {
+ case HDMI_ENABLE:
+ /* Subscribe on plug events */
+ hdmiplug_subscribe();
+
+ /* Subscribe for cec events */
+ res = cecrx_subscribe();
+ break;
+
+ case HDMI_DISABLE:
+ res = hdmi_fb_close();
+ break;
+
+ case HDMI_EDIDREQ:
+ res = edidreq(cmd_obj->data[0], cmd_obj->cmd_id);
+ break;
+
+ case HDMI_CECSEND:
+ res = cecsend(cmd_obj->cmd_id,
+ cmd_obj->data[0],
+ cmd_obj->data[1],
+ cmd_obj->data[2],
+ &cmd_obj->data[3]);
+ break;
+
+ case HDMI_FB_RES_SET:
+ res = hdmi_fb_chres(cmd_obj->data[0], cmd_obj->data[1]);
+ break;
+
+ case HDMI_FB_RELEASE:
+ hdmi_fb_close();
+
+ /* Subscribe on plug events */
+ hdmiplug_subscribe();
+
+ /* Subscribe for cec events */
+ res = cecrx_subscribe();
+ break;
+
+ case HDMI_HDCP_INIT:
+ if (cmd_obj->data_len != (AES_KEYS_SIZE + 12))
+ res = -1;
+ else
+ res = hdcp_init(cmd_obj->data);
+ break;
+
+ case HDMI_VESACEAPRIO_SET:
+ res = vesaceaprio_set(cmd_obj->data[0],
+ &cmd_obj->data[1]);
+ break;
+
+ case HDMI_INFOFR:
+ res = infofr_send(cmd_obj->data[0],
+ cmd_obj->data[1],
+ cmd_obj->data[2],
+ cmd_obj->data[3],
+ &cmd_obj->data[4]);
+ break;
+
+ case HDMI_EXIT:
+ hdmi_fb_close();
+ res = 0;
+
+ /* delete list */
+ cmd_del_all();
+
+ hdmievwakeupfile_wr();
+
+ goto hdmi_eventcmd_end;
+ break;
+
+ default:
+ break;
+ }
+
+ LOGHDMILIB("cmd_id:%x res:%d\n", cmd_obj->cmd_id, res);
+
+ free(cmd_obj);
+
+ pthread_mutex_lock(&cmd_mutex);
+ if (cmd_data) {
+ cmd_obj = cmd_data;
+ cmd_data = cmd_data->next;
+ } else
+ cmd_obj = NULL;
+ pthread_mutex_unlock(&cmd_mutex);
+ }
+
+hdmi_eventcmd_end:
+ LOGHDMILIB("%s end", __func__);
+
+ if (res != 0)
+ ret = res;
+
+ return ret;
+}
+
+static int hdmi_service_exit_do(void)
+{
+ int sock;
+ int res;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ /* Shutdown listen socket to end listen thread */
+ sock = listensocket_get();
+ listensocket_set(-1);
+ res = shutdown(sock, SHUT_RDWR);
+
+ pthread_mutex_destroy(&event_mutex);
+ pthread_mutex_destroy(&cmd_mutex);
+ pthread_mutex_destroy(&fb_state_mutex);
+ pthread_cond_destroy(&event_cond);
+
+ LOGHDMILIB("%s end", __func__);
+ return res;
+}
+
+/* Main thread. Handles messages from client thread or kernel event thread */
+static void thread_main_fn(void *arg)
+{
+ int events;
+ int cont = 1;
+ int dummy = 0;
+ int res;
+ int audio_support;
+ int nr_video;
+ struct vesacea video_supported[FORMATS_MAX];
+
+ LOGHDMILIB("%s begin", __func__);
+
+ pthread_create(&thread_kevent, NULL, (void *)thread_kevent_fn,
+ (void *)&dummy);
+
+ pthread_create(&thread_socklisten, NULL, (void *)thread_socklisten_fn,
+ (void *)&dummy);
+
+ while (cont) {
+ /* Wait for event */
+ pthread_mutex_lock(&event_mutex);
+ if (hdmi_events == 0)
+ /* Wait only if there are no events pending.
+ * event_mutex is automatically unlocked while waiting
+ * and locked again when thread is awakened.
+ */
+ pthread_cond_wait(&event_cond, &event_mutex);
+ events = hdmi_events;
+ hdmi_events = 0;
+ pthread_mutex_unlock(&event_mutex);
+
+ LOGHDMILIB("%s: event:%x", __func__, events);
+
+ /* kernel events */
+ if (events & HDMIEVENT_HDMIPLUGGED) {
+ if (hdmiplugged_handle(&audio_support) == 0) {
+ vesacea_supported(&nr_video, video_supported);
+ plugevent_send(HDMI_PLUGGED_EV, audio_support,
+ nr_video,
+ video_supported);
+ }
+ } else if (events & HDMIEVENT_HDMIUNPLUGGED) {
+ hdmiunplugged_handle();
+ plugevent_send(HDMI_UNPLUGGED_EV, 0, 0, NULL);
+ }
+
+ if (events & HDMIEVENT_CEC)
+ cecrx();
+ if (events & HDMIEVENT_HDCP)
+ hdcp_state();
+ if (events & HDMIEVENT_CECTXERR)
+ cecsenderr();
+
+ /* App cmd event */
+ if (events & HDMIEVENT_CMD) {
+ res = hdmi_eventcmd();
+ if (res == HDMI_EXIT) {
+ cont = 0;
+ /* Wait for kevent thread to exit */
+ usleep(2000000);
+ }
+ }
+ }
+
+ pthread_mutex_lock(&event_mutex);
+ hdmi_events = 0;
+ pthread_mutex_unlock(&event_mutex);
+
+ hdmi_service_exit_do();
+
+ LOGHDMILIB("%s end", __func__);
+
+ /* Exit thread */
+ pthread_exit(NULL);
+}
+
+/* API helper functions */
+int hdmi_service_init(int avoid_return_msg)
+{
+ int dummy = 0;
+ __u8 ceavesadata[2];
+ int socket;
+
+ LOGHDMILIB("%s begin", __func__);
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+ hdmi_service_callback_set(NULL);
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+
+ /* Set sysfs format to binary */
+ storeastext(0);
+
+ ceavesadata[0] = 0;
+ ceavesadata[1] = 0;
+ LOGHDMILIB("desired cea:%d nr:%d", ceavesadata[0], ceavesadata[1]);
+ vesaceaprio_set(1, ceavesadata);
+
+ pthread_mutex_init(&event_mutex, NULL);
+ pthread_mutex_init(&cmd_mutex, NULL);
+ pthread_mutex_init(&fb_state_mutex, NULL);
+ pthread_cond_init(&event_cond, NULL);
+
+ /* Create threads */
+ pthread_create(&thread_main, NULL, (void *)thread_main_fn,
+ (void *)&dummy);
+
+ LOGHDMILIB("%s end", __func__);
+
+ /* Wait for threads to start */
+ usleep(100000);
+ socket = serversocket_create(avoid_return_msg);
+
+ return socket;
+}
+
+int hdmi_service_exit(void)
+{
+ int val;
+ __u8 buf[32];
+ val = HDMI_EXIT;
+
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[8], &val, 4);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_enable(void)
+{
+ int val;
+ __u8 buf[32];
+ val = HDMI_ENABLE;
+
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[8], &val, 4);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_disable(void)
+{
+ int val;
+ __u8 buf[32];
+ val = HDMI_DISABLE;
+
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[8], &val, 4);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+void hdmi_service_callback_set(cb_fn hdmi_cb)
+{
+ hdmi_callback_fn = hdmi_cb;
+}
+
+cb_fn hdmi_service_callback_get(void)
+{
+ return hdmi_callback_fn;
+}
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+
+int hdmi_service_resolution_set(int cea, int vesaceanr)
+{
+ int val;
+ __u8 buf[32];
+
+ val = HDMI_FB_RES_SET;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 2;
+ memcpy(&buf[8], &val, 4);
+ /* data */
+ buf[12] = cea;
+ buf[13] = vesaceanr;
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_fb_release(void)
+{
+ int val;
+ __u8 buf[32];
+
+ val = HDMI_FB_RELEASE;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[8], &val, 4);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_cec_send(__u8 initiator, __u8 destination, __u8 data_size,
+ __u8 *data)
+{
+ int val;
+ __u8 buf[32];
+
+ if (data_size > CEC_MSG_SIZE_MAX)
+ return -1;
+
+ val = HDMI_CECSEND;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = data_size + 3;
+ memcpy(&buf[8], &val, 4);
+ /* data */
+ buf[12] = initiator;
+ buf[13] = destination;
+ buf[14] = data_size;
+ memcpy(&buf[15], data, data_size);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_edid_request(__u8 block)
+{
+ int val;
+ __u8 buf[32];
+
+ if (block >= 2)
+ return -1;
+
+ val = HDMI_EDIDREQ;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 1;
+ memcpy(&buf[8], &val, 4);
+ /* data */
+ buf[12] = block;
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_hdcp_init(__u16 aes_size, __u8 *aes_data)
+{
+ int val;
+ __u8 buf[300];
+
+ if (aes_size != AES_KEYS_SIZE)
+ return -1;
+
+ val = HDMI_HDCP_INIT;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = aes_size;
+ memcpy(&buf[8], &val, 4);
+ /* data */
+ memcpy(&buf[12], aes_data, aes_size);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_infoframe_send(__u8 type, __u8 version, __u8 crc,
+ __u8 data_size, __u8 *data)
+{
+ int val;
+ __u8 buf[300];
+
+ if (data_size > INFOFR_MSG_SIZE_MAX)
+ return -1;
+
+ val = HDMI_INFOFR;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = data_size + 4;
+ memcpy(&buf[8], &val, 4);
+ /* data */
+ buf[12] = type;
+ buf[13] = version;
+ buf[14] = crc;
+ buf[15] = data_size;
+ memcpy(&buf[16], data, data_size);
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_vesa_cea_prio_set(__u8 vesa_cea1, __u8 nr1,
+ __u8 vesa_cea2, __u8 nr2,
+ __u8 vesa_cea3, __u8 nr3)
+{
+ int val;
+ __u8 buf[32];
+
+ val = HDMI_VESACEAPRIO_SET;
+ memcpy(&buf[0], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[4], &val, 4);
+ /* len */
+ val = 7;
+ memcpy(&buf[8], &val, 4);
+ /* data */
+ buf[12] = 3;
+ buf[13] = vesa_cea1;
+ buf[14] = nr1;
+ buf[15] = vesa_cea2;
+ buf[16] = nr2;
+ buf[17] = vesa_cea3;
+ buf[18] = nr3;
+ serversocket_write(12 + val, buf);
+
+ return 0;
+}