summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPer Persson <per.xb.persson@stericsson.com>2011-05-20 11:40:47 +0200
committerPer Persson <per.xb.persson@stericsson.com>2011-05-20 11:41:40 +0200
commitef999bf6ae43ef567c76ac0b473c7d4340760ab2 (patch)
treebb85623a9d2faa7c26a67a2cbc47ecfa51971b1c
HDMIservice added
Initial commit ST-Ericsson ID: 326691 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10177 Signed-off-by: Per Persson <per.xb.persson@stericsson.com>
-rw-r--r--Android.mk36
-rw-r--r--include/hdmi_service_api.h106
-rw-r--r--include/hdmi_service_local.h358
-rw-r--r--src/cec.c149
-rw-r--r--src/edid.c538
-rw-r--r--src/hdcp.c257
-rw-r--r--src/hdmi_service.c1104
-rw-r--r--src/hdmi_service_api.c90
-rw-r--r--src/hdmi_service_start.c29
-rw-r--r--src/kevent.c159
-rw-r--r--src/setres.c400
-rw-r--r--src/socket.c316
12 files changed, 3542 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..4d557f2
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Library
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PRELINK_MODULE := false
+LOCAL_SRC_FILES := src/hdmi_service_api.c src/hdmi_service.c src/cec.c \
+ src/edid.c src/hdcp.c src/setres.c src/kevent.c src/socket.c
+LOCAL_CFLAGS := -DANDROID
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_MODULE := libhdmi_service
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_SHARED_LIBRARY)
+
+# Executable, to be used to start service when there is no daemon
+include $(CLEAR_VARS)
+LOCAL_PRELINK_MODULE := false
+LOCAL_SRC_FILES := src/hdmi_service_start.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := libhdmi_service
+LOCAL_MODULE := hdmi_service_st
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_EXECUTABLE)
diff --git a/include/hdmi_service_api.h b/include/hdmi_service_api.h
new file mode 100644
index 0000000..8133d33
--- /dev/null
+++ b/include/hdmi_service_api.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ * License terms: <FOSS license>.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _HDMI_SERVICE_API_H
+#define _HDMI_SERVICE_API_H
+
+/*
+ * Set to 1 to stay alive when system suspends.
+ * Set to 0 to sleep when system suspends.
+ */
+#define HDMI_SERVICE_STAY_ALIVE_DURING_SUSPEND 1
+
+/* If defined, socket usage is hidden for messages from service,
+ * and a callback function is used instead
+ */
+/*#define HDMI_SERVICE_USE_CALLBACK_FN*/
+
+/* Service initialisation, threads creation
+ * Input avoid_return_msg: set to 1 to avoid messages from service.
+ * Return value: socket number where events will be notified.
+ */
+int hdmi_init(int avoid_return_msg);
+
+/* Service exit, threads destruction */
+int hdmi_exit(void);
+
+/* Enable HDMI HW */
+int hdmi_enable(void);
+
+/* Disable HDMI HW */
+int hdmi_disable(void);
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+/* Set callback for reception of messages from service */
+void hdmi_callback_set(void (*hdmi_cb)(int cmd, int data_size, __u8 *data));
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+
+/* Change resolution. cea=0: VESA resolution, cea=1: CEA resolution */
+int hdmi_resolution_set(int cea, int vesaceanr);
+
+/* Release frame buffer */
+int hdmi_fb_release(void);
+
+/* Send CEC message */
+int hdmi_cec_send(__u8 initiator, __u8 destination, __u8 data_size, __u8 *data);
+
+/* Manually request EDID */
+int hdmi_edid_request(__u8 block);
+
+/* Initialise HDCP. AES data is required */
+int hdmi_hdcp_init(__u16 aes_size, __u8 *aes_data);
+
+/* Send Infoframe */
+int hdmi_infoframe_send(__u8 type, __u8 version, __u8 crc, __u8 data_size,
+ __u8 *data);
+
+/* Set preferred resolution priorities */
+int hdmi_vesa_cea_prio_set(__u8 vesa_cea1, __u8 nr1,
+ __u8 vesa_cea2, __u8 nr2,
+ __u8 vesa_cea3, __u8 nr3);
+
+
+/* Messages from service */
+
+/* cmd=HDMI_EDIDRESP data format
+ *u8 result (0 = ok, 1 = not ok)
+ *u8 edid_data[128] (if result == ok)
+ */
+
+/* cmd=HDMI_HDCPSTATE data format
+ *u8 state
+ * state = 0: No Receiver state
+ * state = 1: Receiver connected state
+ * state = 2: No HDCP receiver state
+ * state = 3: No Encryption state
+ * state = 4: Authentication on going state
+ * state = 5: Authentication fail state
+ * state = 6: Authentication succeed state
+ * state = 7: Encryption on going state
+ */
+
+/* HDMI message cmd sent from hdmi_service */
+#define HDMI_PLUGGED_EV 0x10
+#define HDMI_UNPLUGGED_EV 0x11
+#define HDMI_EDIDRESP 0x12
+#define HDMI_CECRECVD 0x13
+#define HDMI_ILLSTATE_POWERED 0x80
+#define HDMI_ILLSTATE_UNPOWERED 0x81
+#define HDMI_ILLSTATE_UNPLUGGED 0x82
+#define HDMI_ILLSTATE_PWRON_PLUGGED 0x83
+#define HDMI_CECSENDERR 0x84
+#define HDMI_HDCPSTATE 0x85
+
+#endif /* #ifdef _HDMI_SERVICE_API_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/hdmi_service_local.h b/include/hdmi_service_local.h
new file mode 100644
index 0000000..83f2bcb
--- /dev/null
+++ b/include/hdmi_service_local.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ * License terms: <FOSS license>.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _HDMI_SERVICE_LOCAL_H
+#define _HDMI_SERVICE_LOCAL_H
+
+enum hdmi_fb_state {
+ HDMI_FB_CLOSED,
+ HDMI_FB_OPENED
+};
+
+enum hdmi_plug_state {
+ HDMI_UNPLUGGED,
+ HDMI_PLUGGED,
+ HDMI_PLUGUNDEF
+};
+
+enum hdmi_power_state {
+ HDMI_POWEROFF,
+ HDMI_POWERON,
+ HDMI_POWERUNDEF
+};
+
+enum hdmi_format {
+ HDMI_FORMAT_HDMI,
+ HDMI_FORMAT_SDTV,
+ HDMI_FORMAT_DVI
+};
+
+struct cmd_data {
+ __u32 cmd;
+ __u32 cmd_id;
+ __u32 data_len;
+ __u8 data[512];
+ struct cmd_data *next;
+};
+
+struct video_format {
+ __u8 cea; /* 0=VESA, 1=CEA */
+ __u8 vesaceanr;
+ __u8 sink_support;
+ __u8 prio;
+};
+
+struct vesacea {
+ __u8 cea;
+ __u8 nr;
+};
+
+struct edid_latency {
+ int video_latency;
+ int audio_latency;
+ int intlcd_video_latency;
+ int intlcd_audio_latency;
+};
+
+typedef void(*cb_fn)(int cmd, int data_length, __u8 *data);
+
+int cecrx_subscribe(void);
+int cecsend(__u32 cmd_id, __u8 in, __u8 dest, __u8 len, __u8 *data);
+int cecrx(void);
+int edid_read(__u8 block, __u8 *data);
+int edid_parse0(__u8 *data, __u8 *extension, struct video_format *, int size);
+int edid_parse1(__u8 *data, struct video_format formats[], int nr_formats,
+ int *basic_audio_support, struct edid_latency *edid_latency);
+int edidreq(__u8 block, __u32 cmd_id);
+int hdcp_init(__u8 *aes);
+int hdcp_state(void);
+int video_formats_clear(void);
+int vesacea_supported(int *nr_supported, struct vesacea vesacea[]);
+int video_formats_supported_hw(void);
+int nr_formats_get(void);
+struct video_format *video_formats_get(void);
+void set_vesacea_prio_all(void);
+int hdmi_fb_chres(__u8 cea, __u8 vesaceanr);
+int vesaceaprio_set(__u8 len, __u8 *data);
+int hdmievclr(__u8 mask);
+void thread_kevent_fn(void *arg);
+int hdmiplug_subscribe(void);
+int hdmi_event(int event);
+int get_best_videoformat(__u8 *cea, __u8 *vesaceanr);
+int listensocket_set(int sock);
+int listensocket_get(void);
+int clientsocket_get(void);
+int cecsenderr(void);
+int get_new_cmd_id_ind(void);
+void thread_socklisten_fn(void *arg);
+int cmd_add(struct cmd_data *cmd);
+int serversocket_create(int avoid_return_msg);
+int serversocket_write(int len, __u8 *data);
+int serversocket_close(void);
+int poweronoff(__u8 onoff);
+int clientsocket_send(__u8 *buf, int len);
+
+int hdmi_service_init(int avoid_return_msg);
+int hdmi_service_exit(void);
+int hdmi_service_enable(void);
+int hdmi_service_disable(void);
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+void hdmi_service_callback_set(cb_fn hdmi_cb);
+cb_fn hdmi_service_callback_get(void);
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+
+int hdmi_service_resolution_set(int cea, int vesaceanr);
+int hdmi_service_fb_release(void);
+int hdmi_service_cec_send(__u8 initiator, __u8 destination, __u8 data_size,
+ __u8 *data);
+int hdmi_service_edid_request(__u8 block);
+int hdmi_service_hdcp_init(__u16 aes_size, __u8 *aes_data);
+int hdmi_service_infoframe_send(__u8 type, __u8 version, __u8 crc,
+ __u8 data_size, __u8 *data);
+int hdmi_service_vesa_cea_prio_set(__u8 vesa_cea1, __u8 nr1,
+ __u8 vesa_cea2, __u8 nr2,
+ __u8 vesa_cea3, __u8 nr3);
+
+#define AES_KEYS_SIZE 297
+#define FORMATS_MAX 35
+
+#define false 0
+#define true 1
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define LOGHDMILIB LOGE
+#define LOGHDMILIB2 LOGE
+#define LOGHDMILIB3(format, ...)
+
+#ifdef ANDROID
+#define FBPATH "/dev/graphics/"
+#else
+#define FBPATH "/dev/"
+#endif
+
+#define SOCKET_LISTEN_PATH "/dev/socket/hdmi_listen"
+
+#define STOREASTEXT_FILE "/sys/class/misc/hdmi/storeastext"
+#define PLUGDETEN_FILE "/sys/class/misc/hdmi/plugdeten"
+#define EVENT_FILE "/sys/class/misc/hdmi/evread"
+#define EVENTCLR_FILE "/sys/class/misc/hdmi/evclr"
+#define EVWAKEUP_FILE "/sys/class/misc/hdmi/evwakeup"
+#define EDIDREAD_FILE "/sys/class/misc/hdmi/edidread"
+#define DISPONOFF_FILE "/sys/devices/av8100_hdmi.2/disponoff"
+#define HDCPCHKAESOTP_FILE "/sys/class/misc/hdmi/hdcpchkaesotp"
+#define HDCPLOADAES_FILE "/sys/class/misc/hdmi/hdcploadaes"
+#define HDCPSTATEGET_FILE "/sys/class/misc/hdmi/hdcpstateget"
+#define HDCPAUTH_FILE "/sys/class/misc/hdmi/hdcpauthencr"
+#define HDCPEVEN_FILE "/sys/class/misc/hdmi/hdcpeven"
+#define CECSEND_FILE "/sys/class/misc/hdmi/cecsend"
+#define CECRXEVEN_FILE "/sys/class/misc/hdmi/ceceven"
+#define CECREAD_FILE "/sys/class/misc/hdmi/cecread"
+#define INFOFRSEND_FILE "/sys/class/misc/hdmi/infofrsend"
+#define POWERONOFF_FILE "/sys/class/misc/hdmi/poweronoff"
+#define HDMIFORMAT_FILE "/sys/devices/av8100_hdmi.2/hdmisdtvswitch"
+#define VESACEAFORMATS_FILE "/sys/devices/av8100_hdmi.2/vesacea"
+#define TIMING_FILE "/sys/devices/av8100_hdmi.2/timing"
+#define STAYALIVE_FILE "/sys/devices/av8100_hdmi.2/stayalive"
+
+#define STOREASTEXT_STR "01" /* Use hextext format in sysfs files */
+#define STOREASBIN_STR "00" /* Use binary format in sysfs files */
+
+#define HDMI_PLUGGED_IN_EVSTR "01"
+#define HDMI_PLUGGED_OUT_EVSTR "02"
+#define HDMI_CEC_EVSTR "04"
+#define HDMI_HDCP_EVSTR "08"
+#define HDMI_CECTXERR_EVSTR "10"
+#define HDMI_USER_EVSTR "20"
+
+#define EDIDREAD_SIZE 0x80
+#define POLL_READ_SIZE 1
+#define CEAPRIO_MAX_SIZE 10
+#define VESACEAPRIO_DEFAULT 254
+#define OTP_UNPROGGED 0
+#define OTP_PROGGED 1
+#define TIMING_SIZE 32
+#define CEC_MSG_SIZE_MAX 15
+#define INFOFR_MSG_SIZE_MAX 27
+
+#define HDMIEVENT_POLLSIZEFAIL -1
+#define HDMIEVENT_EVENTUNKNOWN -2
+#define HDMIEVENT_NOEVENT 0
+#define HDMIEVENT_HDMIPLUGGED 0x1
+#define HDMIEVENT_HDMIUNPLUGGED 0x2
+#define HDMIEVENT_CEC 0x4
+#define HDMIEVENT_HDCP 0x8
+#define HDMIEVENT_CECTXERR 0x10
+#define HDMIEVENT_WAKEUP 0x20
+
+#define EVENTMASK_ALL 0xFF
+#define EVENTMASK_PLUG 0x03
+
+/* User commands */
+#define HDMIEVENT_CMD 0x010000
+#define CMD_OFFSET 0
+#define CMDID_OFFSET 4
+#define CMDLEN_OFFSET 8
+#define CMDBUF_OFFSET 12
+
+
+#define LOADAES_OK 0
+#define LOADAES_NOT_OK -1
+#define LOADAES_NOT_FUSED -2
+#define LOADAES_CRC_MISMATCH -3
+
+#define RESULT_OK 0
+#define STOREAS_FAIL -1
+#define SYSFS_FILE_FAILED -2
+#define EDIDREAD_FAIL -3
+#define EDIDREAD_NOEXT -4
+#define EDIDREAD_NOVIDEO -5
+#define HDCP_OK 0
+#define AESKEYS_FAIL -1
+#define HDCPSTATE_FAIL -2
+#define HDCPAUTHENCR_FAIL -3
+
+#define EDID_BL0_HEADER_OFFSET 0x00
+#define EDID_BL0_VERSION_OFFSET 0x12
+#define EDID_BL0_REVISION_OFFSET 0x13
+#define EDID_BL0_EST_TIMING1_OFFSET 0x23
+#define EDID_BL0_EST_TIMING2_OFFSET 0x24
+#define EDID_BL0_EXTFLAG_OFFSET 0x7E
+#define EDID_BL0_ESTTIM1_OFFSET 0x23
+#define EDID_BL0_ESTTIM2_OFFSET 0x24
+#define EDID_BL0_STDTIM1_OFFSET 0x26
+#define EDID_BL1_REVNR_OFFSET 0x01
+#define EDID_BL1_OFFSET_OFFSET 0x02
+#define EDID_BL1_AUDIO_SUPPORT_OFFSET 0x03
+#define EDID_BL1_ESTTIM3_1_FLAG_OFFSET 0x48
+#define EDID_BL1_ESTTIM3_2_FLAG_OFFSET 0x5A
+#define EDID_BL1_ESTTIM3_3_FLAG_OFFSET 0x6C
+#define EDID_BL1_ESTTIM3_BYTE_START 6
+#define EDID_BL1_ESTTIM3_BYTE_END 11
+#define EDID_BL1_STDTIM9_1_FLAG_OFFSET 0x48
+#define EDID_BL1_STDTIM9_2_FLAG_OFFSET 0x5A
+#define EDID_BL1_STDTIM9_3_FLAG_OFFSET 0x6C
+#define EDID_BL1_STDTIM9_BYTE_START 5
+#define EDID_SVD_ID_MASK 0x7F
+#define EDID_EXTVER_3 0x03
+#define EDID_NO_DATA 0x04
+#define EDID_BLK_START 0x04
+#define EDID_BLK_CODE_MSK 0xE0
+#define EDID_BLK_CODE_SHIFT 5
+#define EDID_BLK_LENGTH_MSK 0x1F
+#define EDID_CODE_VIDEO 0x02
+#define EDID_CODE_VSDB 0x03
+#define EDID_BL0_STDTIM1_SIZE 8
+#define EDID_BL1_STDTIM9_SIZE 6
+#define EDID_STDTIM_AR_MASK 0xC0
+#define EDID_STDTIM_AR_SHIFT 6
+#define EDID_STDTIM_FREQ_MASK 0x3F
+#define EDID_STDTIM_FREQ_SHIFT 0
+#define EDID_BASIC_AUDIO_SUPPORT_MASK 0x40
+#define EDID_VSD_PHYS_SRC 4
+#define EDID_VSD_LATENCY_IND 8
+#define EDID_VSD_LAT_FLD_MASK 0x80
+#define EDID_VSD_INTLCD_LAT_FLD_MASK 0x40
+#define EDID_VSD_VID_LAT 9
+#define EDID_VSD_AUD_LAT 10
+#define EDID_VSD_INTLCD_VID_LAT 11
+#define EDID_VSD_INTLCD_AUD_LAT 12
+
+/* HDCP states */
+#define HDCP_STATE_NO_RECV 0
+#define HDCP_STATE_RECV_CONN 1
+#define HDCP_STATE_NO_HDCP 2
+#define HDCP_STATE_NO_ENCR 3
+#define HDCP_STATE_AUTH_ONGOING 4
+#define HDCP_STATE_AUTH_FAIL 5
+#define HDCP_STATE_AUTH_SUCCEDED 6
+#define HDCP_STATE_ENCR_ONGOING 7
+
+#define VIDEO_FORMAT_DEFAULT 1 /* 640x480@60P */
+
+#define STARTUP_DELAY_US 6000000
+#define HDCPAUTH_WAITTIME 1000000
+#define LOADAES_WAITTIME 250000
+#define EDIDREAD_WAITTIME 2000000
+
+/* Socket listen thread */
+#define SOCKET_DATA_MAX 256
+#define SOCKET_MAX_CONN 1
+
+/* Command format */
+/* Message data format
+ *u32 cmd
+ *u32 cmd_id
+ *u32 size
+ *u8 data[size] data format is specified below
+ */
+
+/* HDMI message cmd sent to hdmi_service */
+#define HDMI_ENABLE 0x1
+
+#define HDMI_DISABLE 0x2
+
+/* cmd=HDMI_EDIDREQ data format
+ *u8 block (0 or 1)
+ */
+#define HDMI_EDIDREQ 0x3
+
+/* cmd=HDMI_CECSEND and HDMI_CECRECVD data format
+ *u8 initiator
+ *u8 destination
+ *u8 cec_data_size
+ *u8 cec_data[cec_data_size]
+ */
+#define HDMI_CECSEND 0x4
+
+/* cmd=HDMI_FB_RES_SET data format
+ *u8 vesa(0)/cea(1)
+ *u8 vesa/cea nr
+ */
+#define HDMI_FB_RES_SET 0x5
+
+#define HDMI_FB_RELEASE 0x6
+
+/* cmd=HDMI_HDCP_INIT data format
+ *u8 aesdata[297]
+ */
+#define HDMI_HDCP_INIT 0x7
+
+/* cmd=HDMI_VESACEAPRIO_SET data format
+ *u8 nrofprios
+ *u8 vesa/cea[0] 0=VESA, 1=CEA
+ *u8 vesaceanr[0] vesanr or ceanr Prio 1
+ *u8 vesa/cea[1] 0=VESA, 1=CEA
+ *u8 vesaceanr[1] vesanr or ceanr Prio 2
+ *....
+ *u8 vesa/cea[size-1] 0=VESA, 1=CEA
+ *u8 vesaceanr[size-1] vesanr or ceanr Prio size
+ */
+#define HDMI_VESACEAPRIO_SET 0x8
+
+/* cmd=HDMI_INFOFR data format
+ *u8 type
+ *u8 version
+ *u8 crc
+ *u8 size
+ *u8 data[size]
+ */
+#define HDMI_INFOFR 0x9
+
+#define HDMI_EXIT 0xFF
+
+
+
+#endif /* #ifdef _HDMI_SERVICE_LOCAL_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/cec.c b/src/cec.c
new file mode 100644
index 0000000..d5125d9
--- /dev/null
+++ b/src/cec.c
@@ -0,0 +1,149 @@
+/*
+ * 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 <string.h> /* String handling */
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+const __u8 cecrxeven_val[] = {0x01}; /* Enable CEC RX events */
+int cectx_cmd_id;
+
+static int cectxcmdid_set(int cmd_id)
+{
+ cectx_cmd_id = cmd_id;
+ return 0;
+}
+
+static int cectxcmdid_get(void)
+{
+ return cectx_cmd_id;
+}
+
+/* Subscribe for incoming CEC messages */
+int cecrx_subscribe(void)
+{
+ int cecrxfd;
+
+ cecrxfd = open(CECRXEVEN_FILE, O_WRONLY);
+ if (cecrxfd < 0) {
+ LOGHDMILIB(" failed to open %s", CECRXEVEN_FILE);
+ return -1;
+ }
+ write(cecrxfd, cecrxeven_val, sizeof(cecrxeven_val));
+ close(cecrxfd);
+ return 0;
+}
+
+int cecsenderr(void)
+{
+ int val;
+ __u8 buf[32];
+ int cmd_id;
+
+ cmd_id = cectxcmdid_get();
+
+ val = HDMI_CECSENDERR;
+ 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);
+}
+
+/* Send CEC message */
+int cecsend(__u32 cmd_id, __u8 in, __u8 dest, __u8 len, __u8 *data)
+{
+ int cecsendfd;
+ int res;
+ char buf[128];
+
+ LOGHDMILIB("%s begin", __func__);
+
+ cectxcmdid_set(cmd_id);
+
+ res = 0;
+ buf[0] = in;
+ buf[1] = dest;
+ buf[2] = len;
+ memcpy(&buf[3], data, len);
+
+ /* Send CEC cmd */
+ cecsendfd = open(CECSEND_FILE, O_WRONLY);
+ if (cecsendfd <= 0) {
+ LOGHDMILIB("***** Failed to open %s *****\n", CECSEND_FILE);
+ goto cecsend_err;
+ }
+
+ res = write(cecsendfd, buf, len + 3);
+ if (res != len + 3) {
+ LOGHDMILIB("***** cecsend failed %d *****\n", res);
+ close(cecsendfd);
+ goto cecsend_err;
+ }
+
+ close(cecsendfd);
+
+ LOGHDMILIB("%s end", __func__);
+ return 0;
+
+cecsend_err:
+ cecsenderr();
+ LOGHDMILIB("%s end", __func__);
+ return -1;
+}
+
+/* Read received CEC message and forward on client socket */
+int cecrx(void)
+{
+ int cecreadfd;
+ __u8 buf[32];
+ __u8 cecdata[32];
+ int cecsize;
+ int cnt;
+ int val;
+ int res = 0;
+ __u32 cmd_id;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ cmd_id = get_new_cmd_id_ind();
+ cecreadfd = open(CECREAD_FILE, O_RDONLY);
+ if (cecreadfd < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", CECREAD_FILE);
+ return -1;
+ }
+ cecsize = read(cecreadfd, buf, sizeof(buf));
+ close(cecreadfd);
+
+ for (cnt = 0; cnt < cecsize; cnt++)
+ LOGHDMILIB2("cecrx[%d]:%x", cnt, buf[cnt]);
+
+ val = HDMI_CECRECVD;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ memcpy(&buf[CMDID_OFFSET], &cmd_id, 4);
+ val = cecsize;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ memcpy(&buf[CMDBUF_OFFSET], cecdata, val);
+
+ /* Send on socket */
+ res = clientsocket_send(buf, CMDBUF_OFFSET + val);
+
+ LOGHDMILIB("%s end", __func__);
+ return res;
+}
diff --git a/src/edid.c b/src/edid.c
new file mode 100644
index 0000000..1733a73
--- /dev/null
+++ b/src/edid.c
@@ -0,0 +1,538 @@
+/*
+ * 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 <string.h> /* String handling */
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+struct edid_stdtim_ar {
+ int x;
+ int y;
+};
+
+const __u8 edidreqbl0[] = {0xA0, 0x00}; /* Request EDID block 0 */
+const __u8 edidreqbl1[] = {0xA0, 0x01}; /* Request EDID block 1 */
+const __u8 edid_block0_start[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+const __u8 edid_stdtim9_flag[] = {0x00, 0x00, 0x00, 0xFA, 0x00};
+const __u8 edid_esttim3_flag[] = {0x00, 0x00, 0x00, 0xF7, 0x00};
+const __u8 edid_esttim1_2_offset[] = {EDID_BL0_ESTTIM1_OFFSET,
+ EDID_BL0_ESTTIM2_OFFSET};
+const __u8 edid_esttim3_flag_offset[] = {EDID_BL1_ESTTIM3_1_FLAG_OFFSET,
+ EDID_BL1_ESTTIM3_2_FLAG_OFFSET,
+ EDID_BL1_ESTTIM3_3_FLAG_OFFSET};
+const __u8 edid_stdtim9_flag_offset[] = {EDID_BL1_STDTIM9_1_FLAG_OFFSET,
+ EDID_BL1_STDTIM9_2_FLAG_OFFSET,
+ EDID_BL1_STDTIM9_3_FLAG_OFFSET};
+/* Aspect ratios */
+const struct edid_stdtim_ar edid_stdtim_ar[] = {
+ {16, 10},
+ {4, 3},
+ {5, 4},
+ {16, 9}
+};
+
+struct vesa_modes {
+ int xres;
+ int yres;
+ int freq;
+ int vesa_nr;
+};
+
+static struct vesa_modes vesa_modes[] = {
+ {800, 600, 60, 9},
+ {848, 480, 60, 14},
+ {1024, 768, 60, 16},
+ {1280, 768, 60, 23},
+ {1280, 800, 60, 28},
+ {1360, 768, 60, 39},
+ {1366, 768, 60, 81}
+};
+
+static int get_vesanr_from_est_timing(int timing, int byte, int bit)
+{
+ int vesa_nr = -1;
+
+ LOGHDMILIB2("timing:%d bit:%d", timing, bit);
+
+ switch (timing) {
+ /* Established Timing 1 */
+ case 1:
+ switch (bit) {
+ case 5:
+ vesa_nr = 4;
+ break;
+ case 0:
+ vesa_nr = 9;
+ break;
+ default:
+ break;
+ }
+ break;
+ /* Established Timing 2 */
+ case 2:
+ switch (bit) {
+ case 3:
+ vesa_nr = 16;
+ break;
+ default:
+ break;
+ }
+ break;
+ /* Established Timing 3 */
+ case 3:
+ switch (byte) {
+ case 6:
+ switch (bit) {
+ case 3:
+ vesa_nr = 14;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 7:
+ switch (bit) {
+ case 7:
+ vesa_nr = 23;
+ break;
+ case 6:
+ vesa_nr = 22;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 8:
+ switch (bit) {
+ case 7:
+ vesa_nr = 39;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return vesa_nr;
+}
+
+int get_vesanr_from_std_timing(int xres, int yres, int freq)
+{
+ int vesa_nr = -1;
+ int nr_of_timings;
+ int index;
+
+ nr_of_timings = sizeof(vesa_modes)/sizeof(vesa_modes[0]);
+ for (index = 0; index < nr_of_timings; index++) {
+ if ((xres == vesa_modes[index].xres) &&
+ (yres == vesa_modes[index].yres) &&
+ (freq == vesa_modes[index].freq)) {
+ vesa_nr = vesa_modes[index].vesa_nr;
+ break;
+ }
+ }
+
+ return vesa_nr;
+}
+
+/* Request and read EDID message for specified block */
+int edid_read(__u8 block, __u8 *data)
+{
+ int edidread;
+ int res;
+ int result = 0;
+ __u8 buf[16];
+ int size;
+
+ LOGHDMILIB("EDID read blk %d", block);
+
+ /* Request edid block 0 */
+ edidread = open(EDIDREAD_FILE, O_RDWR);
+ if (edidread < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", EDIDREAD_FILE);
+ result = -1;
+ goto edid_read_end2;
+ }
+
+ if (block == 0) {
+ size = sizeof(edidreqbl0);
+ memcpy(buf, edidreqbl0, size);
+ } else {
+ size = sizeof(edidreqbl1);
+ memcpy(buf, edidreqbl1, size);
+ }
+
+ res = write(edidread, buf, size);
+ if (res < 0) {
+ LOGHDMILIB("***** Failed to write %s *****", EDIDREAD_FILE);
+ result = -2;
+ goto edid_read_end1;
+ }
+
+ /* Check edid response */
+ lseek(edidread, 0, SEEK_SET);
+ res = read(edidread, data, EDIDREAD_SIZE);
+ if (res != EDIDREAD_SIZE) {
+ LOGHDMILIB("***** %s read error size: %d *****", EDIDREAD_FILE,
+ res);
+ result = -3;
+ goto edid_read_end1;
+ }
+
+edid_read_end1:
+ close(edidread);
+edid_read_end2:
+ return result;
+}
+
+/* Parse EDID block 0 */
+int edid_parse0(__u8 *data, __u8 *extension, struct video_format formats[],
+ int nr_formats)
+{
+ __u8 version;
+ __u8 revision;
+ __u8 est_timing;
+ int findex;
+ int vesa_nr;
+ int bit;
+ int cnt;
+ int index;
+ int xres;
+ int yres;
+ int byte;
+ int ar_index;
+ int freq;
+ __u8 edidp;
+
+ *extension = 0;
+
+ /* Header */
+ if (memcmp(data + EDID_BL0_HEADER_OFFSET, edid_block0_start, 8) != 0) {
+ LOGHDMILIB("edid response:\n%02x %02x %02x %02x %02x %02x %02x "
+ "%02x",
+ *(data + 1),
+ *(data + 2),
+ *(data + 3),
+ *(data + 4),
+ *(data + 5),
+ *(data + 6),
+ *(data + 7),
+ *(data + 8)
+ );
+ return EDIDREAD_FAIL;
+ } else {
+ LOGHDMILIB("%s", "--- EDID block 0 start OK ---");
+ }
+
+ /* Ver and Rev */
+ version = *(data + EDID_BL0_VERSION_OFFSET);
+ revision = *(data + EDID_BL0_REVISION_OFFSET);
+ LOGHDMILIB("Ver:%d Rev:%d", version, revision);
+
+ /* Read Established Timings 1&2 and set sink_support */
+ for (index = 0; index <= 1; index++) {
+ est_timing = *(data + edid_esttim1_2_offset[index]);
+ LOGHDMILIB2("EstTim%d:%02x", index + 1, est_timing);
+ if (est_timing == 0)
+ continue;
+
+ for (bit = 7; bit >= 0; bit--) {
+ if (est_timing & (1 << bit)) {
+ vesa_nr = get_vesanr_from_est_timing(index + 1,
+ 0, bit);
+ LOGHDMILIB2("vesa_nr:%d", vesa_nr);
+ if (vesa_nr < 1)
+ continue;
+
+ LOGHDMILIB2("EstTim1&2 try vesa_nr:%d",
+ vesa_nr);
+ for (cnt = 0; cnt < nr_formats; cnt++) {
+ LOGHDMILIB3("with:%d",
+ formats[cnt].vesaceanr);
+ if ((formats[cnt].cea == 0) &&
+ (formats[cnt].vesaceanr ==
+ vesa_nr)) {
+ formats[cnt].sink_support = 1;
+ LOGHDMILIB("EstTim1&2 %d "
+ "vesa_nr:%d",
+ index + 1,
+ vesa_nr);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Read Standard Timings 1-8 and set sink_support*/
+ for (index = 0; index < EDID_BL0_STDTIM1_SIZE; index++) {
+ edidp = EDID_BL0_STDTIM1_OFFSET + index * 2;
+ xres = (*(data + edidp) + 31) * 8;
+ byte = *(data + edidp + 1);
+ ar_index = (byte * EDID_STDTIM_AR_MASK) >> EDID_STDTIM_AR_SHIFT;
+ yres = xres * edid_stdtim_ar[ar_index].y /
+ edid_stdtim_ar[ar_index].x;
+ freq = (byte * EDID_STDTIM_FREQ_MASK) >> EDID_STDTIM_FREQ_SHIFT;
+ vesa_nr = get_vesanr_from_std_timing(xres, yres, freq);
+ if (vesa_nr < 1)
+ continue;
+
+ LOGHDMILIB2("StdTim1to8 try vesa_nr:%d", vesa_nr);
+ for (cnt = 0; cnt < nr_formats; cnt++) {
+ LOGHDMILIB3("with:%d",
+ formats[cnt].vesaceanr);
+ if ((formats[cnt].cea == 0) &&
+ (formats[cnt].vesaceanr ==
+ vesa_nr)) {
+ formats[cnt].sink_support = 1;
+ LOGHDMILIB("StdTim1to8 %d vesa_nr:%d",
+ index + 1,
+ vesa_nr);
+ break;
+ }
+ }
+ }
+
+ if (*(data + EDID_BL0_EXTFLAG_OFFSET) != 0)
+ *extension = 1;
+
+ return RESULT_OK;
+}
+
+/* Parse EDID block 1 */
+int edid_parse1(__u8 *data, struct video_format formats[], int nr_formats,
+ int *basic_audio_support, struct edid_latency *edid_latency)
+{
+ __u8 rev;
+ __u8 offset;
+ __u8 blockp;
+ __u8 code;
+ __u8 length = 0;
+ __u8 ceanr;
+ int index;
+ int index2;
+ int cnt;
+ __u8 est_timing3;
+ int byte;
+ int bit;
+ int vesa_nr;
+ int xres;
+ int yres;
+ int ar_index;
+ int freq;
+ __u8 edidp;
+ __u8 *p;
+
+ rev = *(data + EDID_BL1_REVNR_OFFSET);
+ offset = *(data + EDID_BL1_OFFSET_OFFSET);
+
+ LOGHDMILIB("rev:%d offset:%d", rev, offset);
+
+ if ((rev != EDID_EXTVER_3) || (offset == 0) ||
+ (offset == EDID_NO_DATA)) {
+ LOGHDMILIB("%s", "No video block");
+ return EDIDREAD_NOVIDEO;
+ }
+
+ /* Check Audio support */
+ if (*(data + EDID_BL1_AUDIO_SUPPORT_OFFSET) &
+ EDID_BASIC_AUDIO_SUPPORT_MASK) {
+ *basic_audio_support = 1;
+ }
+
+ for (edidp = EDID_BLK_START; edidp < offset;
+ edidp = edidp + length + 1) {
+ code = (*(data + edidp) & EDID_BLK_CODE_MSK) >>
+ EDID_BLK_CODE_SHIFT;
+ length = *(data + edidp) & EDID_BLK_LENGTH_MSK;
+
+ if ((offset + length) >= EDIDREAD_SIZE)
+ return EDIDREAD_FAIL;
+
+ LOGHDMILIB2("code:%d blklen:%d", code, length);
+
+ switch (code) {
+ case EDID_CODE_VIDEO:
+ for (blockp = edidp + 1; blockp < edidp + 1 + length;
+ blockp++) {
+ ceanr = *(data + blockp) & EDID_SVD_ID_MASK;
+ LOGHDMILIB2("try ceanr:%d", ceanr);
+ for (cnt = 0; cnt < nr_formats; cnt++) {
+ LOGHDMILIB3("with:%d",
+ formats[cnt].vesaceanr);
+ if ((formats[cnt].cea == 1) &&
+ (formats[cnt].vesaceanr ==
+ ceanr)) {
+ formats[cnt].sink_support = 1;
+ LOGHDMILIB("cea:%d", ceanr);
+ break;
+ }
+ }
+ }
+ break;
+
+ case EDID_CODE_VSDB:
+ p = data + edidp;
+ if (length >= (EDID_VSD_PHYS_SRC + 1)) {
+ LOGHDMILIB("source physaddr:%02x%02x",
+ *(p + EDID_VSD_PHYS_SRC),
+ *(p + EDID_VSD_PHYS_SRC + 1));
+
+ /*TODO logical addr (HDMI spec p.192)*/
+ }
+
+ /* Video and Audio latency */
+ if ((length >= EDID_VSD_AUD_LAT) &&
+ (*(p + EDID_VSD_LATENCY_IND) &
+ EDID_VSD_LAT_FLD_MASK )) {
+ edid_latency->video_latency =
+ 2 * (*(p + EDID_VSD_VID_LAT) - 1);
+ edid_latency->audio_latency =
+ 2 * (*(p + EDID_VSD_AUD_LAT) - 1);
+ }
+
+ /* Interlaced Video and Audio latency */
+ if ((length >= EDID_VSD_INTLCD_AUD_LAT) &&
+ (*(p + EDID_VSD_LATENCY_IND) &
+ EDID_VSD_INTLCD_LAT_FLD_MASK )) {
+ edid_latency->intlcd_video_latency =
+ 2 * (*(p + EDID_VSD_INTLCD_VID_LAT) - 1);
+ edid_latency->audio_latency =
+ 2 * (*(p + EDID_VSD_INTLCD_AUD_LAT) - 1);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Read Established Timing 3 and set sink_support */
+ for (index = 0; index <= 2; index++) {
+ edidp = edid_esttim3_flag_offset[index];
+
+ /* Check for Established Timing3 flag */
+ if (memcmp(data + edidp, edid_esttim3_flag,
+ sizeof(edid_esttim3_flag)) != 0)
+ /* Flag mismatch, this is not Established Timing 3 */
+ continue;
+
+ for (byte = EDID_BL1_ESTTIM3_BYTE_START;
+ byte <= EDID_BL1_ESTTIM3_BYTE_END; byte++) {
+ est_timing3 = *(data + edidp + byte);
+ for (bit = 7; bit >= 0; bit--) {
+ if ((est_timing3 & (1 << bit)) == 0)
+ /* Not supported in sink */
+ continue;
+
+ vesa_nr = get_vesanr_from_est_timing(3, byte,
+ bit);
+ /* Set sink_suuport */
+ LOGHDMILIB2("EstTim3 try vesa_nr:%d", vesa_nr);
+ for (cnt = 0; cnt < nr_formats; cnt++) {
+ LOGHDMILIB3("with:%d",
+ formats[cnt].vesaceanr);
+ if ((formats[cnt].cea == 0) &&
+ (formats[cnt].vesaceanr ==
+ vesa_nr)) {
+ formats[cnt].sink_support = 1;
+ LOGHDMILIB("EstTim3 vesa_nr:%d",
+ vesa_nr);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Read Standard Timings 9-16 and set sink_support*/
+ for (index2 = 0; index2 <= 2; index2++) {
+ edidp = edid_stdtim9_flag_offset[index2];
+
+ /* Check for Standard Timing flag */
+ if (memcmp(data + edidp, edid_stdtim9_flag,
+ sizeof(edid_stdtim9_flag)) != 0)
+ /* Flag mismatch, this is not Standard Timing 9-16 */
+ continue;
+
+ for (index = 0; index < EDID_BL1_STDTIM9_SIZE; index++) {
+ edidp += EDID_BL1_STDTIM9_BYTE_START + index * 2;
+ xres = (*(data + edidp) + 31) * 8;
+ byte = *(data + edidp + 1);
+ ar_index = (byte * EDID_STDTIM_AR_MASK) >>
+ EDID_STDTIM_AR_SHIFT;
+ yres = xres * edid_stdtim_ar[ar_index].y /
+ edid_stdtim_ar[ar_index].x;
+ freq = (byte * EDID_STDTIM_FREQ_MASK) >>
+ EDID_STDTIM_FREQ_SHIFT;
+ vesa_nr = get_vesanr_from_std_timing(xres, yres, freq);
+ LOGHDMILIB2("StdTim9to16 try vesa_nr:%d", vesa_nr);
+ for (cnt = 0; cnt < nr_formats; cnt++) {
+ LOGHDMILIB3("with:%d",
+ formats[cnt].vesaceanr);
+ if ((formats[cnt].cea == 0) &&
+ (formats[cnt].vesaceanr ==
+ vesa_nr)) {
+ formats[cnt].sink_support = 1;
+ LOGHDMILIB("StdTim9to16 %d vesa_nr:%d",
+ index + 1,
+ vesa_nr);
+ break;
+ }
+ }
+ }
+ }
+
+ return RESULT_OK;
+}
+
+/* Get EDID message of specified block and send it on client socket */
+int edidreq(__u8 block, __u32 cmd_id)
+{
+ int res = 0;
+ int ret = 0;
+ int edidsize = 0;
+ int val;
+ __u8 buf[512];
+ __u8 ediddata[EDIDREAD_SIZE];
+
+ LOGHDMILIB("%s begin", __func__);
+
+ /* Request EDID */
+ res = edid_read(block, ediddata);
+ if (res == 0)
+ edidsize = EDIDREAD_SIZE;
+
+ val = HDMI_EDIDRESP;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ memcpy(&buf[CMDID_OFFSET], &cmd_id, 4);
+ val = edidsize + 1;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ buf[CMDBUF_OFFSET] = res;
+ memcpy(&buf[CMDBUF_OFFSET + 1], ediddata, edidsize);
+
+ /* Send on socket */
+ ret = clientsocket_send(buf, CMDBUF_OFFSET + val);
+
+ LOGHDMILIB("%s end", __func__);
+ return ret;
+}
diff --git a/src/hdcp.c b/src/hdcp.c
new file mode 100644
index 0000000..170afbb
--- /dev/null
+++ b/src/hdcp.c
@@ -0,0 +1,257 @@
+/*
+ * 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 <string.h> /* String handling */
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+const __u8 hdcp_auth_start_val[] = {0x01, 0x00}; /* Start authentication */
+const __u8 hdcp_encr_start_val[] = {0x03, 0x01}; /* Start encryption */
+const __u8 hdcp_encr_stop_val[] = {0x00, 0x00}; /* Stop encryption */
+const __u8 hdcp_even_val[] = {0x01}; /* Enable HDCP events */
+
+static char *dbg_otp(int value)
+{
+ switch (value) {
+ case OTP_UNPROGGED:
+ return "OTP IS NOT PROGRAMMED";
+ break;
+ case OTP_PROGGED:
+ return "OTP IS PROGRAMMED";
+ break;
+ default:
+ return "OTP status UNKNOWN";
+ break;
+ }
+}
+
+static char *dbg_loadaes(int value)
+{
+ switch (value) {
+ case LOADAES_OK:
+ return "LOAD AES OK\n";
+ break;
+ case LOADAES_NOT_OK:
+ return "LOAD AES FAILED\n";
+ break;
+ case LOADAES_NOT_FUSED:
+ return "LOAD AES FAILED NOT FUSED\n";
+ break;
+ case LOADAES_CRC_MISMATCH:
+ return "LOAD AES FAILED CRC MISMATCH\n";
+ break;
+ default:
+ return "LOAD AES result UNKNOWN\n";
+ break;
+ }
+}
+
+static char *dbg_hdcpstate(int value)
+{
+ switch (value) {
+ case HDCP_STATE_NO_RECV:
+ return "HDCP STATE NO_RECV";
+ break;
+ case HDCP_STATE_RECV_CONN:
+ return "HDCP STATE RECV_CONN";
+ break;
+ case HDCP_STATE_NO_HDCP:
+ return "HDCP STATE NO_HDCP";
+ break;
+ case HDCP_STATE_NO_ENCR:
+ return "HDCP STATE NO_ENCR";
+ break;
+ case HDCP_STATE_AUTH_ONGOING:
+ return "HDCP STATE AUTH_ONGOING";
+ break;
+ case HDCP_STATE_AUTH_FAIL:
+ return "HDCP STATE AUTH_FAIL";
+ break;
+ case HDCP_STATE_AUTH_SUCCEDED:
+ return "HDCP STATE AUTH_SUCCEDED";
+ break;
+ case HDCP_STATE_ENCR_ONGOING:
+ return "HDCP STATE ENCR_ONGOING";
+ break;
+ default:
+ return "HDCP STATE UNKNOWN";
+ break;
+ }
+}
+
+/* Load aes keys and start hdcp encryption */
+int hdcp_init(__u8 *aes)
+{
+ int hdcpchkaesotp;
+ int hdcploadaes;
+ int hdcpauthencr;
+ int hdcpeven;
+ int res;
+ int value = 0;
+ char buf[128];
+ int result = HDCP_OK;
+ int events;
+
+ /* Check if OTP is fused */
+ hdcpchkaesotp = open(HDCPCHKAESOTP_FILE, O_RDONLY);
+ if (hdcpchkaesotp < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", HDCPCHKAESOTP_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+ res = read(hdcpchkaesotp, buf, sizeof(buf));
+ close(hdcpchkaesotp);
+ if (res != 1) {
+ LOGHDMILIB("***** %s read error *****", HDCPCHKAESOTP_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+ value = *buf;
+ LOGHDMILIB("%s", dbg_otp(value));
+
+ if (value == OTP_PROGGED) {
+ /* Subscribe for hdcp events */
+ hdcpeven = open(HDCPEVEN_FILE, O_WRONLY);
+ if (hdcpeven < 0) {
+ LOGHDMILIB("***** Failed to open %s *****",
+ HDCPEVEN_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+ write(hdcpeven, hdcp_even_val, sizeof(hdcp_even_val));
+ close(hdcpeven);
+
+ /* Write aes keys */
+ hdcploadaes = open(HDCPLOADAES_FILE, O_WRONLY);
+ if (hdcploadaes < 0) {
+ LOGHDMILIB("***** Failed to open %s *****",
+ HDCPLOADAES_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+ res = write(hdcploadaes, aes, AES_KEYS_SIZE);
+ close(hdcploadaes);
+ if (res != AES_KEYS_SIZE) {
+ LOGHDMILIB("***** Failed to write hdcploadaes %d "
+ "*****", res);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+
+ usleep(LOADAES_WAITTIME);
+
+ /* Check result */
+ hdcploadaes = open(HDCPLOADAES_FILE, O_RDONLY);
+ if (hdcploadaes < 0) {
+ LOGHDMILIB("***** Failed to open %s *****",
+ HDCPLOADAES_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+ res = read(hdcploadaes, buf, sizeof(buf));
+ close(hdcploadaes);
+ if (res != 1) {
+ LOGHDMILIB("***** %s read error *****",
+ HDCPLOADAES_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+ value = *buf;
+ LOGHDMILIB("%s", dbg_loadaes(value));
+ if (value == LOADAES_OK) {
+ LOGHDMILIB("%s", "--- LOAD AES keys OK ---");
+ } else {
+ result = AESKEYS_FAIL;
+ goto hdcp_end;
+ }
+
+ usleep(LOADAES_WAITTIME);
+
+ /* Start HDCP encryption */
+ hdcpauthencr = open(HDCPAUTH_FILE, O_WRONLY);
+ if (hdcpauthencr < 0) {
+ LOGHDMILIB("***** Failed to open %s *****",
+ HDCPAUTH_FILE);
+ result = HDCPAUTHENCR_FAIL;
+ goto hdcp_end;
+ }
+ res = write(hdcpauthencr, hdcp_encr_start_val,
+ sizeof(hdcp_encr_start_val));
+ close(hdcpauthencr);
+ if (res != sizeof(hdcp_encr_start_val)) {
+ LOGHDMILIB("***** Failed to write hdcpauthencr %d "
+ "*****", res);
+ result = HDCPAUTHENCR_FAIL;
+ goto hdcp_end;
+ }
+ usleep(HDCPAUTH_WAITTIME);
+
+ } else {
+ printf("***** Missing aes file or HDCP AES OTP is not fused."
+ " *****\n");
+ }
+
+hdcp_end:
+ return result;
+}
+
+/* Get current hdcp state */
+int hdcp_state(void)
+{
+ int hdcpstateget;
+ int result = HDCP_OK;
+ int res;
+ __u8 buf[128];
+ int val;
+ __u32 cmd_id;
+
+ cmd_id = get_new_cmd_id_ind();
+
+ /* Check hdcpstate */
+ hdcpstateget = open(HDCPSTATEGET_FILE, O_RDONLY);
+ if (hdcpstateget < 0) {
+ LOGHDMILIB("***** Failed to open %s *****",
+ HDCPSTATEGET_FILE);
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_state_end;
+ }
+ res = read(hdcpstateget, buf, sizeof(buf));
+ close(hdcpstateget);
+ if (res != 1) {
+ LOGHDMILIB("***** %s read error *****",
+ HDCPSTATEGET_FILE);
+ result = HDCPSTATE_FAIL;
+ goto hdcp_state_end;
+ }
+
+ val = HDMI_HDCPSTATE;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ memcpy(&buf[CMDID_OFFSET], &cmd_id, 4);
+ val = 1;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ memcpy(&buf[CMDBUF_OFFSET], buf, val);
+
+ /* Send on socket */
+ if (clientsocket_send(buf, CMDBUF_OFFSET + val) != 0)
+ result = HDCPSTATE_FAIL;
+
+ LOGHDMILIB("%s", dbg_hdcpstate(val));
+
+hdcp_state_end:
+ return result;
+}
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;
+}
diff --git a/src/hdmi_service_api.c b/src/hdmi_service_api.c
new file mode 100644
index 0000000..42edf26
--- /dev/null
+++ b/src/hdmi_service_api.c
@@ -0,0 +1,90 @@
+/*
+ * 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 "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+/* API functions
+ * Input avoid_return_msg: set to 1 to avoid messages from service.
+ * Return value: socket number where events will be notified.
+ */
+int hdmi_init(int avoid_return_msg)
+{
+ return hdmi_service_init(avoid_return_msg);
+}
+
+int hdmi_exit(void)
+{
+ return hdmi_service_exit();
+}
+
+int hdmi_enable(void)
+{
+ return hdmi_service_enable();
+}
+
+int hdmi_disable(void)
+{
+ return hdmi_service_disable();
+}
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+void hdmi_callback_set(void (*hdmi_cb)(int cmd, int data_size, __u8 *data))
+{
+ hdmi_service_callback_set(hdmi_cb);
+}
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+
+int hdmi_resolution_set(int cea, int vesaceanr)
+{
+ return hdmi_service_resolution_set(cea, vesaceanr);
+}
+
+int hdmi_fb_release(void)
+{
+ return hdmi_service_fb_release();
+}
+
+int hdmi_cec_send(__u8 initiator, __u8 destination, __u8 data_size, __u8 *data)
+{
+ return hdmi_service_cec_send(initiator, destination, data_size, data);
+}
+
+int hdmi_edid_request(__u8 block)
+{
+ return hdmi_service_edid_request(block);
+}
+
+int hdmi_hdcp_init(__u16 aes_size, __u8 *aes_data)
+{
+ return hdmi_service_hdcp_init(aes_size, aes_data);
+}
+
+int hdmi_infoframe_send(__u8 type, __u8 version, __u8 crc, __u8 data_size,
+ __u8 *data)
+{
+ return hdmi_service_infoframe_send(type, version, crc, data_size, data);
+}
+
+int hdmi_vesa_cea_prio_set(__u8 vesa_cea1, __u8 nr1,
+ __u8 vesa_cea2, __u8 nr2,
+ __u8 vesa_cea3, __u8 nr3)
+{
+ return hdmi_service_vesa_cea_prio_set(vesa_cea1, nr1,
+ vesa_cea2, nr2,
+ vesa_cea3, nr3);
+}
diff --git a/src/hdmi_service_start.c b/src/hdmi_service_start.c
new file mode 100644
index 0000000..49c0914
--- /dev/null
+++ b/src/hdmi_service_start.c
@@ -0,0 +1,29 @@
+/*
+ * 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 "../include/hdmi_service_api.h"
+
+#define NO_RETURN_MESSAGES 1
+
+int main(int argc, char *argv[])
+{
+ hdmi_init(NO_RETURN_MESSAGES);
+
+ hdmi_enable();
+
+ pthread_exit(NULL);
+
+ return 0;
+}
diff --git a/src/kevent.c b/src/kevent.c
new file mode 100644
index 0000000..f6375c4
--- /dev/null
+++ b/src/kevent.c
@@ -0,0 +1,159 @@
+/*
+ * 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/poll.h>
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+static int hdmieventfile_open(struct pollfd *pollfds)
+{
+
+ pollfds->fd = open(EVENT_FILE, O_RDONLY);
+ if (pollfds->fd < 0) {
+ LOGHDMILIB(" failed to open %s", EVENT_FILE);
+ return -1;
+ }
+
+ pollfds->events = POLLERR | POLLPRI;
+ return 0;
+}
+
+static int hdmieventfile_read(int fd)
+{
+ int read_res;
+ char buf[128];
+ int events;
+
+ read_res = read(fd, buf, sizeof(buf));
+ /* seek back so we can read the new state */
+ lseek(fd, 0, SEEK_SET);
+
+ LOGHDMILIB2("read_res:%d", read_res);
+
+ if (read_res != POLL_READ_SIZE)
+ return HDMIEVENT_POLLSIZEFAIL;
+
+ events = *buf;
+
+ LOGHDMILIB2("events read:%02x", events);
+ return events;
+}
+
+static int hdmieventfile_poll(struct pollfd *pollfds)
+{
+ int result;
+ int timeout = -1; /* Timeout in msec. */
+
+ result = poll(pollfds, 1, timeout);
+ switch (result) {
+ case 0:
+ LOGHDMILIB2("%s", "timeout");
+ break;
+ case -1:
+ LOGHDMILIB2("%s", "poll error");
+ break;
+ default:
+ if (pollfds->revents & POLLERR)
+ LOGHDMILIB2("res:%d poll done", result);
+ else {
+ LOGHDMILIB2("rev:%x res:%d poll done2",
+ pollfds->revents, result);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int hdmieventfile_close(int fd)
+{
+ LOGHDMILIB("%s", __func__);
+ close(fd);
+ return 0;
+}
+
+int hdmievclr(__u8 mask)
+{
+ int evclrfd;
+
+ evclrfd = open(EVENTCLR_FILE, O_WRONLY);
+ if (evclrfd < 0) {
+ LOGHDMILIB(" failed to open %s", EVENTCLR_FILE);
+ return -1;
+ }
+ write(evclrfd, &mask, 1);
+ close(evclrfd);
+ return 0;
+}
+
+/*
+ * Reading of kernel events
+ */
+void thread_kevent_fn(void *arg)
+{
+ int event;
+ struct pollfd pollfds;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ /*
+ * Note: events are subscribed at call to api function hdmi_enable
+ * Until then no events will occur.
+ */
+
+ /* Open the event file */
+ hdmieventfile_open(&pollfds);
+
+ while (1) {
+ /* Read poll event file */
+ event = hdmieventfile_read(pollfds.fd);
+ LOGHDMILIB("kevent:%x", event);
+
+ if (event == HDMIEVENT_POLLSIZEFAIL) {
+ usleep(100000);
+
+ /* Close event file */
+ hdmieventfile_close(pollfds.fd);
+
+ /* Open the event file */
+ hdmieventfile_open(&pollfds);
+
+ } else {
+ /* Signal main thread */
+ hdmi_event(event);
+
+ if (event & HDMIEVENT_WAKEUP)
+ break;
+
+ }
+
+ /* Poll plug event file */
+ hdmieventfile_poll(&pollfds);
+ }
+
+ /* Clear events */
+ hdmievclr(EVENTMASK_ALL);
+
+ /* Close event file */
+ hdmieventfile_close(pollfds.fd);
+
+ LOGHDMILIB("%s end", __func__);
+
+ /* Exit thread */
+ pthread_exit(NULL);
+}
diff --git a/src/setres.c b/src/setres.c
new file mode 100644
index 0000000..1bf566f
--- /dev/null
+++ b/src/setres.c
@@ -0,0 +1,400 @@
+/*
+ * 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 <string.h> /* String handling */
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include "linux/fb.h"
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+/* List of cea numbers. First ceanr has highest priority */
+struct vesacea vesaceaprio[CEAPRIO_MAX_SIZE];
+
+/*
+ * During edid_parse, sink_support will be filled in.
+ * The first format in this list with sink_support set will be chosen.
+ */
+int video_formats_nr;
+struct video_format video_formats[FORMATS_MAX];
+
+int video_formats_clear(void)
+{
+ memset(video_formats, 0, sizeof(video_formats));
+ return 0;
+}
+
+int vesacea_supported(int *nr, struct vesacea vesacea[])
+{
+ int index;
+
+ *nr = 0;
+ LOGHDMILIB2("%s begin", __func__);
+ for (index = 0; index < FORMATS_MAX; index++) {
+ if (video_formats[index].sink_support) {
+ vesacea[*nr].cea = video_formats[index].cea;
+ vesacea[*nr].nr = video_formats[index].vesaceanr;
+ LOGHDMILIB2("cea:%d nr:%d", vesacea[*nr].cea, vesacea[*nr].nr);
+ (*nr)++;
+ }
+ }
+ LOGHDMILIB2("%s end", __func__);
+ return 0;
+}
+
+int video_formats_supported_hw(void)
+{
+ int res;
+ int index;
+ int vesacea;
+ char buf[FORMATS_MAX * 2 + 1];
+
+ /* Get hw supported formats */
+ vesacea = open(VESACEAFORMATS_FILE, O_RDONLY);
+ if (vesacea < 0) {
+ LOGHDMILIB("***** Failed to open %s *****",
+ VESACEAFORMATS_FILE);
+ return -1;
+ }
+
+ res = read(vesacea, buf, sizeof(buf));
+ close(vesacea);
+ if (res <= 0) {
+ LOGHDMILIB("***** Failed to read %s *****",
+ VESACEAFORMATS_FILE);
+ return -1;
+ }
+
+ for (index = 0; index < FORMATS_MAX; index++) {
+ if ((index * 2 + 2) > res) {
+ /* No more to read */
+ video_formats[index].cea = 0;
+ video_formats[index].vesaceanr = 0;
+ video_formats[index].sink_support = 0;
+ video_formats[index].prio = VESACEAPRIO_DEFAULT;
+ break;
+ }
+ video_formats[index].cea = *(buf + index * 2);
+ video_formats[index].vesaceanr = *(buf + index * 2 + 1);
+ video_formats[index].sink_support = 0;
+ video_formats[index].prio = VESACEAPRIO_DEFAULT;
+ }
+ video_formats_nr = index;
+ return 0;
+}
+
+int nr_formats_get(void)
+{
+ return video_formats_nr;
+}
+
+struct video_format *video_formats_get(void)
+{
+ return video_formats;
+}
+
+static int vesaceanrtovar(struct fb_var_screeninfo *var, __u8 cea,
+ __u8 vesaceanr, __u8 num_buffers)
+{
+ int timing;
+ int res;
+ unsigned int index;
+ char buf[128];
+ int interlaced;
+
+ /* Request timing info */
+ timing = open(TIMING_FILE, O_RDWR);
+ if (timing < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", TIMING_FILE);
+ return -1;
+ }
+ buf[0] = cea;
+ buf[1] = vesaceanr;
+ res = write(timing, buf, 2);
+ if (res <= 0) {
+ LOGHDMILIB("***** Failed to write %s *****", TIMING_FILE);
+ close(timing);
+ return -1;
+ }
+
+ lseek(timing, 0, SEEK_SET);
+ res = read(timing, buf, sizeof(buf));
+ close(timing);
+ if (res <= 0) {
+ LOGHDMILIB("***** Failed to read %s *****", TIMING_FILE);
+ return -1;
+ }
+
+ /* Read timing info */
+ if (res == TIMING_SIZE) {
+ index = 0;
+ memcpy(&var->xres, buf + index, 4);
+ LOGHDMILIB3("xres:%d", var->xres);
+ index += 4;
+ memcpy(&var->yres, buf + index, 4);
+ LOGHDMILIB3("yres:%d", var->yres);
+ index += 4;
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres * num_buffers;
+ memcpy(&var->pixclock, buf + index, 4);
+ LOGHDMILIB3("pixclock:%d", var->pixclock);
+ index += 4;
+ memcpy(&var->left_margin, buf + index, 4);
+ LOGHDMILIB3("left_margin:%d", var->left_margin);
+ index += 4;
+ memcpy(&var->right_margin, buf + index, 4);
+ LOGHDMILIB3("right_margin:%d", var->right_margin);
+ index += 4;
+ memcpy(&var->upper_margin, buf + index, 4);
+ LOGHDMILIB3("upper_margin:%d", var->upper_margin);
+ index += 4;
+ memcpy(&var->lower_margin, buf + index, 4);
+ LOGHDMILIB3("lower_margin:%d", var->lower_margin);
+ index += 4;
+ var->vmode &= ~FB_VMODE_INTERLACED;
+ memcpy(&interlaced, buf + index, 4);
+ LOGHDMILIB3("vmode:%x", var->vmode);
+ var->vmode |= interlaced ? FB_VMODE_INTERLACED :
+ FB_VMODE_NONINTERLACED;
+ LOGHDMILIB("CEA %d nr %d found\n", cea, vesaceanr);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void vesacea_prio_default(void)
+{
+ /* 1920x1080P@30 */
+ vesaceaprio[0].cea = 1;
+ vesaceaprio[0].nr = 34;
+
+ /* 1920x1080P@25 */
+ vesaceaprio[1].cea = 1;
+ vesaceaprio[1].nr = 33;
+
+ /* 1920x1080P@24 */
+ vesaceaprio[2].cea = 1;
+ vesaceaprio[2].nr = 32;
+
+ /* 1920x1080I@60 */
+ vesaceaprio[3].cea = 1;
+ vesaceaprio[3].nr = 5;
+
+ /* 1920x1080I@30 */
+ vesaceaprio[4].cea = 1;
+ vesaceaprio[4].nr = 20;
+
+ /* 1280x720P@60 */
+ vesaceaprio[5].cea = 1;
+ vesaceaprio[5].nr = 4;
+
+ /* 1280x720P@50 */
+ vesaceaprio[6].cea = 1;
+ vesaceaprio[6].nr = 19;
+
+ /* 720x480P@60 */
+ vesaceaprio[7].cea = 1;
+ vesaceaprio[7].nr = 3;
+
+ /* end of list */
+ vesaceaprio[8].cea = 0;
+ vesaceaprio[8].nr = 0;
+}
+
+static void set_vesacea_prio(__u8 cea, __u8 vesaceanr, __u8 prio)
+{
+ int nr_formats = sizeof(video_formats)/sizeof(video_formats[0]);
+ int index;
+
+ for (index = 0; index < nr_formats; index++) {
+ if ((video_formats[index].cea == cea) &
+ (video_formats[index].vesaceanr == vesaceanr)) {
+ video_formats[index].prio = prio;
+ LOGHDMILIB("set_cea_prio %d %d", index, prio);
+ break;
+ }
+ }
+}
+
+void set_vesacea_prio_all(void)
+{
+ int index;
+
+ vesacea_prio_default();
+
+ /* Set cea prio. Continue until prio = 0 or maxsize */
+ for (index = 0; index < CEAPRIO_MAX_SIZE; index++) {
+ LOGHDMILIB("index:%d cea:%d prio:%d",
+ index,
+ vesaceaprio[index].cea,
+ vesaceaprio[index].nr);
+ if (vesaceaprio[index].nr == 0)
+ break;
+
+ set_vesacea_prio(vesaceaprio[index].cea,
+ vesaceaprio[index].nr,
+ index + 1);
+ }
+}
+
+int get_best_videoformat(__u8 *cea, __u8 *vesaceanr)
+{
+ int index;
+ int nr_formats;
+ struct video_format *video_formats;
+ __u8 best_prio;
+ int best_ceanr;
+ int best_vesanr;
+
+ *cea = 1;
+ *vesaceanr = VIDEO_FORMAT_DEFAULT;
+
+ nr_formats = nr_formats_get();
+ video_formats = video_formats_get();
+ best_prio = VESACEAPRIO_DEFAULT + 1;
+ best_ceanr = 0;
+ best_vesanr = 0;
+
+ /* Choose best video format */
+ for (index = 0; index < nr_formats; index++) {
+ LOGHDMILIB("test cea:%d nr:%d prio:%d",
+ video_formats[index].cea,
+ video_formats[index].vesaceanr,
+ video_formats[index].prio);
+ if (video_formats[index].sink_support == 0)
+ /* No sink support, check next format */
+ continue;
+
+ if (video_formats[index].prio < best_prio) {
+ /* Prio is best */
+ *cea = video_formats[index].cea;
+ *vesaceanr = video_formats[index].vesaceanr;
+ best_prio = video_formats[index].prio;
+ } else if (best_prio >= VESACEAPRIO_DEFAULT) {
+ /* Prio is not set; check ceanr */
+ if (video_formats[index].cea &&
+ (video_formats[index].vesaceanr >
+ best_ceanr)) {
+ /* It is the highest ceanr */
+ *cea = 1;
+ *vesaceanr = video_formats[index].vesaceanr;
+ best_ceanr = *vesaceanr;
+ }
+
+ /* If no cea has been chosen, check vesa */
+ if ((best_ceanr == 0) &
+ (!video_formats[index].cea &&
+ (video_formats[index].vesaceanr >
+ best_vesanr))) {
+ /* It is the highest veasanr */
+ *cea = 0;
+ *vesaceanr = video_formats[index].vesaceanr;
+ best_vesanr = *vesaceanr;
+ }
+ }
+ LOGHDMILIB("cea:%d nr:%d best_prio:%d",
+ *cea, *vesaceanr, best_prio);
+ }
+
+ return 0;
+}
+
+int hdmi_fb_chres(__u8 cea, __u8 vesaceanr)
+{
+ struct fb_var_screeninfo var;
+ int fd;
+ char fbname[128];
+ char buf[128];
+ int read_res;
+ int disponoff;
+ __u8 num_buffers;
+
+ /* Get fb dev name */
+ disponoff = open(DISPONOFF_FILE, O_RDONLY);
+ if (disponoff < 0) {
+ LOGHDMILIB("***** Failed to open %s *****", DISPONOFF_FILE);
+ return -1;
+ }
+
+ read_res = read(disponoff, buf, sizeof(buf));
+ close(disponoff);
+ if (read_res <= 0) {
+ LOGHDMILIB("***** Failed to read %s *****", DISPONOFF_FILE);
+ return -1;
+ }
+
+ /* Open fb */
+ sprintf(fbname, "%s%s", FBPATH, buf);
+ LOGHDMILIB("fbname:%s", fbname);
+ fd = open(fbname, O_RDONLY);
+ if (fd <= 0) {
+ LOGHDMILIB("%s", "***** Open fb failed *****");
+ return -2;
+ }
+
+ /* Get screen info */
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &var)) {
+ LOGHDMILIB("%s", "***** FBIOGET_VSCREENINFO failed *****");
+ close(fd);
+ return -3;
+ }
+
+ num_buffers = var.yres_virtual / var.yres;
+ /* Convert ceanr to screeninfo */
+ vesaceanrtovar(&var, cea, vesaceanr, num_buffers);
+
+ /* Set screen info */
+ if (ioctl(fd, FBIOPUT_VSCREENINFO, &var)) {
+ LOGHDMILIB("%s", "***** FBIOPUT_VSCREENINFO failed *****");
+ close(fd);
+ return -4;
+ }
+
+ /* Close fb */
+ close(fd);
+ return 0;
+}
+
+int vesaceaprio_set(__u8 len, __u8 *data)
+{
+ int index;
+ int index_last;
+ int cnt = 0;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ if (len < CEAPRIO_MAX_SIZE)
+ index_last = len;
+ else
+ index_last = CEAPRIO_MAX_SIZE;
+
+ for (index = 0; index < CEAPRIO_MAX_SIZE; index++) {
+ if (index < index_last) {
+ vesaceaprio[index].cea = data[index * 2];
+ vesaceaprio[index].nr = data[index * 2 + 1];
+ LOGHDMILIB("prio:%d cea:%d nr:%d", cnt,
+ vesaceaprio[index].cea,
+ vesaceaprio[index].nr);
+ } else {
+ vesaceaprio[index].cea = 0;
+ vesaceaprio[index].nr = 0;
+ break;
+ }
+ cnt++;
+ }
+
+ LOGHDMILIB("%s end", __func__);
+ return 0;
+}
diff --git a/src/socket.c b/src/socket.c
new file mode 100644
index 0000000..e6256c3
--- /dev/null
+++ b/src/socket.c
@@ -0,0 +1,316 @@
+/*
+ * 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/socket.h>
+#include <sys/un.h>
+#include <utils/Log.h>
+#include "../include/hdmi_service_api.h"
+#include "../include/hdmi_service_local.h"
+
+pthread_t thread_sockclient;
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+pthread_t thread_sockserver;
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+int clientsocket = -1;
+int listensocket = -1;
+int serversocket = -1;
+int no_return_msg = -1;
+
+int listensocket_set(int sock)
+{
+ listensocket = sock;
+ return 0;
+}
+
+int listensocket_get(void)
+{
+ return listensocket;
+}
+
+static int clientsocket_set(int sock)
+{
+ clientsocket = sock;
+ return 0;
+}
+
+int clientsocket_get(void)
+{
+ return clientsocket;
+}
+
+/* Client socket thread. Handles incoming socket messages */
+static void thread_sockclient_fn(void *arg)
+{
+ int res;
+ char buffer[SOCKET_DATA_MAX];
+ struct cmd_data cmd_data;
+ int cont = 1;
+ int sock;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ sock = (int)arg;
+ clientsocket_set(sock);
+ LOGHDMILIB("clisock:%d", sock);
+
+ while (cont) {
+ memset(buffer, 0, SOCKET_DATA_MAX);
+ res = read(sock, buffer, SOCKET_DATA_MAX);
+ if (res <= 0) {
+ LOGHDMILIB("clisocket closed:%d", res);
+ goto thread_sockclient_fn_end;
+ }
+
+ LOGHDMILIB("clisockread res:%d", res);
+
+ cmd_data.cmd = (__u32)buffer[0];
+ cmd_data.cmd_id = (__u32)buffer[4];
+ cmd_data.data_len = (__u32)buffer[8];
+ memcpy(cmd_data.data, &buffer[12], cmd_data.data_len);
+ cmd_data.next = NULL;
+
+ /* Add to list */
+ cmd_add(&cmd_data);
+
+ /* Signal */
+ hdmi_event(HDMIEVENT_CMD);
+
+ if (cmd_data.cmd == HDMI_EXIT)
+ cont = 0;
+ }
+
+thread_sockclient_fn_end:
+ close(sock);
+ clientsocket_set(-1);
+
+ LOGHDMILIB("%s end res:%d", __func__, res);
+ pthread_exit(NULL);
+}
+
+int clientsocket_send(__u8 *buf, int len)
+{
+ int sock;
+ int sent = -1;
+ int res = -1;
+
+ if (no_return_msg == 1)
+ return 0;
+
+ sock = clientsocket_get();
+ if (sock >= 0) {
+ sent = write(sock, buf, len);
+ LOGHDMILIB("%s written %d bytes on sock", __func__, sent);
+ }
+
+ if (sent == len)
+ res = 0;
+ return res;
+}
+
+/* Socket listen thread.
+ * Creates a listen socket.
+ * Listens for incoming connection.
+ * At connection attempt, creates a client socket in client thread.
+ */
+void thread_socklisten_fn(void *arg)
+{
+ int socknew;
+ socklen_t clilen;
+ struct sockaddr_un serv_addr;
+ struct sockaddr_un cli_addr;
+ int res;
+ fd_set descr;
+ struct timeval timeout;
+ int sockl;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ /* Create listen socket */
+ sockl = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockl < 0) {
+ LOGHDMILIB("%s", "socket create fail");
+ goto thread_socklisten_fn_end;
+ }
+ listensocket_set(sockl);
+ LOGHDMILIB2("Listen socket create:%d", sockl);
+
+ /* Remove any old path */
+ unlink(SOCKET_LISTEN_PATH);
+
+ /* Bind to path */
+ memset((char *) &serv_addr, 0, sizeof(struct sockaddr_un));
+ serv_addr.sun_family = AF_UNIX;
+ strcpy(serv_addr.sun_path, SOCKET_LISTEN_PATH);
+ res = bind(sockl, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
+ if (res < 0) {
+ LOGHDMILIB("socket bind fail:%d", res);
+ goto thread_socklisten_fn_end;
+ }
+
+ LOGHDMILIB2("Listen socket bind: %d", res);
+
+ /* while loop is breaked by shutdown on listen socket */
+ while (1) {
+ /* Listen for incoming connection */
+ listen(sockl, SOCKET_MAX_CONN);
+
+ /* Establish a client connection */
+ clilen = sizeof(cli_addr);
+ socknew = accept(sockl, (struct sockaddr *) &cli_addr, &clilen);
+ if (socknew < 0) {
+ LOGHDMILIB("socket accept fail:%d", socknew);
+ goto thread_socklisten_fn_end;
+ }
+ LOGHDMILIB2("socket accept:%d", socknew);
+
+ if (socknew >= 0)
+ /* Create a client thread */
+ pthread_create(&thread_sockclient, NULL,
+ (void *)thread_sockclient_fn, (void *)socknew);
+ }
+
+thread_socklisten_fn_end:
+ /* Remove any old path */
+ unlink(SOCKET_LISTEN_PATH);
+
+ LOGHDMILIB("%s end", __func__);
+ pthread_exit(NULL);
+}
+
+static int serversocket_set(int sock)
+{
+ serversocket = sock;
+ return 0;
+}
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+/* Server socket thread. Handles outgoing socket messages */
+static void thread_sockserver_fn(void *arg)
+{
+ int res;
+ char buffer[SOCKET_DATA_MAX];
+ struct cmd_data cmd_data;
+ int cont = 1;
+ int sock;
+ cb_fn callback;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ sock = (int)arg;
+
+ while (cont) {
+ memset(buffer, 0, SOCKET_DATA_MAX);
+ res = read(sock, buffer, SOCKET_DATA_MAX);
+ if (res <= 0) {
+ LOGHDMILIB("servsocket closed:%d", res);
+ goto thread_sockserver_fn_end;
+ }
+
+ LOGHDMILIB("servsockread res:%d", res);
+
+ if (cmd_data.cmd == HDMI_EXIT) {
+ cont = 0;
+ } else {
+ cmd_data.cmd = (__u32)buffer[0];
+ cmd_data.cmd_id = (__u32)buffer[4];
+ cmd_data.data_len = (__u32)buffer[8];
+ memcpy(cmd_data.data, &buffer[12], cmd_data.data_len);
+ cmd_data.next = NULL;
+
+ /* Send through callback fn */
+ callback = hdmi_service_callback_get();
+ LOGHDMILIB("callback:%p", callback);
+ if (callback)
+ callback(cmd_data.cmd, cmd_data.data_len,
+ cmd_data.data);
+ }
+ }
+
+thread_sockserver_fn_end:
+ close(sock);
+
+ LOGHDMILIB("%s end res:%d", __func__, res);
+ pthread_exit(NULL);
+}
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+
+int serversocket_create(int avoid_return_msg)
+{
+ int sock;
+ int len;
+ struct sockaddr_un addr;
+ int result;
+ int n;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ no_return_msg = avoid_return_msg;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ LOGHDMILIB("sock:%d", sock);
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, SOCKET_LISTEN_PATH);
+ len = sizeof(addr);
+
+ n = connect(sock, (struct sockaddr *)&addr, len);
+ LOGHDMILIB("connect:%d", n);
+
+ if (n < 0) {
+ LOGHDMILIB("socket connect err:%d", n);
+ goto socket_test_end;
+ }
+
+socket_test_end:
+ LOGHDMILIB("%s end", __func__);
+ if (sock >= 0)
+ serversocket_set(sock);
+ LOGHDMILIB("servsock:%d", sock);
+
+#ifdef HDMI_SERVICE_USE_CALLBACK_FN
+ /* Create a server thread */
+ pthread_create(&thread_sockserver, NULL,
+ (void *)thread_sockserver_fn, (void *)sock);
+#endif /*HDMI_SERVICE_USE_CALLBACK_FN*/
+ return sock;
+}
+
+static int serversocket_get(void)
+{
+ return serversocket;
+}
+
+int serversocket_write(int len, __u8 *data)
+{
+ int n;
+ int sock;
+
+ sock = serversocket_get();
+ n = write(sock, data, len);
+ LOGHDMILIB("write socket len res:%d %d %d", sock, len, n);
+
+ return n;
+}
+
+int serversocket_close(void)
+{
+ int sock;
+
+ sock = serversocket_get();
+ return close(sock);
+}