blob: 90865da80b623a306085ad9ea9b18ec632b0ecca [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017#include <assert.h>
18#include <errno.h>
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22
23#include <unistd.h>
24#include <fcntl.h>
25#include <signal.h>
26#include <termios.h>
27#include <sys/ioctl.h>
28#include <sys/mman.h>
29#include <sys/time.h>
30#include <sys/types.h>
31#include <sys/resource.h>
32
33#include <linux/unistd.h>
34
35#include <utils/Log.h>
36
37#include "DisplayHardware/DisplayHardwareBase.h"
38#include "SurfaceFlinger.h"
39
40// ----------------------------------------------------------------------------
41// the sim build doesn't have gettid
42
43#ifndef HAVE_GETTID
44# define gettid getpid
45#endif
46
47// ----------------------------------------------------------------------------
48namespace android {
49
50static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep";
51static char const * kWakeFileName = "/sys/power/wait_for_fb_wake";
52static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep";
53static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake";
54
55// This dir exists if the framebuffer console is present, either built into
56// the kernel or loaded as a module.
57static char const * const kFbconSysDir = "/sys/class/graphics/fbcon";
58
59// ----------------------------------------------------------------------------
60
61DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase(
62 const sp<SurfaceFlinger>& flinger)
63 : Thread(false), mFlinger(flinger) {
64}
65
66DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() {
67}
68
69// ----------------------------------------------------------------------------
70
71DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
72 const sp<SurfaceFlinger>& flinger)
73 : DisplayEventThreadBase(flinger)
74{
75}
76
77DisplayHardwareBase::DisplayEventThread::~DisplayEventThread()
78{
79}
80
81bool DisplayHardwareBase::DisplayEventThread::threadLoop()
82{
83 int err = 0;
84 char buf;
85 int fd;
86
87 fd = open(kSleepFileName, O_RDONLY, 0);
88 do {
89 err = read(fd, &buf, 1);
90 } while (err < 0 && errno == EINTR);
91 close(fd);
92 LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
93 if (err >= 0) {
94 sp<SurfaceFlinger> flinger = mFlinger.promote();
95 LOGD("About to give-up screen, flinger = %p", flinger.get());
96 if (flinger != 0) {
97 mBarrier.close();
98 flinger->screenReleased(0);
99 mBarrier.wait();
100 }
101 }
102 fd = open(kWakeFileName, O_RDONLY, 0);
103 do {
104 err = read(fd, &buf, 1);
105 } while (err < 0 && errno == EINTR);
106 close(fd);
107 LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
108 if (err >= 0) {
109 sp<SurfaceFlinger> flinger = mFlinger.promote();
110 LOGD("Screen about to return, flinger = %p", flinger.get());
111 if (flinger != 0)
112 flinger->screenAcquired(0);
113 }
114 return true;
115}
116
117status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
118{
119 mBarrier.open();
120 return NO_ERROR;
121}
122
123status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
124{
125 if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) {
126 if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) {
127 LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName);
128 return NO_INIT;
129 }
130 kSleepFileName = kOldSleepFileName;
131 kWakeFileName = kOldWakeFileName;
132 }
133 return NO_ERROR;
134}
135
136status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
137{
138 return (((access(kSleepFileName, R_OK) == 0 &&
139 access(kWakeFileName, R_OK) == 0) ||
140 (access(kOldSleepFileName, R_OK) == 0 &&
141 access(kOldWakeFileName, R_OK) == 0)) &&
142 access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT;
143}
144
145// ----------------------------------------------------------------------------
146
147pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0;
148
149DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread(
150 const sp<SurfaceFlinger>& flinger)
151 : DisplayEventThreadBase(flinger), consoleFd(-1)
152{
153 sSignalCatcherPid = 0;
154
155 // create a new console
156 char const * const ttydev = "/dev/tty0";
157 int fd = open(ttydev, O_RDWR | O_SYNC);
158 if (fd<0) {
159 LOGE("Can't open %s", ttydev);
160 this->consoleFd = -errno;
161 return;
162 }
163
164 // to make sure that we are in text mode
165 int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT);
166 if (res<0) {
167 LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)",
168 fd, res, strerror(errno));
169 }
170
171 // get the current console
172 struct vt_stat vs;
173 res = ioctl(fd, VT_GETSTATE, &vs);
174 if (res<0) {
175 LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)",
176 fd, res, strerror(errno));
177 this->consoleFd = -errno;
178 return;
179 }
180
181 // switch to console 7 (which is what X normaly uses)
182 int vtnum = 7;
183 do {
184 res = ioctl(fd, VT_ACTIVATE, (void*)vtnum);
185 } while(res < 0 && errno == EINTR);
186 if (res<0) {
187 LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d",
188 fd, errno, strerror(errno), vtnum);
189 this->consoleFd = -errno;
190 return;
191 }
192
193 do {
194 res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum);
195 } while(res < 0 && errno == EINTR);
196 if (res<0) {
197 LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d",
198 fd, res, errno, strerror(errno), vtnum);
199 this->consoleFd = -errno;
200 return;
201 }
202
203 // open the new console
204 close(fd);
205 fd = open(ttydev, O_RDWR | O_SYNC);
206 if (fd<0) {
207 LOGE("Can't open new console %s", ttydev);
208 this->consoleFd = -errno;
209 return;
210 }
211
212 /* disable console line buffer, echo, ... */
213 struct termios ttyarg;
214 ioctl(fd, TCGETS , &ttyarg);
215 ttyarg.c_iflag = 0;
216 ttyarg.c_lflag = 0;
217 ioctl(fd, TCSETS , &ttyarg);
218
219 // set up signals so we're notified when the console changes
220 // we can't use SIGUSR1 because it's used by the java-vm
221 vm.mode = VT_PROCESS;
222 vm.waitv = 0;
223 vm.relsig = SIGUSR2;
224 vm.acqsig = SIGUNUSED;
225 vm.frsig = 0;
226
227 struct sigaction act;
228 sigemptyset(&act.sa_mask);
229 act.sa_handler = sigHandler;
230 act.sa_flags = 0;
231 sigaction(vm.relsig, &act, NULL);
232
233 sigemptyset(&act.sa_mask);
234 act.sa_handler = sigHandler;
235 act.sa_flags = 0;
236 sigaction(vm.acqsig, &act, NULL);
237
238 sigset_t mask;
239 sigemptyset(&mask);
240 sigaddset(&mask, vm.relsig);
241 sigaddset(&mask, vm.acqsig);
242 sigprocmask(SIG_BLOCK, &mask, NULL);
243
244 // switch to graphic mode
245 res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS);
246 LOGW_IF(res<0,
247 "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res);
248
249 this->prev_vt_num = vs.v_active;
250 this->vt_num = vtnum;
251 this->consoleFd = fd;
252}
253
254DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread()
255{
256 if (this->consoleFd >= 0) {
257 int fd = this->consoleFd;
258 int prev_vt_num = this->prev_vt_num;
259 int res;
260 ioctl(fd, KDSETMODE, (void*)KD_TEXT);
261 do {
262 res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num);
263 } while(res < 0 && errno == EINTR);
264 do {
265 res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num);
266 } while(res < 0 && errno == EINTR);
267 close(fd);
268 char const * const ttydev = "/dev/tty0";
269 fd = open(ttydev, O_RDWR | O_SYNC);
270 ioctl(fd, VT_DISALLOCATE, 0);
271 close(fd);
272 }
273}
274
275status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun()
276{
277 if (this->consoleFd >= 0) {
278 sSignalCatcherPid = gettid();
279
280 sigset_t mask;
281 sigemptyset(&mask);
282 sigaddset(&mask, vm.relsig);
283 sigaddset(&mask, vm.acqsig);
284 sigprocmask(SIG_BLOCK, &mask, NULL);
285
286 int res = ioctl(this->consoleFd, VT_SETMODE, &vm);
287 if (res<0) {
288 LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)",
289 this->consoleFd, errno, strerror(errno));
290 }
291 return NO_ERROR;
292 }
293 return this->consoleFd;
294}
295
296void DisplayHardwareBase::ConsoleManagerThread::requestExit()
297{
298 Thread::requestExit();
299 if (sSignalCatcherPid != 0) {
300 // wake the thread up
301 kill(sSignalCatcherPid, SIGINT);
302 // wait for it...
303 }
304}
305
306void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig)
307{
308 // resend the signal to our signal catcher thread
309 LOGW("received signal %d in thread %d, resending to %d",
310 sig, gettid(), sSignalCatcherPid);
311
312 // we absolutely need the delays below because without them
313 // our main thread never gets a chance to handle the signal.
314 usleep(10000);
315 kill(sSignalCatcherPid, sig);
316 usleep(10000);
317}
318
319status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const
320{
321 int fd = this->consoleFd;
322 int err = ioctl(fd, VT_RELDISP, (void*)1);
323 LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)",
324 fd, errno, strerror(errno));
325 return (err<0) ? (-errno) : status_t(NO_ERROR);
326}
327
328bool DisplayHardwareBase::ConsoleManagerThread::threadLoop()
329{
330 sigset_t mask;
331 sigemptyset(&mask);
332 sigaddset(&mask, vm.relsig);
333 sigaddset(&mask, vm.acqsig);
334
335 int sig = 0;
336 sigwait(&mask, &sig);
337
338 if (sig == vm.relsig) {
339 sp<SurfaceFlinger> flinger = mFlinger.promote();
340 //LOGD("About to give-up screen, flinger = %p", flinger.get());
341 if (flinger != 0)
342 flinger->screenReleased(0);
343 } else if (sig == vm.acqsig) {
344 sp<SurfaceFlinger> flinger = mFlinger.promote();
345 //LOGD("Screen about to return, flinger = %p", flinger.get());
346 if (flinger != 0)
347 flinger->screenAcquired(0);
348 }
349
350 return true;
351}
352
353status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const
354{
355 return consoleFd >= 0 ? NO_ERROR : NO_INIT;
356}
357
358// ----------------------------------------------------------------------------
359
360DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
361 uint32_t displayIndex)
Mathias Agopianaab758e2010-10-11 12:37:43 -0700362 : mCanDraw(true), mScreenAcquired(true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363{
364 mDisplayEventThread = new DisplayEventThread(flinger);
365 if (mDisplayEventThread->initCheck() != NO_ERROR) {
366 // fall-back on the console
367 mDisplayEventThread = new ConsoleManagerThread(flinger);
368 }
369}
370
371DisplayHardwareBase::~DisplayHardwareBase()
372{
373 // request exit
374 mDisplayEventThread->requestExitAndWait();
375}
376
Mathias Agopianaab758e2010-10-11 12:37:43 -0700377void DisplayHardwareBase::setCanDraw(bool canDraw)
378{
379 mCanDraw = canDraw;
380}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381
382bool DisplayHardwareBase::canDraw() const
383{
Mathias Agopianaab758e2010-10-11 12:37:43 -0700384 return mCanDraw && mScreenAcquired;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385}
386
387void DisplayHardwareBase::releaseScreen() const
388{
389 status_t err = mDisplayEventThread->releaseScreen();
390 if (err >= 0) {
Mathias Agopianaab758e2010-10-11 12:37:43 -0700391 mScreenAcquired = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
393}
394
395void DisplayHardwareBase::acquireScreen() const
396{
397 status_t err = mDisplayEventThread->acquireScreen();
398 if (err >= 0) {
Mathias Agopianaab758e2010-10-11 12:37:43 -0700399 mScreenAcquired = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 }
401}
402
Mathias Agopianaab758e2010-10-11 12:37:43 -0700403bool DisplayHardwareBase::isScreenAcquired() const
404{
405 return mScreenAcquired;
406}
407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408}; // namespace android