mce-dsme.c 35 KB
Newer Older
1 2 3 4 5 6
/**
 * @file mce-dsme.c
 * Interface code and logic between
 * DSME (the Device State Management Entity)
 * and MCE (the Mode Control Entity)
 * <p>
Santtu Lakkala's avatar
Santtu Lakkala committed
7
 * Copyright © 2004-2011 Nokia Corporation and/or its subsidiary(-ies).
8
 * Copyright © 2012-2019 Jolla Ltd.
9 10 11
 * <p>
 * @author David Weinehall <david.weinehall@nokia.com>
 * @author Ismo Laitinen <ismo.laitinen@nokia.com>
12 13
 * @author Tapio Rantala <ext-tapio.rantala@nokia.com>
 * @author Santtu Lakkala <ext-santtu.1.lakkala@nokia.com>
14
 * @author Simo Piiroinen <simo.piiroinen@jollamobile.com>
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * 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-dsme.h"

31 32 33
#include "mce.h"
#include "mce-log.h"
#include "mce-dbus.h"
34
#include "mce-worker.h"
35 36 37 38 39 40 41

#include <stdlib.h>
#include <unistd.h>

#include <dsme/state.h>
#include <dsme/protocol.h>
#include <dsme/processwd.h>
42
#include <dsme/thermalmanager_dbus_if.h>
43

44 45 46 47 48 49 50 51 52 53 54 55 56
/* ========================================================================= *
 * MODULE DATA
 * ========================================================================= */

/** Pointer to the dsmesock connection */
static dsmesock_connection_t *mce_dsme_socket_connection = NULL;

/** I/O watch for mce_dsme_socket_connection */
static guint mce_dsme_socket_recv_id = 0;

/** ID for delayed state transition reporting timer */
static guint mce_dsme_transition_id = 0;

57 58
/** Availability of dsme; from dsme_service_state_pipe */
static service_state_t dsme_service_state = SERVICE_STATE_UNDEF;
59

60 61 62
/** Availability of thermalmanager; from thermalmanager_service_state_pipe */
static service_state_t thermalmanager_service_state = SERVICE_STATE_UNDEF;

63
/** System state from dsme; fed to system_state_pipe */
64
static system_state_t system_state = MCE_SYSTEM_STATE_UNDEF;
65

66 67 68
/** Thermal state from thermalmanager (dsme); fed to thermal_state_pipe */
static thermal_state_t thermal_state = THERMAL_STATE_UNDEF;

69 70
/** Shutdown warning from dsme; fed to shutting_down_pipe */
static bool mce_dsme_shutting_down_flag = false;
71

72 73 74
/** Cached init_done state; assume unknown */
static tristate_t init_done = TRISTATE_UNKNOWN;

75 76 77
/* ========================================================================= *
 * MODULE FUNCTIONS
 * ========================================================================= */
78

79 80 81
/* ------------------------------------------------------------------------- *
 * UTILITY_FUNCTIONS
 * ------------------------------------------------------------------------- */
82

83 84
static thermal_state_t mce_dsme_parse_thermal_state    (const char *name);
static system_state_t  mce_dsme_normalise_system_state (dsme_state_t dsmestate);
85

86 87 88 89 90 91 92 93
/* ------------------------------------------------------------------------- *
 * WORKER_WATCHDOG
 * ------------------------------------------------------------------------- */

static void           mce_dsme_worker_done_cb  (void *aptr, void *reply);
static void          *mce_dsme_worker_pong_cb  (void *aptr);
static void           mce_dsme_worker_ping     (void);;

94 95 96 97 98 99 100 101 102 103 104
/* ------------------------------------------------------------------------- *
 * PROCESS_WATCHDOG
 * ------------------------------------------------------------------------- */

static void           mce_dsme_processwd_pong (void);
static void           mce_dsme_processwd_init (void);
static void           mce_dsme_processwd_quit (void);

/* ------------------------------------------------------------------------- *
 * SYSTEM_STATE
 * ------------------------------------------------------------------------- */
105

106 107 108 109
static void           mce_dsme_query_system_state      (void);
void                  mce_dsme_request_powerup         (void);
void                  mce_dsme_request_reboot          (void);
void                  mce_dsme_request_normal_shutdown (void);
110

111 112 113
/* ------------------------------------------------------------------------- *
 * TRANSITION_SUBMODE
 * ------------------------------------------------------------------------- */
114

115 116 117
static gboolean       mce_dsme_transition_cb       (gpointer data);
static void           mce_dsme_transition_cancel   (void);
static void           mce_dsme_transition_schedule (void);
118

119 120 121
/* ------------------------------------------------------------------------- *
 * SHUTTING_DOWN
 * ------------------------------------------------------------------------- */
122

123 124
static bool           mce_dsme_is_shutting_down  (void);
static void           mce_dsme_set_shutting_down (bool shutting_down);
125

126 127 128
/* ------------------------------------------------------------------------- *
 * SOCKET_CONNECTION
 * ------------------------------------------------------------------------- */
129

130
static bool           mce_dsme_socket_send         (void *msg);
131 132 133 134 135 136 137 138
static gboolean       mce_dsme_socket_recv_cb      (GIOChannel *source, GIOCondition condition, gpointer data);
static bool           mce_dsme_socket_is_connected (void);
static bool           mce_dsme_socket_connect      (void);
static void           mce_dsme_socket_disconnect   (void);

/* ------------------------------------------------------------------------- *
 * DBUS_HANDLERS
 * ------------------------------------------------------------------------- */
139

140 141 142 143 144
static gboolean       mce_dsme_dbus_thermal_state_change_cb   (DBusMessage *const msg);
static void           mce_dsme_dbus_thermal_state_reply_cb    (DBusPendingCall *pc, void *aptr);
static void           mce_dsme_dbus_cancel_thermal_state_query(void);
static void           mce_dsme_dbus_start_thermal_state_query (void);

145 146 147 148 149
static gboolean       mce_dsme_dbus_init_done_cb              (DBusMessage *const msg);
static gboolean       mce_dsme_dbus_shutdown_cb               (DBusMessage *const msg);
static gboolean       mce_dsme_dbus_thermal_shutdown_cb       (DBusMessage *const msg);
static gboolean       mce_dsme_dbus_battery_empty_shutdown_cb (DBusMessage *const msg);

150 151
static void           mce_dsme_dbus_init(void);
static void           mce_dsme_dbus_quit(void);
152

153 154 155
/* ------------------------------------------------------------------------- *
 * DATAPIPE_TRACKING
 * ------------------------------------------------------------------------- */
156

157 158
static void           mce_dsme_datapipe_set_thermal_state     (thermal_state_t state);

159
static void           mce_dsme_datapipe_dsme_service_state_cb (gconstpointer data);
160
static void           mce_dsme_datapipe_thermalmanager_service_state_cb(gconstpointer const data);
161 162
static void           mce_dsme_datapipe_init_done_cb          (gconstpointer data);
static void           mce_dsme_datapipe_system_state_cb       (gconstpointer data);
163

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
static void           mce_dsme_datapipe_init(void);
static void           mce_dsme_datapipe_quit(void);

/* ------------------------------------------------------------------------- *
 * MODULE_INIT_EXIT
 * ------------------------------------------------------------------------- */

gboolean              mce_dsme_init(void);
void                  mce_dsme_exit(void);

/* ========================================================================= *
 * UTILITY_FUNCTIONS
 * ========================================================================= */

/** Convert system states used by dsme to the ones used in mce datapipes
 *
 * @param dsmestate System state constant used by dsme
 *
 * @return System state constant used within mce
 */
static system_state_t mce_dsme_normalise_system_state(dsme_state_t dsmestate)
{
186
    system_state_t state = MCE_SYSTEM_STATE_UNDEF;
187

188 189
    switch (dsmestate) {
    case DSME_STATE_SHUTDOWN:
190
        state = MCE_SYSTEM_STATE_SHUTDOWN;
191
        break;
192

193
    case DSME_STATE_USER:
194
        state = MCE_SYSTEM_STATE_USER;
195 196 197
        break;

    case DSME_STATE_ACTDEAD:
198
        state = MCE_SYSTEM_STATE_ACTDEAD;
199 200 201
        break;

    case DSME_STATE_REBOOT:
202
        state = MCE_SYSTEM_STATE_REBOOT;
203 204 205
        break;

    case DSME_STATE_BOOT:
206
        state = MCE_SYSTEM_STATE_BOOT;
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
        break;

    case DSME_STATE_NOT_SET:
        break;

    case DSME_STATE_TEST:
        mce_log(LL_WARN,
                "Received DSME_STATE_TEST; treating as undefined");
        break;

    case DSME_STATE_MALF:
        mce_log(LL_WARN,
                "Received DSME_STATE_MALF; treating as undefined");
        break;

    case DSME_STATE_LOCAL:
        mce_log(LL_WARN,
                "Received DSME_STATE_LOCAL; treating as undefined");
        break;

    default:
        mce_log(LL_ERR,
                "Received an unknown state from DSME; "
                "treating as undefined");
        break;
    }

    return state;
235 236
}

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
/** Convert thermal manager thermal state name to mce thermal_state_t
 *
 * @param name  Thermal state name as received over D-Bus
 *
 * @return name mapped to thermal_state_t enumeration value
 */
static thermal_state_t mce_dsme_parse_thermal_state(const char *name)
{
    thermal_state_t state = THERMAL_STATE_UNDEF;

    if( name == 0 )
        ;
    else if( !strcmp(name, thermalmanager_thermal_status_low) )
        state = THERMAL_STATE_OK;
    else if( !strcmp(name, thermalmanager_thermal_status_normal) )
        state = THERMAL_STATE_OK;
    else if( !strcmp(name, thermalmanager_thermal_status_warning) )
        state = THERMAL_STATE_OK;
    else if( !strcmp(name, thermalmanager_thermal_status_alert) )
        state = THERMAL_STATE_OVERHEATED;
    else if( !strcmp(name, thermalmanager_thermal_status_fatal) )
        state = THERMAL_STATE_OVERHEATED;

    return state;
}

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
/* ========================================================================= *
 * WORKER_WATCHDOG
 * ========================================================================= */

/** Validation context for the jobs passed from this module */
#define MCE_DSME_WORKERWD_JOB_CONTEXT "mce-dsme"

/** Descriptive name for the dummy sanity check worker thread jobs */
#define MCE_DSME_WORKERWD_JOB_NAME    "ping"

/** Number of worker jobs scheduled */
static guint mce_dsme_worker_ping_cnt = 0;

/** Number of worker jobs executed */
static guint mce_dsme_worker_pong_cnt = 0;

/** Number of worker jobs notified */
static guint mce_dsme_worker_done_cnt = 0;

/** Flag for: worker thread issues noticed */
static bool  mce_dsme_worker_misbehaving = false;

/** Handle dummy job finished notification
 *
 * @param aptr   Ping count passed to the worker thread (as void pointer)
 * @param reply  Ping count returned from the worker thread (as void pointer)
 */
static void mce_dsme_worker_done_cb(void *aptr, void *reply)
{
    (void)reply;

    mce_dsme_worker_done_cnt = GPOINTER_TO_INT(aptr);

    /* Check if the last job scheduled matches what got executed
     * and notified as finished */

    if( mce_dsme_worker_ping_cnt != mce_dsme_worker_pong_cnt ||
        mce_dsme_worker_ping_cnt != mce_dsme_worker_done_cnt ) {
        mce_log(LL_CRIT, "worker thread is misbehaving");
        mce_dsme_worker_misbehaving = true;
    }
}

/** Dummy job to be executed by the worker thread
 *
 * @param aptr  Ping count (as void pointer)
 *
 * @return Ping count (as void pointer)
 */
static void *mce_dsme_worker_pong_cb(void *aptr)
{
    /* Note: This is executed in the worker thread context */

    mce_dsme_worker_pong_cnt = GPOINTER_TO_INT(aptr);

    /* Check if the job we got to execute is the latest one
     * scheduled from the main thread */

    if( mce_dsme_worker_ping_cnt != mce_dsme_worker_pong_cnt ) {
        mce_log(LL_CRIT, "worker thread is misbehaving");
        mce_dsme_worker_misbehaving = true;
    }

    return aptr;
}

/** Run a dummy job through worker thread to make sure it is still functioning
 */
static void mce_dsme_worker_ping(void)
{
    /* Check if previous job got executed as expected */
    if( mce_dsme_worker_ping_cnt != mce_dsme_worker_pong_cnt ||
        mce_dsme_worker_ping_cnt != mce_dsme_worker_done_cnt ) {
        mce_log(LL_CRIT, "worker thread is possibly stuck");
        mce_dsme_worker_misbehaving = true;
    }
    else if( mce_dsme_worker_misbehaving ) {
        mce_dsme_worker_misbehaving = false;
        mce_log(LL_CRIT, "worker thread is working again");
    }

    mce_dsme_worker_ping_cnt += 1;

    mce_worker_add_job(MCE_DSME_WORKERWD_JOB_CONTEXT,
                       MCE_DSME_WORKERWD_JOB_NAME,
                       mce_dsme_worker_pong_cb,
                       mce_dsme_worker_done_cb,
                       GINT_TO_POINTER(mce_dsme_worker_ping_cnt));
}

353 354 355 356
/* ========================================================================= *
 * PROCESS_WATCHDOG
 * ========================================================================= */

357 358 359
/**
 * Send pong message to the DSME process watchdog
 */
360
static void mce_dsme_processwd_pong(void)
361
{
362 363 364 365
    /* Set up the message */
    DSM_MSGTYPE_PROCESSWD_PONG msg =
        DSME_MSG_INIT(DSM_MSGTYPE_PROCESSWD_PONG);
    msg.pid = getpid();
366

367
    /* Send the message */
368
    mce_dsme_socket_send(&msg);
369

370 371 372
    /* Run worker thread sanity check */
    mce_dsme_worker_ping();

373
    /* Execute hearbeat actions even if ping-pong ipc failed */
374
    datapipe_exec_full(&heartbeat_event_pipe, GINT_TO_POINTER(0));
375 376 377 378 379
}

/**
 * Register to DSME process watchdog
 */
380
static void mce_dsme_processwd_init(void)
381
{
382 383 384 385
    /* Set up the message */
    DSM_MSGTYPE_PROCESSWD_CREATE msg =
        DSME_MSG_INIT(DSM_MSGTYPE_PROCESSWD_CREATE);
    msg.pid = getpid();
386

387
    /* Send the message */
388
    mce_dsme_socket_send(&msg);
389 390 391 392 393
}

/**
 * Unregister from DSME process watchdog
 */
394
static void mce_dsme_processwd_quit(void)
395
{
396
    mce_log(LL_DEBUG, "Disabling DSME process watchdog");
397

398 399 400 401
    /* Set up the message */
    DSM_MSGTYPE_PROCESSWD_DELETE msg =
        DSME_MSG_INIT(DSM_MSGTYPE_PROCESSWD_DELETE);
    msg.pid = getpid();
402

403
    /* Send the message */
404
    mce_dsme_socket_send(&msg);
405 406
}

407 408 409 410 411
/* ========================================================================= *
 * SYSTEM_STATE
 * ========================================================================= */

/** Send system state inquiry
412
 */
413
static void mce_dsme_query_system_state(void)
414
{
415 416
    /* Set up the message */
    DSM_MSGTYPE_STATE_QUERY msg = DSME_MSG_INIT(DSM_MSGTYPE_STATE_QUERY);
417

418
    /* Send the message */
419
    mce_dsme_socket_send(&msg);
420 421
}

422
/** Request powerup
423
 */
424
void mce_dsme_request_powerup(void)
425
{
426 427
    /* Set up the message */
    DSM_MSGTYPE_POWERUP_REQ msg = DSME_MSG_INIT(DSM_MSGTYPE_POWERUP_REQ);
428

429
    /* Send the message */
430
    mce_dsme_socket_send(&msg);
431 432
}

433
/** Request reboot
434
 */
435
void mce_dsme_request_reboot(void)
436
{
437
    if( datapipe_get_gint(osupdate_running_pipe) ) {
438 439 440
        mce_log(LL_WARN, "reboot blocked; os update in progress");
        goto EXIT;
    }
441

442 443
    /* Set up the message */
    DSM_MSGTYPE_REBOOT_REQ msg = DSME_MSG_INIT(DSM_MSGTYPE_REBOOT_REQ);
444

445
    /* Send the message */
446
    mce_dsme_socket_send(&msg);
447
EXIT:
448
    return;
449 450
}

451 452 453 454
/** Request normal shutdown
 */
void mce_dsme_request_normal_shutdown(void)
{
455
    if( datapipe_get_gint(osupdate_running_pipe) ) {
456 457 458 459 460 461 462 463
        mce_log(LL_WARN, "shutdown blocked; os update in progress");
        goto EXIT;
    }

    /* Set up the message */
    DSM_MSGTYPE_SHUTDOWN_REQ msg = DSME_MSG_INIT(DSM_MSGTYPE_SHUTDOWN_REQ);

    /* Send the message */
464
    mce_dsme_socket_send(&msg);
465 466 467 468 469 470 471 472 473 474 475
EXIT:
    return;
}

/* ========================================================================= *
 * TRANSITION_SUBMODE
 * ========================================================================= */

/** Timer callback for ending transition submode
 *
 * @param data (not used)
476 477 478
 *
 * @return Always returns FALSE, to disable the timeout
 */
479
static gboolean mce_dsme_transition_cb(gpointer data)
480
{
481
    (void)data;
482

483
    mce_dsme_transition_id = 0;
484

485
    mce_rem_submode_int32(MCE_SUBMODE_TRANSITION);
486

487
    return FALSE;
488 489
}

490
/** Cancel delayed end of transition submode
491
 */
492
static void mce_dsme_transition_cancel(void)
493
{
494 495 496 497
    if( mce_dsme_transition_id ) {
        g_source_remove(mce_dsme_transition_id),
            mce_dsme_transition_id = 0;
    }
498 499
}

500
/** Schedule delayed end of transition submode
501
 */
502
static void mce_dsme_transition_schedule(void)
503
{
504 505 506 507
    /* Remove existing timeout */
    mce_dsme_transition_cancel();

    /* Check if we have transition to end */
508
    if( !(mce_get_submode_int32() & MCE_SUBMODE_TRANSITION) )
509
        goto EXIT;
510

511
#if TRANSITION_DELAY > 0
512 513 514
    /* Setup new timeout */
    mce_dsme_transition_id =
        g_timeout_add(TRANSITION_DELAY, mce_dsme_transition_cb, NULL);
515
#elif TRANSITION_DELAY == 0
516 517 518
    /* Set up idle callback */
    mce_dsme_transition_id =
        g_idle_add(mce_dsme_transition_cb, NULL);
519
#else
520 521
    /* Trigger immediately */
    mce_dsme_transition_cb(0);
522
#endif
523

524
EXIT:
525
    return;
526 527
}

528 529 530 531
/* ========================================================================= *
 * SHUTTING_DOWN
 * ========================================================================= */

532 533 534 535
/** Predicate for: device is shutting down
 *
 * @return true if shutdown is in progress, false otherwise
 */
536
static bool mce_dsme_is_shutting_down(void)
537
{
538
    return mce_dsme_shutting_down_flag;
539
}
540

541 542 543 544 545 546
/** Update device is shutting down state
 *
 * @param shutting_down true if shutdown has started, false otherwise
 */
static void mce_dsme_set_shutting_down(bool shutting_down)
{
547 548
    if( mce_dsme_shutting_down_flag == shutting_down )
        goto EXIT;
549

550
    mce_dsme_shutting_down_flag = shutting_down;
551

552 553
    mce_log(LL_DEVEL, "Shutdown %s",
            mce_dsme_shutting_down_flag ? "started" : "canceled");
554

555 556 557 558
    /* Re-evaluate dsmesock connection */
    if( !mce_dsme_shutting_down_flag )
        mce_dsme_socket_connect();

559
    datapipe_exec_full(&shutting_down_pipe,
560
                       GINT_TO_POINTER(mce_dsme_shutting_down_flag));
561

562
EXIT:
563
    return;
564 565
}

566 567 568 569
/* ========================================================================= *
 * SOCKET_CONNECTION
 * ========================================================================= */

570
/**
571
 * Generic send function for dsmesock messages
572
 *
573
 * @param msg A pointer to the message to send
574
 */
575
static bool mce_dsme_socket_send(void *msg)
576
{
577 578 579 580
    bool res = false;

    if( !mce_dsme_socket_connection ) {
        mce_log(LL_WARN, "failed to send %s to dsme; %s",
581
                dsmemsg_name(msg), "not connected");
582 583
        goto EXIT;
    }
584

585 586
    if( dsmesock_send(mce_dsme_socket_connection, msg) == -1) {
        mce_log(LL_ERR, "failed to send %s to dsme; %m",
587
                dsmemsg_name(msg));
588
    }
589

590
    mce_log(LL_DEBUG, "%s sent to DSME", dsmemsg_name(msg));
591

592
    res = true;
593

594 595 596
EXIT:
    return res;
}
597

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
/** Callback for pending I/O from dsmesock
 *
 * @param source     (not used)
 * @param condition  I/O condition that caused the callback to be called
 * @param data       (not used)
 *
 * @return TRUE if iowatch is to be kept, or FALSE if it should be removed
 */
static gboolean mce_dsme_socket_recv_cb(GIOChannel *source,
                                        GIOCondition condition,
                                        gpointer data)
{
    gboolean keep_going = TRUE;
    dsmemsg_generic_t *msg = 0;

    DSM_MSGTYPE_STATE_CHANGE_IND *msg2;

    (void)source;
    (void)data;

    if( condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL) ) {
        if( !mce_dsme_is_shutting_down() )
            mce_log(LL_CRIT, "DSME socket hangup/error");
        keep_going = FALSE;
        goto EXIT;
    }

    if( !(msg = dsmesock_receive(mce_dsme_socket_connection)) )
        goto EXIT;

    if( DSMEMSG_CAST(DSM_MSGTYPE_CLOSE, msg) ) {
        if( !mce_dsme_is_shutting_down() )
            mce_log(LL_WARN, "DSME socket closed");
        keep_going = FALSE;
    }
    else if( DSMEMSG_CAST(DSM_MSGTYPE_PROCESSWD_PING, msg) ) {
        mce_dsme_processwd_pong();
    }
    else if( (msg2 = DSMEMSG_CAST(DSM_MSGTYPE_STATE_CHANGE_IND, msg)) ) {
        system_state_t state = mce_dsme_normalise_system_state(msg2->state);
638
        datapipe_exec_full(&system_state_pipe,
639
                           GINT_TO_POINTER(state));
640 641
    }
    else {
642 643
        mce_log(LL_DEBUG, "Unhandled %s message received from DSME",
                dsmemsg_name(msg));
644
    }
645

646 647
EXIT:
    free(msg);
648

649 650 651 652 653
    if( !keep_going ) {
        if( !mce_dsme_is_shutting_down() ) {
            mce_log(LL_WARN, "DSME i/o notifier disabled;"
                    " assuming dsme was stopped");
        }
654

655 656
        /* mark notifier as removed */
        mce_dsme_socket_recv_id = 0;
657

658 659 660
        /* close and wait for possible dsme restart */
        mce_dsme_socket_disconnect();
    }
661

662
    return keep_going;
663 664
}

665
/** Predicate for: socket connection to dsme exists
666
 *
667 668 669 670 671 672 673 674
 * @return true if connected, false otherwise
 */
static bool mce_dsme_socket_is_connected(void)
{
    return mce_dsme_socket_connection && mce_dsme_socket_recv_id;
}

/** Initialise dsmesock connection
675
 *
676
 * @return true on success, false on failure
677
 */
678
static bool mce_dsme_socket_connect(void)
679
{
680
    GIOChannel *iochan = NULL;
681

682 683 684 685 686
    /* No new connections during shutdown */
    if( mce_dsme_is_shutting_down() )
        goto EXIT;

    /* No new connections unless dsme dbus service is up */
687
    if( dsme_service_state != SERVICE_STATE_RUNNING )
688 689 690 691 692
        goto EXIT;

    /* Already connected ? */
    if( mce_dsme_socket_recv_id )
        goto EXIT;
693

694
    mce_log(LL_DEBUG, "Opening DSME socket");
695

696 697 698 699
    if( !(mce_dsme_socket_connection = dsmesock_connect()) ) {
        mce_log(LL_ERR, "Failed to open DSME socket");
        goto EXIT;
    }
700

701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
    mce_log(LL_DEBUG, "Adding DSME socket notifier");

    if( !(iochan = g_io_channel_unix_new(mce_dsme_socket_connection->fd)) ) {
        mce_log(LL_ERR,"Failed to set up I/O channel for DSME socket");
        goto EXIT;
    }

    mce_dsme_socket_recv_id =
        g_io_add_watch(iochan,
                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
                       mce_dsme_socket_recv_cb, NULL);

    /* Query the current system state; if the mainloop isn't running,
     * this will trigger an update when the mainloop starts
     */
    mce_dsme_query_system_state();

    /* Register with DSME's process watchdog */
    mce_dsme_processwd_init();
720

721
EXIT:
722 723
    if( iochan ) g_io_channel_unref(iochan);

724 725 726
    if( !mce_dsme_socket_recv_id )
        mce_dsme_socket_disconnect();

727 728 729 730 731 732 733 734 735 736 737
    return mce_dsme_socket_is_connected();
}

/** Close dsmesock connection
 */
static void mce_dsme_socket_disconnect(void)
{
    if( mce_dsme_socket_recv_id ) {
        mce_log(LL_DEBUG, "Removing DSME socket notifier");
        g_source_remove(mce_dsme_socket_recv_id);
        mce_dsme_socket_recv_id = 0;
738 739 740 741 742

        /* Still having had a live socket notifier means: We have
         * initiated the dsmesock disconnect and need to deactivate
         * the process watchdog before actually disconnecting. */
        mce_dsme_processwd_quit();
743
    }
744

745 746 747 748 749
    if( mce_dsme_socket_connection ) {
        mce_log(LL_DEBUG, "Closing DSME socket");
        dsmesock_close(mce_dsme_socket_connection);
        mce_dsme_socket_connection = 0;
    }
750 751
}

752 753 754 755
/* ========================================================================= *
 * DBUS_HANDLERS
 * ========================================================================= */

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
/** Pending thermal state query */
static DBusPendingCall *mce_dsme_thermal_state_query_pc = 0;

/** D-Bus callback for the thermal state change notification signal
 *
 * @param msg The D-Bus message
 *
 * @return TRUE
 */
static gboolean
mce_dsme_dbus_thermal_state_change_cb(DBusMessage *const msg)
{
    DBusError error = DBUS_ERROR_INIT;
    const char *name = 0;

    if( !dbus_message_get_args(msg, &error,
                               DBUS_TYPE_STRING, &name,
                               DBUS_TYPE_INVALID)) {
        mce_log(LL_ERR, "Failed to parse %s.%s; %s: %s",
                dbus_message_get_interface(msg),
                dbus_message_get_member(msg),
                error.name, error.message);
        goto EXIT;
    }

    mce_log(LL_WARN, "thermal state signal: %s", name);
    thermal_state_t state = mce_dsme_parse_thermal_state(name);
    mce_dsme_datapipe_set_thermal_state(state);

EXIT:
    dbus_error_free(&error);
    return TRUE;
}

/** Handle reply to async query made from mce_dsme_thermal_state_query_async()
 */
static void
mce_dsme_dbus_thermal_state_reply_cb(DBusPendingCall *pc, void *aptr)
{
    (void)aptr;

    DBusMessage *rsp  = 0;
    DBusError    err  = DBUS_ERROR_INIT;
    const char  *name = 0;

    if( pc != mce_dsme_thermal_state_query_pc )
        goto EXIT;

    dbus_pending_call_unref(mce_dsme_thermal_state_query_pc),
        mce_dsme_thermal_state_query_pc = 0;

    if( !(rsp = dbus_pending_call_steal_reply(pc)) ) {
        mce_log(LL_WARN, "no reply");
        goto EXIT;
    }

    if( dbus_set_error_from_message(&err, rsp) ) {
        mce_log(LL_WARN, "error reply: %s: %s", err.name, err.message);
        goto EXIT;
    }

    if( !dbus_message_get_args(rsp, &err,
                               DBUS_TYPE_STRING, &name,
                               DBUS_TYPE_INVALID) ) {
        mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
        goto EXIT;
    }

    mce_log(LL_DEBUG, "thermal state reply: %s", name);
    thermal_state_t state = mce_dsme_parse_thermal_state(name);
    mce_dsme_datapipe_set_thermal_state(state);

EXIT:
    if( rsp ) dbus_message_unref(rsp);
    dbus_error_free(&err);
    return;
}

/** Cancel pending async thermal state query
 */
static void
mce_dsme_dbus_cancel_thermal_state_query(void)
{
    if( mce_dsme_thermal_state_query_pc ) {
        mce_log(LL_DEBUG, "cancel thermal state query");
        dbus_pending_call_cancel(mce_dsme_thermal_state_query_pc);
        dbus_pending_call_unref(mce_dsme_thermal_state_query_pc);
        mce_dsme_thermal_state_query_pc = 0;
    }
}

/** Initiate async query to find out current thermal state
 */
static void
mce_dsme_dbus_start_thermal_state_query(void)
{
    mce_dsme_dbus_cancel_thermal_state_query();

    mce_log(LL_DEBUG, "start thermal state query");
    dbus_send_ex(thermalmanager_service,
                 thermalmanager_path,
                 thermalmanager_interface,
                 thermalmanager_get_thermal_state,
                 mce_dsme_dbus_thermal_state_reply_cb, 0, 0,
                 &mce_dsme_thermal_state_query_pc,
                 DBUS_TYPE_INVALID);
}

864
/** D-Bus callback for the init done notification signal
865 866
 *
 * @param msg The D-Bus message
867 868
 *
 * @return TRUE
869
 */
870
static gboolean mce_dsme_dbus_init_done_cb(DBusMessage *const msg)
871
{
872
    (void)msg;
873

874
    mce_log(LL_DEVEL, "Received init done notification");
875

876 877
    /* Remove transition submode after brief delay */
    mce_dsme_transition_schedule();
878

879
    return TRUE;
880 881 882 883 884 885 886 887 888 889
}

/** D-Bus callback for the shutdown notification signal
 *
 * @param msg The D-Bus message
 *
 * @return TRUE
 */
static gboolean mce_dsme_dbus_shutdown_cb(DBusMessage *const msg)
{
890
    (void)msg;
891

892 893
    mce_log(LL_WARN, "Received shutdown notification");
    mce_dsme_set_shutting_down(true);
894

895
    return TRUE;
896 897 898 899 900 901 902 903 904 905 906
}

/** D-Bus callback for the thermal shutdown notification signal
 *
 * @param msg The D-Bus message
 *
 * @return TRUE
 */
static gboolean
mce_dsme_dbus_thermal_shutdown_cb(DBusMessage *const msg)
{
907
    (void)msg;
908

909
    mce_log(LL_WARN, "Received thermal shutdown notification");
910
    mce_datapipe_request_display_state(MCE_DISPLAY_ON);
911

912
    return TRUE;
913 914 915 916 917 918 919 920 921 922 923
}

/** D-Bus callback for the battery empty shutdown notification signal
 *
 * @param msg The D-Bus message
 *
 * @return TRUE
 */
static gboolean
mce_dsme_dbus_battery_empty_shutdown_cb(DBusMessage *const msg)
{
924
    (void)msg;
925

926
    mce_log(LL_WARN, "Received battery empty shutdown notification");
927
    mce_datapipe_request_display_state(MCE_DISPLAY_ON);
928

929
    return TRUE;
930 931
}

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
/** Array of dbus message handlers */
static mce_dbus_handler_t mce_dsme_dbus_handlers[] =
{
    /* signals */
    {
        .interface = "com.nokia.startup.signal",
        .name      = "init_done",
        .type      = DBUS_MESSAGE_TYPE_SIGNAL,
        .callback  = mce_dsme_dbus_init_done_cb,
    },
    {
        .interface = "com.nokia.dsme.signal",
        .name      = "shutdown_ind",
        .type      = DBUS_MESSAGE_TYPE_SIGNAL,
        .callback  = mce_dsme_dbus_shutdown_cb,
    },
    {
        .interface = "com.nokia.dsme.signal",
        .name      = "thermal_shutdown_ind",
        .type      = DBUS_MESSAGE_TYPE_SIGNAL,
        .callback  = mce_dsme_dbus_thermal_shutdown_cb,
    },
    {
        .interface = "com.nokia.dsme.signal",
        .name      = "battery_empty_ind",
        .type      = DBUS_MESSAGE_TYPE_SIGNAL,
        .callback  = mce_dsme_dbus_battery_empty_shutdown_cb,
    },
960 961 962 963 964 965
    {
        .interface = thermalmanager_interface,
        .name      = thermalmanager_state_change_ind,
        .type      = DBUS_MESSAGE_TYPE_SIGNAL,
        .callback  = mce_dsme_dbus_thermal_state_change_cb,
    },
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
    /* sentinel */
    {
        .interface = 0
    }
};

/** Add dbus handlers
 */
static void mce_dsme_dbus_init(void)
{
    mce_dbus_handler_register_array(mce_dsme_dbus_handlers);
}

/** Remove dbus handlers
 */
static void mce_dsme_dbus_quit(void)
{
    mce_dbus_handler_unregister_array(mce_dsme_dbus_handlers);
}

/* ========================================================================= *
 * DATAPIPE_TRACKING
 * ========================================================================= */

990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
static void mce_dsme_datapipe_set_thermal_state(thermal_state_t state)
{
    if( thermal_state != state ) {
        /* All thermal state transitions are of interest - except for the
         * UNDEF -> OK that is expected to happen during mce startup. */
        int level = LL_WARN;
        if( thermal_state == THERMAL_STATE_UNDEF && state == THERMAL_STATE_OK )
            level = LL_DEBUG;
        mce_log(level, "thermal state changed: %s -> %s",
                thermal_state_repr(thermal_state),
                thermal_state_repr(state));
        thermal_state = state;
        datapipe_exec_full(&thermal_state_pipe, GINT_TO_POINTER(thermal_state));
    }
}

1006
/** Datapipe trigger for dsme availability
1007 1008
 *
 * @param data DSME D-Bus service availability (as a void pointer)
1009
 */
1010
static void mce_dsme_datapipe_dsme_service_state_cb(gconstpointer const data)
1011
{
1012 1013
    service_state_t prev = dsme_service_state;
    dsme_service_state = GPOINTER_TO_INT(data);
1014

1015
    if( dsme_service_state == prev )
1016
        goto EXIT;
1017

1018 1019
    mce_log(LL_DEVEL, "DSME dbus service: %s -> %s",
            service_state_repr(prev),
1020
            service_state_repr(dsme_service_state));
1021

1022
    /* Re-evaluate dsmesock connection */
1023
    if( dsme_service_state == SERVICE_STATE_RUNNING )
1024
        mce_dsme_socket_connect();
1025 1026

EXIT:
1027
    return;
1028 1029
}

1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
/** Datapipe trigger for thermalmanager availability
 *
 * @param data thermalmanager D-Bus service availability (as a void pointer)
 */
static void mce_dsme_datapipe_thermalmanager_service_state_cb(gconstpointer const data)
{
    service_state_t prev = thermalmanager_service_state;
    thermalmanager_service_state = GPOINTER_TO_INT(data);

    if( thermalmanager_service_state == prev )
        goto EXIT;

    mce_log(LL_DEBUG, "thermalmanager dbus service: %s -> %s",
            service_state_repr(prev),
            service_state_repr(thermalmanager_service_state));

    if( thermalmanager_service_state == SERVICE_STATE_RUNNING )
        mce_dsme_dbus_start_thermal_state_query();
    else
        mce_dsme_dbus_cancel_thermal_state_query();

EXIT:
    return;
}

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
/** Change notifications for init_done
 */
static void mce_dsme_datapipe_init_done_cb(gconstpointer data)
{
    tristate_t prev = init_done;
    init_done = GPOINTER_TO_INT(data);

    if( init_done == prev )
        goto EXIT;

    mce_log(LL_DEBUG, "init_done = %s -> %s",
            tristate_repr(prev),
            tristate_repr(init_done));

    if( init_done == TRISTATE_TRUE ) {
        /* Remove transition submode after brief delay */
        mce_dsme_transition_schedule();
    }

EXIT:
    return;
}

1078 1079
/** Handle system_state_pipe notifications
 *
1080
 * Implemented as an input trigger to ensure this function gets
1081 1082 1083 1084
 * executed before output triggers from other modules/plugins.
 *
 * @param data The system state (as a void pointer)
 */
1085
static void mce_dsme_datapipe_system_state_cb(gconstpointer data)
1086
{
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
    system_state_t prev = system_state;
    system_state = GPOINTER_TO_INT(data);

    if( system_state == prev )
        goto EXIT;

    mce_log(LL_DEVEL, "system_state: %s -> %s",
            system_state_repr(prev),
            system_state_repr(system_state));

1097 1098 1099
    /* Set transition submode unless coming from MCE_SYSTEM_STATE_UNDEF */
    if( prev != MCE_SYSTEM_STATE_UNDEF )
        mce_add_submode_int32(MCE_SUBMODE_TRANSITION);
1100 1101 1102

    /* Handle LED patterns */
    switch( system_state ) {
1103
    case MCE_SYSTEM_STATE_USER:
1104 1105
        datapipe_exec_full(&led_pattern_activate_pipe,
                           MCE_LED_PATTERN_DEVICE_ON);
1106 1107
        break;

1108 1109
    case MCE_SYSTEM_STATE_SHUTDOWN:
    case MCE_SYSTEM_STATE_REBOOT:
1110 1111
        datapipe_exec_full(&led_pattern_deactivate_pipe,
                           MCE_LED_PATTERN_DEVICE_ON);
1112 1113 1114 1115 1116 1117 1118 1119
        break;

    default:
        break;
    }

    /* Handle shutdown flag */
    switch( system_state ) {
1120 1121
    case MCE_SYSTEM_STATE_ACTDEAD:
    case MCE_SYSTEM_STATE_USER:
1122 1123 1124 1125 1126
        /* Re-entry to actdead/user also means shutdown
         * has been cancelled */
        mce_dsme_set_shutting_down(false);
        break;

1127 1128
    case MCE_SYSTEM_STATE_SHUTDOWN:
    case MCE_SYSTEM_STATE_REBOOT:
1129 1130 1131 1132 1133 1134
        mce_dsme_set_shutting_down(true);
        break;

    default:
        break;
    }
1135

1136
EXIT:
1137
    return;
1138 1139
}

1140 1141 1142
/** Array of datapipe handlers */
static datapipe_handler_t mce_dsme_datapipe_handlers[] =
{
1143
    // input triggers
1144 1145
    {
        .datapipe  = &system_state_pipe,
1146
        .input_cb  = mce_dsme_datapipe_system_state_cb,
1147 1148 1149
    },
    // output triggers
    {
1150 1151
        .datapipe  = &dsme_service_state_pipe,
        .output_cb = mce_dsme_datapipe_dsme_service_state_cb,
1152
    },
1153 1154 1155 1156
    {
        .datapipe  = &thermalmanager_service_state_pipe,
        .output_cb = mce_dsme_datapipe_thermalmanager_service_state_cb,
    },
1157 1158 1159 1160
    {
        .datapipe  = &init_done_pipe,
        .output_cb = mce_dsme_datapipe_init_done_cb,
    },
1161 1162 1163 1164
    // sentinel
    {
        .datapipe = 0,
    }
1165 1166 1167 1168
};

static datapipe_bindings_t mce_dsme_datapipe_bindings =
{
1169 1170
    .module   = "mce-dsme",
    .handlers = mce_dsme_datapipe_handlers,
1171 1172
};

1173 1174
/** Append triggers/filters to datapipes
 */
1175
static void mce_dsme_datapipe_init(void)
1176
{
1177
    mce_datapipe_init_bindings(&mce_dsme_datapipe_bindings);
1178 1179 1180 1181
}

/** Remove triggers/filters from datapipes
 */
1182
static void mce_dsme_datapipe_quit(void)
1183
{
1184
    mce_datapipe_quit_bindings(&mce_dsme_datapipe_bindings);
1185 1186
}

1187 1188 1189 1190 1191
/* ========================================================================= *
 * MODULE_INIT_EXIT
 * ========================================================================= */

/** Init function for the mce-dsme component
1192 1193 1194 1195 1196
 *
 * @return TRUE on success, FALSE on failure
 */
gboolean mce_dsme_init(void)
{
1197 1198
    mce_worker_add_context(MCE_DSME_WORKERWD_JOB_CONTEXT);

1199
    mce_dsme_datapipe_init();
1200

1201
    mce_dsme_dbus_init();
1202

1203
    return TRUE;
1204 1205
}

1206
/** Exit function for the mce-dsme component
1207 1208 1209
 */
void mce_dsme_exit(void)
{
1210 1211
    mce_worker_rem_context(MCE_DSME_WORKERWD_JOB_CONTEXT);

1212
    mce_dsme_dbus_quit();
1213

1214
    mce_dsme_socket_disconnect();
1215

1216
    mce_dsme_datapipe_quit();
1217

1218
    /* Remove all timer sources & pending calls before returning */
1219
    mce_dsme_transition_cancel();
1220
    mce_dsme_dbus_cancel_thermal_state_query();
1221
    return;
1222
}