diff options
Diffstat (limited to 'services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp')
-rw-r--r-- | services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp new file mode 100644 index 0000000..1d09f84 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -0,0 +1,401 @@ +/* + * 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 |