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 000000000000..1d09f84dca84 --- /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 | 
