~nemo-mobile-team/nemo-mobile/dsme

« back to all changes in this revision

Viewing changes to modules/thermalmanager.c

  • Committer: David Greaves
  • Author(s): David Greaves
  • Date: 2015-10-20 12:06:05 UTC
  • Revision ID: git-v1:896c2138774b3f06e9886779eb59dcfb7559bfb8
Migration to git.merproject.org

Signed-off-by: David Greaves <david.greaves@jolla.com>

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
   @file thermalmanager.c
3
 
 
4
 
   This file implements part of the device thermal management policy
5
 
   by providing the current thermal state for interested sw components.
6
 
   <p>
7
 
   Copyright (C) 2009-2010 Nokia Corporation
8
 
   Copyright (C) 2013-2015 Jolla Oy.
9
 
 
10
 
   @author Semi Malinen <semi.malinen@nokia.com>
11
 
   @author Pekka Lundstrom <pekka.lundstrom@jollamobile.com>
12
 
   @author Simo Piiroinen <simo.piiroinen@jollamobile.com>
13
 
 
14
 
   This file is part of Dsme.
15
 
 
16
 
   Dsme is free software; you can redistribute it and/or modify
17
 
   it under the terms of the GNU Lesser General Public License
18
 
   version 2.1 as published by the Free Software Foundation.
19
 
 
20
 
   Dsme is distributed in the hope that it will be useful,
21
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
 
   Lesser General Public License for more details.
24
 
 
25
 
   You should have received a copy of the GNU Lesser General Public
26
 
   License along with Dsme.  If not, see <http://www.gnu.org/licenses/>.
27
 
*/
28
 
 
29
 
/*
30
 
 * An example command line to obtain thermal state over D-Bus:
31
 
 * $ dbus-send --system --print-reply --dest=com.nokia.thermalmanager /com/nokia/thermalmanager com.nokia.thermalmanager.get_thermal_state
32
 
 *
33
 
 * An example command line to obtain surface temperature estimate over D-Bus:
34
 
 * $ dbus-send --system --print-reply --dest=com.nokia.thermalmanager /com/nokia/thermalmanager com.nokia.thermalmanager.estimate_surface_temperature
35
 
 *
36
 
 * An example command line to obtain core temperature over D-Bus:
37
 
 * $ dbus-send --system --print-reply --dest=com.nokia.thermalmanager /com/nokia/thermalmanager com.nokia.thermalmanager.core_temperature
38
 
 *
39
 
 * An example command line to obtain battery temperature over D-Bus:
40
 
 * $ dbus-send --system --print-reply --dest=com.nokia.thermalmanager /com/nokia/thermalmanager com.nokia.thermalmanager.battery_temperature
41
 
 *
42
 
 * An example command line to obtain named sensor temperature over D-Bus:
43
 
 * $ dbus-send --system --print-reply --dest=com.nokia.thermalmanager /com/nokia/thermalmanager com.nokia.thermalmanager.sensor_temperature string:core
44
 
 */
45
 
 
46
 
/* ========================================================================= *
47
 
 *
48
 
 * Thermal manager:
49
 
 * - maintains a set of registered thermal objects
50
 
 * - polls status of each thermal object periodically
51
 
 * - evaluates overall device thermal status
52
 
 * - broadcasts thermal status changes within dsme and over D-Bus
53
 
 * - handles thermal sensor related D-Bus queries
54
 
 *
55
 
 * Thermal sensor:
56
 
 * - handles the actual sensor reading
57
 
 * - defines the thermal limits and associated polling delays
58
 
 * - hw specific sensors can be made available as plugins
59
 
 *
60
 
 * Thermal object:
61
 
 * - acts as glue layer allowing communication between thermal
62
 
 *   manager and possibly hw specific sensor logic
63
 
 *
64
 
 * +-------------------------+
65
 
 * |                         |
66
 
 * |  THERMAL_MANAGER        |
67
 
 * |                         |
68
 
 * +-------------------------+
69
 
 *           ^ 1
70
 
 *           |
71
 
 *           |
72
 
 *           v N
73
 
 * +-------------------------+
74
 
 * |                         |
75
 
 * |  THERMAL_OBJECT         |
76
 
 * |                 +-------|
77
 
 * |-----------------| VTAB  |
78
 
 * |                 +-------|
79
 
 * |  THERMAL_SENSOR         |
80
 
 * |                         |
81
 
 * +-------------------------+
82
 
 *
83
 
 * The included-by-default generic thermal sensor plugin:
84
 
 * - can read file based sensors (in /sys and /proc file systems)
85
 
 * - allows enabling/disabling sensors on dsme startup/exit
86
 
 * - allows configuring meta-sensors which take temperature
87
 
 *   value from some other sensor, apply optional offset and
88
 
 *   can define separate thermal limits for the resulting
89
 
 *   values (e.g. surface_temp = battery_temp - 3 etc).
90
 
 *
91
 
 * HW specific plugins can/must be added to deal with thermal
92
 
 * sensors that can't be accessed via filesystem (e.g. old nokia
93
 
 * devices where battery temperature is accessible via bme ipc only).
94
 
 * The sensor update logic in thermal manager has been implemented
95
 
 * to expect asynchronous sensor access to facilitate such indirect
96
 
 * sensor polling.
97
 
 *
98
 
 * Simplified graph of sensor poll duty loop:
99
 
 *
100
 
 *     DSME_STARTUP
101
 
 *           |
102
 
 *           v
103
 
 *     thermal_sensor
104
 
 *           |
105
 
 *           v
106
 
 *     thermal_manager_register_object
107
 
 *           |
108
 
 *           v
109
 
 *     thermal_manager_request_object_update <---------.
110
 
 *           |                                         |
111
 
 *           v                                         |
112
 
 *     thermal_object_request_update                   |
113
 
 *           |                                         |
114
 
 *           v                                         |
115
 
 *     thermal_sensor                                  |
116
 
 *           |                                         |
117
 
 *           Z (assumed: sensor access is async)       Z iphb_timer
118
 
 *           |                                         |
119
 
 *           v                                         |
120
 
 *     thermal_object_handle_update                    |
121
 
 *           |                                         |
122
 
 *           v                                         |
123
 
 *     thermal_manager_handle_object_update            |
124
 
 *           |                 |                       |
125
 
 *           |                 v                       |
126
 
 *           |              thermal_manager_schedule_object_poll
127
 
 *           v
128
 
 *     thermal_manager_broadcast_status
129
 
 *         |                   |
130
 
 *         Z DBUS              Z DSME
131
 
 *         |                   |
132
 
 *         v                   v
133
 
 *     UI_NOTIFICATION     SHUTDOWN_POLICY
134
 
 *
135
 
 * More descriptive diagram can be generated from thermalmanager.dot.
136
 
 *
137
 
 * ========================================================================= */
138
 
 
139
 
#define _GNU_SOURCE
140
 
 
141
 
#include "thermalmanager.h"
142
 
 
143
 
#include <iphbd/iphb_internal.h>
144
 
 
145
 
#include "dbusproxy.h"
146
 
#include "dsme_dbus.h"
147
 
 
148
 
#include "../include/dsme/modules.h"
149
 
#include "../include/dsme/modulebase.h"
150
 
#include "../include/dsme/logging.h"
151
 
#include "heartbeat.h"
152
 
 
153
 
#include <dsme/state.h>
154
 
#include <dsme/thermalmanager_dbus_if.h>
155
 
 
156
 
#include <glib.h>
157
 
#include <stdlib.h>
158
 
#include <string.h>
159
 
#include <stdio.h>
160
 
 
161
 
/* ========================================================================= *
162
 
 * FORWARD_DECLARATIONS
163
 
 * ========================================================================= */
164
 
 
165
 
/* ------------------------------------------------------------------------- *
166
 
 * DIAGNOSTIC_LOGGING
167
 
 * ------------------------------------------------------------------------- */
168
 
 
169
 
/** Prefix to use for all logging from this module */
170
 
#define PFIX "thermalmanager: "
171
 
 
172
 
/* ------------------------------------------------------------------------- *
173
 
 * THERMAL_STATUS
174
 
 * ------------------------------------------------------------------------- */
175
 
 
176
 
const char *thermal_status_name (THERMAL_STATUS status);
177
 
const char *thermal_status_repr (THERMAL_STATUS status);
178
 
 
179
 
/* ------------------------------------------------------------------------- *
180
 
 * THERMAL_MANAGER
181
 
 * ------------------------------------------------------------------------- */
182
 
 
183
 
void        thermal_manager_register_object            (thermal_object_t *thermal_object);
184
 
void        thermal_manager_unregister_object          (thermal_object_t *thermal_object);
185
 
bool        thermal_manager_object_is_registered       (thermal_object_t *thermal_object);
186
 
static void thermal_manager_request_object_update      (thermal_object_t *thermal_object);
187
 
void        thermal_manager_handle_object_update       (thermal_object_t *changed_object);
188
 
static void thermal_manager_schedule_object_poll       (thermal_object_t *thermal_object);
189
 
 
190
 
bool        thermal_manager_get_sensor_status          (const char *sensor_name, THERMAL_STATUS *status, int *temperature);
191
 
bool        thermal_manager_request_sensor_update      (const char *sensor_name);
192
 
void        thermal_manager_handle_sensor_update       (const thermal_object_t *thermal_object);
193
 
static bool thermal_manager_have_pending_sensor_update (const char *sensor_name);
194
 
 
195
 
static void thermal_manager_broadcast_status           (THERMAL_STATUS status, thermal_object_t *changed_object);
196
 
static void thermal_manager_broadcast_status_dsme      (THERMAL_STATUS status, int temperature, const char *sensor_name);
197
 
static void thermal_manager_broadcast_status_dbus      (THERMAL_STATUS status);
198
 
 
199
 
/* ------------------------------------------------------------------------- *
200
 
 * DBUS_HANDLERS
201
 
 * ------------------------------------------------------------------------- */
202
 
 
203
 
static void thermal_manager_get_thermal_state_cb       (const DsmeDbusMessage *request, DsmeDbusMessage **reply);
204
 
 
205
 
static void thermal_manager_handle_temperature_query   (const DsmeDbusMessage *req, const char *sensor_name, DsmeDbusMessage **rsp);
206
 
static void thermal_manager_get_surface_temperature_cb (const DsmeDbusMessage *req, DsmeDbusMessage **rsp);
207
 
static void thermal_manager_get_core_temperature_cb    (const DsmeDbusMessage *req, DsmeDbusMessage **rsp);
208
 
static void thermal_manager_get_battery_temperature_cb (const DsmeDbusMessage *req, DsmeDbusMessage **rsp);
209
 
static void thermal_manager_get_sensor_temperature_cb  (const DsmeDbusMessage *req, DsmeDbusMessage **rsp);
210
 
 
211
 
/* ------------------------------------------------------------------------- *
212
 
 * MODULE_GLUE
213
 
 * ------------------------------------------------------------------------- */
214
 
 
215
 
void module_init (module_t *handle);
216
 
void module_fini (void);
217
 
 
218
 
/* ========================================================================= *
219
 
 * MODULE_DATA
220
 
 * ========================================================================= */
221
 
 
222
 
/** DSME module handle for this module */
223
 
static module_t *this_module = 0;
224
 
 
225
 
/** List of registered thermal objects */
226
 
static GSList *thermal_objects = 0;
227
 
 
228
 
/** Currently accepted device thermal state */
229
 
static THERMAL_STATUS current_status = THERMAL_STATUS_NORMAL;
230
 
 
231
 
/** Flag for: D-Bus method handlers have been registered */
232
 
static bool dbus_methods_bound = false;
233
 
 
234
 
/* ========================================================================= *
235
 
 * THERMAL_STATUS
236
 
 * ========================================================================= */
237
 
 
238
 
/** Convert thermal status to name used in D-Bus signaling
239
 
 *
240
 
 * @param  status thermal status
241
 
 *
242
 
 * @return string expected by dbus peers
243
 
 */
244
 
const char *
245
 
thermal_status_name(THERMAL_STATUS status)
246
 
{
247
 
    /* Note: Any deviation from strings defined in thermalmanager_dbus_if
248
 
     *       from libdsme constitutes a possible D-Bus API break and should
249
 
     *       be avoided.
250
 
     */
251
 
 
252
 
    const char *res = "unknown";
253
 
 
254
 
    switch( status ) {
255
 
    case THERMAL_STATUS_LOW:
256
 
      res = thermalmanager_thermal_status_low;
257
 
      break;
258
 
 
259
 
    case THERMAL_STATUS_NORMAL:
260
 
      res = thermalmanager_thermal_status_normal;
261
 
      break;
262
 
 
263
 
    case THERMAL_STATUS_WARNING:
264
 
      res = thermalmanager_thermal_status_warning;
265
 
      break;
266
 
 
267
 
    case THERMAL_STATUS_ALERT:
268
 
      res = thermalmanager_thermal_status_alert;
269
 
      break;
270
 
 
271
 
    case THERMAL_STATUS_FATAL:
272
 
      res = thermalmanager_thermal_status_fatal;
273
 
      break;
274
 
 
275
 
    default: break;
276
 
    }
277
 
 
278
 
    return res;
279
 
}
280
 
 
281
 
/** Convert thermal status to human readable string
282
 
 *
283
 
 * @param  status thermal status
284
 
 *
285
 
 * @return name of the status
286
 
 */
287
 
const char *
288
 
thermal_status_repr(THERMAL_STATUS status)
289
 
{
290
 
    const char *repr = "UNKNOWN";
291
 
 
292
 
    switch (status) {
293
 
    case THERMAL_STATUS_LOW:     repr = "LOW";     break;
294
 
    case THERMAL_STATUS_NORMAL:  repr = "NORMAL";  break;
295
 
    case THERMAL_STATUS_WARNING: repr = "WARNING"; break;
296
 
    case THERMAL_STATUS_ALERT:   repr = "ALERT";   break;
297
 
    case THERMAL_STATUS_FATAL:   repr = "FATAL";   break;
298
 
    case THERMAL_STATUS_INVALID: repr = "INVALID"; break;
299
 
    default: break;
300
 
    }
301
 
 
302
 
    return repr;
303
 
}
304
 
 
305
 
/* ========================================================================= *
306
 
 * THERMAL_MANAGER
307
 
 * ========================================================================= */
308
 
 
309
 
/** Initiate thermal object state update
310
 
 *
311
 
 * Pass request to thermal sensor via thermal object abstraction layer.
312
 
 *
313
 
 * The temperature query is assumed to be asynchronous.
314
 
 *
315
 
 * Control returns to thermal manager when thermal object layer calls
316
 
 * thermal_manager_handle_object_update() function.
317
 
 *
318
 
 * @param thermal_object  registered thermal object
319
 
 */
320
 
void
321
 
thermal_manager_request_object_update(thermal_object_t *thermal_object)
322
 
{
323
 
    /* Ignore invalid / unregistered objects */
324
 
    if( !thermal_manager_object_is_registered(thermal_object) )
325
 
        goto EXIT;
326
 
 
327
 
    /* Initiate fetching and evaluating of the current value */
328
 
    thermal_object_request_update(thermal_object);
329
 
 
330
 
    /* ... lower level activity ...
331
 
     * -> thermal_object_handle_update()
332
 
     *    -> thermal_manager_handle_object_update()
333
 
     *       -> thermal_manager_schedule_object_poll()
334
 
     */
335
 
 
336
 
EXIT:
337
 
    return;
338
 
}
339
 
 
340
 
/** Scedule iphb wakeup for updating thermal object
341
 
 *
342
 
 * Normally the required poll delay is decided at thermal sensor
343
 
 * and depends on the current thermal state for the sensor.
344
 
 *
345
 
 * However, when status change is detected, several measurements
346
 
 * are needed before the new status is accepted. For this purpose
347
 
 * shorter polling delays are used while the status is in
348
 
 * transitional state.
349
 
 *
350
 
 * Control returns to thermal manager when iphb wakeup message
351
 
 * handler calls thermal_manager_request_object_update() function.
352
 
 *
353
 
 * @param thermal_object  registered thermal object
354
 
 */
355
 
void
356
 
thermal_manager_schedule_object_poll(thermal_object_t *thermal_object)
357
 
{
358
 
    /* Ignore invalid / unregistered objects
359
 
     */
360
 
    if( !thermal_manager_object_is_registered(thermal_object) )
361
 
        goto EXIT;
362
 
 
363
 
    /* Schedule the next measurement point
364
 
     */
365
 
    DSM_MSGTYPE_WAIT msg = DSME_MSG_INIT(DSM_MSGTYPE_WAIT);
366
 
 
367
 
    msg.req.pid = 0;
368
 
    msg.data    = thermal_object;
369
 
 
370
 
    /* Start with fall back defaults */
371
 
    int mintime = THERMAL_STATUS_POLL_DELAY_DEFAULT_MINIMUM;
372
 
    int maxtime = THERMAL_STATUS_POLL_DELAY_DEFAULT_MAXIMUM;
373
 
 
374
 
    if( thermal_object_status_in_transition(thermal_object) ) {
375
 
        /* In transition: multiple measurements are neede to
376
 
         * verify the status change - wake up more frequently */
377
 
        mintime = THERMAL_STATUS_POLL_DELAY_TRANSITION_MINIMUM;
378
 
        maxtime = THERMAL_STATUS_POLL_DELAY_TRANSITION_MAXIMUM;
379
 
 
380
 
        /* and wake up from suspend to do the measurement */
381
 
        msg.req.wakeup  = true;
382
 
    }
383
 
    else if( !thermal_object_get_poll_delay(thermal_object,
384
 
                                            &mintime, &maxtime) ) {
385
 
        /* No wait period defined in the configuration - use
386
 
         * shorter waits for abnormal states */
387
 
 
388
 
        THERMAL_STATUS    status = THERMAL_STATUS_INVALID;
389
 
        int               temperature = INVALID_TEMPERATURE;
390
 
 
391
 
        thermal_object_get_sensor_status(thermal_object, &status,
392
 
                                         &temperature);
393
 
 
394
 
        switch( status ) {
395
 
        case THERMAL_STATUS_ALERT:
396
 
        case THERMAL_STATUS_FATAL:
397
 
            mintime =  THERMAL_STATUS_POLL_ALERT_TRANSITION_MINIMUM;
398
 
            maxtime =  THERMAL_STATUS_POLL_ALERT_TRANSITION_MAXIMUM;
399
 
            break;
400
 
 
401
 
        default:
402
 
            break;
403
 
        }
404
 
    }
405
 
 
406
 
    if( mintime == maxtime ) {
407
 
        dsme_log(LOG_DEBUG, PFIX"%s: check again in %d sec global slot",
408
 
                 thermal_object_get_name(thermal_object), mintime);
409
 
    }
410
 
    else {
411
 
        dsme_log(LOG_DEBUG, PFIX"%s: check again in %d to %d seconds",
412
 
                 thermal_object_get_name(thermal_object), mintime, maxtime);
413
 
    }
414
 
 
415
 
    msg.req.mintime = mintime;
416
 
    msg.req.maxtime = maxtime;
417
 
 
418
 
    /* Wakeup will be sent to "originating module". Since
419
 
     * this function can end up being called from events
420
 
     * dispatched at other modules, we need to maintain
421
 
     * the context manually ... */
422
 
    const module_t *from_module = current_module();
423
 
    enter_module(this_module);
424
 
    broadcast_internally(&msg);
425
 
    enter_module(from_module);
426
 
 
427
 
    /* ... wait for DSM_MSGTYPE_WAKEUP ...
428
 
     * -> thermal_manager_request_object_update()
429
 
     */
430
 
 
431
 
EXIT:
432
 
    return;
433
 
}
434
 
 
435
 
/** Register thermal object to thermal manager
436
 
 *
437
 
 * Add sensor object to the list of registered objects.
438
 
 *
439
 
 * Start update cycle:
440
 
 *   1. query sensor temperature and status
441
 
 *      ... wait for sensor status
442
 
 *
443
 
 *   2. update device thermal state
444
 
 *   3. broadcast device thermal state
445
 
 *
446
 
 *   4. schedule sensor specific iphb wakeup
447
 
 *      ... wait for iphb wakeup
448
 
 *
449
 
 *   5. go back to 1
450
 
 *
451
 
 * @param thermal_object  unregistered thermal object
452
 
 */
453
 
void
454
 
thermal_manager_register_object(thermal_object_t *thermal_object)
455
 
{
456
 
    if( !thermal_object )
457
 
        goto EXIT;
458
 
 
459
 
    if( thermal_manager_object_is_registered(thermal_object) )
460
 
        goto EXIT;
461
 
 
462
 
    dsme_log(LOG_DEBUG, PFIX"%s: registered",
463
 
             thermal_object_get_name(thermal_object));
464
 
 
465
 
    // add the thermal object to the list of know thermal objects
466
 
    thermal_objects = g_slist_append(thermal_objects, thermal_object);
467
 
 
468
 
    thermal_manager_request_object_update(thermal_object);
469
 
 
470
 
EXIT:
471
 
    return;
472
 
}
473
 
 
474
 
/** Unregister thermal object from thermal manager
475
 
 *
476
 
 * Remove sensor object from the list of registered objects.
477
 
 *
478
 
 * It is expected that this function will be called only
479
 
 * when dsme is on exit path and sensor backend plugins are
480
 
 * being unloaded.
481
 
 *
482
 
 * Pending temperature requests are not canceled as it
483
 
 * is assumed to be done by the thermal sensor plugin.
484
 
 * If lower levels however do use thermal manager API
485
 
 * to report changes later on, the calls are ignored
486
 
 * because the objects are no longer registered.
487
 
 *
488
 
 * Similarly iphb wakeups relating to unregistered objects
489
 
 * are ignored. And once thermal manager plugin is unloaded
490
 
 * they will not be even dispatched anymore.
491
 
 *
492
 
 * @param thermal_object  registered thermal object
493
 
 */
494
 
void
495
 
thermal_manager_unregister_object(thermal_object_t *thermal_object)
496
 
{
497
 
    if( !thermal_object )
498
 
        goto EXIT;
499
 
 
500
 
    if( !thermal_manager_object_is_registered(thermal_object) )
501
 
        goto EXIT;
502
 
 
503
 
    // remove the thermal object from the list of know thermal objects
504
 
    thermal_objects = g_slist_remove(thermal_objects, thermal_object);
505
 
 
506
 
    dsme_log(LOG_DEBUG, PFIX"%s: unregistered",
507
 
             thermal_object_get_name(thermal_object));
508
 
 
509
 
EXIT:
510
 
    return;
511
 
}
512
 
 
513
 
/** Check if thermal object is registered to thermal manager
514
 
 *
515
 
 * @param thermal_object  thermal object
516
 
 *
517
 
 * @return true if object is registerd, false otherwise
518
 
 */
519
 
bool
520
 
thermal_manager_object_is_registered(thermal_object_t *thermal_object)
521
 
{
522
 
    bool is_registered = false;
523
 
 
524
 
    if( !thermal_object )
525
 
        goto EXIT;
526
 
 
527
 
    if( g_slist_find(thermal_objects, thermal_object) )
528
 
        is_registered = true;
529
 
 
530
 
EXIT:
531
 
    return is_registered;
532
 
}
533
 
 
534
 
/** Get sensor status and temperature
535
 
 *
536
 
 * The status reported is the worst deviation from normal status found in
537
 
 * the thermal objects that match the sensor_name given.
538
 
 *
539
 
 * For example, if there are sensors "core0", "core1", "core2" and "core3"
540
 
 *
541
 
 * Requesting "core0" status returns info from the "core0" as is.
542
 
 *
543
 
 * But requesting "core" status depends on overall status of all four
544
 
 * matching sensors, which is highest state and temperature found, unless
545
 
 * no sensors are in grave overheat state and at least one sensor is in
546
 
 * low temperature states - in which case lowest state/temperature is
547
 
 * returned.
548
 
 *
549
 
 * Notes:
550
 
 *  - The status and temperature values are not modified if no matching
551
 
 *    sensors are found
552
 
 *  - Values cached at thermal object level are used - the temperature
553
 
 *    is always the latest seen, but during lower level status transition
554
 
 *    the previous (stable) status is used
555
 
 *
556
 
 * @param sensor_name  sensor name / sensor group name prefix
557
 
 * @param status       where to store sensor thermal status
558
 
 * @param temperature  where to store sensor temperature value
559
 
 *
560
 
 * @param return true if matching sensors were found and output values set;
561
 
 *               false otherwise
562
 
 */
563
 
bool
564
 
thermal_manager_get_sensor_status(const char *sensor_name,
565
 
                                  THERMAL_STATUS *status, int *temperature)
566
 
{
567
 
    static volatile int recursing = 0;
568
 
 
569
 
    bool ack = false;
570
 
 
571
 
    /* In theory backend config parser should eliminate
572
 
     * cyclic sensor dependencies - but if it still happens,
573
 
     * it must not end up in infinite recursion  ... */
574
 
    if( ++recursing != 1 )
575
 
        goto EXIT;
576
 
 
577
 
    /* Initialize to invalid [lo,hi] range */
578
 
    THERMAL_STATUS status_hi = THERMAL_STATUS_LOW;
579
 
    THERMAL_STATUS status_lo = THERMAL_STATUS_FATAL;
580
 
    int            temp_lo   = IGNORE_TEMP_ABOVE;
581
 
    int            temp_hi   = IGNORE_TEMP_BELOW;
582
 
 
583
 
    for( GSList *item = thermal_objects; item; item = item->next ) {
584
 
        thermal_object_t *object = item->data;
585
 
 
586
 
        if( !thermal_object_has_name_like(object, sensor_name) )
587
 
            continue;
588
 
 
589
 
        THERMAL_STATUS s = THERMAL_STATUS_INVALID;
590
 
        int            t = INVALID_TEMPERATURE;
591
 
 
592
 
        if( !thermal_object_get_sensor_status(object, &s, &t) )
593
 
            continue;
594
 
 
595
 
        if( status_hi < s )   status_hi = s;
596
 
        if( status_lo > s )   status_lo = s;
597
 
 
598
 
        if( temp_hi < t ) temp_hi = t;
599
 
        if( temp_lo > t ) temp_lo = t;
600
 
    }
601
 
 
602
 
    /* Skip if there were no matching sensors */
603
 
    if( status_lo > status_hi || temp_lo > temp_hi )
604
 
        goto EXIT;
605
 
 
606
 
    /* Precedence: FATAL > ALERT > LOW > WARNING > NORMAL
607
 
     *
608
 
     * There is implicit exceptation that group of matching
609
 
     * sensors share similar enough temperature config that
610
 
     * this simplistic temperature selection makes sense.
611
 
     */
612
 
 
613
 
    if( status_lo < THERMAL_STATUS_NORMAL &&
614
 
        status_hi < THERMAL_STATUS_ALERT ) {
615
 
        *status = status_lo;
616
 
        *temperature = temp_lo;
617
 
    }
618
 
    else {
619
 
        *status = status_hi;
620
 
        *temperature = temp_hi;
621
 
    }
622
 
 
623
 
    ack = true;
624
 
 
625
 
EXIT:
626
 
 
627
 
    --recursing;
628
 
 
629
 
    return ack;
630
 
}
631
 
 
632
 
/** Handle updated thermal object status
633
 
 *
634
 
 * Called by thermal object layer when
635
 
 * a) fresh temperature & status data is available
636
 
 * b) temperature query via thermal sensor failed
637
 
 *
638
 
 * The overall device thermal state is re-evaluated and
639
 
 * changes broadcast both internally and externally.
640
 
 *
641
 
 * The next temperature poll time is scheduled.
642
 
 *
643
 
 * @param thermal_object  registered thermal object
644
 
 */
645
 
void
646
 
thermal_manager_handle_object_update(thermal_object_t *changed_object)
647
 
{
648
 
    if( !thermal_manager_object_is_registered(changed_object) )
649
 
        goto EXIT;
650
 
 
651
 
    /* Scan all thermal objects for lowest/highest status */
652
 
    THERMAL_STATUS highest_status = THERMAL_STATUS_NORMAL;
653
 
    THERMAL_STATUS lowest_status  = THERMAL_STATUS_NORMAL;
654
 
    THERMAL_STATUS overall_status = THERMAL_STATUS_NORMAL;
655
 
 
656
 
    for( GSList *item = thermal_objects; item; item = item->next ) {
657
 
        thermal_object_t *object = item->data;
658
 
        THERMAL_STATUS    status = thermal_object_get_status(object);
659
 
 
660
 
        /* Ignore sensors in invalid state */
661
 
        if( status == THERMAL_STATUS_INVALID )
662
 
            continue;
663
 
 
664
 
        if( highest_status < status )
665
 
            highest_status = status;
666
 
 
667
 
        if( lowest_status > status )
668
 
            lowest_status = status;
669
 
    }
670
 
 
671
 
    /* Decide overall status:
672
 
     *  If we have any ALERT of FATAL then that decides overall status
673
 
     *  During LOW, NORMAL or WARNING, any LOW wins
674
 
     *  Else status is the highest
675
 
     */
676
 
    if( lowest_status  < THERMAL_STATUS_NORMAL &&
677
 
        highest_status < THERMAL_STATUS_ALERT )
678
 
        overall_status = lowest_status;
679
 
    else
680
 
        overall_status = highest_status;
681
 
 
682
 
    /* Send notifications */
683
 
    thermal_manager_broadcast_status(overall_status, changed_object);
684
 
 
685
 
    /* Schedule the next inspection point */
686
 
    thermal_manager_schedule_object_poll(changed_object);
687
 
 
688
 
EXIT:
689
 
    return;
690
 
}
691
 
 
692
 
/** Update current overall device thermal status
693
 
 *
694
 
 * Update bookkeeping and broadcast status both within dsme
695
 
 * and via dbus.
696
 
 *
697
 
 * @param status          device overall thermal status
698
 
 * @param changed_object  thermal object that caused status change
699
 
 */
700
 
static void
701
 
thermal_manager_broadcast_status(THERMAL_STATUS status,
702
 
                                 thermal_object_t *changed_object)
703
 
{
704
 
    /* Skip broadcast if no change */
705
 
    if( current_status == status )
706
 
        goto EXIT;
707
 
 
708
 
    current_status = status;
709
 
 
710
 
    /* First send an indication to D-Bus */
711
 
    thermal_manager_broadcast_status_dbus(status);
712
 
 
713
 
    /* Then broadcast an indication internally */
714
 
 
715
 
    /* Note: The level gets attributed to the sensor that caused the
716
 
     *       change. Which is not wrong when elevating level, but can
717
 
     *       be completely bogus when returning to more normal state
718
 
     *       after having several sensors in elevated status.
719
 
     *
720
 
     *       Since consumers of this if should not care what is the
721
 
     *       sensor that is reported, leaving this the way it has
722
 
     *       always been...
723
 
     */
724
 
 
725
 
    int         temperature = thermal_object_get_temperature(changed_object);
726
 
    const char *sensor_name = thermal_object_get_name(changed_object);
727
 
 
728
 
    thermal_manager_broadcast_status_dsme(status, temperature, sensor_name);
729
 
 
730
 
EXIT:
731
 
    return;
732
 
}
733
 
 
734
 
/** Broadcast overall device thermal status within dsme
735
 
 *
736
 
 * Map sensor thermal status to device status levels used by
737
 
 * for example shutdown policy engine.
738
 
 *
739
 
 * Broadcast the changes via DSM_MSGTYPE_SET_THERMAL_STATUS event.
740
 
 *
741
 
 * @param status          device overall thermal status
742
 
 * @param temperature     temperature to report
743
 
 * @param sensor_name     sensor to report as cause of the change
744
 
 */
745
 
static void
746
 
thermal_manager_broadcast_status_dsme(THERMAL_STATUS status,
747
 
                                      int temperature,
748
 
                                      const char *sensor_name)
749
 
{
750
 
    static dsme_thermal_status_t prev = DSM_THERMAL_STATUS_NORMAL;
751
 
 
752
 
    /* Map sensor status to device status */
753
 
    dsme_thermal_status_t curr = DSM_THERMAL_STATUS_NORMAL;
754
 
 
755
 
    switch( status ) {
756
 
    case THERMAL_STATUS_LOW:
757
 
        curr = DSM_THERMAL_STATUS_LOWTEMP;
758
 
        break;
759
 
 
760
 
    default:
761
 
    case THERMAL_STATUS_INVALID:
762
 
    case THERMAL_STATUS_NORMAL:
763
 
    case THERMAL_STATUS_WARNING:
764
 
    case THERMAL_STATUS_ALERT:
765
 
        break;
766
 
 
767
 
    case THERMAL_STATUS_FATAL:
768
 
        curr = DSM_THERMAL_STATUS_OVERHEATED;
769
 
        break;
770
 
    }
771
 
 
772
 
    /* Skip broadcast if no change */
773
 
    if( prev == curr )
774
 
        goto EXIT;
775
 
 
776
 
    prev = curr;
777
 
 
778
 
    /* Log state change */
779
 
    switch( curr ) {
780
 
    case DSM_THERMAL_STATUS_LOWTEMP:
781
 
        dsme_log(LOG_WARNING, PFIX"policy: low temperature (%s %dC)",
782
 
                 sensor_name, temperature);
783
 
        break;
784
 
 
785
 
    default:
786
 
    case DSM_THERMAL_STATUS_NORMAL:
787
 
        dsme_log(LOG_NOTICE, PFIX"policy: acceptable temperature (%s %dC)",
788
 
                 sensor_name, temperature);
789
 
        break;
790
 
 
791
 
    case DSM_THERMAL_STATUS_OVERHEATED:
792
 
        dsme_log(LOG_CRIT, PFIX"policy: overheated (%s %dC)", sensor_name,
793
 
                 temperature);
794
 
        break;
795
 
    }
796
 
 
797
 
    /* Broadcast state change */
798
 
    DSM_MSGTYPE_SET_THERMAL_STATUS msg =
799
 
        DSME_MSG_INIT(DSM_MSGTYPE_SET_THERMAL_STATUS);
800
 
 
801
 
    msg.status      = curr;
802
 
    msg.temperature = temperature;
803
 
    strncat(msg.sensor_name, sensor_name, sizeof msg.sensor_name - 1);
804
 
 
805
 
    broadcast_internally(&msg);
806
 
 
807
 
EXIT:
808
 
    return;
809
 
}
810
 
 
811
 
/** Broadcast overall device thermal status via D-Bus
812
 
 *
813
 
 * Broadcast the changes as D-Bus signal
814
 
 *   com.nokia.thermalmanager.thermal_state_change_ind(state_name)
815
 
 *
816
 
 * @param status          device overall thermal status
817
 
 */
818
 
static void
819
 
thermal_manager_broadcast_status_dbus(THERMAL_STATUS status)
820
 
{
821
 
    static THERMAL_STATUS prev = THERMAL_STATUS_NORMAL;
822
 
 
823
 
    if( prev == status )
824
 
        goto EXIT;
825
 
 
826
 
    prev = status;
827
 
 
828
 
    const char *arg = thermal_status_name(status);
829
 
 
830
 
    dsme_log(LOG_NOTICE, PFIX"send dbus signal %s.%s(%s)",
831
 
             thermalmanager_interface,
832
 
             thermalmanager_state_change_ind, arg);
833
 
 
834
 
    DsmeDbusMessage *sig =
835
 
        dsme_dbus_signal_new(thermalmanager_path,
836
 
                             thermalmanager_interface,
837
 
                             thermalmanager_state_change_ind);
838
 
 
839
 
    dsme_dbus_message_append_string(sig, arg);
840
 
    dsme_dbus_signal_emit(sig);
841
 
 
842
 
EXIT:
843
 
    return;
844
 
}
845
 
 
846
 
/** Request sensor status update
847
 
 *
848
 
 * All thermal objects have name matching the one given are
849
 
 * updated.
850
 
 *
851
 
 * For example, if there are sensors "core0", "core1", "core2" and "core3"
852
 
 *
853
 
 * Requesting "core0" update does "core0" only.
854
 
 *
855
 
 * But requesting "core" update does all four.
856
 
 *
857
 
 * @param sensor_name  sensor name / sensor group name prefix
858
 
 */
859
 
bool
860
 
thermal_manager_request_sensor_update(const char *sensor_name)
861
 
{
862
 
    bool ack = false;
863
 
    for( GSList *item = thermal_objects; item; item = item->next ) {
864
 
        thermal_object_t *object = item->data;
865
 
        if( !thermal_object_has_name_like(object, sensor_name) )
866
 
            continue;
867
 
        thermal_object_request_update(object);
868
 
        ack = true;
869
 
        break;
870
 
    }
871
 
    return ack;
872
 
}
873
 
 
874
 
/** Check for pending sensor status request
875
 
 *
876
 
 * Check if any thermal objects with name matching the given on
877
 
 * are still waiting for status update.
878
 
 *
879
 
 * For example, if there are sensors "core0", "core1", "core2" and "core3"
880
 
 *
881
 
 * Requesting "core0" checks "core0" only.
882
 
 *
883
 
 * But requesting "core" checks all four.
884
 
 *
885
 
 * @param sensor_name  sensor name / sensor group name prefix
886
 
 */
887
 
static bool
888
 
thermal_manager_have_pending_sensor_update(const char *sensor_name)
889
 
{
890
 
    bool pending = false;
891
 
 
892
 
    for( GSList *item = thermal_objects; item; item = item->next ) {
893
 
        thermal_object_t *object = item->data;
894
 
 
895
 
        /* Must be waiting for status */
896
 
        if( !thermal_object_update_is_pending(object) )
897
 
            continue;
898
 
 
899
 
        /* And matching the name */
900
 
        if( !thermal_object_has_name_like(object, sensor_name) )
901
 
            continue;
902
 
 
903
 
        /* But self-dependencies must not be allowed */
904
 
        if( thermal_object_has_name(object, sensor_name) )
905
 
            continue;
906
 
 
907
 
        pending = true;
908
 
        break;
909
 
    }
910
 
 
911
 
    return pending;
912
 
}
913
 
 
914
 
/** Update sensors that depend on the given thermal object
915
 
 *
916
 
 * If there are meta sensors that depend on temperature
917
 
 * of the sensor that was updated, this function will
918
 
 * re-evaluate the depending sensors.
919
 
 *
920
 
 * @param changed_object  thermal object that just got updated
921
 
 */
922
 
void
923
 
thermal_manager_handle_sensor_update(const thermal_object_t *changed_object)
924
 
{
925
 
    const char *sensor_name = thermal_object_get_name(changed_object);
926
 
 
927
 
    for( GSList *item = thermal_objects; item; item = item->next ) {
928
 
        thermal_object_t *object = item->data;
929
 
 
930
 
        /* Must be waiting for status */
931
 
        if( !thermal_object_update_is_pending(object) )
932
 
            continue;
933
 
 
934
 
        /* and depending on the changed sensor */
935
 
        const char *depends_on = thermal_object_get_depends_on(object);
936
 
        if( !depends_on )
937
 
            continue;
938
 
 
939
 
        if( !thermal_object_has_name_like(changed_object, depends_on) )
940
 
            continue;
941
 
 
942
 
        /* but self-dependency must not be allowed */
943
 
        if( thermal_object_has_name(object, sensor_name) )
944
 
            continue;
945
 
 
946
 
        /* in case it is a group dependency, check all matching sensors */
947
 
        if( thermal_manager_have_pending_sensor_update(depends_on) )
948
 
            continue;
949
 
 
950
 
        /* Initiate sensor re-evaluation at backend. When finished,
951
 
         * a call to thermal_object_handle_update() is made.
952
 
         */
953
 
        if( !thermal_object_read_sensor(object) )
954
 
            thermal_object_cancel_update(object);
955
 
 
956
 
        // -> thermal_object_handle_update(object);
957
 
    }
958
 
}
959
 
 
960
 
/* ========================================================================= *
961
 
 * DBUS_HANDLERS
962
 
 * ========================================================================= */
963
 
 
964
 
/* Handle com.nokia.thermalmanager.get_thermal_state D-Bus method call
965
 
 *
966
 
 * @param req   D-Bus method call message
967
 
 * @param rsp   Where to store D-Bus method return message
968
 
 */
969
 
static void
970
 
thermal_manager_get_thermal_state_cb(const DsmeDbusMessage *req,
971
 
                                     DsmeDbusMessage **rsp)
972
 
{
973
 
    *rsp = dsme_dbus_reply_new(req);
974
 
    dsme_dbus_message_append_string(*rsp, thermal_status_name(current_status));
975
 
}
976
 
 
977
 
/** Helper for handling temperature query D-Bus method calls
978
 
 *
979
 
 * @param req          D-Bus method call message
980
 
 * @param sensor_name  Sensor name / sensor group name prefix
981
 
 * @param rsp          Where to store D-Bus method return message
982
 
 */
983
 
static void
984
 
thermal_manager_handle_temperature_query(const DsmeDbusMessage *req,
985
 
                                         const char *sensor_name,
986
 
                                         DsmeDbusMessage **rsp)
987
 
{
988
 
    THERMAL_STATUS    status      = THERMAL_STATUS_INVALID;
989
 
    int               temperature = INVALID_TEMPERATURE;
990
 
 
991
 
    thermal_manager_get_sensor_status(sensor_name, &status, &temperature);
992
 
    *rsp = dsme_dbus_reply_new(req);
993
 
    dsme_dbus_message_append_int(*rsp, temperature);
994
 
}
995
 
 
996
 
/* Handle com.nokia.thermalmanager.estimate_surface_temperature method call
997
 
 *
998
 
 * Provides backwards compatibility with legacy D-Bus method
999
 
 * that was originally implemented in Harmattan specific
1000
 
 * sensor backend plugin.
1001
 
 *
1002
 
 * @param req   D-Bus method call message
1003
 
 * @param rsp   Where to store D-Bus method return message
1004
 
 */
1005
 
static void
1006
 
thermal_manager_get_surface_temperature_cb(const DsmeDbusMessage *req,
1007
 
                                           DsmeDbusMessage **rsp)
1008
 
{
1009
 
    thermal_manager_handle_temperature_query(req, "surface", rsp);
1010
 
}
1011
 
 
1012
 
/* Handle com.nokia.thermalmanager.core_temperature D-Bus method call
1013
 
 *
1014
 
 * Provides backwards compatibility with legacy D-Bus method
1015
 
 * that was originally implemented in Harmattan specific
1016
 
 * sensor backend plugin.
1017
 
 *
1018
 
 * @param req   D-Bus method call message
1019
 
 * @param rsp   Where to store D-Bus method return message
1020
 
 */
1021
 
static void
1022
 
thermal_manager_get_core_temperature_cb(const DsmeDbusMessage *req,
1023
 
                                        DsmeDbusMessage **rsp)
1024
 
{
1025
 
    thermal_manager_handle_temperature_query(req, "core", rsp);
1026
 
}
1027
 
 
1028
 
/* Handle com.nokia.thermalmanager.battery_temperature D-Bus method call
1029
 
 *
1030
 
 * Provides backwards compatibility with legacy D-Bus method
1031
 
 * that was originally implemented in Harmattan specific
1032
 
 * sensor backend plugin.
1033
 
 *
1034
 
 * @param req   D-Bus method call message
1035
 
 * @param rsp   Where to store D-Bus method return message
1036
 
 */
1037
 
static void
1038
 
thermal_manager_get_battery_temperature_cb(const DsmeDbusMessage *req,
1039
 
                                           DsmeDbusMessage **rsp)
1040
 
{
1041
 
    thermal_manager_handle_temperature_query(req, "battery", rsp);
1042
 
}
1043
 
 
1044
 
/* Handle com.nokia.thermalmanager.sensor_temperature D-Bus method call
1045
 
 *
1046
 
 * Provides backwards compatibility with legacy D-Bus method
1047
 
 * that was originally implemented in Harmattan specific
1048
 
 * sensor backend plugin.
1049
 
 *
1050
 
 * @param req   D-Bus method call message
1051
 
 * @param rsp   Where to store D-Bus method return message
1052
 
 */
1053
 
static void
1054
 
thermal_manager_get_sensor_temperature_cb(const DsmeDbusMessage *req,
1055
 
                                          DsmeDbusMessage **rsp)
1056
 
{
1057
 
    const char *sensor = dsme_dbus_message_get_string(req);
1058
 
    thermal_manager_handle_temperature_query(req, sensor, rsp);
1059
 
}
1060
 
 
1061
 
/** Array of D-Bus method calls supported by this plugin */
1062
 
static const dsme_dbus_binding_t dbus_methods_lut[] =
1063
 
{
1064
 
    { thermal_manager_get_thermal_state_cb,       thermalmanager_get_thermal_state },
1065
 
    { thermal_manager_get_surface_temperature_cb, thermalmanager_estimate_surface_temperature },
1066
 
    { thermal_manager_get_core_temperature_cb,    thermalmanager_core_temperature  },
1067
 
    { thermal_manager_get_battery_temperature_cb, thermalmanager_battery_temperature },
1068
 
    { thermal_manager_get_sensor_temperature_cb,  thermalmanager_sensor_temperature },
1069
 
 
1070
 
    { 0, 0 }
1071
 
};
1072
 
 
1073
 
/* ========================================================================= *
1074
 
 * MESSAGE_HANDLERS
1075
 
 * ========================================================================= */
1076
 
 
1077
 
/** Handler for iphb wakeup events
1078
 
 */
1079
 
DSME_HANDLER(DSM_MSGTYPE_WAKEUP, client, msg)
1080
 
{
1081
 
    thermal_object_t *thermal_object = msg->data;
1082
 
 
1083
 
    if( !thermal_manager_object_is_registered(thermal_object) )
1084
 
        goto EXIT;
1085
 
 
1086
 
    thermal_manager_request_object_update(thermal_object);
1087
 
 
1088
 
EXIT:
1089
 
    return;
1090
 
}
1091
 
 
1092
 
/** Handler for connected to D-Bus system bus event
1093
 
 */
1094
 
DSME_HANDLER(DSM_MSGTYPE_DBUS_CONNECT, client, msg)
1095
 
{
1096
 
    dsme_log(LOG_DEBUG, PFIX"DBUS_CONNECT");
1097
 
 
1098
 
    /* Add dbus method call handlers */
1099
 
    dsme_dbus_bind_methods(&dbus_methods_bound, dbus_methods_lut,
1100
 
                           thermalmanager_service, thermalmanager_interface);
1101
 
}
1102
 
 
1103
 
/** Handler for disconnected to D-Bus system bus event
1104
 
 */
1105
 
DSME_HANDLER(DSM_MSGTYPE_DBUS_DISCONNECT, client, msg)
1106
 
{
1107
 
    dsme_log(LOG_DEBUG, PFIX"DBUS_DISCONNECT");
1108
 
 
1109
 
    /* Remove dbus method call handlers */
1110
 
    dsme_dbus_unbind_methods(&dbus_methods_bound, dbus_methods_lut,
1111
 
                             thermalmanager_service, thermalmanager_interface);
1112
 
}
1113
 
 
1114
 
/** Array of DSME event handlers implemented by this plugin */
1115
 
module_fn_info_t message_handlers[] =
1116
 
{
1117
 
    DSME_HANDLER_BINDING(DSM_MSGTYPE_WAKEUP),
1118
 
    DSME_HANDLER_BINDING(DSM_MSGTYPE_DBUS_CONNECT),
1119
 
    DSME_HANDLER_BINDING(DSM_MSGTYPE_DBUS_DISCONNECT),
1120
 
    { 0 }
1121
 
};
1122
 
 
1123
 
/* ========================================================================= *
1124
 
 * MODULE_GLUE
1125
 
 * ========================================================================= */
1126
 
 
1127
 
/** Init hook that DSME plugins need to implement
1128
 
 *
1129
 
 * @param handle DSME plugin handle
1130
 
 */
1131
 
void
1132
 
module_init(module_t *handle)
1133
 
{
1134
 
    dsme_log(LOG_DEBUG, PFIX"loaded");
1135
 
 
1136
 
    this_module = handle;
1137
 
}
1138
 
 
1139
 
/** Exit hook that DSME plugins need to implement
1140
 
 */
1141
 
void
1142
 
module_fini(void)
1143
 
{
1144
 
    /* Clear remaining thermal objects from the registered list */
1145
 
    if( thermal_objects ) {
1146
 
        dsme_log(LOG_ERR, PFIX"registered thermal objects remain "
1147
 
                 "at unload time");
1148
 
 
1149
 
        do
1150
 
            thermal_manager_unregister_object(thermal_objects->data);
1151
 
        while( thermal_objects );
1152
 
    }
1153
 
 
1154
 
    /* Remove dbus method call handlers */
1155
 
    dsme_dbus_unbind_methods(&dbus_methods_bound, dbus_methods_lut,
1156
 
                             thermalmanager_service, thermalmanager_interface);
1157
 
 
1158
 
    dsme_log(LOG_DEBUG, PFIX"unloaded");
1159
 
}