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