/**
* @file mce-hbtimer.c
*
* Mode Control Entity - Suspend proof timer functionality
*
*
*
* Copyright (C) 2014-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-hbtimer.h"
#include "mce.h"
#include "mce-log.h"
#include "mce-lib.h"
#ifdef ENABLE_WAKELOCKS
# include "libwakelock.h"
#endif
#include
#include
#include
#include
#include
#include
#include
/* ========================================================================= *
* Types and functions
* ========================================================================= */
/* ------------------------------------------------------------------------- *
* TIMER_METHODS
* ------------------------------------------------------------------------- */
/** State data for mce heartbeat timers */
struct mce_hbtimer_t
{
/** Timer name, used for debug logging purposes */
char *hbt_name;
/** Trigger time, milliseconds in CLOCK_BOOTTIME base */
int64_t hbt_trigger;
/** Timer callback function */
GSourceFunc hbt_notify;
/** Timer delay in milliseconds */
int hbt_period;
/** Flag for: control within hbt_notify() */
bool hbt_in_notify;
/** User data to pass to hbt_notify() */
void *hbt_user_data;
};
mce_hbtimer_t * mce_hbtimer_create (const char *name, int period, GSourceFunc notify, void *user_data);
void mce_hbtimer_delete (mce_hbtimer_t *self);
bool mce_hbtimer_is_active (const mce_hbtimer_t *self);
const char *mce_hbtimer_get_name (const mce_hbtimer_t *self);
void mce_hbtimer_set_period (mce_hbtimer_t *self, int period);
void mce_hbtimer_start (mce_hbtimer_t *self);
void mce_hbtimer_stop (mce_hbtimer_t *self);
static void mce_hbtimer_notify (mce_hbtimer_t *self);
static void mce_hbtimer_set_trigger (mce_hbtimer_t *self, int64_t trigger);
/* ------------------------------------------------------------------------- *
* GENERIC_UTILITIES
* ------------------------------------------------------------------------- */
/** Monotonic tick value used to signify "not-set" */
#define NO_TICK INT64_MAX
static guint mht_add_iowatch (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr);
/* ------------------------------------------------------------------------- *
* QUEUE_MANAGEMENT
* ------------------------------------------------------------------------- */
/** List of registered timers */
static GSList *mht_queue_timer_list = 0;
void mht_queue_dispatch_timers (void);
static void mht_queue_schedule_wakeups (void);
static void mht_queue_garbage_collect (void);
static void mht_queue_add_timer (mce_hbtimer_t *self);
static void mht_queue_remove_timer (mce_hbtimer_t *self);
static bool mht_queue_has_timer (const mce_hbtimer_t *self);
/* ------------------------------------------------------------------------- *
* GLIB_WAKEUPS
* ------------------------------------------------------------------------- */
/** Source ID for currently active glib timer wakeup */
static guint mce_hbtimer_glib_wait_id = 0;
static gboolean mht_glib_wakeup_cb (gpointer aptr);
static void mht_glib_set_wakeup (int64_t trigger, int64_t now);
/* ------------------------------------------------------------------------- *
* IPHB_WAKEUPS
* ------------------------------------------------------------------------- */
/** How much wakeups from suspend can be delayed [s]
*
* To increase changes of aligning with other iphb wakeups this
* should be >= heartbeat period (=12 seconds) */
#define MHT_IPHB_WAKEUP_MAX_DELAY_S 12
/** Cached timestamp of last requested iphb wakeup */
static int64_t mht_iphb_wakeup_tick = NO_TICK;
/** Source id for iphb wakeup input watch */
static guint mht_iphb_wakeup_watch_id = 0;
static gboolean mht_iphb_wakeup_cb (GIOChannel *chn, GIOCondition cnd, gpointer data);
static void mht_iphb_set_wakeup (int64_t trigger, int64_t now);
/* ------------------------------------------------------------------------- *
* IPHB_CONNECTION
* ------------------------------------------------------------------------- */
/** Number of times iphb connect is attempted after dsme startup */
#define MHT_CONNECTION_MAX_RETRIES 5
/** Delay between iphb connect attempts [ms] */
#define MHT_CONNECTION_RETRY_DELAY_MS 5000
/** Timer ID for: iphb connection attempts */
static guint mht_connection_timer_id = 0;
/** Number of connection attempts so far */
static gint mht_connection_retry_no = 0;
/** Handle for iphb connection */
static iphb_t mht_connection_handle = 0;
static bool mht_connection_try_to_open (void);
static gboolean mht_connection_timer_cb (gpointer aptr);
static bool mht_connection_is_pending (void);
static void mht_connection_start_timer (void);
static void mht_connection_stop_timer (void);
static void mht_connection_open (void);
static void mht_connection_close (void);
/* ------------------------------------------------------------------------- *
* DATAPIPE_HANDLERS
* ------------------------------------------------------------------------- */
/** Availability of dsme; from dsme_service_state_pipe */
static service_state_t dsme_service_state = SERVICE_STATE_UNDEF;
/** Device is shutting down; assume false */
static bool shutting_down = false;
static void mht_datapipe_dsme_service_state_cb (gconstpointer data);
static void mht_datapipe_resume_detected_event_cb (gconstpointer data);
static void mht_datapipe_shutting_down_cb (gconstpointer data);
static void mht_datapipe_init(void);
static void mht_datapipe_quit(void);
/* ------------------------------------------------------------------------- *
* MODULE_INIT
* ------------------------------------------------------------------------- */
/** Flag for: mce_hbtimer_init() has been called */
static bool mce_hbtimer_initialized = false;
void mce_hbtimer_init (void);
void mce_hbtimer_quit (void);
/* ========================================================================= *
* GENERIC_UTILITIES
* ========================================================================= */
/** Helper for creating I/O watch for file descriptor
*/
static guint
mht_add_iowatch(int fd, bool close_on_unref,
GIOCondition cnd, GIOFunc io_cb, gpointer aptr)
{
guint wid = 0;
GIOChannel *chn = 0;
if( !(chn = g_io_channel_unix_new(fd)) )
goto cleanup;
g_io_channel_set_close_on_unref(chn, close_on_unref);
cnd |= G_IO_ERR | G_IO_HUP | G_IO_NVAL;
if( !(wid = g_io_add_watch(chn, cnd, io_cb, aptr)) )
goto cleanup;
cleanup:
if( chn != 0 ) g_io_channel_unref(chn);
return wid;
}
/* ========================================================================= *
* TIMER_METHODS
* ========================================================================= */
/** Create heatbeat 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 heartbeat timer object
*/
mce_hbtimer_t *
mce_hbtimer_create(const char *name,
int period,
GSourceFunc notify,
void *user_data)
{
mce_hbtimer_t *self = calloc(1, sizeof *self);
self->hbt_name = name ? strdup(name) : 0;
self->hbt_notify = notify;
self->hbt_period = period;
self->hbt_user_data = user_data;
self->hbt_trigger = NO_TICK;
self->hbt_in_notify = false;
mht_queue_add_timer(self);
return self;
}
/** Delete heatbeat timer
*
* @param self heartbeat timer object, or NULL
*/
void
mce_hbtimer_delete(mce_hbtimer_t *self)
{
if( !self )
goto EXIT;
mht_queue_remove_timer(self);
mht_queue_schedule_wakeups();
free(self->hbt_name),
self->hbt_name = 0;
free(self);
EXIT:
return;
}
/** Predicate for: heatbeat timer has been started
*
* @param self heartbeat timer object, or NULL
*
* @return true if heartbeat timer has been started, false otherwise
*/
bool
mce_hbtimer_is_active(const mce_hbtimer_t *self)
{
bool active = false;
if( self )
active = (self->hbt_trigger < NO_TICK);
return active;
}
/** Get heatbeat timer name
*
* @param self heartbeat timer object, or NULL
*
* @return "invalid" if object is not valid,
* "unknown" if object has no name, or
* object name
*/
const char *
mce_hbtimer_get_name(const mce_hbtimer_t *self)
{
const char *name = "invalid";
if( self )
name = self->hbt_name;
return name ?: "unknown";
}
/** Set heatbeat timer period
*
* @param self heartbeat timer object, or NULL
* @param period timer delay [ms]
*/
void
mce_hbtimer_set_period(mce_hbtimer_t *self, int period)
{
if( self )
self->hbt_period = period;
}
/** Call heatbeat timer notification functiom
*
* @param self heartbeat timer object, or NULL
*/
static void
mce_hbtimer_notify(mce_hbtimer_t *self)
{
if( !self )
goto EXIT;
if( self->hbt_in_notify )
goto EXIT;
if( !self->hbt_notify )
goto EXIT;
self->hbt_in_notify = true;
self->hbt_trigger = NO_TICK;
bool again = self->hbt_notify(self->hbt_user_data);
/* Check that notify callback did not delete the timer */
if( !mht_queue_has_timer(self) )
goto EXIT;
self->hbt_in_notify = false;
if( again )
mce_hbtimer_start(self);
EXIT:
return;
}
/** Set heatbeat timer trigger time stamp
*
* @param self heartbeat timer object, or NULL
* @param trigger trigger time
*/
static void
mce_hbtimer_set_trigger(mce_hbtimer_t *self, int64_t trigger)
{
if( !self )
goto EXIT;
if( self->hbt_trigger == trigger )
goto EXIT;
self->hbt_trigger = trigger;
mht_queue_schedule_wakeups();
EXIT:
return;
}
/** Start heatbeat timer
*
* @param self heartbeat timer object, or NULL
*/
void
mce_hbtimer_start(mce_hbtimer_t *self)
{
if( !self )
goto EXIT;
mce_log(LL_DEBUG, "start %s %d", mce_hbtimer_get_name(self),
self->hbt_period);
int64_t now = mce_lib_get_boot_tick();
int64_t trigger = now + self->hbt_period;
mce_hbtimer_set_trigger(self, trigger);
EXIT:
return;
}
/** Stop heatbeat timer
*
* @param self heartbeat timer object, or NULL
*/
void
mce_hbtimer_stop(mce_hbtimer_t *self)
{
if( !self )
goto EXIT;
mce_log(LL_DEBUG, "stop %s", mce_hbtimer_get_name(self));
mce_hbtimer_set_trigger(self, NO_TICK);
mht_queue_schedule_wakeups();
EXIT:
return;
}
/* ========================================================================= *
* QUEUE_MANAGEMENT
* ========================================================================= */
/** Clean up unused timer list slots
*
* When timers are deleted, the associated node in mht_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
mht_queue_garbage_collect(void)
{
GSList **tail = &mht_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);
}
}
}
/** Predicate for: heartbeat timer is registered
*
* @param self heartbeat timer object, or NULL
*
* return true if timer is registered, false otherwise
*/
static bool
mht_queue_has_timer(const mce_hbtimer_t *self)
{
bool has_timer = false;
if( !self )
goto EXIT;
has_timer = g_slist_find(mht_queue_timer_list, self) != 0;
EXIT:
return has_timer;
}
/** Register heartbeat timer
*
* @param self heartbeat timer object, or NULL
*/
static void
mht_queue_add_timer(mce_hbtimer_t *self)
{
if( !self )
goto EXIT;
/* Try to find recyclable vacated timer slot */
GSList *item = g_slist_find(mht_queue_timer_list, 0);
if( item )
item->data = self;
else
mht_queue_timer_list = g_slist_prepend(mht_queue_timer_list, self);
EXIT:
return;
}
/** Unregister heartbeat timer
*
* @param self heartbeat timer object, or NULL
*/
static void
mht_queue_remove_timer(mce_hbtimer_t *self)
{
if( !self )
goto EXIT;
GSList *item = g_slist_find(mht_queue_timer_list, self);
/* Vacate the slot by clearing the data link */
if( item )
item->data = 0;
EXIT:
return;
}
/** Scan registered heartbeat timers and schdule nearest wakeup
*/
static void
mht_queue_schedule_wakeups(void)
{
if( !mce_hbtimer_initialized )
goto EXIT;
int64_t trigger = NO_TICK;
bool compact = false;
for( GSList *item = mht_queue_timer_list; item; item = item->next ) {
mce_hbtimer_t *timer = item->data;
if( !timer )
compact = true;
else if( trigger > timer->hbt_trigger )
trigger = timer->hbt_trigger;
}
if( compact )
mht_queue_garbage_collect();
int64_t now = mce_lib_get_boot_tick();
if( trigger < now )
trigger = now;
mht_glib_set_wakeup(trigger, now);
mht_iphb_set_wakeup(trigger, now);
EXIT:
return;
}
/** Scan registered heartbeat timers and notify triggered ones
*/
void
mht_queue_dispatch_timers(void)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
if( !mce_hbtimer_initialized )
goto EXIT;
/* We need to be sure that actions resulting from
* timer notifications do not cause recursive dispatching
* to take place. */
if( pthread_mutex_trylock(&mutex) != 0 )
goto EXIT;
/* Block suspend during dispatching */
#ifdef ENABLE_WAKELOCKS
wakelock_lock("mce_hbtimer_dispatch", -1);
#endif
int64_t now = mce_lib_get_boot_tick();
for( GSList *item = mht_queue_timer_list; item; item = item->next ) {
mce_hbtimer_t *timer = item->data;
if( !timer )
continue;
if( timer->hbt_trigger == NO_TICK )
continue;
mce_log(LL_DEBUG, "%s T%+"PRId64" ms",
mce_hbtimer_get_name(timer),
now - timer->hbt_trigger);
if( now < timer->hbt_trigger )
continue;
mce_hbtimer_notify(timer);
}
/* Check the next timer to trigger */
mht_queue_schedule_wakeups();
#ifdef ENABLE_WAKELOCKS
wakelock_unlock("mce_hbtimer_dispatch");
#endif
pthread_mutex_unlock(&mutex);
EXIT:
return;
}
/* ========================================================================= *
* GLIB_WAKEUPS
* ========================================================================= */
/** Glib timeout callback for dispatching heartbeat timers
*
* @param aptr (not used)
*
* @return FALSE to stop timeout from repeating
*/
static gboolean
mht_glib_wakeup_cb(gpointer aptr)
{
(void) aptr;
if( !mce_hbtimer_glib_wait_id )
goto EXIT;
mce_hbtimer_glib_wait_id = 0;
mce_log(LL_DEBUG, "glib wakeup; dispatch hbtimers");
mht_queue_dispatch_timers();
EXIT:
return FALSE;
}
/** Reprogram glib timeout for dispatching heartbeat timers
*
* @param trigger when to trigger
* @param now current time
*/
static void
mht_glib_set_wakeup(int64_t trigger, int64_t now)
{
static int64_t prev = NO_TICK;
int delay = -1;
/* The glib timers use CLOCK_MONOTONIC while we need to evaluate
* triggering in CLOCK_BOOTTIME.
*
* Assume glib reprogramming is cheap enough so that we do not need
* to try to avoid it.
*/
if( mce_hbtimer_glib_wait_id ) {
g_source_remove(mce_hbtimer_glib_wait_id),
mce_hbtimer_glib_wait_id = 0;
}
if( trigger != NO_TICK ) {
delay = (int)(trigger - now);
mce_hbtimer_glib_wait_id =
g_timeout_add(delay, mht_glib_wakeup_cb, 0);
}
if( prev != trigger ) {
prev = trigger;
mce_log(LL_DEBUG, "glib wakeup in %d ms", delay);
}
}
/* ========================================================================= *
* IPHB_WAKEUPS
* ========================================================================= */
/** iphb wakeup callback for dispatching heartbeat timers
*
* @param chn io channel
* @param cnd io condition
* @param data (unused)
*
* @return TRUE to keep io watch alive, or FALSE to disable it
*/
static gboolean
mht_iphb_wakeup_cb(GIOChannel *chn, GIOCondition cnd, gpointer data)
{
(void)data;
gboolean keep_going = FALSE;
if( !mht_iphb_wakeup_watch_id )
goto cleanup_nak;
int fd = g_io_channel_unix_get_fd(chn);
if( fd < 0 )
goto cleanup_nak;
if( cnd & ~G_IO_IN )
goto cleanup_nak;
if( !(cnd & G_IO_IN) )
goto cleanup_ack;
char buf[256];
int rc = recv(fd, buf, sizeof buf, MSG_DONTWAIT);
if( rc == 0 ) {
if( !shutting_down )
mce_log(LL_ERR, "unexpected eof");
goto cleanup_nak;
}
if( rc == -1 ) {
if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
goto cleanup_ack;
mce_log(LL_ERR, "read error: %m");
goto cleanup_nak;
}
if( mht_iphb_wakeup_tick == NO_TICK )
goto cleanup_ack;
/* clear programmed state */
mht_iphb_wakeup_tick = NO_TICK;
/* notify */
mce_log(LL_DEBUG, "iphb wakeup; dispatch hbtimers");
mht_queue_dispatch_timers();
cleanup_ack:
keep_going = TRUE;
cleanup_nak:
if( !keep_going ) {
mht_iphb_wakeup_watch_id = 0;
mht_connection_close();
}
return keep_going;
}
/** Reprogram iphb timeout for dispatching heartbeat timers
*
* @param trigger when to trigger
* @param now current time
*/
static void
mht_iphb_set_wakeup(int64_t trigger, int64_t now)
{
/* Assume: iphb timer should be stopped */
int lo = 0;
int hi = 0;
int64_t tick = NO_TICK;
if( mht_connection_handle && trigger != NO_TICK) {
/* Calculate the iphb wakeup range to be used. */
int64_t delay = (trigger - now + 999) / 1000;
lo = (int)delay;
hi = lo + MHT_IPHB_WAKEUP_MAX_DELAY_S;
/* Calculate the next full BOOTTIME second after low bound
* of iphb wakeup. This is used for avoiding constant iphb
* ipc when wakeups get re-evaluated.
*
* There might be up to one second jitter in actual wakeup
* vs cached time stamp, but it does not matter since:
* 1) the wide iphb range means the wake ups from suspend
* are going to be off by several seconds on average
* anyway
* 2) if the device does not get suspended, the higher
* resolution glib timeout gets triggered on time
*/
tick = now + delay * 1000;
tick += 999;
tick -= tick % 1000;
}
if( mht_iphb_wakeup_tick != tick ) {
mht_iphb_wakeup_tick = tick;
if( mht_connection_handle )
iphb_wait2(mht_connection_handle, lo, hi, 0, 1);
mce_log(LL_DEBUG, "iphb wakeup in [%d, %d] s", lo, hi);
}
}
/* ========================================================================= *
* IPHB_CONNECTION
* ========================================================================= */
/** Try to establish iphb socket connection
*
* Note: This is a helper function meant to be called only from
* mht_connection_open() and mht_connection_timer_cb()
* functions.
*
* @return true if connection is made, false otherwise
*/
static bool
mht_connection_try_to_open(void)
{
iphb_t handle = 0;
if( mht_connection_handle )
goto cleanup;
if( !(handle = iphb_open(0)) ) {
mce_log(LL_WARN, "iphb_open: %m");
goto cleanup;
}
int fd = iphb_get_fd(handle);
if( fd == -1 ) {
mce_log(LL_WARN, "iphb_get_fd: %m");
goto cleanup;
}
/* set up io watch */
mht_iphb_wakeup_watch_id =
mht_add_iowatch(fd, false, G_IO_IN, mht_iphb_wakeup_cb, 0);
if( !mht_iphb_wakeup_watch_id )
goto cleanup;
/* cache the handle */
mht_connection_handle = handle, handle = 0;
mce_log(LL_DEBUG, "iphb connected; dispatch hbtimers");
mht_queue_dispatch_timers();
cleanup:
if( handle ) iphb_close(handle);
return mht_connection_handle != 0;
}
/** Callback for connect reattempt timer
*
* @param aptr (unused)
*
* @return TRUE to keep timer repeating, or FALSE to stop it
*/
static gboolean
mht_connection_timer_cb(gpointer aptr)
{
(void)aptr;
if( !mht_connection_timer_id )
goto cleanup;
++mht_connection_retry_no;
if( !mht_connection_try_to_open() ) {
if( mht_connection_retry_no < MHT_CONNECTION_MAX_RETRIES )
goto cleanup;
mce_log(LL_WARN, "connect failed %d times; giving up",
mht_connection_retry_no);
}
else {
mce_log(LL_DEBUG, "connected after %d retries",
mht_connection_retry_no);
}
mht_connection_timer_id = 0;
cleanup:
return mht_connection_timer_id != 0;
}
/** Start connect reattempt timer
*/
static void
mht_connection_start_timer(void)
{
if( !mht_connection_timer_id && mce_hbtimer_initialized ) {
mht_connection_retry_no = 0;
mht_connection_timer_id =
g_timeout_add(MHT_CONNECTION_RETRY_DELAY_MS,
mht_connection_timer_cb, 0);
}
}
/** Cancel connect reattempt timer
*/
static void
mht_connection_stop_timer(void)
{
if( mht_connection_timer_id ) {
g_source_remove(mht_connection_timer_id),
mht_connection_timer_id = 0;
}
}
/** Predicate for: Connect reattempt timer is active
*/
static bool
mht_connection_is_pending(void)
{
return mht_connection_timer_id != 0;
}
/** Start connecting to iphb socket
*
* If the connection cant be made immediately it
* will be reattempted few times via timer callback.
*/
static void
mht_connection_open(void)
{
if( mht_connection_is_pending() ) {
// Retry timer already set up
}
else if( !mht_connection_try_to_open() ) {
// Could not connect now - start retry timer
mht_connection_start_timer();
}
}
/** Close connection to iphb socket
*/
static void
mht_connection_close(void)
{
/* stop pending connect attempts */
mht_connection_stop_timer();
/* remove io watch */
if( mht_iphb_wakeup_watch_id ) {
g_source_remove(mht_iphb_wakeup_watch_id),
mht_iphb_wakeup_watch_id = 0;
}
/* close handle */
if( mht_connection_handle ) {
iphb_close(mht_connection_handle),
mht_connection_handle = 0;
mce_log(LL_DEBUG, "iphb disconnected");
/* reset last programmed wakeup */
mht_iphb_set_wakeup(NO_TICK, NO_TICK);
}
}
/* ========================================================================= *
* DATAPIPE_HANDLERS
* ========================================================================= */
/** Resumed from suspend notification */
static void mht_datapipe_resume_detected_event_cb(gconstpointer data)
{
(void) data;
/* Check triggers & update glib wakeup after resume */
mce_log(LL_DEBUG, "resumed; dispatch hbtimers");
mht_queue_dispatch_timers();
}
/** Datapipe trigger for dsme availability
*/
static void mht_datapipe_dsme_service_state_cb(gconstpointer const data)
{
service_state_t prev = dsme_service_state;
dsme_service_state = GPOINTER_TO_INT(data);
if( dsme_service_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "DSME dbus service: %s -> %s",
service_state_repr(prev),
service_state_repr(dsme_service_state));
if( dsme_service_state == SERVICE_STATE_RUNNING )
mht_connection_open();
else
mht_connection_close();
EXIT:
return;
}
/** Change notifications for shutting_down
*/
static void mht_datapipe_shutting_down_cb(gconstpointer data)
{
bool prev = shutting_down;
shutting_down = GPOINTER_TO_INT(data);
if( shutting_down == prev )
goto EXIT;
mce_log(LL_DEBUG, "shutting_down = %d -> %d",
prev, shutting_down);
/* Loss of iphb connection is expected during shutdown */
EXIT:
return;
}
/** Array of datapipe handlers */
static datapipe_handler_t mht_datapipe_handlers[] =
{
// output triggers
{
.datapipe = &resume_detected_event_pipe,
.output_cb = mht_datapipe_resume_detected_event_cb,
},
{
.datapipe = &dsme_service_state_pipe,
.output_cb = mht_datapipe_dsme_service_state_cb,
},
{
.datapipe = &shutting_down_pipe,
.output_cb = mht_datapipe_shutting_down_cb,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t mht_datapipe_bindings =
{
.module = "mce_hbtimer",
.handlers = mht_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void mht_datapipe_init(void)
{
mce_datapipe_init_bindings(&mht_datapipe_bindings);
}
/** Remove triggers/filters from datapipes
*/
static void mht_datapipe_quit(void)
{
mce_datapipe_quit_bindings(&mht_datapipe_bindings);
}
/* ========================================================================= *
* MODULE_INIT
* ========================================================================= */
void
mce_hbtimer_init(void)
{
/* Connect to datapipes */
mht_datapipe_init();
/* Mark as initialized */
mce_hbtimer_initialized = true;
/* Schedule timers that have been created
* before initialization took place */
mht_queue_schedule_wakeups();
}
void
mce_hbtimer_quit(void)
{
/* Mark as de-initialized */
mce_hbtimer_initialized = false;
/* Disconnect from datapipes */
mht_datapipe_quit();
/* Remove wakeups */
mht_glib_set_wakeup(NO_TICK, NO_TICK);
mht_iphb_set_wakeup(NO_TICK, NO_TICK);
/* close iphb connection */
mht_connection_close();
}