summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFathi Boudra <fathi.boudra@linaro.org>2013-03-30 17:46:27 +0200
committerFathi Boudra <fathi.boudra@linaro.org>2013-03-30 17:46:27 +0200
commit3207612d48156d268a75aa781b2d6d81a1f64de7 (patch)
treeb433af981fb4cb7ca62c4f6a18708de7fbdf2531
Imported Upstream version 1.1HEADupstream/1.1upstreammaster
-rwxr-xr-xAndroid.mk43
-rw-r--r--Makefile42
-rw-r--r--debian/changelog31
-rw-r--r--debian/compat1
-rw-r--r--debian/control22
-rw-r--r--debian/copyright30
-rw-r--r--debian/docs0
-rw-r--r--debian/hdmiservice-dev.dirs2
-rw-r--r--debian/hdmiservice-dev.install1
-rw-r--r--debian/hdmiservice.conf16
-rw-r--r--debian/hdmiservice.dirs3
-rw-r--r--debian/hdmiservice.install3
-rwxr-xr-xdebian/rules18
-rw-r--r--debian/source/format1
-rw-r--r--include/hdmi_service_api.h124
-rw-r--r--include/hdmi_service_local.h393
-rw-r--r--src/cec.c176
-rw-r--r--src/edid.c568
-rw-r--r--src/hdcp.c281
-rw-r--r--src/hdmi_service.c1194
-rw-r--r--src/hdmi_service_api.c109
-rw-r--r--src/hdmi_service_start.c48
-rw-r--r--src/kevent.c182
-rw-r--r--src/setres.c439
-rw-r--r--src/socket.c367
25 files changed, 4094 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100755
index 0000000..77f5bda
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,43 @@
+# 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)
+
+# check for u5500
+ifeq ($(STE_HDMISERVICE_SET_PLATFORM),u5500)
+LOCAL_CFLAGS := -DSTE_PLATFORM_U5500
+endif #module configuration u5500
+
+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 := optional
+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 := optional
+include $(BUILD_EXECUTABLE)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d956aa9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,42 @@
+# Makefile for HDMIservice
+#
+# Copyright (C) ST-Ericsson AB 2011.
+#
+
+PACKAGE_NAME=hdmi_service
+
+CFLAGS += -c -Wall -O2 -fPIC
+# no-as-needed is a work-around, pthread is dropped from the DT_NEEDED
+# for some reason, and yet the final link fails due to missing symbols
+LDFLAGS += -L./ -Wl,--no-as-needed -lpthread -shared
+LDFLAGS_2 = -L./
+INCLUDES += -I./include
+HDMILIBS = hdmiservice.so
+
+build: hdmiservice.so hdmistart
+
+install: build
+# @$(PACKAGE_FILE) /usr/lib/hdmiservice.so $(CURDIR)/hdmiservice.so 755 0 0
+# @$(PACKAGE_FILE) /usr/bin/hdmistart $(CURDIR)/hdmistart 755 0 0
+ mkdir -p $(DESTDIR)/usr/lib
+ mkdir -p $(DESTDIR)/usr/bin
+ mkdir -p $(DESTDIR)/usr/include
+ cp $(CURDIR)/hdmiservice.so $(DESTDIR)/usr/lib
+ cp $(CURDIR)/hdmistart $(DESTDIR)/usr/bin
+ cp $(CURDIR)/include/*.h $(DESTDIR)/usr/include
+
+%.o: src/%.c
+ ${CC} ${CFLAGS} ${INCLUDES} -c $<
+
+hdmiservice.so: cec.o edid.o hdcp.o hdmi_service_api.o hdmi_service.o kevent.o \
+ setres.o socket.o
+ $(CC) $(LDFLAGS) $^ -o $@
+
+hdmistart: hdmi_service_start.o
+ $(CC) $(LDFLAGS_2) $^ -o $@ $(HDMILIBS)
+
+clean:
+ @rm -rf cec.o edid.o hdcp.o hdmi_service_api.o hdmi_service.o kevent.o \
+ setres.o socket.o hdmiservice.so hdmi_service_start.o hdmistart
+
+.PHONY: hdmiservice.so clean
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..43b3ce6
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,31 @@
+hdmiservice (1.1-0linaro1) oneiric; urgency=low
+
+ * debian/control: enabling build for armhf
+
+ -- Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> Wed, 28 Mar 2012 19:24:08 -0300
+
+hdmiservice (1.1-0) oneiric; urgency=low
+
+ * Improved EDID handling
+ * Misc. build fixes
+
+ -- Kalle Vahlman <kalle.vahlman@movial.com> Thu, 08 Dec 2011 10:22:35 +0000
+
+hdmiservice (1.0.1-0) oneiric; urgency=low
+
+ * Work-around pthread linking issue on oneiric environment (--as-needed)
+ * Adjust packaging for launchpad
+
+ -- Kalle Vahlman <kalle.vahlman@movial.com> Thu, 08 Dec 2011 05:34:23 +0000
+
+hdmiservice (1.0-1) unstable; urgency=low
+
+ * Add support for starting hdmiservice with Oneric display manager
+
+ -- Kalle Vahlman <kalle.vahlman@movial.com> Mon, 31 Oct 2011 07:44:59 +0200
+
+hdmiservice (1.0) unstable; urgency=low
+
+ * Initial Release.
+
+ -- John Fredriksson <john.xj.fredriksson@stericsson.com> Wed, 28 Sep 2011 17:38:58 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..b857424
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,22 @@
+Source: hdmiservice
+Priority: extra
+Maintainer: John Fredriksson <john.xj.fredriksson@stericsson.com>
+Build-Depends: debhelper (>= 7.0.50~)
+Standards-Version: 3.9.1
+Section: libs
+Homepage: http://igloocommunity.org
+
+Package: hdmiservice-dev
+Section: libdevel
+Architecture: armel armhf
+Depends: hdmiservice (= ${binary:Version})
+Description: HDMI userspace control daemon library development files
+ This package contains the HDMIservice library develompent files
+
+Package: hdmiservice
+Section: libs
+Architecture: armel armhf
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: HDMI userspace control daemon
+ HDMIservice is a userspace daemon to negotiate display modes for AV8100
+ display system found on Snowball
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..7dda4a9
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,30 @@
+Format: http://dep.debian.net/deps/dep5
+Upstream-Name: hdmiservice
+
+Files: *
+
+Copyright (C) ST-Ericsson SA 2011
+Author: Per Persson per.xb.persson@stericsson.com for
+ST-Ericsson.
+
+License terms:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Copyright: 2011 ST-Ericsson SA
+License: MIT
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debian/docs
diff --git a/debian/hdmiservice-dev.dirs b/debian/hdmiservice-dev.dirs
new file mode 100644
index 0000000..4418816
--- /dev/null
+++ b/debian/hdmiservice-dev.dirs
@@ -0,0 +1,2 @@
+usr/lib
+usr/include
diff --git a/debian/hdmiservice-dev.install b/debian/hdmiservice-dev.install
new file mode 100644
index 0000000..41a1414
--- /dev/null
+++ b/debian/hdmiservice-dev.install
@@ -0,0 +1 @@
+usr/include/*
diff --git a/debian/hdmiservice.conf b/debian/hdmiservice.conf
new file mode 100644
index 0000000..b4adb57
--- /dev/null
+++ b/debian/hdmiservice.conf
@@ -0,0 +1,16 @@
+# hdmiservice
+#
+# logic for creating and selecting framebuffer resolution
+
+description "HDMI service"
+
+start on starting gdm
+start on starting lightdm
+
+pre-start script
+ hdmistart &
+end script
+
+post-start script
+ while [ ! -e /dev/fb0 ]; do sleep 2; done
+end script
diff --git a/debian/hdmiservice.dirs b/debian/hdmiservice.dirs
new file mode 100644
index 0000000..0e037cf
--- /dev/null
+++ b/debian/hdmiservice.dirs
@@ -0,0 +1,3 @@
+usr/lib
+usr/bin
+etc/init
diff --git a/debian/hdmiservice.install b/debian/hdmiservice.install
new file mode 100644
index 0000000..80cb3ca
--- /dev/null
+++ b/debian/hdmiservice.install
@@ -0,0 +1,3 @@
+usr/lib/*.so
+usr/bin/*
+etc/init/*.conf
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..8332782
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,18 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+override_dh_auto_install:
+ mkdir -p debian/tmp/etc/init
+ cp debian/hdmiservice.conf debian/tmp/etc/init/
+ $(MAKE) DESTDIR=debian/tmp install
+
+%:
+ dh $@
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/include/hdmi_service_api.h b/include/hdmi_service_api.h
new file mode 100644
index 0000000..70d9d34
--- /dev/null
+++ b/include/hdmi_service_api.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 0
+
+/* 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..9d5ff7e
--- /dev/null
+++ b/include/hdmi_service_local.h
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 *hdmi);
+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);
+void vesacea_prio_default(void);
+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 dispdevice_file_open(char *file, int attr);
+
+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]))
+
+#ifdef ANDROID
+#define FBPATH "/dev/graphics/"
+#define LOGHDMILIB LOGE
+#define LOGHDMILIB2 LOGE
+#define LOGHDMILIB3(format, ...)
+#else
+#define FBPATH "/dev/"
+#define LOGHDR "libhdmi:"
+#define LOGHDMILIB(format, ...) printf(LOGHDR format"\r\n", __VA_ARGS__)
+#define LOGHDMILIB2(format, ...) printf(LOGHDR format"\r\n", __VA_ARGS__)
+#define LOGHDMILIB3(format, ...) printf(LOGHDR format"\r\n", __VA_ARGS__)
+#endif
+
+#ifdef ANDROID
+#define SOCKET_LISTEN_PATH "/dev/socket/hdmi_listen"
+#else
+#define SOCKET_LISTEN_PATH "/dev/hdmi_listen"
+#endif
+
+#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 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 DISPDEVICE_PATH_1 "/sys/devices/"
+#define DISPDEVICE_PATH_2 "av8100_hdmi"
+#define DISPONOFF_FILE "disponoff"
+#define HDMIFORMAT_FILE "hdmisdtvswitch"
+#define VESACEAFORMATS_FILE "vesacea"
+#define TIMING_FILE "timing"
+#define STAYALIVE_FILE "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 EDIDREAD_BL1_TAG_REV_ERR -6
+#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_TAG_OFFSET 0x00
+#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_BL1_TAG_EXPECTED 0x02
+#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_WAITTIME0 2000000
+#define EDIDREAD_WAITTIME1 100000
+
+/* 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..4b6acf5
--- /dev/null
+++ b/src/cec.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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>
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#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;
+ int ret = 0;
+
+ cecrxfd = open(CECRXEVEN_FILE, O_WRONLY);
+ if (cecrxfd < 0) {
+ LOGHDMILIB(" failed to open %s", CECRXEVEN_FILE);
+ return -1;
+ }
+ if (write(cecrxfd, cecrxeven_val, sizeof(cecrxeven_val)) !=
+ sizeof(cecrxeven_val))
+ ret = -2;
+ close(cecrxfd);
+ return ret;
+}
+
+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);
+
+ if (cecsize < 0)
+ return -1;
+
+ 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..e3381e5
--- /dev/null
+++ b/src/edid.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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>
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#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 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 = 60 + ((byte & EDID_STDTIM_FREQ_MASK) >>
+ EDID_STDTIM_FREQ_SHIFT);
+ LOGHDMILIB2("xres:%d yres:%d freq:%d", xres, yres, freq);
+ 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,
+ int *hdmi)
+{
+ __u8 tag;
+ __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;
+
+ tag = *(data + EDID_BL1_TAG_OFFSET);
+ rev = *(data + EDID_BL1_REVNR_OFFSET);
+ if (tag != EDID_BL1_TAG_EXPECTED) {
+ LOGHDMILIB("edid bl1 tag:%02x or rev:%02x", tag, rev);
+ return EDIDREAD_BL1_TAG_REV_ERR;
+ }
+
+ if (rev >= EDID_EXTVER_3)
+ *hdmi = 1;
+ else
+ *hdmi = 0; /* Only DVI */
+
+ offset = *(data + EDID_BL1_OFFSET_OFFSET);
+
+ LOGHDMILIB("rev:%d offset:%d", rev, offset);
+
+ /* 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 = 60 + ((byte & EDID_STDTIM_FREQ_MASK) >>
+ EDID_STDTIM_FREQ_SHIFT);
+ LOGHDMILIB2("xres:%d yres:%d freq:%d", xres, yres,
+ freq);
+ 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..a7d1a90
--- /dev/null
+++ b/src/hdcp.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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>
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#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;
+
+ /* 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;
+ }
+ res = write(hdcpeven, hdcp_even_val, sizeof(hdcp_even_val));
+ close(hdcpeven);
+ if (res != sizeof(hdcp_even_val)) {
+ result = SYSFS_FILE_FAILED;
+ goto hdcp_end;
+ }
+
+ /* 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..0a64425
--- /dev/null
+++ b/src/hdmi_service.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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>
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#include <dirent.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 = HDMI_PLUGUNDEF;
+struct cmd_data *cmd_data;
+int cmd_id_ind;
+char dispdevice_path[64];
+
+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*/
+
+static int hdmidirsort(const struct dirent **a, const struct dirent **b)
+{
+ return 1;
+}
+
+/* Find the correct device path since the device minor number can vary */
+static int dispdevice_path_set(void)
+{
+ struct dirent **namelist;
+ int n;
+ int found = 0;
+
+ n = scandir(DISPDEVICE_PATH_1, &namelist, 0, hdmidirsort);
+ if (n < 0) {
+ LOGHDMILIB("%s", "scandir error");
+ return -1;
+ }
+
+ while (n--) {
+ if (!found && strncmp(namelist[n]->d_name, DISPDEVICE_PATH_2,
+ strlen(DISPDEVICE_PATH_2)) == 0) {
+ strcpy(dispdevice_path, namelist[n]->d_name);
+ LOGHDMILIB("%s found:%s\n", __func__, dispdevice_path);
+ found = 1;
+ }
+ free(namelist[n]);
+ }
+ free(namelist);
+ return 0;
+}
+
+static char *dispdevice_path_get(void)
+{
+ return &dispdevice_path[0];
+}
+
+int dispdevice_file_open(char *file, int attr)
+{
+ int fd = -1;
+ char fname[128];
+
+ if (dispdevice_path[0] == 0)
+ dispdevice_path_set();
+
+ if (dispdevice_path[0] != 0) {
+ sprintf(fname, "%s%s/%s", DISPDEVICE_PATH_1,
+ dispdevice_path_get(), file);
+ fd = open(fname, attr);
+ }
+ return fd;
+}
+
+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;
+ int ret = 0;
+
+ pwrfd = open(POWERONOFF_FILE, O_WRONLY);
+ if (pwrfd < 0) {
+ LOGHDMILIB(" failed to open %s", POWERONOFF_FILE);
+ return -1;
+ }
+ if (write(pwrfd, &onoff, 1) != 1)
+ ret = -2;
+ close(pwrfd);
+
+ return ret;
+}
+
+/* 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;
+ int ret = 0;
+
+ fd = dispdevice_file_open(HDMIFORMAT_FILE, O_WRONLY);
+ if (fd < 0) {
+ LOGHDMILIB(" failed to open %s", HDMIFORMAT_FILE);
+ return -1;
+ }
+ if (write(fd, &format, 1) != 1)
+ ret = -2;
+ close(fd);
+
+ return ret;
+}
+
+/* Send illegal state message on client socket */
+static int illegalstate_send(__u32 cmd, __u32 cmd_id)
+{
+ int val;
+ __u8 buf[16];
+
+ 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;
+ }
+
+ /* 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 = dispdevice_file_open(STAYALIVE_FILE, O_WRONLY);
+ while ((stayalivefd < 0) && (cnt++ < 30)) {
+ usleep(200000);
+ stayalivefd = dispdevice_file_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;
+ 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;
+ enum hdmi_plug_state plug_state;
+ int hdmi_support = 0;
+
+ LOGHDMILIB("%s", "HDMIEVENT_HDMIPLUGGED");
+
+ if ((plugstate_get(&plug_state) == 0) && (plug_state == HDMI_PLUGGED)) {
+ LOGHDMILIB("%s", "Already plugged, ignore");
+ return -1;
+ }
+
+ 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_WAITTIME0);
+ cnt++;
+ }
+ if (res) {
+ ret = -1;
+ goto hdmiplugged_handle_end;
+ }
+ if (extension) {
+ /* Extension data exists */
+ cnt = 0;
+ res = -1;
+ while (res && (cnt < 3)) {
+ res = edid_read(1, data);
+ if (res == 0)
+ res = edid_parse1(data + 1, formats, nr_formats,
+ basic_audio_support,
+ &edid_latency,
+ &hdmi_support);
+ if (res && (cnt < 2))
+ usleep(EDIDREAD_WAITTIME1);
+ cnt++;
+ }
+
+ if (res) {
+ ret = -1;
+ goto hdmiplugged_handle_end;
+ }
+
+ }
+
+ if (hdmi_support) {
+ /* 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 = dispdevice_file_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_plug_state plug_state;
+
+ LOGHDMILIB("%s", "HDMIEVENT_HDMIUNPLUGGED");
+
+ if ((plugstate_get(&plug_state) == 0) &&
+ (plug_state == HDMI_UNPLUGGED)) {
+ LOGHDMILIB("%s", "Already unplugged, ignore");
+ return -1;
+ }
+
+ plugstate_set(HDMI_UNPLUGGED);
+
+ /* Allow early suspend */
+ stayalive(0);
+ 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 = dispdevice_file_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];
+ __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;
+ 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_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 + CMDBUF_OFFSET))
+ 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:%d cmd_id:%x res:%d\n", cmd_obj->cmd,
+ 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) {
+ if (hdmiunplugged_handle() == 0)
+ 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;
+ 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);
+
+ vesacea_prio_default();
+
+ 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[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ serversocket_write(CMDBUF_OFFSET + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_enable(void)
+{
+ int val;
+ __u8 buf[32];
+ val = HDMI_ENABLE;
+
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ serversocket_write(CMDBUF_OFFSET + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_disable(void)
+{
+ int val;
+ __u8 buf[32];
+ val = HDMI_DISABLE;
+
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ serversocket_write(CMDBUF_OFFSET + 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[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 2;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ /* data */
+ buf[CMDBUF_OFFSET] = cea;
+ buf[CMDBUF_OFFSET + 1] = vesaceanr;
+ serversocket_write(CMDBUF_OFFSET + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_fb_release(void)
+{
+ int val;
+ __u8 buf[32];
+
+ val = HDMI_FB_RELEASE;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 0;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ serversocket_write(CMDBUF_OFFSET + 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[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = data_size + 3;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ /* data */
+ buf[CMDBUF_OFFSET] = initiator;
+ buf[CMDBUF_OFFSET + 1] = destination;
+ buf[CMDBUF_OFFSET + 2] = data_size;
+ memcpy(&buf[CMDBUF_OFFSET + 3], data, data_size);
+ serversocket_write(CMDBUF_OFFSET + 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[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 1;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ /* data */
+ buf[CMDBUF_OFFSET] = block;
+ serversocket_write(CMDBUF_OFFSET + val, buf);
+
+ return 0;
+}
+
+int hdmi_service_hdcp_init(__u16 aes_size, __u8 *aes_data)
+{
+ int val;
+ __u8 buf[AES_KEYS_SIZE + CMDBUF_OFFSET];
+
+ if (aes_size != AES_KEYS_SIZE)
+ return -1;
+
+ val = HDMI_HDCP_INIT;
+ memcpy(&buf[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = aes_size;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ /* data */
+ memcpy(&buf[CMDBUF_OFFSET], aes_data, aes_size);
+ serversocket_write(CMDBUF_OFFSET + 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[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = data_size + 4;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ /* data */
+ buf[CMDBUF_OFFSET] = type;
+ buf[CMDBUF_OFFSET + 1] = version;
+ buf[CMDBUF_OFFSET + 2] = crc;
+ buf[CMDBUF_OFFSET + 3] = data_size;
+ memcpy(&buf[CMDBUF_OFFSET + 4], data, data_size);
+ serversocket_write(CMDBUF_OFFSET + 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[CMD_OFFSET], &val, 4);
+ /* cmd_id */
+ val = 0;
+ memcpy(&buf[CMDID_OFFSET], &val, 4);
+ /* len */
+ val = 7;
+ memcpy(&buf[CMDLEN_OFFSET], &val, 4);
+ /* data */
+ buf[CMDBUF_OFFSET] = 3;
+ buf[CMDBUF_OFFSET + 1] = vesa_cea1;
+ buf[CMDBUF_OFFSET + 2] = nr1;
+ buf[CMDBUF_OFFSET + 3] = vesa_cea2;
+ buf[CMDBUF_OFFSET + 4] = nr2;
+ buf[CMDBUF_OFFSET + 5] = vesa_cea3;
+ buf[CMDBUF_OFFSET + 6] = nr3;
+ serversocket_write(CMDBUF_OFFSET + val, buf);
+
+ return 0;
+}
diff --git a/src/hdmi_service_api.c b/src/hdmi_service_api.c
new file mode 100644
index 0000000..1c2a3d8
--- /dev/null
+++ b/src/hdmi_service_api.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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..4831dd1
--- /dev/null
+++ b/src/hdmi_service_start.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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..4d84fbb
--- /dev/null
+++ b/src/kevent.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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>
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#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;
+ int ret = 0;
+
+ evclrfd = open(EVENTCLR_FILE, O_WRONLY);
+ if (evclrfd < 0) {
+ LOGHDMILIB(" failed to open %s", EVENTCLR_FILE);
+ return -1;
+ }
+ if (write(evclrfd, &mask, 1) != 1)
+ ret = -2;
+ close(evclrfd);
+ return ret;
+}
+
+/*
+ * 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..b7042eb
--- /dev/null
+++ b/src/setres.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#include <sys/ioctl.h>
+#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"
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#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 = dispdevice_file_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 = dispdevice_file_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;
+}
+
+void vesacea_prio_default(void)
+{
+#ifdef STE_PLATFORM_U5500
+ /* 1280x720P@60 */
+ vesaceaprio[0].cea = 1;
+ vesaceaprio[0].nr = 4;
+
+ /* 1280x720P@50 */
+ vesaceaprio[1].cea = 1;
+ vesaceaprio[1].nr = 19;
+
+ /* 720x480P@60 */
+ vesaceaprio[2].cea = 1;
+ vesaceaprio[2].nr = 3;
+
+ /* end of list */
+ vesaceaprio[3].cea = 0;
+ vesaceaprio[3].nr = 0;
+#else
+ /* 1920x1080P@30 */
+ vesaceaprio[0].cea = 1;
+ vesaceaprio[0].nr = 34;
+
+ /* 1280x720P@60 */
+ vesaceaprio[1].cea = 1;
+ vesaceaprio[1].nr = 4;
+
+ /* 1920x1080P@25 */
+ vesaceaprio[2].cea = 1;
+ vesaceaprio[2].nr = 33;
+
+ /* 1920x1080P@24 */
+ vesaceaprio[3].cea = 1;
+ vesaceaprio[3].nr = 32;
+
+ /* 1920x1080I@60 */
+ vesaceaprio[4].cea = 1;
+ vesaceaprio[4].nr = 5;
+
+ /* 1920x1080I@30 */
+ vesaceaprio[5].cea = 1;
+ vesaceaprio[5].nr = 20;
+
+ /* 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;
+#endif
+}
+
+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;
+
+ /* 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 = dispdevice_file_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..fc3f5c4
--- /dev/null
+++ b/src/socket.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Per Persson per.xb.persson@stericsson.com for
+ * ST-Ericsson.
+ *
+ * License terms:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h> /* Symbolic Constants */
+#include <sys/types.h> /* Primitive System Data Types */
+#include <linux/types.h>
+#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>
+#ifdef ANDROID
+#include <utils/Log.h>
+#endif
+#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(int *arg)
+{
+ int bytes = 0;
+ char buffer[SOCKET_DATA_MAX];
+ struct cmd_data cmd_data;
+ int cont = 1;
+ int sock;
+ int buf_index = 0;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ sock = *arg;
+ clientsocket_set(sock);
+ LOGHDMILIB("clisock:%d", sock);
+
+ while (cont) {
+ if (bytes < CMDBUF_OFFSET) {
+ /* Read from socket */
+ bytes += read(sock, buffer + buf_index,
+ SOCKET_DATA_MAX - buf_index);
+ if (bytes <= 0) {
+ LOGHDMILIB("clisocket closed:%d", bytes);
+ goto thread_sockclient_fn_end;
+ }
+
+ LOGHDMILIB("clisockread:%d", bytes);
+
+ if (bytes < CMDBUF_OFFSET)
+ /* Not enough data */
+ continue;
+ }
+
+ /* Valid command */
+ cmd_data.cmd = (__u32)buffer[buf_index + CMD_OFFSET];
+ cmd_data.cmd_id = (__u32)buffer[buf_index + CMDID_OFFSET];
+ cmd_data.data_len = (__u32)buffer[buf_index + CMDLEN_OFFSET];
+ memcpy(cmd_data.data, &buffer[buf_index + CMDBUF_OFFSET],
+ cmd_data.data_len);
+ cmd_data.next = NULL;
+
+ /* Remaining bytes to handle */
+ bytes -= (CMDBUF_OFFSET + cmd_data.data_len);
+ buf_index += (CMDBUF_OFFSET + cmd_data.data_len);
+ if (bytes < 0)
+ bytes = 0;
+ if (bytes < CMDBUF_OFFSET) {
+ memcpy(buffer, buffer + buf_index, bytes);
+ buf_index = bytes;
+ }
+
+ /* 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: %d", __func__, bytes);
+ 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;
+ 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);
+ close(sockl);
+ 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 */
+ if (listen(sockl, SOCKET_MAX_CONN) != 0) {
+ LOGHDMILIB("%s listen error", __func__);
+ close(sockl);
+ goto thread_socklisten_fn_end;
+ }
+
+ /* 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);
+ close(sockl);
+ 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[CMD_OFFSET];
+ cmd_data.cmd_id = (__u32)buffer[CMDID_OFFSET];
+ cmd_data.data_len = (__u32)buffer[CMDLEN_OFFSET];
+ memcpy(cmd_data.data, &buffer[CMDBUF_OFFSET],
+ 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 n;
+
+ LOGHDMILIB("%s begin", __func__);
+
+ no_return_msg = avoid_return_msg;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ LOGHDMILIB("sock:%d", sock);
+ if (sock < 0) {
+ LOGHDMILIB("%s %s", __func__, "socket create fail");
+ goto serversocket_create_end;
+ }
+
+ 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("%s socket connect err:%d", __func__, n);
+ close(sock);
+ sock = -1;
+ }
+
+serversocket_create_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);
+}