blob: 1d09f84dca840be28ae8aaba1bfbb2ddcdb275b0 [file] [log] [blame]
/*
* Copyright (C) 2007 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.
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <linux/unistd.h>
#include <utils/Log.h>
#include "DisplayHardware/DisplayHardwareBase.h"
#include "SurfaceFlinger.h"
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
#ifndef HAVE_GETTID
# define gettid getpid
#endif
// ----------------------------------------------------------------------------
namespace android {
static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep";
static char const * kWakeFileName = "/sys/power/wait_for_fb_wake";
static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep";
static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake";
// This dir exists if the framebuffer console is present, either built into
// the kernel or loaded as a module.
static char const * const kFbconSysDir = "/sys/class/graphics/fbcon";
// ----------------------------------------------------------------------------
DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase(
const sp<SurfaceFlinger>& flinger)
: Thread(false), mFlinger(flinger) {
}
DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() {
}
// ----------------------------------------------------------------------------
DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
const sp<SurfaceFlinger>& flinger)
: DisplayEventThreadBase(flinger)
{
}
DisplayHardwareBase::DisplayEventThread::~DisplayEventThread()
{
}
bool DisplayHardwareBase::DisplayEventThread::threadLoop()
{
int err = 0;
char buf;
int fd;
fd = open(kSleepFileName, O_RDONLY, 0);
do {
err = read(fd, &buf, 1);
} while (err < 0 && errno == EINTR);
close(fd);
LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
if (err >= 0) {
sp<SurfaceFlinger> flinger = mFlinger.promote();
LOGD("About to give-up screen, flinger = %p", flinger.get());
if (flinger != 0) {
mBarrier.close();
flinger->screenReleased(0);
mBarrier.wait();
}
}
fd = open(kWakeFileName, O_RDONLY, 0);
do {
err = read(fd, &buf, 1);
} while (err < 0 && errno == EINTR);
close(fd);
LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
if (err >= 0) {
sp<SurfaceFlinger> flinger = mFlinger.promote();
LOGD("Screen about to return, flinger = %p", flinger.get());
if (flinger != 0)
flinger->screenAcquired(0);
}
return true;
}
status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
{
mBarrier.open();
return NO_ERROR;
}
status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
{
if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) {
if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) {
LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName);
return NO_INIT;
}
kSleepFileName = kOldSleepFileName;
kWakeFileName = kOldWakeFileName;
}
return NO_ERROR;
}
status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
{
return (((access(kSleepFileName, R_OK) == 0 &&
access(kWakeFileName, R_OK) == 0) ||
(access(kOldSleepFileName, R_OK) == 0 &&
access(kOldWakeFileName, R_OK) == 0)) &&
access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT;
}
// ----------------------------------------------------------------------------
pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0;
DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread(
const sp<SurfaceFlinger>& flinger)
: DisplayEventThreadBase(flinger), consoleFd(-1)
{
sSignalCatcherPid = 0;
// create a new console
char const * const ttydev = "/dev/tty0";
int fd = open(ttydev, O_RDWR | O_SYNC);
if (fd<0) {
LOGE("Can't open %s", ttydev);
this->consoleFd = -errno;
return;
}
// to make sure that we are in text mode
int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT);
if (res<0) {
LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)",
fd, res, strerror(errno));
}
// get the current console
struct vt_stat vs;
res = ioctl(fd, VT_GETSTATE, &vs);
if (res<0) {
LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)",
fd, res, strerror(errno));
this->consoleFd = -errno;
return;
}
// switch to console 7 (which is what X normaly uses)
int vtnum = 7;
do {
res = ioctl(fd, VT_ACTIVATE, (void*)vtnum);
} while(res < 0 && errno == EINTR);
if (res<0) {
LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d",
fd, errno, strerror(errno), vtnum);
this->consoleFd = -errno;
return;
}
do {
res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum);
} while(res < 0 && errno == EINTR);
if (res<0) {
LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d",
fd, res, errno, strerror(errno), vtnum);
this->consoleFd = -errno;
return;
}
// open the new console
close(fd);
fd = open(ttydev, O_RDWR | O_SYNC);
if (fd<0) {
LOGE("Can't open new console %s", ttydev);
this->consoleFd = -errno;
return;
}
/* disable console line buffer, echo, ... */
struct termios ttyarg;
ioctl(fd, TCGETS , &ttyarg);
ttyarg.c_iflag = 0;
ttyarg.c_lflag = 0;
ioctl(fd, TCSETS , &ttyarg);
// set up signals so we're notified when the console changes
// we can't use SIGUSR1 because it's used by the java-vm
vm.mode = VT_PROCESS;
vm.waitv = 0;
vm.relsig = SIGUSR2;
vm.acqsig = SIGUNUSED;
vm.frsig = 0;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = sigHandler;
act.sa_flags = 0;
sigaction(vm.relsig, &act, NULL);
sigemptyset(&act.sa_mask);
act.sa_handler = sigHandler;
act.sa_flags = 0;
sigaction(vm.acqsig, &act, NULL);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, vm.relsig);
sigaddset(&mask, vm.acqsig);
sigprocmask(SIG_BLOCK, &mask, NULL);
// switch to graphic mode
res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS);
LOGW_IF(res<0,
"ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res);
this->prev_vt_num = vs.v_active;
this->vt_num = vtnum;
this->consoleFd = fd;
}
DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread()
{
if (this->consoleFd >= 0) {
int fd = this->consoleFd;
int prev_vt_num = this->prev_vt_num;
int res;
ioctl(fd, KDSETMODE, (void*)KD_TEXT);
do {
res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num);
} while(res < 0 && errno == EINTR);
do {
res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num);
} while(res < 0 && errno == EINTR);
close(fd);
char const * const ttydev = "/dev/tty0";
fd = open(ttydev, O_RDWR | O_SYNC);
ioctl(fd, VT_DISALLOCATE, 0);
close(fd);
}
}
status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun()
{
if (this->consoleFd >= 0) {
sSignalCatcherPid = gettid();
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, vm.relsig);
sigaddset(&mask, vm.acqsig);
sigprocmask(SIG_BLOCK, &mask, NULL);
int res = ioctl(this->consoleFd, VT_SETMODE, &vm);
if (res<0) {
LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)",
this->consoleFd, errno, strerror(errno));
}
return NO_ERROR;
}
return this->consoleFd;
}
void DisplayHardwareBase::ConsoleManagerThread::requestExit()
{
Thread::requestExit();
if (sSignalCatcherPid != 0) {
// wake the thread up
kill(sSignalCatcherPid, SIGINT);
// wait for it...
}
}
void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig)
{
// resend the signal to our signal catcher thread
LOGW("received signal %d in thread %d, resending to %d",
sig, gettid(), sSignalCatcherPid);
// we absolutely need the delays below because without them
// our main thread never gets a chance to handle the signal.
usleep(10000);
kill(sSignalCatcherPid, sig);
usleep(10000);
}
status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const
{
int fd = this->consoleFd;
int err = ioctl(fd, VT_RELDISP, (void*)1);
LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)",
fd, errno, strerror(errno));
return (err<0) ? (-errno) : status_t(NO_ERROR);
}
bool DisplayHardwareBase::ConsoleManagerThread::threadLoop()
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, vm.relsig);
sigaddset(&mask, vm.acqsig);
int sig = 0;
sigwait(&mask, &sig);
if (sig == vm.relsig) {
sp<SurfaceFlinger> flinger = mFlinger.promote();
//LOGD("About to give-up screen, flinger = %p", flinger.get());
if (flinger != 0)
flinger->screenReleased(0);
} else if (sig == vm.acqsig) {
sp<SurfaceFlinger> flinger = mFlinger.promote();
//LOGD("Screen about to return, flinger = %p", flinger.get());
if (flinger != 0)
flinger->screenAcquired(0);
}
return true;
}
status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const
{
return consoleFd >= 0 ? NO_ERROR : NO_INIT;
}
// ----------------------------------------------------------------------------
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
uint32_t displayIndex)
: mCanDraw(true)
{
mDisplayEventThread = new DisplayEventThread(flinger);
if (mDisplayEventThread->initCheck() != NO_ERROR) {
// fall-back on the console
mDisplayEventThread = new ConsoleManagerThread(flinger);
}
}
DisplayHardwareBase::~DisplayHardwareBase()
{
// request exit
mDisplayEventThread->requestExitAndWait();
}
bool DisplayHardwareBase::canDraw() const
{
return mCanDraw;
}
void DisplayHardwareBase::releaseScreen() const
{
status_t err = mDisplayEventThread->releaseScreen();
if (err >= 0) {
//LOGD("screen given-up");
mCanDraw = false;
}
}
void DisplayHardwareBase::acquireScreen() const
{
status_t err = mDisplayEventThread->acquireScreen();
if (err >= 0) {
//LOGD("screen returned");
mCanDraw = true;
}
}
}; // namespace android