/
batterytracker.c
1985 lines (1559 loc) · 56.7 KB
/
batterytracker.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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
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
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
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
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
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
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
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
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
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
@file batterytracker.c
Track the battery charge level. If charge level goes too low,
issue warnings. If battery level goes below safe limit, make shutdown
<p>
Copyright (C) 2013-2019 Jolla Oy.
@author Pekka Lundstrom <pekka.lundstrom@jolla.com>
@author Simo Piiroinen <simo.piiroinen@jollamobile.com>
This file is part of Dsme.
Dsme 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.
Dsme 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 Dsme. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dbusproxy.h"
#include "dsme_dbus.h"
#include "../include/dsme/modulebase.h"
#include "../include/dsme/timers.h"
#include "../include/dsme/modules.h"
#include "../include/dsme/logging.h"
#include "../dsme/utility.h"
#include <dsme/state.h>
#include <dsme/protocol.h>
#include <iphbd/iphb_internal.h>
#include <mce/dbus-names.h>
#include <mce/mode-names.h>
#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <errno.h>
#include <string.h>
/* ========================================================================= *
* CONSTANTS
* ========================================================================= */
/** Logging prefix for this module */
#define PFIX "batterytracker: "
/** Path to optional battery level configuration file */
#define BATTERY_LEVEL_CONFIG_FILE "/etc/dsme/battery_levels.conf"
/** Timer value for alarm shutdown timer.
*
* This is how long we wait before reporting battery empty when handling
* alarms in act-dead mode - i.e. if user does not react to alarm, the
* device will power off despite having an alarm on screen.
*/
#define ALARM_DELAYED_TIMEOUT 60
#define BATTERY_LEVEL_CRITICAL 1
/* ========================================================================= *
* TYPES
* ========================================================================= */
/** String name <--> integer value mapping
*/
typedef struct
{
const char *key;
int val;
} symbol_t;
/** Possible USB cable states
*/
typedef enum
{
DSME_USB_CABLE_STATE_UNKNOWN,
DSME_USB_CABLE_STATE_CONNECTED,
DSME_USB_CABLE_STATE_DISCONNECTED,
} dsme_usb_cable_state_t;
/** Possible charger states
*/
typedef enum
{
DSME_CHARGER_STATE_UNKNOWN,
DSME_CHARGER_STATE_ON,
DSME_CHARGER_STATE_OFF,
} dsme_charger_state_t;
/** Possible battery status values
*/
typedef enum
{
DSME_BATTERY_STATUS_UNKNOWN,
DSME_BATTERY_STATUS_FULL,
DSME_BATTERY_STATUS_OK,
DSME_BATTERY_STATUS_LOW,
DSME_BATTERY_STATUS_EMPTY,
} dsme_battery_status_t;
/** DSME battery configuration levels
*/
typedef enum
{
DSME_BATTERY_CONFIG_FULL,
DSME_BATTERY_CONFIG_NORMAL,
DSME_BATTERY_CONFIG_LOW,
DSME_BATTERY_CONFIG_WARNING,
DSME_BATTERY_CONFIG_EMPTY,
DSME_BATTERY_CONFIG_COUNT
} battery_status_t;
/** DSME battery configuration level data
*/
typedef struct config_level_t
{
int min_level; /* percentance */
int polling_time; /* Polling time in sec */
bool wakeup; /* Resume from suspend to check battery level */
} config_level_t;
/* ========================================================================= *
* PROTOS
* ========================================================================= */
/* ------------------------------------------------------------------------- *
* Misc utils
* ------------------------------------------------------------------------- */
static const char *bool_repr(bool val);
/* ------------------------------------------------------------------------- *
* symbol
* ------------------------------------------------------------------------- */
static int symbol_lookup_val(const symbol_t *lut, const char *key, int def);
static const char *symbol_lookup_key(const symbol_t *lut, int val, const char *def);
/* ------------------------------------------------------------------------- *
* init_done
* ------------------------------------------------------------------------- */
static void init_done_set (bool reached);
static void init_done_probe (void);
static void init_done_signal_cb(const DsmeDbusMessage* ind);
/* ------------------------------------------------------------------------- *
* dsme_usb_cable_state
* ------------------------------------------------------------------------- */
static const char *dsme_usb_cable_state_repr (dsme_usb_cable_state_t state);
static dsme_usb_cable_state_t dsme_usb_cable_state_parse(const char *name);
static void dsme_usb_cable_state_set (dsme_usb_cable_state_t state);
/* ------------------------------------------------------------------------- *
* dsme_charger_state
* ------------------------------------------------------------------------- */
static const char *dsme_charger_state_repr (dsme_charger_state_t state);
static dsme_charger_state_t dsme_charger_state_parse (const char *name);
static void dsme_charger_state_set (dsme_charger_state_t state);
/* ------------------------------------------------------------------------- *
* dsme_battery_status
* ------------------------------------------------------------------------- */
static const char *dsme_battery_status_repr (dsme_battery_status_t state);
static dsme_battery_status_t dsme_battery_status_parse (const char *name);
static void dsme_battery_status_set (dsme_battery_status_t status);
/* ------------------------------------------------------------------------- *
* dsme_battery_level
* ------------------------------------------------------------------------- */
static const char *dsme_battery_level_repr (dsme_battery_level_t level);
static void dsme_battery_level_set (dsme_battery_level_t level);
/* ------------------------------------------------------------------------- *
* dsme_state
* ------------------------------------------------------------------------- */
static void dsme_state_set (dsme_state_t state);
/* ------------------------------------------------------------------------- *
* alarm_active
* ------------------------------------------------------------------------- */
static void alarm_active_set (bool active);
/* ------------------------------------------------------------------------- *
* alarm_holdon
* ------------------------------------------------------------------------- */
static int alarm_holdon_cb (void *unused);
static void alarm_holdon_start (void);
static void alarm_holdon_cancel(void);
/* ------------------------------------------------------------------------- *
* config
* ------------------------------------------------------------------------- */
static void config_load(void);
/* ------------------------------------------------------------------------- *
* condition
* ------------------------------------------------------------------------- */
static bool condition_battery_is_empty (void);
static bool condition_charging_is_on (void);
static bool condition_alarm_is_active (void);
static bool condition_level_is_critical (void);
/* ------------------------------------------------------------------------- *
* battery_empty_rethink
* ------------------------------------------------------------------------- */
static int battery_empty_rethink_cb (void *aptr);
static void battery_empty_cancel_rethink (void);
static void battery_empty_schedule_rethink(void);
/* ------------------------------------------------------------------------- *
* xmce_running
* ------------------------------------------------------------------------- */
static void xmce_running_set (bool running);
/* ------------------------------------------------------------------------- *
* xmce_tracking
* ------------------------------------------------------------------------- */
static void xmce_tracking_init (void);
static void xmce_tracking_quit (void);
/* ------------------------------------------------------------------------- *
* xmce_name_owner_query
* ------------------------------------------------------------------------- */
static DBusHandlerResult xmce_name_owner_filter_cb (DBusConnection *con, DBusMessage *msg, void *aptr);
static void xmce_name_owner_reply_cb (DBusPendingCall *pc, void *aptr);
static void xmce_forget_mce_name_owner_query (void);
static void xmce_send_name_owner_query (void);
/* ------------------------------------------------------------------------- *
* xmce_usb_cable_state_query
* ------------------------------------------------------------------------- */
static void xmce_usb_cable_state_signal_cb (const DsmeDbusMessage *ind);
static void xmce_usb_cable_state_reply_cb (DBusPendingCall *pc, void *aptr);
static void xmce_forget_usb_cable_state_query(void);
static void xmce_send_usb_cable_state_query (void);
/* ------------------------------------------------------------------------- *
* xmce_charger_state_query
* ------------------------------------------------------------------------- */
static void xmce_charger_state_signal_cb (const DsmeDbusMessage *ind);
static void xmce_charger_state_reply_cb (DBusPendingCall *pc, void *aptr);
static void xmce_forget_charger_state_query (void);
static void xmce_send_charger_state_query (void);
/* ------------------------------------------------------------------------- *
* xmce_battery_status_query
* ------------------------------------------------------------------------- */
static void xmce_battery_status_signal_cb (const DsmeDbusMessage *ind);
static void xmce_battery_status_reply_cb (DBusPendingCall *pc, void *aptr);
static void xmce_forget_battery_status_query (void);
static void xmce_send_battery_status_query (void);
/* ------------------------------------------------------------------------- *
* xmce_battery_level_query
* ------------------------------------------------------------------------- */
static void xmce_battery_level_signal_cb (const DsmeDbusMessage *ind);
static void xmce_battery_level_reply_cb (DBusPendingCall *pc, void *aptr);
static void xmce_forget_battery_level_query (void);
static void xmce_send_battery_level_query (void);
/* ------------------------------------------------------------------------- *
* systembus
* ------------------------------------------------------------------------- */
static void systembus_connect (void);
static void systembus_disconnect(void);
/* ------------------------------------------------------------------------- *
* send
* ------------------------------------------------------------------------- */
static void send_charger_state (bool charging);
static void send_battery_state (bool empty);
static void send_battery_level (dsme_battery_level_t level);
static void send_dsme_state_query (void);
/* ------------------------------------------------------------------------- *
* module
* ------------------------------------------------------------------------- */
void module_init(module_t *handle);
void module_fini(void);
/* ========================================================================= *
* State Data
* ========================================================================= */
/** Cached module handle for this plugin */
static const module_t *this_module = 0;
/** Cached SystemBus connection */
static DBusConnection *systembus = 0;
/* ========================================================================= *
* Misc Utils
* ========================================================================= */
static const char *
bool_repr(bool val)
{
return val ? "true" : "false";
}
/* ========================================================================= *
* symbol
* ========================================================================= */
static int
symbol_lookup_val(const symbol_t *lut, const char *key, int def)
{
int val = def;
for( ; lut->key; ++lut ) {
if( !strcmp(lut->key, key) ) {
val = lut->val;
break;
}
}
return val;
}
static const char *
symbol_lookup_key(const symbol_t *lut, int val, const char *def)
{
const char *key = def;
for( ; lut->key; ++lut ) {
if( lut->val == val ) {
key = lut->key;
break;
}
}
return key;
}
/* ========================================================================= *
* init_done
* ========================================================================= */
/** Flag for: init_done has been reached i.e. bootup has finished */
static bool init_done = false;
static void
init_done_set(bool reached)
{
if( init_done == reached )
goto EXIT;
dsme_log(LOG_INFO, PFIX"init_done: %s -> %s",
bool_repr(init_done),
bool_repr(reached));
init_done = reached;
battery_empty_schedule_rethink();
EXIT:
return;
}
static void
init_done_probe(void)
{
/* If dsme was restarted after bootup is finished, init-done signal
* is not going to come again -> check flag file for initial state */
if( access("/run/systemd/boot-status/init-done", F_OK) == 0 )
init_done_set(true);
}
/** Callback for handling incoming init_done signals
*/
static void
init_done_signal_cb(const DsmeDbusMessage* ind)
{
init_done_set(true);
}
/* ========================================================================= *
* dsme_usb_cable_state
* ========================================================================= */
/** Lookup table for converting usb cable state names <--> enumeration values
*/
static const symbol_t dsme_usb_cable_state_lut[] =
{
{ MCE_USB_CABLE_STATE_UNKNOWN, DSME_USB_CABLE_STATE_UNKNOWN },
{ MCE_USB_CABLE_STATE_CONNECTED, DSME_USB_CABLE_STATE_CONNECTED },
{ MCE_USB_CABLE_STATE_DISCONNECTED, DSME_USB_CABLE_STATE_DISCONNECTED },
{ 0, }
};
static const char *
dsme_usb_cable_state_repr(dsme_usb_cable_state_t state)
{
return symbol_lookup_key(dsme_usb_cable_state_lut, state, "invalid");
}
static dsme_usb_cable_state_t
dsme_usb_cable_state_parse(const char *name)
{
return symbol_lookup_val(dsme_usb_cable_state_lut, name,
DSME_USB_CABLE_STATE_UNKNOWN);
}
static dsme_usb_cable_state_t dsme_usb_cable_state = DSME_USB_CABLE_STATE_UNKNOWN;
static void dsme_usb_cable_state_set(dsme_usb_cable_state_t state)
{
if( dsme_usb_cable_state == state )
goto EXIT;
dsme_log(LOG_INFO, PFIX"dsme_usb_cable_state: %s -> %s",
dsme_usb_cable_state_repr(dsme_usb_cable_state),
dsme_usb_cable_state_repr(state));
dsme_usb_cable_state = state;
battery_empty_schedule_rethink();
EXIT:
return;
}
/* ========================================================================= *
* dsme_charger_state
* ========================================================================= */
/** Lookup table for converting charger state names <--> enumeration values
*/
static const symbol_t dsme_charger_state_lut[] =
{
{ MCE_CHARGER_STATE_UNKNOWN, DSME_CHARGER_STATE_UNKNOWN },
{ MCE_CHARGER_STATE_ON, DSME_CHARGER_STATE_ON },
{ MCE_CHARGER_STATE_OFF, DSME_CHARGER_STATE_OFF },
{ 0, }
};
static const char *
dsme_charger_state_repr(dsme_charger_state_t state)
{
return symbol_lookup_key(dsme_charger_state_lut, state, "invalid");
}
static dsme_charger_state_t
dsme_charger_state_parse(const char *name)
{
return symbol_lookup_val(dsme_charger_state_lut, name,
DSME_CHARGER_STATE_UNKNOWN);
}
static dsme_charger_state_t dsme_charger_state = DSME_CHARGER_STATE_UNKNOWN;
static void dsme_charger_state_set(dsme_charger_state_t state)
{
if( dsme_charger_state == state )
goto EXIT;
dsme_log(LOG_INFO, PFIX"dsme_charger_state: %s -> %s",
dsme_charger_state_repr(dsme_charger_state),
dsme_charger_state_repr(state));
dsme_charger_state = state;
battery_empty_schedule_rethink();
if( dsme_charger_state != DSME_CHARGER_STATE_UNKNOWN ) {
bool charging = (dsme_charger_state == DSME_CHARGER_STATE_ON);
send_charger_state(charging);
}
EXIT:
return;
}
/* ========================================================================= *
* dsme_battery_status
* ========================================================================= */
/** Lookup table for converting battery status names <--> enumeration values
*/
static const symbol_t dsme_battery_status_lut[] =
{
{ MCE_BATTERY_STATUS_UNKNOWN, DSME_BATTERY_STATUS_UNKNOWN },
{ MCE_BATTERY_STATUS_FULL, DSME_BATTERY_STATUS_FULL },
{ MCE_BATTERY_STATUS_OK, DSME_BATTERY_STATUS_OK },
{ MCE_BATTERY_STATUS_LOW, DSME_BATTERY_STATUS_LOW },
{ MCE_BATTERY_STATUS_EMPTY, DSME_BATTERY_STATUS_EMPTY },
{ 0, }
};
static const char *
dsme_battery_status_repr(dsme_battery_status_t state)
{
return symbol_lookup_key(dsme_battery_status_lut, state, "invalid");
}
static dsme_battery_status_t
dsme_battery_status_parse(const char *name)
{
return symbol_lookup_val(dsme_battery_status_lut, name,
DSME_BATTERY_STATUS_UNKNOWN);
}
static dsme_battery_status_t dsme_battery_status = DSME_BATTERY_STATUS_UNKNOWN;
static void dsme_battery_status_set(dsme_battery_status_t status)
{
if( dsme_battery_status == status )
goto EXIT;
dsme_log(LOG_INFO, PFIX"dsme_battery_status: %s -> %s",
dsme_battery_status_repr(dsme_battery_status),
dsme_battery_status_repr(status));
dsme_battery_status = status;
battery_empty_schedule_rethink();
EXIT:
return;
}
/* ========================================================================= *
* dsme_battery_level
* ========================================================================= */
static const char *
dsme_battery_level_repr(dsme_battery_level_t level)
{
if( level == DSME_BATTERY_LEVEL_UNKNOWN )
return "unknown";
if( level < DSME_BATTERY_LEVEL_MINIMUM ||
level > DSME_BATTERY_LEVEL_MAXIMUM )
return "invalid";
/* Assume: two statically allocated buffers are enough to
* deal with "change from A to B" type diagnostic
* logging.
*/
static char buf[2][8];
static int n = 0;
int i = ++n & 1;
snprintf(buf[i], sizeof *buf, "%d%%", (int)level);
return buf[i];
}
static dsme_battery_level_t dsme_battery_level = DSME_BATTERY_LEVEL_UNKNOWN;
static void dsme_battery_level_set(dsme_battery_level_t level)
{
if( dsme_battery_level == level )
goto EXIT;
dsme_log(LOG_INFO, PFIX"dsme_battery_level: %s -> %s",
dsme_battery_level_repr(dsme_battery_level),
dsme_battery_level_repr(level));
dsme_battery_level = level;
battery_empty_schedule_rethink();
if( dsme_battery_level != DSME_BATTERY_LEVEL_UNKNOWN ) {
send_battery_level(dsme_battery_level);
}
EXIT:
return;
}
/* ========================================================================= *
* dsme_state
* ========================================================================= */
static dsme_state_t dsme_state = DSME_STATE_NOT_SET;
static void
dsme_state_set(dsme_state_t state)
{
if( dsme_state == state )
goto EXIT;
dsme_log(LOG_INFO, PFIX"dsme_state: %s -> %s",
dsme_state_repr(dsme_state),
dsme_state_repr(state));
dsme_state = state;
battery_empty_schedule_rethink();
EXIT:
return;
}
/* ========================================================================= *
* alarm_active
* ========================================================================= */
static bool alarm_active = false;
static void
alarm_active_set(bool active)
{
if( alarm_active == active )
goto EXIT;
dsme_log(LOG_INFO, PFIX"alarm_active: %s -> %s",
bool_repr(alarm_active),
bool_repr(active));
alarm_active = active;
if( alarm_active )
alarm_holdon_start();
else
alarm_holdon_cancel();
battery_empty_schedule_rethink();
EXIT:
return;
}
/* ========================================================================= *
* alarm_holdon
* ========================================================================= */
static dsme_timer_t alarm_holdon_id = 0;
static int alarm_holdon_cb(void* unused)
{
dsme_log(LOG_INFO, PFIX"Alarm hold on time is over");
alarm_holdon_id = 0;
/* Simulate end-of-alarm */
alarm_active_set(false);
return 0; /* stop the interval */
}
static void
alarm_holdon_start(void)
{
if( !alarm_holdon_id ) {
dsme_log(LOG_INFO, PFIX"Alarm hold on time started");
alarm_holdon_id =
dsme_create_timer_seconds(ALARM_DELAYED_TIMEOUT,
alarm_holdon_cb, NULL);
}
}
static void
alarm_holdon_cancel(void)
{
if( alarm_holdon_id ) {
dsme_log(LOG_INFO, PFIX"Alarm hold on time canceled");
dsme_destroy_timer(alarm_holdon_id),
alarm_holdon_id = 0;
}
}
/* ========================================================================= *
* config
* ========================================================================= */
/** Lookup table for battery configuration level names
*/
static const char * const config_level_name[DSME_BATTERY_CONFIG_COUNT] =
{
[DSME_BATTERY_CONFIG_FULL] = "FULL",
[DSME_BATTERY_CONFIG_NORMAL] = "NORMAL",
[DSME_BATTERY_CONFIG_LOW] = "LOW",
[DSME_BATTERY_CONFIG_WARNING] = "WARNING",
[DSME_BATTERY_CONFIG_EMPTY] = "EMPTY",
};
/** This is default config for battery levels
*
* It can be overridden by external file BATTERY_LEVEL_CONFIG_FILE.
*/
static config_level_t config_level_data[DSME_BATTERY_CONFIG_COUNT] =
{
/* Min %, polling time */
{ 80, 300, false }, /* Full 80 - 100, polling 5 mins */
{ 20, 180, false }, /* Normal 20 - 79 */
{ 10, 120, true }, /* Low 10 - 19 */
{ 3, 60, true }, /* Warning 3 - 9, shutdown happens below this */
{ 0, 60, true } /* Empty 0 - 2, shutdown should have happened already */
};
/** Load battery level configuration file
*
* If external config file exists, then read it and use
* values defined there for battery level and polling times.
*
* @note: Since the battery data is no longer polled, the
* only relevant data in the config is the defined
* battery empty shutdown limit.
*/
static void config_load(void)
{
FILE *input = 0;
bool success = false;
config_level_t temp[DSME_BATTERY_CONFIG_COUNT] = {};
if( !(input = fopen(BATTERY_LEVEL_CONFIG_FILE, "r")) ) {
if( errno != ENOENT )
dsme_log(LOG_ERR, PFIX"%s: can't read config: %m",
BATTERY_LEVEL_CONFIG_FILE);
goto EXIT;
}
for( size_t i = 0; i < DSME_BATTERY_CONFIG_COUNT; ++i ) {
int wakeup = 0;
int values = fscanf(input, "%d, %d, %d",
&temp[i].min_level,
&temp[i].polling_time,
&wakeup);
/* Must define at least "min_level" and "polling time".
*/
if( values < 2 ) {
dsme_log(LOG_ERR, PFIX"%s:%zd: %s: not enough data",
BATTERY_LEVEL_CONFIG_FILE, i+1,
config_level_name[i]);
goto EXIT;
}
/* The "wakeup" is optional, default is to wakeup from
* suspend to check battery level when on LOW/WARNING/EMPTY.
*/
if( values < 3 )
temp[i].wakeup = (i >= DSME_BATTERY_CONFIG_LOW);
else
temp[i].wakeup = (wakeup != 0);
/* Do some sanity checking for values
*
* Battery level values must be in 0-100 range, and in descending order.
*
* Polling times should also make sense 10-1000s
*/
if( temp[i].polling_time < 10 || temp[i].polling_time > 1000 ) {
dsme_log(LOG_ERR, PFIX"%s:%zd: %s: invalid polling_time=%d",
BATTERY_LEVEL_CONFIG_FILE, i+1,
config_level_name[i],
temp[i].polling_time);
goto EXIT;
}
if( temp[i].min_level < 0 || temp[i].min_level > 100 ) {
dsme_log(LOG_ERR, PFIX"%s:%zd: %s: invalid min_level=%d",
BATTERY_LEVEL_CONFIG_FILE, i+1,
config_level_name[i],
temp[i].min_level);
goto EXIT;
}
if( (i > 0) && temp[i-1].min_level <= temp[i].min_level ) {
dsme_log(LOG_ERR, PFIX"%s:%zd: %s: min_level=%d is not descending",
BATTERY_LEVEL_CONFIG_FILE, i+1,
config_level_name[i],
temp[i].min_level);
goto EXIT;
}
}
success = true;
EXIT:
if( input )
fclose(input);
if( success ) {
memcpy(config_level_data, temp, sizeof config_level_data);
dsme_log(LOG_INFO, PFIX"Using battery level values from %s",
BATTERY_LEVEL_CONFIG_FILE);
}
else {
dsme_log(LOG_DEBUG, PFIX"Using internal battery level values");
}
dsme_log(LOG_DEBUG, PFIX"Shutdown limit is < %d%%",
config_level_data[DSME_BATTERY_CONFIG_WARNING].min_level);
}
/* ========================================================================= *
* condition
* ========================================================================= */
/** Predicate for: Battery level is known to be empty
*/
static bool condition_battery_is_empty(void)
{
bool empty = true;
int limit = config_level_data[DSME_BATTERY_CONFIG_WARNING].min_level;
if( dsme_battery_level == DSME_BATTERY_LEVEL_UNKNOWN ) {
/* Do not equate unknown with empty */
empty = false;
}
else if( dsme_battery_level < DSME_BATTERY_LEVEL_MINIMUM ||
dsme_battery_level > DSME_BATTERY_LEVEL_MAXIMUM ) {
/* Do not equate invalid with empty */
empty = false;
}
else if( dsme_battery_level >= limit ) {
/* Known to be above the limit */
empty = false;
}
return empty;
}
/** Predicate for: Battery is getting charged
*/
static bool condition_charging_is_on(void)
{
return dsme_charger_state == DSME_CHARGER_STATE_ON;
}
/** Predicate for: UI is handling active alarm
*/
static bool condition_alarm_is_active(void)
{
return alarm_active;
}
/** Predicate for: Battery level is dropping despite charging
*/
static bool condition_level_is_critical(void)
{
return (dsme_state != DSME_STATE_ACTDEAD &&
dsme_battery_level <= BATTERY_LEVEL_CRITICAL);
}
/* ========================================================================= *
* battery_empty_rethink
* ========================================================================= */
/** Idle timer for: Evaluate battery empty shutdown condition */
static dsme_timer_t battery_empty_rethink_id = 0;
/** Timer callback for: Evaluate battery empty shutdown condition */
static int battery_empty_rethink_cb(void *aptr)
{
(void)aptr;
battery_empty_rethink_id = 0;
static bool shutdown_requested = false;
bool request_shutdown = false;
if( condition_battery_is_empty() ) {
request_shutdown = true;
/* Do not shutdown while handling active alarm
*/
if( request_shutdown && condition_alarm_is_active() ) {
request_shutdown = false;
dsme_log(LOG_DEBUG, PFIX"Active alarm - do not shutdown");
}
/* Before starting shutdown, check charging. If charging is
* going, give battery change to charge. But if battery level
* keeps dropping even charger is connected (could be that we get only 100 mA)
* we need to do shutdown. But let charging continue in actdead state
*/
if( request_shutdown && condition_charging_is_on() ) {
request_shutdown = false;
dsme_log(LOG_DEBUG, PFIX"Charging - do not shutdown");
}
/* If charging in USER state, make sure level won't drop too much, keep min 1% */
if( !request_shutdown && condition_level_is_critical() ) {
request_shutdown = true;
dsme_log(LOG_INFO, PFIX"Battery level keeps dropping - must shutdown");
}
/* In any case we must not change direction in the middle
* of bootup - say, when user has pressed power key and
* is ready to connect charger on first signs of life in
* attempt to bypass act dead mode */
if( request_shutdown && !init_done ) {
request_shutdown = false;
dsme_log(LOG_DEBUG, PFIX"Mid boot up - must not shutdown");
}
}
if( shutdown_requested != request_shutdown ) {
dsme_log(LOG_CRIT, PFIX"Battery empty shutdown %s",
request_shutdown ? "requested" : "canceled");
shutdown_requested = request_shutdown;
send_battery_state(shutdown_requested);
}
return 0; /* stop the interval */
}
/** Cancel already scheduled battery empty shutdown evaluation
*/
static void
battery_empty_cancel_rethink(void)
{
if( battery_empty_rethink_id ) {
dsme_destroy_timer(battery_empty_rethink_id),
battery_empty_rethink_id = 0;
}
}
/** Schedule evaluation of battery empty shutdown condition
*/
static void
battery_empty_schedule_rethink(void)
{
if( !battery_empty_rethink_id ) {
battery_empty_rethink_id =
dsme_create_timer_seconds(0, battery_empty_rethink_cb, 0);
}
}
/* ========================================================================= *
* xmce_running
* ========================================================================= */
/** Flag for: MCE D-Bus service is available on SystemBus */
static bool xmce_running = false;
/** Change availability of MCE on SystemBus status
*
* @param running whether MCE_SERVICE has an owner or not
*/
static void
xmce_running_set(bool running)
{
dsme_log(LOG_DEBUG, PFIX"xmce_running=%d running=%d",
xmce_running, running);
if( xmce_running == running )
goto cleanup;
xmce_running = running;
dsme_log(LOG_DEBUG, PFIX"mce is %s", xmce_running ? "running" : "stopped");
if( xmce_running ) {
xmce_send_usb_cable_state_query();
xmce_send_charger_state_query();
xmce_send_battery_status_query();
xmce_send_battery_level_query();
}
else {
xmce_forget_usb_cable_state_query();
xmce_forget_charger_state_query();
xmce_forget_battery_status_query();
xmce_forget_battery_level_query();
}
cleanup:
return;
}
/* ========================================================================= *
* xmce_tracking
* ========================================================================= */