/* * 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