M7350/base/cmds/hdmid/HDMIDaemon.cpp
2024-09-09 08:52:07 +00:00

742 lines
23 KiB
C++

/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. 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"
#define HDMI_PANEL '7'
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 what is external display connected(HDMI or Analog TV)
// and if HDCP Keys are present
if((!isHDMIPanel()) || checkHDCPPresent()) {
/*
If the external display is Analog TV - do not broadcast
Connected event.
If the HDCP keys are present, delay the broadcast to audio
as a different event will be triggred to notify audio.
*/
LOGD("threadLoop: 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::isHDMIPanel() {
int len = strlen("msmfbXX_");
bool ret = false;
struct fb_fix_screeninfo fb_finfo;
if (!openFramebuffer())
return false;
if (ioctl(fd1, FBIOGET_FSCREENINFO, &fb_finfo) < 0) {
LOGE("%s: Cannot retreive fixed screeninfo...!!", __func__);
} else {
LOGD("%s: fb_finfo.id == %s", __func__, fb_finfo.id);
if(fb_finfo.id[len] == HDMI_PANEL)
ret = true;
}
if (fd1 > 0) {
close(fd1);
fd1 = -1;
}
LOGD("%s: isHDMIPanel returns %d", __func__, ret);
return ret;
}
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(HDMIOUT_DISABLE);
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(HDMIOUT_ENABLE);
}
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)) {
SurfaceComposerClient::enableHDMIOutput(HDMIFB_OPEN);
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(HDMIOUT_DISABLE);
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);
if (option)
SurfaceComposerClient::enableHDMIOutput(HDMIHPD_ON);
else
SurfaceComposerClient::enableHDMIOutput(HDMIHPD_OFF);
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