Commit 824acab1 authored by spiiroin's avatar spiiroin

Move fbdev control from display plugin to separate mce-fbdev module

Having mce hold open framebuffer device file descriptor during bootup
blocks display from making implicit power cycling while switching ui
components that draw on screen. But since the frame buffer is opened
from display plugin, it happens only after mce core functionality is
already initialized.

Making frame buffer handling be part of core functionality to allow the
device node to be opened earlier.

Also keept the framebuffer device opened also during normal operation
so that mce already has the file descriptor that is needed to keep the
shutdown logo on screen after ui and mce services are terminated.

To give visibility to possible lipstick crashes and switches between
various act dead ui processes, close the file descriptor if compositor
dbus service is terminated eventhought he device is not shutting down.

[mce] Move fbdev control from display plugin to separate mce-fbdev module. Fixes JB#26215
parent 5a1b3886
......@@ -168,6 +168,18 @@ mce-dsme.pic.o:\
mce-log.h\
mce.h\
mce-fbdev.o:\
mce-fbdev.c\
mce-fbdev.h\
mce-hybris.h\
mce-log.h\
mce-fbdev.pic.o:\
mce-fbdev.c\
mce-fbdev.h\
mce-hybris.h\
mce-log.h\
mce-gconf.o:\
mce-gconf.c\
builtin-gconf.h\
......@@ -295,6 +307,7 @@ mce.o:\
mce-conf.h\
mce-dbus.h\
mce-dsme.h\
mce-fbdev.h\
mce-gconf.h\
mce-log.h\
mce-modules.h\
......@@ -315,6 +328,7 @@ mce.pic.o:\
mce-conf.h\
mce-dbus.h\
mce-dsme.h\
mce-fbdev.h\
mce-gconf.h\
mce-log.h\
mce-modules.h\
......@@ -494,6 +508,7 @@ modules/display.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -511,6 +526,7 @@ modules/display.pic.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -831,6 +847,7 @@ tests/ut/ut_display.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-io.h\
mce-lib.h\
......@@ -850,6 +867,7 @@ tests/ut/ut_display.pic.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-io.h\
mce-lib.h\
......@@ -869,6 +887,7 @@ tests/ut/ut_display_blanking_inhibit.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -889,6 +908,7 @@ tests/ut/ut_display_blanking_inhibit.pic.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -909,6 +929,7 @@ tests/ut/ut_display_conf.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -929,6 +950,7 @@ tests/ut/ut_display_conf.pic.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -949,6 +971,7 @@ tests/ut/ut_display_filter.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -969,6 +992,7 @@ tests/ut/ut_display_filter.pic.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -989,6 +1013,7 @@ tests/ut/ut_display_stm.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......@@ -1009,6 +1034,7 @@ tests/ut/ut_display_stm.pic.o:\
libwakelock.h\
mce-conf.h\
mce-dbus.h\
mce-fbdev.h\
mce-gconf.h\
mce-hybris.h\
mce-io.h\
......
......@@ -282,6 +282,7 @@ MCE_LDLIBS += $(MCE_PKG_LDLIBS)
MCE_CORE += tklock.c
MCE_CORE += modetransition.c
MCE_CORE += powerkey.c
MCE_CORE += mce-fbdev.c
MCE_CORE += mce-dbus.c
MCE_CORE += mce-dsme.c
MCE_CORE += mce-gconf.c
......@@ -542,6 +543,8 @@ NORMALIZE_USES_SPC =\
filewatcher.c\
filewatcher.h\
libwakelock.h\
mce-fbdev.c\
mce-fbdev.h\
mce-command-line.c\
mce-command-line.h\
mce-hybris.c\
......
/**
* @file mce-fbdev.c
* Frame buffer device handling code for the Mode Control Entity
* <p>
* Copyright 2015 Jolla Ltd.
* <p>
* @author Simo Piiroinen <simo.piiroinen@jollamobile.com>
*
* mce is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* mce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mce. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mce-fbdev.h"
#include "mce-log.h"
#ifdef ENABLE_HYBRIS
# include "mce-hybris.h"
#endif
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
/* ========================================================================= *
* CONSTANTS
* ========================================================================= */
/** Path to the framebuffer device */
#define FB_DEVICE "/dev/fb0"
/* ========================================================================= *
* STATE_DATA
* ========================================================================= */
/** File descriptor for frame buffer device */
static int mce_fbdev_handle = -1;
/** Flag for: use hybris for fb power control */
#ifdef ENABLE_HYBRIS
static bool fbdev_use_hybris = false;
#endif
/** Flag for: Opening frame buffer device is allowed */
static bool mce_fbdev_allow_open = false;
/* ========================================================================= *
* FBDEV_FILE_DESCRIPTOR
* ========================================================================= */
/** Frame buffer is open predicate
*
* @return true if frame buffer device is currently opened, false otherwise
*/
bool mce_fbdev_is_open(void)
{
return mce_fbdev_handle != -1;
}
/** Open frame buffer device unless denied
*
* @return true on success, or false on failure
*/
bool mce_fbdev_open(void)
{
#ifdef ENABLE_HYBRIS
if( fbdev_use_hybris )
goto EXIT;
#endif
if( mce_fbdev_handle != -1 )
goto EXIT;
if( !mce_fbdev_allow_open )
goto EXIT;
mce_log(LL_NOTICE, "open frame buffer device");
if( (mce_fbdev_handle = open(FB_DEVICE, O_RDWR)) == -1 ) {
if( errno != ENOENT )
mce_log(LL_WARN, "failed to open frame buffer device: %m");
goto EXIT;
}
mce_log(LL_DEBUG, "frame buffer device opened");
EXIT:
return mce_fbdev_handle != -1;
}
/** Close frame buffer device
*/
void mce_fbdev_close(void)
{
if( mce_fbdev_handle == -1 )
goto EXIT;
mce_log(LL_NOTICE, "closing frame buffer device");
close(mce_fbdev_handle), mce_fbdev_handle = -1;
mce_log(LL_DEBUG, "closed frame buffer device");
EXIT:
return;
}
/** Reopen frame buffer device unless denied
*/
void mce_fbdev_reopen(void)
{
if( mce_fbdev_allow_open )
mce_fbdev_close();
mce_fbdev_open();
}
/* ========================================================================= *
* POST_EXIT_LINGER
* ========================================================================= */
/** Create a child process to keep frame buffer device open after mce exits
*
* The frame buffer device powers off automatically when the last open
* file descriptor gets closed.
*
* To allow the shutdown logo to stay on screen after lipstick and mce
* have been terminated, we create a detached child process that hangs
* on to frame buffer device.
*
* @param delay_ms how long the child process should linger
*/
void mce_fbdev_linger_after_exit(int delay_ms)
{
static const char msg[] = "closing frame buffer device after delay\n";
/* Fork a child process */
int child_pid = fork();
/* Deal with parent side and return to caller */
if( child_pid != 0 ) {
if( child_pid < 0 )
mce_log(LL_ERR, "forking fbdev linger child failed: %m");
else
mce_log(LL_DEBUG, "fbdev linger child: pid %d", child_pid);
return;
}
/* Detach from parent so that we will not get killed with it */
setsid();
/* Close all files, except fbdev & stderr */
int nfd = getdtablesize();
for( int fd = 0; fd < nfd; ++fd ) {
if( fd != mce_fbdev_handle && fd != STDERR_FILENO )
close(fd);
}
if( delay_ms < 500 )
delay_ms = 500;
/* Wait ... */
struct timespec ts =
{
.tv_sec = (time_t)(delay_ms / 1000),
.tv_nsec = (long)(delay_ms % 1000) * 1000000,
};
while( nanosleep(&ts, &ts) == -1 && errno == EINTR ) { /* nop */ }
/* If journald is still up, the end-of-linger message written to stderr
* ends up in journal and attributed to parent mce process.
*
* ... and in case journald has already made an exit, we do not want
* to die by SIGPIPE, so it needs to be ignored. */
signal(SIGPIPE, SIG_IGN);
if( write(STDERR_FILENO, msg, sizeof msg - 1) < 0 ) { /* dontcare */ }
/* Exit - the frame buffer device will power off if we
* were the last process to have an open file descriptor */
_exit(EXIT_SUCCESS);
}
/* ========================================================================= *
* FRAMEBUFFER_POWER
* ========================================================================= */
/** Set the frame buffer power state
*
* MCE uses this function for display power control only if autosuspend
* control sysfs files are not present.
*
* If there is a hw composer that is also doing fbdev ioctl() calls,
* there is some change that kernel side troubles can be caused by
* having two entities trying to control the same resource.
*
* @param power_in true to power up, false to power down
*/
void mce_fbdev_set_power(bool power_on)
{
mce_log(LL_DEBUG, "fbdev power %s", power_on ? "up" : "down");
if( mce_fbdev_handle != -1 ) {
int value = power_on ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
if( ioctl(mce_fbdev_handle, FBIOBLANK, value) == -1 )
mce_log(LL_ERR, "%s: ioctl(FBIOBLANK,%d): %m", FB_DEVICE, value);
else
mce_log(LL_DEBUG, "success");
}
#ifdef ENABLE_HYBRIS
else if( fbdev_use_hybris ) {
mce_hybris_framebuffer_set_power(power_on);
}
#endif
return;
}
/* ========================================================================= *
* MODULE_INIT
* ========================================================================= */
/** Initialize frame buffer module
*/
void mce_fbdev_init(void)
{
/* allow opening frame buffer device */
mce_fbdev_allow_open = true;
if( mce_fbdev_open() ) {
mce_log(LL_NOTICE, "using ioctl for fb power control");
}
#ifdef ENABLE_HYBRIS
else if( mce_hybris_framebuffer_init() ) {
mce_log(LL_NOTICE, "using libhybris for fb power control");
fbdev_use_hybris = true;
}
#endif
else {
mce_log(LL_WARN, "no fb power control available");;
}
}
/** De-initialize frame buffer module
*/
void mce_fbdev_quit(void)
{
/* deny opening frame buffer device */
mce_fbdev_allow_open = false;
mce_fbdev_close();
#ifdef ENABLE_HYBRIS
if( fbdev_use_hybris ) {
fbdev_use_hybris = false;
mce_hybris_framebuffer_quit();
}
#endif
}
/**
* @file mce-fbdev.h
* Frame buffer device handling code for the Mode Control Entity
* <p>
* Copyright 2015 Jolla Ltd.
* <p>
* @author Simo Piiroinen <simo.piiroinen@jollamobile.com>
*
* mce is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* mce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mce. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MCE_FBDEV_H_
# define MCE_FBDEV_H_
# include <stdbool.h>
# ifdef __cplusplus
extern "C" {
# endif
void mce_fbdev_init (void);
void mce_fbdev_quit (void);
bool mce_fbdev_open (void);
void mce_fbdev_close (void);
void mce_fbdev_reopen (void);
bool mce_fbdev_is_open (void);
void mce_fbdev_set_power (bool power_on);
void mce_fbdev_linger_after_exit (int delay_ms);
# ifdef __cplusplus
};
# endif
#endif /* MCE_FBDEV_H_ */
......@@ -22,6 +22,7 @@
#include "mce.h"
#include "mce-log.h"
#include "mce-conf.h"
#include "mce-fbdev.h"
#include "mce-gconf.h"
#include "mce-dbus.h"
#include "mce-dsme.h"
......@@ -969,6 +970,9 @@ int main(int argc, char **argv)
/* Initialise subsystems */
/* Open fbdev as early as possible */
mce_fbdev_init();
/* Get configuration options */
if( !mce_conf_init() ) {
mce_log(LL_CRIT,
......@@ -1091,6 +1095,7 @@ EXIT:
mce_gconf_exit();
mce_dbus_exit();
mce_conf_exit();
mce_fbdev_quit();
/* If the mainloop is initialised, unreference it */
if (mainloop != NULL) {
......
This diff is collapsed.
......@@ -96,9 +96,6 @@
/** Generic maximum brightness file */
#define DISPLAY_GENERIC_MAX_BRIGHTNESS_FILE "/backlight_max"
/** Path to the framebuffer device */
#define FB_DEVICE "/dev/fb0"
/** Path to the GConf settings for the display */
#define MCE_GCONF_DISPLAY_PATH "/system/osso/dsm/display"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment