diff --git a/.depend b/.depend
index cad572af..7e8ee38f 100644
--- a/.depend
+++ b/.depend
@@ -342,6 +342,24 @@ mce-wakelock.pic.o:\
mce-log.h\
mce-wakelock.h\
+mce-wltimer.o:\
+ mce-wltimer.c\
+ datapipe.h\
+ mce-lib.h\
+ mce-log.h\
+ mce-wakelock.h\
+ mce-wltimer.h\
+ mce.h\
+
+mce-wltimer.pic.o:\
+ mce-wltimer.c\
+ datapipe.h\
+ mce-lib.h\
+ mce-log.h\
+ mce-wakelock.h\
+ mce-wltimer.h\
+ mce.h\
+
mce.o:\
mce.c\
builtin-gconf.h\
@@ -360,6 +378,7 @@ mce.o:\
mce-modules.h\
mce-sensorfw.h\
mce-wakelock.h\
+ mce-wltimer.h\
mce.h\
modetransition.h\
powerkey.h\
@@ -383,6 +402,7 @@ mce.pic.o:\
mce-modules.h\
mce-sensorfw.h\
mce-wakelock.h\
+ mce-wltimer.h\
mce.h\
modetransition.h\
powerkey.h\
diff --git a/Makefile b/Makefile
index 29f887f7..90f5f166 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-wltimer.c
MCE_CORE += mce-wakelock.c
MCE_CORE += event-input.c
MCE_CORE += event-switches.c
@@ -537,6 +538,8 @@ NORMALIZE_USES_SPC =\
mce-command-line.h\
mce-hbtimer.c\
mce-hbtimer.h\
+ mce-wltimer.c\
+ mce-wltimer.h\
mce-hybris.c\
mce-hybris.h\
mce-modules.h\
diff --git a/mce-wltimer.c b/mce-wltimer.c
new file mode 100644
index 00000000..ea78fe01
--- /dev/null
+++ b/mce-wltimer.c
@@ -0,0 +1,516 @@
+/**
+ * @file mce-wltimer.c
+ *
+ * Mode Control Entity - Timers that block suspend until triggered
+ *
+ *
+ *
+ * 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-wltimer.h"
+
+#include "mce.h"
+#include "mce-log.h"
+#include "mce-lib.h"
+#include "mce-wakelock.h"
+
+#include
+#include
+
+/* ========================================================================= *
+ * Types and functions
+ * ========================================================================= */
+
+/* ------------------------------------------------------------------------- *
+ * TIMER_METHODS
+ * ------------------------------------------------------------------------- */
+
+/** State data for mce wakelock timers */
+struct mce_wltimer_t
+{
+ /** Timer name, used as wakelock name too */
+ char *wlt_name;
+
+ /** Timer delay in milliseconds */
+ int wlt_period;
+
+ /** Underlying glib timeout id */
+ guint wlt_timer_id;
+
+ /** Timer callback function */
+ GSourceFunc wlt_notify;
+
+ /** User data to pass to wlt_notify() */
+ void *wlt_user_data;
+};
+
+mce_wltimer_t * mce_wltimer_create (const char *name, int period, GSourceFunc notify, void *user_data);
+void mce_wltimer_delete (mce_wltimer_t *self);
+
+static void mce_wltimer_eval_wakelock (const mce_wltimer_t *self);
+
+bool mce_wltimer_is_active (const mce_wltimer_t *self);
+const char *mce_wltimer_get_name (const mce_wltimer_t *self);
+void mce_wltimer_set_period (mce_wltimer_t *self, int period);
+void mce_wltimer_start (mce_wltimer_t *self);
+void mce_wltimer_stop (mce_wltimer_t *self);
+
+static gboolean mce_wltimer_gate_cb (gpointer aptr);
+
+/* ------------------------------------------------------------------------- *
+ * QUEUE_MANAGEMENT
+ * ------------------------------------------------------------------------- */
+
+/** Idle callback id for delayed garbage collect */
+static guint mwt_queue_compact_id = 0;
+
+/** List of registered timers */
+static GSList *mwt_queue_timer_list = 0;
+
+static void mwt_queue_compact (void);
+static gboolean mwt_queue_compact_cb (gpointer aptr);
+static void mwt_queue_schedule_compact (void);
+static void mwt_queue_cancel_compact (void);
+
+static void mwt_queue_add_timer (mce_wltimer_t *self);
+static void mwt_queue_remove_timer (mce_wltimer_t *self);
+static bool mwt_queue_has_timer (const mce_wltimer_t *self);
+
+/* ------------------------------------------------------------------------- *
+ * MODULE_INIT
+ * ------------------------------------------------------------------------- */
+
+/** Flag for: timers can be started */
+static bool mce_wltimer_ready = true;
+
+void mce_wltimer_init (void);
+void mce_wltimer_quit (void);
+
+/* ========================================================================= *
+ * TIMER_METHODS
+ * ========================================================================= */
+
+/** Create wakelock timer
+ *
+ * @param name timer name
+ * @param period timer delay [ms]
+ * @param notify function to be called when triggered
+ * @param user_data pointer to be passed to notify function
+ *
+ * @return wakelock timer object
+ */
+mce_wltimer_t *
+mce_wltimer_create(const char *name,
+ int period,
+ GSourceFunc notify,
+ void *user_data)
+{
+ mce_wltimer_t *self = calloc(1, sizeof *self);
+
+ self->wlt_name = name ? strdup(name) : 0;
+ self->wlt_period = period;
+ self->wlt_timer_id = 0;
+ self->wlt_notify = notify;
+ self->wlt_user_data = user_data;
+
+ mwt_queue_add_timer(self);
+
+ return self;
+}
+
+/** Delete wakelock timer
+ *
+ * @param self wakelock timer object, or NULL
+ */
+void
+mce_wltimer_delete(mce_wltimer_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ mce_wltimer_stop(self);
+ mwt_queue_remove_timer(self);
+
+ free(self->wlt_name),
+ self->wlt_name = 0;
+
+ free(self);
+
+EXIT:
+ return;
+}
+
+/** Evaluate need for wakelock
+ *
+ * @param self wakelock timer object, or NULL
+ */
+static void
+mce_wltimer_eval_wakelock(const mce_wltimer_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ if( !self->wlt_name)
+ goto EXIT;
+
+ if( self->wlt_timer_id )
+ mce_wakelock_obtain(self->wlt_name, -1);
+ else
+ mce_wakelock_release(self->wlt_name);
+
+EXIT:
+ return;
+}
+
+/** Predicate for: wakelock timer has been started
+ *
+ * @param self wakelock timer object, or NULL
+ *
+ * @return true if wakelock timer has been started, false otherwise
+ */
+bool
+mce_wltimer_is_active(const mce_wltimer_t *self)
+{
+ bool active = false;
+ if( self )
+ active = (self->wlt_timer_id != 0);
+ return active;
+}
+
+/** Get wakelock timer name
+ *
+ * @param self wakelock timer object, or NULL
+ *
+ * @return "invalid" if object is not valid,
+ * "unknown" if object has no name, or
+ * object name
+ */
+
+const char *
+mce_wltimer_get_name(const mce_wltimer_t *self)
+{
+ const char *name = "invalid";
+ if( self )
+ name = self->wlt_name;
+ return name ?: "unknown";
+}
+
+/** Set wakelock timer period
+ *
+ * @param self wakelock timer object, or NULL
+ * @param period timer delay [ms]
+ */
+void
+mce_wltimer_set_period(mce_wltimer_t *self, int period)
+{
+ if( self )
+ self->wlt_period = period;
+}
+
+/** Call wakelock timer notification functiom
+ *
+ * @param aptr wakelock timer object (as void pointer)
+ */
+static gboolean
+mce_wltimer_gate_cb(gpointer aptr)
+{
+ gboolean repeat = FALSE;
+
+ mce_wltimer_t *self = aptr;
+
+ if( !self->wlt_timer_id )
+ goto EXIT;
+
+ if( self->wlt_notify ) {
+ bool res = self->wlt_notify(self->wlt_user_data);
+
+ if( !mwt_queue_has_timer(self) ) {
+ /* The notify callback managed to delete the timer
+ * object, invalidate the pointer */
+ self = 0;
+ }
+ else {
+ /* Repeat/stop according to the callback return value */
+ repeat = res;
+ }
+ }
+
+EXIT:
+
+ if( self ) {
+ if( !repeat && self->wlt_timer_id )
+ self->wlt_timer_id = 0;
+ mce_wltimer_eval_wakelock(self);
+ }
+
+ return repeat;
+}
+
+/** Start wakelock timer
+ *
+ * @param self wakelock timer object, or NULL
+ */
+void
+mce_wltimer_start(mce_wltimer_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ if( self->wlt_timer_id ) {
+ g_source_remove(self->wlt_timer_id),
+ self->wlt_timer_id = 0;
+ }
+
+ if( !mce_wltimer_ready )
+ goto EXIT;
+
+ if( self->wlt_period < 0 )
+ goto EXIT;
+
+ mce_log(LL_DEBUG, "start %s %d", mce_wltimer_get_name(self),
+ self->wlt_period);
+
+ if( self->wlt_period > 0 ) {
+ self->wlt_timer_id = g_timeout_add(self->wlt_period,
+ mce_wltimer_gate_cb,
+ self);
+ }
+ else {
+ self->wlt_timer_id = g_idle_add(mce_wltimer_gate_cb,
+ self);
+ }
+
+EXIT:
+ mce_wltimer_eval_wakelock(self);
+ return;
+}
+
+/** Stop wakelock timer
+ *
+ * @param self wakelock timer object, or NULL
+ */
+void
+mce_wltimer_stop(mce_wltimer_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ if( !self->wlt_timer_id )
+ goto EXIT;
+
+ mce_log(LL_DEBUG, "stop %s", mce_wltimer_get_name(self));
+
+ g_source_remove(self->wlt_timer_id),
+ self->wlt_timer_id = 0;
+
+EXIT:
+ mce_wltimer_eval_wakelock(self);
+
+ return;
+}
+
+/* ========================================================================= *
+ * QUEUE_MANAGEMENT
+ * ========================================================================= */
+
+/** Clean up unused timer list slots
+ *
+ * When timers are deleted, the associated node in mwt_queue_timer_list
+ * is left in place with data pointer set to zero.
+ *
+ * This function can be used to remove such stale entries in suitable
+ * point outside the dispatch iteration loop.
+ */
+static void
+mwt_queue_compact(void)
+{
+ mwt_queue_cancel_compact();
+
+ GSList **tail = &mwt_queue_timer_list;
+
+ GSList *item;
+
+ while( (item = *tail) ) {
+ if( item->data ) {
+ tail = &item->next;
+ }
+ else {
+ *tail = item->next;
+ item->next = 0;
+ g_slist_free(item);
+ }
+ }
+}
+
+/** Idle callback function for delayed garbage collect
+ *
+ * @param aptr user data pointer (unused)
+ *
+ * @return FALSE, to stop repeats
+ */
+static gboolean
+mwt_queue_compact_cb(gpointer aptr)
+{
+ (void)aptr;
+
+ if( !mwt_queue_compact_id )
+ goto EXIT;
+
+ mwt_queue_compact_id = 0;
+
+ mwt_queue_compact();
+
+EXIT:
+ return FALSE;
+}
+
+/** Schedule delayed garbage collection
+ */
+static void mwt_queue_schedule_compact(void)
+{
+ if( mwt_queue_compact_id )
+ goto EXIT;
+
+ mwt_queue_compact_id = g_idle_add(mwt_queue_compact_cb, 0);
+
+EXIT:
+ return;
+}
+
+/** Cancel delayed garbage collection
+ */
+static void mwt_queue_cancel_compact(void)
+{
+ if( !mwt_queue_compact_id )
+ goto EXIT;
+
+ g_source_remove(mwt_queue_compact_id),
+ mwt_queue_compact_id = 0;
+
+EXIT:
+ return;
+}
+
+/** Predicate for: wakelock timer is registered
+ *
+ * @param self wakelock timer object, or NULL
+ *
+ * return true if timer is registered, false otherwise
+ */
+static bool
+mwt_queue_has_timer(const mce_wltimer_t *self)
+{
+ bool has_timer = false;
+
+ if( !self )
+ goto EXIT;
+
+ has_timer = g_slist_find(mwt_queue_timer_list, self) != 0;
+
+EXIT:
+ return has_timer;
+}
+
+/** Register wakelock timer
+ *
+ * @param self wakelock timer object, or NULL
+ */
+static void
+mwt_queue_add_timer(mce_wltimer_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ /* Try to find recyclable vacated timer slot */
+ GSList *item = g_slist_find(mwt_queue_timer_list, 0);
+
+ if( item )
+ item->data = self;
+ else
+ mwt_queue_timer_list = g_slist_prepend(mwt_queue_timer_list, self);
+
+EXIT:
+ return;
+}
+
+/** Unregister wakelock timer
+ *
+ * @param self wakelock timer object, or NULL
+ */
+static void
+mwt_queue_remove_timer(mce_wltimer_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ GSList *item = g_slist_find(mwt_queue_timer_list, self);
+
+ /* Vacate the slot by clearing the data link */
+ if( item )
+ item->data = 0;
+
+ /* Schedule removal of the link it self */
+ mwt_queue_schedule_compact();
+
+EXIT:
+ return;
+}
+
+/* ========================================================================= *
+ * MODULE_INIT
+ * ========================================================================= */
+
+void
+mce_wltimer_init(void)
+{
+ /* nop */
+}
+
+void
+mce_wltimer_quit(void)
+{
+ mce_log(LL_DEBUG, "deny suspend block timers");
+
+ /* Deny starting of timers */
+ mce_wltimer_ready = false;
+
+ /* Disable left-behind timer objects */
+ for( GSList *item = mwt_queue_timer_list; item; item = item->next ) {
+ mce_wltimer_t *timer = item->data;
+ if( !timer )
+ continue;
+
+ /* Note: What we have here is effectively a resource leak
+ * somewhere else. But all that can be done is to make
+ * sure we do not leave behind active timeouts that
+ * might then trigger callbacks in unexpected manner.
+ */
+
+ mce_log(LL_WARN, "timer '%s' exists at deinit",
+ mce_wltimer_get_name(timer));
+
+ mce_wltimer_stop(timer);
+ item->data = 0;
+ }
+
+ /* Flush timer list */
+ mwt_queue_compact();
+
+}
diff --git a/mce-wltimer.h b/mce-wltimer.h
new file mode 100644
index 00000000..53f50271
--- /dev/null
+++ b/mce-wltimer.h
@@ -0,0 +1,58 @@
+/**
+ * @file mce-wltimer.h
+ *
+ * Mode Control Entity - Timers that block suspend until triggered
+ *
+ *
+ *
+ * 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_WLTIMER_H_
+# define MCE_WLTIMER_H_
+
+# include
+# include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+typedef struct mce_wltimer_t mce_wltimer_t;
+
+mce_wltimer_t * mce_wltimer_create (const char *name, int period, GSourceFunc notify, void *user_data);
+void mce_wltimer_delete (mce_wltimer_t *self);
+
+bool mce_wltimer_is_active (const mce_wltimer_t *self);
+const char *mce_wltimer_get_name (const mce_wltimer_t *self);
+void mce_wltimer_set_period (mce_wltimer_t *self, int period);
+
+void mce_wltimer_start (mce_wltimer_t *self);
+void mce_wltimer_stop (mce_wltimer_t *self);
+
+void mce_wltimer_dispatch (void);
+
+void mce_wltimer_init (void);
+void mce_wltimer_quit (void);
+
+# ifdef __cplusplus
+};
+# endif
+
+#endif /* MCE_WLTIMER_H_ */
diff --git a/mce.c b/mce.c
index 7f413b1b..21e385da 100644
--- a/mce.c
+++ b/mce.c
@@ -24,6 +24,7 @@
#include "mce-conf.h"
#include "mce-fbdev.h"
#include "mce-hbtimer.h"
+#include "mce-wltimer.h"
#include "mce-gconf.h"
#include "mce-dbus.h"
#include "mce-dsme.h"
@@ -976,6 +977,9 @@ int main(int argc, char **argv)
/* Allow registering of suspend proof timers */
mce_hbtimer_init();
+ /* Allow registering of suspend blocking timers */
+ mce_wltimer_init();
+
/* Initialise mode management
* pre-requisite: mce_gconf_init()
* pre-requisite: mce_dbus_init()
@@ -1060,6 +1064,7 @@ int main(int argc, char **argv)
mce_powerkey_exit();
mce_dsme_exit();
mce_mode_exit();
+ mce_wltimer_quit();
mce_hbtimer_quit();
/* Free all datapipes */