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 */