diff options
Diffstat (limited to 'cmds/hdmid')
-rw-r--r-- | cmds/hdmid/Android.mk | 50 | ||||
-rw-r--r-- | cmds/hdmid/HDMIDaemon.cpp | 705 | ||||
-rw-r--r-- | cmds/hdmid/HDMIDaemon.h | 126 | ||||
-rw-r--r-- | cmds/hdmid/hdmid_main.cpp | 52 |
4 files changed, 933 insertions, 0 deletions
diff --git a/cmds/hdmid/Android.mk b/cmds/hdmid/Android.mk new file mode 100644 index 0000000..53e6da6 --- /dev/null +++ b/cmds/hdmid/Android.mk @@ -0,0 +1,50 @@ +# 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 +# + +ifeq ($(TARGET_HAVE_HDMI_OUT),true) +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + hdmid_main.cpp \ + HDMIDaemon.cpp \ + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt + endif +endif + +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder \ + libgui \ + +LOCAL_C_INCLUDES := \ + $(call include-path-for, corecg graphics) +LOCAL_C_INCLUDES += -I$(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include/linux/msm_mdp.h +LOCAL_C_INCLUDES += -I$(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include/linux/fb.h +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + +LOCAL_MODULE:= hdmid +LOCAL_MODULE_TAGS:= eng + +include $(BUILD_EXECUTABLE) + +endif diff --git a/cmds/hdmid/HDMIDaemon.cpp b/cmds/hdmid/HDMIDaemon.cpp new file mode 100644 index 0000000..9e8acec --- /dev/null +++ b/cmds/hdmid/HDMIDaemon.cpp @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * 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. + */ + +#define LOG_TAG "HDMIDaemon" + +#include <ctype.h> +#include <stdint.h> +#include <sys/types.h> +#include <math.h> +#include <fcntl.h> +#include <utils/misc.h> +#include <signal.h> + +#include <binder/IPCThreadState.h> +#include <utils/threads.h> +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/AssetManager.h> + +#include <ui/DisplayInfo.h> +#include <ui/FramebufferNativeWindow.h> +#include <linux/msm_mdp.h> +#include <linux/fb.h> +#include <sys/ioctl.h> + +#include <cutils/properties.h> + +#include "HDMIDaemon.h" + +namespace android { + +// --------------------------------------------------------------------------- + +#define DEVICE_ROOT "/sys/class/graphics" +#define DEVICE_NODE "fb1" + +#define HDMI_SOCKET_NAME "hdmid" + +#define HDMI_EVT_CONNECTED "hdmi_connected" +#define HDMI_EVT_DISCONNECTED "hdmi_disconnected" +#define HDMI_EVT_AUDIO_ON "hdmi_audio_on" +#define HDMI_EVT_AUDIO_OFF "hdmi_audio_off" +#define HDMI_EVT_NO_BROADCAST_ONLINE "hdmi_no_broadcast_online" + +#define HDMI_CMD_ENABLE_HDMI "enable_hdmi" +#define HDMI_CMD_DISABLE_HDMI "disable_hdmi" +#define HDMI_CMD_CHANGE_MODE "change_mode: " +#define HDMI_CMD_SET_ASWIDTH "set_aswidth: " +#define HDMI_CMD_SET_ASHEIGHT "set_asheight: " +#define HDMI_CMD_HPDOPTION "hdmi_hpd: " + +#define SYSFS_CONNECTED DEVICE_ROOT "/" DEVICE_NODE "/connected" +#define SYSFS_EDID_MODES DEVICE_ROOT "/" DEVICE_NODE "/edid_modes" +#define SYSFS_HPD DEVICE_ROOT "/" DEVICE_NODE "/hpd" +#define SYSFS_HDCP_PRESENT DEVICE_ROOT "/" DEVICE_NODE "/hdcp_present" + +HDMIDaemon::HDMIDaemon() : Thread(false), + mFrameworkSock(-1), mAcceptedConnection(-1), mUeventSock(-1), + mHDMIUeventQueueHead(NULL), fd1(-1), mCurrentID(-1), mNxtMode(-1) +{ +} + +HDMIDaemon::~HDMIDaemon() { + HDMIUeventQueue* tmp = mHDMIUeventQueueHead, *tmp1; + while (tmp != NULL) { + tmp1 = tmp; + tmp = tmp->next; + delete tmp1; + } + mHDMIUeventQueueHead = NULL; + if (fd1 > 0) + close(fd1); +} + +void HDMIDaemon::onFirstRef() { + run("HDMIDaemon", PRIORITY_AUDIO); +} + +sp<SurfaceComposerClient> HDMIDaemon::session() const { + return mSession; +} + + +void HDMIDaemon::binderDied(const wp<IBinder>& who) +{ + requestExit(); +} + +status_t HDMIDaemon::readyToRun() { + + if ((mFrameworkSock = android_get_control_socket(HDMI_SOCKET_NAME)) < 0) { + LOGE("Obtaining file descriptor socket '%s' failed: %s", + HDMI_SOCKET_NAME, strerror(errno)); + return -1; + } + + if (listen(mFrameworkSock, 4) < 0) { + LOGE("Unable to listen on fd '%d' for socket '%s': %s", + mFrameworkSock, HDMI_SOCKET_NAME, strerror(errno)); + return -1; + } + + struct sockaddr_nl nladdr; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = getpid(); + nladdr.nl_groups = 0xffffffff; + + if ((mUeventSock = socket(PF_NETLINK, + SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { + LOGE("Unable to create uevent socket: %s", strerror(errno)); + return -1; + } + + int uevent_sz = 64 * 1024; + if (setsockopt(mUeventSock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz, + sizeof(uevent_sz)) < 0) { + LOGE("Unable to set uevent socket options: %s", strerror(errno)); + return -1; + } + + if (bind(mUeventSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { + LOGE("Unable to bind uevent socket: %s", strerror(errno)); + return -1; + } + + LOGD("readyToRun: success"); + + return NO_ERROR; +} + +bool HDMIDaemon::threadLoop() +{ + int max = -1; + fd_set read_fds; + FD_ZERO(&read_fds); + + FD_SET(mFrameworkSock, &read_fds); + if (max < mFrameworkSock) + max = mFrameworkSock; + FD_SET(mUeventSock, &read_fds); + if (max < mUeventSock) + max = mUeventSock; + + if (mAcceptedConnection != -1) { + FD_SET(mAcceptedConnection, &read_fds); + if (max < mAcceptedConnection) + max = mAcceptedConnection; + } + + struct timeval to; + to.tv_sec = (60 * 60); + to.tv_usec = 0; + + int ret; + if ((ret = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) { + LOGE("select() failed (%s)", strerror(errno)); + sleep(1); + return true; + } + + if (!ret) { + return true; + } + + if (mAcceptedConnection != -1 && FD_ISSET(mAcceptedConnection, &read_fds)) { + if (processFrameworkCommand() == -1) + mAcceptedConnection = -1; + } + + if (FD_ISSET(mFrameworkSock, &read_fds)) { + struct sockaddr addr; + socklen_t alen; + alen = sizeof(addr); + + if (mAcceptedConnection != -1) { + close(mAcceptedConnection); + mAcceptedConnection = accept(mFrameworkSock, &addr, &alen); + return true; + } + + if ((mAcceptedConnection = accept(mFrameworkSock, &addr, &alen)) < 0) { + LOGE("Unable to accept framework connection (%s)", + strerror(errno)); + } + else { + // Check if HDCP Keys are present + if(checkHDCPPresent()) { + LOGD("threadLoop: HDCP keys are present, delay Broadcast."); + sendCommandToFramework(action_no_broadcast_online); + } + + mSession = new SurfaceComposerClient(); + processUeventQueue(); + + if (!mDriverOnline) { + LOGE("threadLoop: driver not online; use state-file"); + sendCommandToFramework(action_offline); + } + } + + LOGD("threadLoop: Accepted connection from framework"); + } + + if (FD_ISSET(mUeventSock, &read_fds)) { + if (mAcceptedConnection == -1) + queueUevent(); + else + processUevent(); + } + + return true; +} + +bool HDMIDaemon::checkHDCPPresent() { + char present = '0'; + //Open the hdcp file - to know if HDCP is supported + int hdcpFile = open(SYSFS_HDCP_PRESENT, O_RDONLY, 0); + if (hdcpFile < 0) { + LOGE("%s: hdcp_present file '%s' not found", __func__, SYSFS_HDCP_PRESENT); + } else { + //Read from the hdcp_present file + int r = read(hdcpFile, &present, 1); + if (r <= 0) { + LOGE("%s: hdcp_present file empty '%s'", __func__, SYSFS_HDCP_PRESENT); + } + } + close(hdcpFile); + return (present == '1') ? true : false; +} +bool HDMIDaemon::cableConnected(bool defaultValue) const +{ + int hdmiStateFile = open(SYSFS_CONNECTED, O_RDONLY, 0); + if (hdmiStateFile < 0) { + LOGE("cableConnected: state file '%s' not found", SYSFS_CONNECTED); + return defaultValue; + } else { + char buf; + bool ret = defaultValue; + int err = read(hdmiStateFile, &buf, 1); + if (err <= 0) { + LOGE("cableConnected: empty state file '%s'", SYSFS_CONNECTED); + } else { + if (buf == '1') { + LOGD("cableConnected: %s indicates CONNECTED", SYSFS_CONNECTED); + ret = true; + } else { + LOGD("cableConnected: %s indicates DISCONNECTED", SYSFS_CONNECTED); + ret = false; + } + } + close(hdmiStateFile); + return ret; + } +} + +bool HDMIDaemon::processUeventMessage(uevent& event) +{ + char buffer[64 * 1024]; + int count; + char *s = buffer; + char *end; + int param_idx = 0; + int i; + bool first = true; + + if ((count = recv(mUeventSock, buffer, sizeof(buffer), 0)) < 0) { + LOGE("Error receiving uevent (%s)", strerror(errno)); + return false; + } + + end = s + count; + while (s < end) { + if (first) { + char *p; + for (p = s; *p != '@'; p++); + p++; + if (!strcasestr(p, DEVICE_NODE)) { + return false; + } + LOGD("device uevent (%s)", buffer); + event.path = new char[strlen(p) + 1]; + strcpy(event.path, p); + first = false; + } else { + if (!strncmp(s, "ACTION=", strlen("ACTION="))) { + char *a = s + strlen("ACTION="); + + if (!strcmp(a, "add")) + event.action = action_add; + else if (!strcmp(a, "change")) + event.action = action_change; + else if (!strcmp(a, "remove")) + event.action = action_remove; + else if (!strcmp(a, "online")) + event.action = action_online; + else if (!strcmp(a, "offline")) + event.action = action_offline; + else + LOGD("%s: action (%s) unknown", __func__, a); + } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM="))) { + event.seqnum = atoi(s + strlen("SEQNUM=")); + } else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM="))) { + event.subsystem = new char[strlen(s + strlen("SUBSYSTEM=")) + 1]; + strcpy(event.subsystem, (s + strlen("SUBSYSTEM="))); + } else if (!strncmp(s, "HDCP_STATE=", strlen("HDCP_STATE="))) { + if(!strcmp(s+strlen("HDCP_STATE="),"PASS")) { + //Event HDCP_STATE=PASS, send Audio On. + event.action = action_audio_on; + } else if(!strcmp(s+strlen("HDCP_STATE="), "FAIL")) { + //Event HDCP_STATE=FAIL, send Audio Off + event.action = action_audio_off; + } + } else { + event.param[param_idx] = new char[strlen(s) + 1]; + strcpy(event.param[param_idx], s); + param_idx++; + } + } + s += strlen(s) + 1; + } + return true; +} + +void HDMIDaemon::queueUevent() +{ + HDMIUeventQueue* tmp = mHDMIUeventQueueHead, *tmp1; + while (tmp != NULL && tmp->next != NULL) + tmp = tmp->next; + if (!tmp) { + tmp = new HDMIUeventQueue(); + tmp->next = NULL; + if(!processUeventMessage(tmp->mEvent)) + delete tmp; + else + mHDMIUeventQueueHead = tmp; + } + else { + tmp1 = new HDMIUeventQueue(); + tmp1->next = NULL; + if(!processUeventMessage(tmp1->mEvent)) + delete tmp1; + else + tmp->next = tmp1; + } +} + +void HDMIDaemon::processUeventQueue() +{ + HDMIUeventQueue* tmp = mHDMIUeventQueueHead, *tmp1; + while (tmp != NULL) { + tmp1 = tmp; + if (tmp->mEvent.action) { + LOGD("processUeventQueue: event.action == %d", tmp->mEvent.action); + mDriverOnline = true; + sendCommandToFramework(tmp->mEvent.action); + } + tmp = tmp->next; + delete tmp1; + } + mHDMIUeventQueueHead = NULL; +} + +void HDMIDaemon::processUevent() +{ + uevent event; + if(processUeventMessage(event)) { + if (event.action) { + LOGD("processUevent: event.action == %d", event.action); + mDriverOnline = true; + sendCommandToFramework(event.action); + } + } +} + +struct disp_mode_timing_type { + int video_format; + + int active_h; + int active_v; + + int front_porch_h; + int pulse_width_h; + int back_porch_h; + + int front_porch_v; + int pulse_width_v; + int back_porch_v; + + int pixel_freq; + bool interlaced; + + void set_info(struct fb_var_screeninfo &info) const; +}; + +void disp_mode_timing_type::set_info(struct fb_var_screeninfo &info) const +{ + info.reserved[0] = 0; + info.reserved[1] = 0; + info.reserved[2] = 0; + info.reserved[3] = video_format; + + info.xoffset = 0; + info.yoffset = 0; + info.xres = active_h; + info.yres = active_v; + + info.pixclock = pixel_freq*1000; + info.vmode = interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED; + + info.right_margin = front_porch_h; + info.hsync_len = pulse_width_h; + info.left_margin = back_porch_h; + info.lower_margin = front_porch_v; + info.vsync_len = pulse_width_v; + info.upper_margin = back_porch_v; +} + +/* Video formates supported by the HDMI Standard */ +/* Indicates the resolution, pix clock and the aspect ratio */ +#define m640x480p60_4_3 1 +#define m720x480p60_4_3 2 +#define m720x480p60_16_9 3 +#define m1280x720p60_16_9 4 +#define m1920x1080i60_16_9 5 +#define m1440x480i60_4_3 6 +#define m1440x480i60_16_9 7 +#define m1920x1080p60_16_9 16 +#define m720x576p50_4_3 17 +#define m720x576p50_16_9 18 +#define m1280x720p50_16_9 19 +#define m1440x576i50_4_3 21 +#define m1440x576i50_16_9 22 +#define m1920x1080p50_16_9 31 +#define m1920x1080p24_16_9 32 +#define m1920x1080p25_16_9 33 +#define m1920x1080p30_16_9 34 + +static struct disp_mode_timing_type supported_video_mode_lut[] = { + {m640x480p60_4_3, 640, 480, 16, 96, 48, 10, 2, 33, 25200, false}, + {m720x480p60_4_3, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false}, + {m720x480p60_16_9, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false}, + {m1280x720p60_16_9, 1280, 720, 110, 40, 220, 5, 5, 20, 74250, false}, + {m1920x1080i60_16_9, 1920, 540, 88, 44, 148, 2, 5, 5, 74250, false}, + {m1440x480i60_4_3, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true}, + {m1440x480i60_16_9, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true}, + {m1920x1080p60_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 148500, false}, + {m720x576p50_4_3, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false}, + {m720x576p50_16_9, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false}, + {m1280x720p50_16_9, 1280, 720, 440, 40, 220, 5, 5, 20, 74250, false}, + {m1440x576i50_4_3, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true}, + {m1440x576i50_16_9, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true}, + {m1920x1080p50_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 148500, false}, + {m1920x1080p24_16_9, 1920, 1080, 638, 44, 148, 4, 5, 36, 74250, false}, + {m1920x1080p25_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 74250, false}, + {m1920x1080p30_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 74250, false}, +}; + +bool HDMIDaemon::readResolution() +{ + int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0); + + memset(mEDIDs, 0, sizeof(mEDIDs)); + if (hdmiEDIDFile < 0) { + LOGE("%s: edid_modes file '%s' not found", __func__, SYSFS_EDID_MODES); + return false; + } else { + int r = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1); + if (r <= 0) + LOGE("%s: edid_modes file empty '%s'", __func__, SYSFS_EDID_MODES); + else { + while (r > 1 && isspace(mEDIDs[r-1])) + --r; + mEDIDs[r] = 0; + } + } + close(hdmiEDIDFile); + + return (strlen(mEDIDs) > 0); +} + +bool HDMIDaemon::openFramebuffer() +{ + if (fd1 == -1) { + fd1 = open("/dev/graphics/fb1", O_RDWR); + if (fd1 < 0) + LOGE("ERROR: /dev/graphics/fb1 not available\n"); + } + return (fd1 > 0); +} + +inline bool HDMIDaemon::isValidMode(int ID) +{ + return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9)); +} + +void HDMIDaemon::setResolution(int ID) +{ + struct fb_var_screeninfo info; + if (!openFramebuffer()) + return; + //If its a valid mode and its a new ID - update var_screeninfo + if ((isValidMode(ID)) && mCurrentID != ID) { + const struct disp_mode_timing_type *mode = &supported_video_mode_lut[0]; + for (unsigned int i = 0; i < sizeof(supported_video_mode_lut)/sizeof(*supported_video_mode_lut); ++i) { + const struct disp_mode_timing_type *cur = &supported_video_mode_lut[i]; + if (cur->video_format == ID) + mode = cur; + } + SurfaceComposerClient::enableHDMIOutput(0); + ioctl(fd1, FBIOGET_VSCREENINFO, &info); + LOGD("GET Info<ID=%d %dx%d (%d,%d,%d), (%d,%d,%d) %dMHz>", + info.reserved[3], info.xres, info.yres, + info.right_margin, info.hsync_len, info.left_margin, + info.lower_margin, info.vsync_len, info.upper_margin, + info.pixclock/1000/1000); + mode->set_info(info); + LOGD("SET Info<ID=%d => Info<ID=%d %dx%d (%d,%d,%d), (%d,%d,%d) %dMHz>", ID, + info.reserved[3], info.xres, info.yres, + info.right_margin, info.hsync_len, info.left_margin, + info.lower_margin, info.vsync_len, info.upper_margin, + info.pixclock/1000/1000); + info.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE; + ioctl(fd1, FBIOPUT_VSCREENINFO, &info); + mCurrentID = ID; + } + //Powerup + ioctl(fd1, FBIOBLANK, FB_BLANK_UNBLANK); + ioctl(fd1, FBIOGET_VSCREENINFO, &info); + //Pan_Display + ioctl(fd1, FBIOPAN_DISPLAY, &info); + property_set("hw.hdmiON", "1"); + //Inform SF about HDMI + SurfaceComposerClient::enableHDMIOutput(1); +} + +int HDMIDaemon::processFrameworkCommand() +{ + char buffer[128]; + int ret; + + if ((ret = read(mAcceptedConnection, buffer, sizeof(buffer) -1)) < 0) { + LOGE("Unable to read framework command (%s)", strerror(errno)); + return -1; + } + else if (!ret) + return -1; + + buffer[ret] = 0; + + if (!strcmp(buffer, HDMI_CMD_ENABLE_HDMI)) { + if (!openFramebuffer()) + return -1; + LOGD(HDMI_CMD_ENABLE_HDMI); + if(mNxtMode != -1) { + LOGD("processFrameworkCommand: setResolution with =%d", mNxtMode); + setResolution(mNxtMode); + } + } else if (!strcmp(buffer, HDMI_CMD_DISABLE_HDMI)) { + LOGD(HDMI_CMD_DISABLE_HDMI); + + if (!openFramebuffer()) + return -1; + property_set("hw.hdmiON", "0"); + SurfaceComposerClient::enableHDMIOutput(0); + close(fd1); + fd1 = -1; + } else if (!strncmp(buffer, HDMI_CMD_SET_ASWIDTH, strlen(HDMI_CMD_SET_ASWIDTH))) { + float asWidthRatio; + int ret = sscanf(buffer, HDMI_CMD_SET_ASWIDTH "%f", &asWidthRatio); + if(ret==1) { + SurfaceComposerClient::setActionSafeWidthRatio(asWidthRatio); + } + } else if (!strncmp(buffer, HDMI_CMD_SET_ASHEIGHT, strlen(HDMI_CMD_SET_ASHEIGHT))) { + float asHeightRatio; + int ret = sscanf(buffer, HDMI_CMD_SET_ASHEIGHT "%f", &asHeightRatio); + if(ret==1) { + SurfaceComposerClient::setActionSafeHeightRatio(asHeightRatio); + } + } else if (!strncmp(buffer, HDMI_CMD_HPDOPTION, strlen(HDMI_CMD_HPDOPTION))) { + int option; + int ret = sscanf(buffer, HDMI_CMD_HPDOPTION "%d", &option); + if (ret == 1) { + LOGD(HDMI_CMD_HPDOPTION ": %d", option); + writeHPDOption(option); + } + } else { + int mode; + int ret = sscanf(buffer, HDMI_CMD_CHANGE_MODE "%d", &mode); + if (ret == 1) { + LOGD(HDMI_CMD_CHANGE_MODE); + /* To change the resolution */ + char prop_val[PROPERTY_VALUE_MAX]; + property_get("enable.hdmi.edid", prop_val, "0"); + int val = atoi(prop_val); + if(val == 1) { + /* Based on the hw.yRes set the resolution */ + char property_value[PROPERTY_VALUE_MAX]; + property_get("hdmi.yRes", property_value, "0"); + int yres = atoi(property_value); + switch(yres){ + case 480: + mode = 3; + break; + case 720: + mode = 4; + break; + case 1080: + mode = 16; + break; + default: + break; + } + } + // If we have a valid fd1 - setresolution + if(fd1 > 0) { + setResolution(mode); + } else { + // Store the mode + mNxtMode = mode; + } + } + } + + return 0; +} + +bool HDMIDaemon::sendCommandToFramework(uevent_action action) +{ + char message[512]; + + switch (action) + { + //Disconnect + case action_offline: + strncpy(message, HDMI_EVT_DISCONNECTED, sizeof(message)); + break; + //Connect + case action_online: + readResolution(); + snprintf(message, sizeof(message), "%s: %s", HDMI_EVT_CONNECTED, mEDIDs); + break; + //action_audio_on + case action_audio_on: + strncpy(message, HDMI_EVT_AUDIO_ON, sizeof(message)); + break; + //action_audio_off + case action_audio_off: + strncpy(message, HDMI_EVT_AUDIO_OFF, sizeof(message)); + break; + //action_no_broadcast_online + case action_no_broadcast_online: + strncpy(message, HDMI_EVT_NO_BROADCAST_ONLINE, sizeof(message)); + break; + default: + LOGE("sendCommandToFramework: Unknown event received"); + break; + } + int result = write(mAcceptedConnection, message, strlen(message) + 1); + LOGD("sendCommandToFramework: '%s' %s", message, result >= 0 ? "successful" : "failed"); + return result >= 0; +} + +bool HDMIDaemon::writeHPDOption(int userOption) const +{ + bool ret = true; + int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0); + if (hdmiHPDFile < 0) { + LOGE("writeHPDOption: state file '%s' not found", SYSFS_HPD); + ret = false; + } else { + int err = -1; + if(userOption) + err = write(hdmiHPDFile, "1", 2); + else + err = write(hdmiHPDFile, "0" , 2); + if (err <= 0) { + LOGE("writeHPDOption: file write failed '%s'", SYSFS_HPD); + ret = false; + } + close(hdmiHPDFile); + } + return ret; +} + + +// --------------------------------------------------------------------------- + +} +; // namespace android diff --git a/cmds/hdmid/HDMIDaemon.h b/cmds/hdmid/HDMIDaemon.h new file mode 100644 index 0000000..6f63b95 --- /dev/null +++ b/cmds/hdmid/HDMIDaemon.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * 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. + */ + +#ifndef ANDROID_HDMISERVICE_H +#define ANDROID_HDMISERVICE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/AssetManager.h> + +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <pthread.h> + +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/un.h> + +#include <cutils/config_utils.h> +#include <cutils/cpu_info.h> +#include <cutils/properties.h> +#include <cutils/sockets.h> + +#include <linux/netlink.h> + +#include <private/android_filesystem_config.h> + + +namespace android { + +enum uevent_action { action_add, action_remove, action_change, + action_online, action_offline, action_audio_on, action_audio_off, action_no_broadcast_online }; +const int ueventParamMax = 32; +struct uevent { + char *path; + enum uevent_action action; + char *subsystem; + char *param[ueventParamMax]; + unsigned int seqnum; + uevent() : path(NULL), subsystem(NULL) { + for (int i = 0; i < ueventParamMax; i++) + param[i] = NULL; + } +}; + +struct HDMIUeventQueue { + HDMIUeventQueue* next; + uevent mEvent; + ~HDMIUeventQueue() { + delete[] mEvent.path; + delete[] mEvent.subsystem; + for (int i = 0; i < ueventParamMax; i++) { + if (!mEvent.param[i]) + break; + delete[] mEvent.param[i]; + } + } +}; + +class HDMIDaemon : public Thread, public IBinder::DeathRecipient +{ + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + virtual void binderDied(const wp<IBinder>& who); + bool processUeventMessage(uevent& event); + void queueUevent(); + void processUeventQueue(); + void processUevent(); + int processFrameworkCommand(); + bool sendCommandToFramework(uevent_action action = action_offline); + bool cableConnected(bool defaultValue = true) const; + bool readResolution(); + void setResolution(int ID); + bool openFramebuffer(); + bool writeHPDOption(int userOption) const; + inline bool isValidMode(int ID); + bool checkHDCPPresent(); + + int mFrameworkSock; + int mAcceptedConnection; + int mUeventSock; + HDMIUeventQueue* mHDMIUeventQueueHead; + sp<SurfaceComposerClient> mSession; + int fd1; + bool mDriverOnline; + int mCurrentID; + int mNxtMode; + char mEDIDs[128]; + +public: + HDMIDaemon(); + virtual ~HDMIDaemon(); + + sp<SurfaceComposerClient> session() const; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif diff --git a/cmds/hdmid/hdmid_main.cpp b/cmds/hdmid/hdmid_main.cpp new file mode 100644 index 0000000..b47f2a5 --- /dev/null +++ b/cmds/hdmid/hdmid_main.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * 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. + */ + +#define LOG_TAG "HDMIDaemon" + +#include <cutils/properties.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include <utils/Log.h> +#include <utils/threads.h> + +#if defined(HAVE_PTHREADS) +# include <pthread.h> +# include <sys/resource.h> +#endif + +#include "HDMIDaemon.h" + +using namespace android; + +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ +#if defined(HAVE_PTHREADS) + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); +#endif + + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + sp<HDMIDaemon> hdmiService = new HDMIDaemon(); + IPCThreadState::self()->joinThreadPool(); + + return 0; +} |