diff --git a/.depend b/.depend index 450709c5..b865d517 100644 --- a/.depend +++ b/.depend @@ -330,6 +330,16 @@ mce-sensorfw.pic.o:\ mce-sensorfw.h\ mce.h\ +mce-wakelock.o:\ + mce-wakelock.c\ + mce-log.h\ + mce-wakelock.h\ + +mce-wakelock.pic.o:\ + mce-wakelock.c\ + mce-log.h\ + mce-wakelock.h\ + mce.o:\ mce.c\ builtin-gconf.h\ @@ -347,6 +357,7 @@ mce.o:\ mce-log.h\ mce-modules.h\ mce-sensorfw.h\ + mce-wakelock.h\ mce.h\ modetransition.h\ powerkey.h\ @@ -369,6 +380,7 @@ mce.pic.o:\ mce-log.h\ mce-modules.h\ mce-sensorfw.h\ + mce-wakelock.h\ mce.h\ modetransition.h\ powerkey.h\ diff --git a/Makefile b/Makefile index 87bd896a..29f887f7 100644 --- a/Makefile +++ b/Makefile @@ -280,6 +280,7 @@ MCE_CORE += mce-dbus.c MCE_CORE += mce-dsme.c MCE_CORE += mce-gconf.c MCE_CORE += mce-hbtimer.c +MCE_CORE += mce-wakelock.c MCE_CORE += event-input.c MCE_CORE += event-switches.c MCE_CORE += mce-hal.c @@ -541,6 +542,8 @@ NORMALIZE_USES_SPC =\ mce-modules.h\ mce-sensorfw.c\ mce-sensorfw.h\ + mce-wakelock.c\ + mce-wakelock.h\ modetransition.h\ modules/audiorouting.c\ modules/battery-upower.c\ diff --git a/mce-wakelock.c b/mce-wakelock.c new file mode 100644 index 00000000..498a3309 --- /dev/null +++ b/mce-wakelock.c @@ -0,0 +1,479 @@ +/** + * @file mce-wakelock.c + * Wakelock multiplexing code for the Mode Control Entity + *

+ * Copyright (C) 2015 Jolla Ltd. + *

+ * @author Simo Piiroinen + * + * 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 . + */ + +#include "mce-wakelock.h" +#include "mce-log.h" + +#include +#include +#include +#include + +#include + +/* ========================================================================= * + * FUNCTIONS & DATA + * ========================================================================= */ + +/* ------------------------------------------------------------------------- * + * SYSFS_API + * ------------------------------------------------------------------------- */ + +/** Path to kernel wakelock obtain sysfs file */ +static const char mwl_sysfs_lock_path[] = "/sys/power/wake_lock"; + +/** Path to kernel wakelock release sysfs file */ +static const char mwl_sysfs_unlock_path[] = "/sys/power/wake_unlock"; + +static bool mwl_sysfs_write(const char *path, const char *data, int size); + +/* ------------------------------------------------------------------------- * + * RAWLOCK_API + * ------------------------------------------------------------------------- */ + +/** Name of the multiplexed "real" wakelock */ +static const char mwl_rawlock_name[] = "mce_mux"; + +/** Flag for: "real" wakelock is held */ +static bool mce_rawlock_locked = false; + +static bool mwl_rawlock_supported (void); +static bool mwl_rawlock_lock (void); +static bool mwl_rawlock_unlock (void); +static void mwl_rawlock_set (bool lock); + +/* ------------------------------------------------------------------------- * + * mwl_wakelock_t + * ------------------------------------------------------------------------- */ + +/** Virtual wakelock object */ +typedef struct mwl_wakelock_t +{ + /** Name of the virtual wakelock */ + gchar *wl_name; + + /** Release timer id */ + guint wl_timer_id; +} mwl_wakelock_t; + +static gboolean mwl_wakelock_timer_cb (gpointer aptr); +static void mwl_wakelock_start_timer (mwl_wakelock_t *self, int delay_ms); +static void mwl_wakelock_stop_timer (mwl_wakelock_t *self); + +static mwl_wakelock_t *mwl_wakelock_create (const char *name); +static void mwl_wakelock_delete (mwl_wakelock_t *self); +static void mwl_wakelock_delete_cb (void *self); + +/* ------------------------------------------------------------------------- * + * MODULE_API + * ------------------------------------------------------------------------- */ + +/** Flag for: wakelock module is ready for use */ +static bool mce_wakelock_ready = false; + +/** Lookup table for tracked wakelock objects */ +static GHashTable *mce_wakelock_lut = 0; // [name] -> mwl_wakelock_t * + +static mwl_wakelock_t *mce_wakelock_add_entry (const char *name); +static void mce_wakelock_rem_entry (const char *name); +static bool mce_wakelock_have_entries (void); + +void mce_wakelock_obtain (const char *name, int duration_ms); +void mce_wakelock_release (const char *name); + +void mce_wakelock_init (void); +void mce_wakelock_quit (void); +void mce_wakelock_abort (void); + +/* ========================================================================= * + * SYSFS_API + * ========================================================================= */ + +/** Helper for writing to sysfs files + */ +static bool +mwl_sysfs_write(const char *path, const char *data, int size) +{ + bool res = false; + int fd = -1; + + if( !path || !data || size <= 0 ) + goto cleanup; + + if( (fd = open(path, O_WRONLY)) == -1 ) + goto cleanup; + + if( write(fd, data, size) == -1 ) + goto cleanup; + + res = true; + +cleanup: + if( fd != -1 ) close(fd); + + return res; +} + +/* ========================================================================= * + * RAWLOCK_API + * ========================================================================= */ + +/** Predicate for: wakelock sysfs control files exist + */ +static bool +mwl_rawlock_supported(void) +{ + return (access(mwl_sysfs_lock_path, W_OK) == 0 && + access(mwl_sysfs_unlock_path, W_OK) == 0); +} + +/** Async signal safe wakelock obtain + */ +static bool +mwl_rawlock_lock(void) +{ + return mwl_sysfs_write(mwl_sysfs_lock_path, mwl_rawlock_name, + sizeof mwl_rawlock_name - 1); +} + +/** Async signal safe wakelock release + */ +static bool +mwl_rawlock_unlock(void) +{ + return mwl_sysfs_write(mwl_sysfs_unlock_path, mwl_rawlock_name, + sizeof mwl_rawlock_name - 1); +} + +/** Set wakelock state + * + * @param lock true to obtain real wakelock, false to release + */ +static void +mwl_rawlock_set(bool lock) +{ + if( mce_rawlock_locked == lock ) + goto EXIT; + + mce_log(LL_DEBUG, "wakelock %s", lock ? "obtain" : "release"); + + errno = 0; + + if( (mce_rawlock_locked = lock) ) { + if( !mwl_rawlock_lock() ) + mce_log(LL_ERR, "failed to obtain wakelock: %m"); + } + else { + if( !mwl_rawlock_unlock() ) + mce_log(LL_ERR, "failed to release wakelock: %m"); + } + +EXIT: + return; +} + +/* ========================================================================= * + * mwl_wakelock_t + * ========================================================================= */ + +/** Timer callback for releasing virtual wakelock + * + * @param aptr wakelock object pointer + */ +static gboolean +mwl_wakelock_timer_cb(gpointer aptr) +{ + mwl_wakelock_t *self = aptr; + + if( !self ) + goto EXIT; + + if( !self->wl_timer_id ) + goto EXIT; + + self->wl_timer_id = 0; + + mce_wakelock_release(self->wl_name); + +EXIT: + return FALSE; +} + +/** Start virtual wakelock object release timer + * + * @param self wakelock object pointer, or NULL + * @param delay_ms delay before releasing wakelock + */ +static void +mwl_wakelock_start_timer(mwl_wakelock_t *self, int delay_ms) +{ + if( !self ) + goto EXIT; + + if( self->wl_timer_id ) { + g_source_remove(self->wl_timer_id), + self->wl_timer_id = 0; + } + + if( delay_ms < 0 ) + goto EXIT; + + if( delay_ms > 0 ) { + self->wl_timer_id = g_timeout_add(delay_ms, + mwl_wakelock_timer_cb, + self); + } + else { + self->wl_timer_id = g_idle_add(mwl_wakelock_timer_cb, self); + } + +EXIT: + return; +} + +/** Stop virtual wakelock object release timer + * + * @param self wakelock object pointer, or NULL + */ +static void +mwl_wakelock_stop_timer(mwl_wakelock_t *self) +{ + if( !self ) + goto EXIT; + + if( self->wl_timer_id ) { + g_source_remove(self->wl_timer_id), + self->wl_timer_id = 0; + } + +EXIT: + return; +} + +/** Create virtual wakelock object + * + * @param name Name of wakelock object to create + * + * @return wakelock object pointer + */ +static mwl_wakelock_t * +mwl_wakelock_create(const char *name) +{ + mwl_wakelock_t *self = g_malloc0(sizeof *self); + + self->wl_name = g_strdup(name); + self->wl_timer_id = 0; + + mce_log(LL_DEBUG, "wakelock %s obtain (mux)", self->wl_name); + + return self; +} + +/** Delete virtual wakelock object + * + * @param self wakelock object pointer, or NULL + */ +static void +mwl_wakelock_delete(mwl_wakelock_t *self) +{ + if( !self ) + goto EXIT; + + mwl_wakelock_stop_timer(self); + + mce_log(LL_DEBUG, "wakelock %s release (mux)", self->wl_name); + + g_free(self->wl_name); + g_free(self); + +EXIT: + return; +} + +/** GDestroyNotify compatible delete callback + * + * @param aptr wakelock object pointer (as void pointer), or NULL + */ +static void +mwl_wakelock_delete_cb(void *aptr) +{ + mwl_wakelock_delete(aptr); +} + +/* ========================================================================= * + * MODULE_API + * ========================================================================= */ + +/** Lookup or create a wakelock object by name + * + * @param name Name of the virtual wakelock + * + * @return object pointer, or NULL on errors + */ +static mwl_wakelock_t * +mce_wakelock_add_entry(const char *name) +{ + mwl_wakelock_t *self = 0; + + if( !mce_wakelock_lut ) + goto EXIT; + + if( !(self = g_hash_table_lookup(mce_wakelock_lut, name)) ) { + self = mwl_wakelock_create(name); + g_hash_table_replace(mce_wakelock_lut, g_strdup(name), self); + } + +EXIT: + return self; +} + +/** Remove a wakelock object by name + * + * @param name Name of the virtual wakelock + */ +static void +mce_wakelock_rem_entry(const char *name) +{ + if( !mce_wakelock_lut ) + goto EXIT; + + g_hash_table_remove(mce_wakelock_lut, name); + +EXIT: + return; +} + +/** Predicate for: have virtual wakelocks + * + * @return true if there are active virtual wakelocks, false otherwise + */ +static bool +mce_wakelock_have_entries(void) +{ + bool have_entries = false; + + if( !mce_wakelock_lut ) + goto EXIT; + + if( g_hash_table_size(mce_wakelock_lut) > 0 ) + have_entries = true; + +EXIT: + return have_entries; +} + +/** Obtain virtual wakelock + * + * @param name Name of the virtual wakelock + */ +void +mce_wakelock_obtain(const char *name, int duration_ms) +{ + if( !mce_wakelock_ready ) + goto EXIT; + + /* Add entry & start release timer */ + mwl_wakelock_start_timer(mce_wakelock_add_entry(name), + duration_ms); + + /* Re-evaluate need for real wakelock */ + mwl_rawlock_set(mce_wakelock_have_entries()); + +EXIT: + return; +} + +/** Release virtual wakelock + * + * @param name Name of the virtual wakelock + */ +void +mce_wakelock_release(const char *name) +{ + if( !mce_wakelock_ready ) + goto EXIT; + + /* Remove entry */ + mce_wakelock_rem_entry(name); + + /* Re-evaluate need for real wakelock */ + mwl_rawlock_set(mce_wakelock_have_entries()); + +EXIT: + return; +} + +/** Initialize mce wakelock subsystem + */ +void +mce_wakelock_init(void) +{ + /* Leave disabled if sysfs control files do not exist */ + if( !mwl_rawlock_supported() ) + goto EXIT; + + /* Setup entry look up table */ + if( !mce_wakelock_lut ) + mce_wakelock_lut = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, mwl_wakelock_delete_cb); + + /* In case previous mce instance managed to exit without + * clearing wakelocks: unlock without error checking */ + mwl_rawlock_unlock(); + + /* allow locking */ + mce_wakelock_ready = true; + +EXIT: + mce_log(LL_DEBUG, "wakelock usage %s", + mce_wakelock_ready ? "enabled" : "disabled"); + + return; +} + +/** Cleanup mce wakelock subsystem + */ +void +mce_wakelock_quit(void) +{ + /* Deny further locking */ + mce_wakelock_ready = false; + + /* Flush entry look up table */ + if( mce_wakelock_lut ) + g_hash_table_unref(mce_wakelock_lut), mce_wakelock_lut = 0; + + /* If there were active internal wakelocks, + * remove the real kernel wakelock too */ + mwl_rawlock_set(false); +} + +/** Async signal safe wakelock cleanup + */ +void +mce_wakelock_abort(void) +{ + /* Deny further locking */ + mce_wakelock_ready = false; + + /* Uncondition unlock, using syscalls only */ + mwl_rawlock_unlock(); +} diff --git a/mce-wakelock.h b/mce-wakelock.h new file mode 100644 index 00000000..534688b1 --- /dev/null +++ b/mce-wakelock.h @@ -0,0 +1,41 @@ +/** @file mce-wakelock.h + * Wakelock multiplexing code for the Mode Control Entity + *

+ * Copyright (C) 2015 Jolla Ltd. + *

+ * @author Simo Piiroinen + * + * 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 . + */ + +#ifndef MCE_WAKELOCK_H_ +# define MCE_WAKELOCK_H_ + +# ifdef __cplusplus +extern "C" { +# elif 0 +} /* fool JED indentation ... */ +# endif + +void mce_wakelock_obtain (const char *name, int duration_ms); +void mce_wakelock_release (const char *name); + +void mce_wakelock_init (void); +void mce_wakelock_quit (void); +void mce_wakelock_abort (void); + +# ifdef __cplusplus +}; +# endif + +#endif /* MCE_WAKELOCK_H_ */ diff --git a/mce.c b/mce.c index 8be7237b..7f413b1b 100644 --- a/mce.c +++ b/mce.c @@ -30,6 +30,7 @@ #include "mce-modules.h" #include "mce-command-line.h" #include "mce-sensorfw.h" +#include "mce-wakelock.h" #include "tklock.h" #include "powerkey.h" #include "event-input.h" @@ -140,6 +141,7 @@ static void mce_exit_via_signal(int signr) #ifdef ENABLE_WAKELOCKS /* Cancel auto suspend */ mce_cleanup_wakelocks(); + mce_wakelock_abort(); #endif /* Try to exit via default handler */ signal(signr, SIG_DFL); @@ -916,6 +918,9 @@ int main(int argc, char **argv) /* Since mce enables automatic suspend, we must try to * disable it when mce process exits */ atexit(mce_cleanup_wakelocks); + + /* Allow acquiring of multiplexed wakelock */ + mce_wakelock_init(); #endif /* Identify mce version & flavor on start up */ @@ -1075,6 +1080,9 @@ int main(int argc, char **argv) /* Close signal pipe & remove io watch for it */ mce_quit_signal_pipe(); + /* Release multiplexed wakelock */ + mce_wakelock_quit(); + /* Log a farewell message and close the log */ mce_log(LL_INFO, "Exiting...");