lcd-bitmap-common.c: Change calculation of the horizontal position in lcd_puts_style_...
[kugel-rb.git] / firmware / powermgmt.c
blob041fed036145f32761fbf9a6496cc503cf53e057
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
11 * Revisions copyright (C) 2005 by Gerald Van Baren
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "config.h"
23 #include "system.h"
24 #include "kernel.h"
25 #include "thread.h"
26 #include "debug.h"
27 #include "adc.h"
28 #include "string.h"
29 #include "storage.h"
30 #include "power.h"
31 #include "audio.h"
32 #include "mp3_playback.h"
33 #include "usb.h"
34 #include "powermgmt.h"
35 #include "backlight.h"
36 #include "lcd.h"
37 #include "rtc.h"
38 #if CONFIG_TUNER
39 #include "fmradio.h"
40 #endif
41 #include "sound.h"
42 #ifdef HAVE_LCD_BITMAP
43 #include "font.h"
44 #endif
45 #include "logf.h"
46 #include "lcd-remote.h"
47 #ifdef SIMULATOR
48 #include <time.h>
49 #endif
51 #if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR)
52 #include "lcd-remote-target.h"
53 #endif
54 #if (defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(COWON_D2)) \
55 && !defined (SIMULATOR)
56 #include "pcf50606.h"
57 #endif
59 /** Shared by sim **/
60 int last_sent_battery_level = 100;
61 /* battery level (0-100%) */
62 int battery_percent = -1;
63 void send_battery_level_event(void);
65 #if CONFIG_CHARGING
66 /* State of the charger input as seen by the power thread */
67 enum charger_input_state_type charger_input_state;
68 /* Power inputs as seen by the power thread */
69 unsigned int power_thread_inputs;
70 #if CONFIG_CHARGING >= CHARGING_MONITOR
71 /* Charging state (mode) as seen by the power thread */
72 enum charge_state_type charge_state = DISCHARGING;
73 #endif
74 #endif /* CONFIG_CHARGING */
76 #ifndef SIMULATOR
77 static int shutdown_timeout = 0;
79 * Average battery voltage and charger voltage, filtered via a digital
80 * exponential filter (aka. exponential moving average, scaled):
81 * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N.
83 static unsigned int avgbat;
84 /* filtered battery voltage, millivolts */
85 static unsigned int battery_millivolts;
86 /* default value, mAh */
87 static int battery_capacity = BATTERY_CAPACITY_DEFAULT;
90 #if BATTERY_TYPES_COUNT > 1
91 static int battery_type = 0;
92 #else
93 #define battery_type 0
94 #endif
96 /* Power history: power_history[0] is the newest sample */
97 unsigned short power_history[POWER_HISTORY_LEN];
99 #if CONFIG_CPU == JZ4732 /* FIXME! */
100 static char power_stack[DEFAULT_STACK_SIZE + POWERMGMT_DEBUG_STACK];
101 #else
102 static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK];
103 #endif
104 static const char power_thread_name[] = "power";
106 static int poweroff_timeout = 0;
107 static int powermgmt_est_runningtime_min = -1;
109 static bool sleeptimer_active = false;
110 static long sleeptimer_endtick;
112 static long last_event_tick;
114 static int voltage_to_battery_level(int battery_millivolts);
115 static void battery_status_update(void);
116 static int runcurrent(void);
118 void battery_read_info(int *voltage, int *level)
120 int millivolts = battery_adc_voltage();
122 if (voltage)
123 *voltage = millivolts;
125 if (level)
126 *level = voltage_to_battery_level(millivolts);
129 void reset_poweroff_timer(void)
131 last_event_tick = current_tick;
134 #if BATTERY_TYPES_COUNT > 1
135 void set_battery_type(int type)
137 if (type != battery_type) {
138 if ((unsigned)type >= BATTERY_TYPES_COUNT)
139 type = 0;
141 battery_type = type;
142 battery_status_update(); /* recalculate the battery status */
145 #endif
147 void set_battery_capacity(int capacity)
149 if (capacity > BATTERY_CAPACITY_MAX)
150 capacity = BATTERY_CAPACITY_MAX;
151 if (capacity < BATTERY_CAPACITY_MIN)
152 capacity = BATTERY_CAPACITY_MIN;
154 battery_capacity = capacity;
156 battery_status_update(); /* recalculate the battery status */
159 int get_battery_capacity(void)
161 return battery_capacity;
164 int battery_time(void)
166 return powermgmt_est_runningtime_min;
169 /* Returns battery level in percent */
170 int battery_level(void)
172 #ifdef HAVE_BATTERY_SWITCH
173 if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
174 return -1;
175 #endif
176 return battery_percent;
179 /* Returns filtered battery voltage [millivolts] */
180 unsigned int battery_voltage(void)
182 return battery_millivolts;
185 /* Tells if the battery level is safe for disk writes */
186 bool battery_level_safe(void)
188 #if defined(NO_LOW_BATTERY_SHUTDOWN)
189 return true;
190 #elif defined(HAVE_BATTERY_SWITCH)
191 /* Cannot rely upon the battery reading to be valid and the
192 * device could be powered externally. */
193 return input_millivolts() > battery_level_dangerous[battery_type];
194 #else
195 return battery_millivolts > battery_level_dangerous[battery_type];
196 #endif
199 void set_poweroff_timeout(int timeout)
201 poweroff_timeout = timeout;
204 void set_sleep_timer(int seconds)
206 if (seconds) {
207 sleeptimer_active = true;
208 sleeptimer_endtick = current_tick + seconds * HZ;
210 else {
211 sleeptimer_active = false;
212 sleeptimer_endtick = 0;
216 int get_sleep_timer(void)
218 if (sleeptimer_active)
219 return (sleeptimer_endtick - current_tick) / HZ;
220 else
221 return 0;
224 /* look into the percent_to_volt_* table and get a realistic battery level */
225 static int voltage_to_percent(int voltage, const short* table)
227 if (voltage <= table[0]) {
228 return 0;
230 else if (voltage >= table[10]) {
231 return 100;
233 else {
234 /* search nearest value */
235 int i = 0;
237 while (i < 10 && table[i+1] < voltage)
238 i++;
240 /* interpolate linear between the smaller and greater value */
241 /* Tens digit, 10% per entry, ones digit: interpolated */
242 return i*10 + (voltage - table[i])*10 / (table[i+1] - table[i]);
246 /* update battery level and estimated runtime, called once per minute or
247 * when battery capacity / type settings are changed */
248 static int voltage_to_battery_level(int battery_millivolts)
250 int level;
252 #if CONFIG_CHARGING >= CHARGING_MONITOR
253 if (charging_state()) {
254 /* battery level is defined to be < 100% until charging is finished */
255 level = voltage_to_percent(battery_millivolts,
256 percent_to_volt_charge);
257 if (level > 99)
258 level = 99;
260 else
261 #endif /* CONFIG_CHARGING >= CHARGING_MONITOR */
263 /* DISCHARGING or error state */
264 level = voltage_to_percent(battery_millivolts,
265 percent_to_volt_discharge[battery_type]);
268 return level;
271 static void battery_status_update(void)
273 int level = voltage_to_battery_level(battery_millivolts);
275 /* calculate estimated remaining running time */
276 #if CONFIG_CHARGING >= CHARGING_MONITOR
277 if (charging_state()) {
278 /* charging: remaining charging time */
279 powermgmt_est_runningtime_min = (100 - level)*battery_capacity*60
280 / 100 / (CURRENT_MAX_CHG - runcurrent());
282 else
283 #endif
284 /* discharging: remaining running time */
285 if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0]) {
286 powermgmt_est_runningtime_min = (level + battery_percent)*60
287 * battery_capacity / 200 / runcurrent();
289 else if (battery_millivolts <= battery_level_shutoff[0]) {
290 powermgmt_est_runningtime_min = 0;
292 else {
293 powermgmt_est_runningtime_min =
294 (battery_millivolts - battery_level_shutoff[0]) / 2;
297 battery_percent = level;
298 send_battery_level_event();
302 * We shut off in the following cases:
303 * 1) The unit is idle, not playing music
304 * 2) The unit is playing music, but is paused
305 * 3) The battery level has reached shutdown limit
307 * We do not shut off in the following cases:
308 * 1) The USB is connected
309 * 2) The charger is connected
310 * 3) We are recording, or recording with pause
311 * 4) The radio is playing
313 static void handle_auto_poweroff(void)
315 long timeout = poweroff_timeout*60*HZ;
316 int audio_stat = audio_status();
317 long tick = current_tick;
319 #if CONFIG_CHARGING
321 * Inhibit shutdown as long as the charger is plugged in. If it is
322 * unplugged, wait for a timeout period and then shut down.
324 if (charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) {
325 last_event_tick = current_tick;
327 #endif
329 if (!shutdown_timeout && query_force_shutdown()) {
330 backlight_on();
331 sys_poweroff();
334 if (timeout &&
335 #if CONFIG_TUNER
336 !(get_radio_status() & FMRADIO_PLAYING) &&
337 #endif
338 !usb_inserted() &&
339 (audio_stat == 0 ||
340 (audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE) &&
341 !sleeptimer_active))) {
343 if (TIME_AFTER(tick, last_event_tick + timeout) &&
344 TIME_AFTER(tick, storage_last_disk_activity() + timeout)) {
345 sys_poweroff();
348 else if (sleeptimer_active) {
349 /* Handle sleeptimer */
350 if (TIME_AFTER(tick, sleeptimer_endtick)) {
351 audio_stop();
353 if (usb_inserted()
354 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
355 || charger_input_state != NO_CHARGER
356 #endif
358 DEBUGF("Sleep timer timeout. Stopping...\n");
359 set_sleep_timer(0);
360 backlight_off(); /* Nighty, nighty... */
362 else {
363 DEBUGF("Sleep timer timeout. Shutting off...\n");
364 sys_poweroff();
371 * Estimate how much current we are drawing just to run.
373 static int runcurrent(void)
375 int current;
377 #if MEM == 8 && !(defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM))
378 /* assuming 192 kbps, the running time is 22% longer with 8MB */
379 current = CURRENT_NORMAL*100 / 122;
380 #else
381 current = CURRENT_NORMAL;
382 #endif /* MEM == 8 */
384 #ifndef BOOTLOADER
385 if (usb_inserted()
386 #ifdef HAVE_USB_POWER
387 #if (CURRENT_USB < CURRENT_NORMAL)
388 || usb_powered()
389 #else
390 && !usb_powered()
391 #endif
392 #endif
394 current = CURRENT_USB;
397 #if defined(HAVE_BACKLIGHT)
398 if (backlight_get_current_timeout() == 0) /* LED always on */
399 current += CURRENT_BACKLIGHT;
400 #endif
402 #if defined(HAVE_RECORDING) && defined(CURRENT_RECORD)
403 if (audio_status() & AUDIO_STATUS_RECORD)
404 current += CURRENT_RECORD;
405 #endif
407 #ifdef HAVE_SPDIF_POWER
408 if (spdif_powered())
409 current += CURRENT_SPDIF_OUT;
410 #endif
412 #ifdef HAVE_REMOTE_LCD
413 if (remote_detect())
414 current += CURRENT_REMOTE;
415 #endif
416 #endif /* BOOTLOADER */
418 return current;
422 /* Check to see whether or not we've received an alarm in the last second */
423 #ifdef HAVE_RTC_ALARM
424 static void power_thread_rtc_process(void)
426 if (rtc_check_alarm_flag())
427 rtc_enable_alarm(false);
429 #endif
431 /* switch off unit if battery level is too low for reliable operation */
432 bool query_force_shutdown(void)
434 #if defined(NO_LOW_BATTERY_SHUTDOWN)
435 return false;
436 #elif defined(HAVE_BATTERY_SWITCH)
437 /* Cannot rely upon the battery reading to be valid and the
438 * device could be powered externally. */
439 return input_millivolts() < battery_level_shutoff[battery_type];
440 #else
441 return battery_millivolts < battery_level_shutoff[battery_type];
442 #endif
445 #if defined(HAVE_BATTERY_SWITCH) || defined(HAVE_RESET_BATTERY_FILTER)
447 * Reset the battery voltage filter to a new value and update the
448 * status.
450 void reset_battery_filter(int millivolts)
452 avgbat = millivolts * BATT_AVE_SAMPLES;
453 battery_millivolts = millivolts;
454 battery_status_update();
456 #endif /* HAVE_BATTERY_SWITCH */
458 /** Generic charging algorithms for common charging types **/
459 #if CONFIG_CHARGING == 0 || CONFIG_CHARGING == CHARGING_SIMPLE
460 static inline void powermgmt_init_target(void)
462 /* Nothing to do */
465 static inline void charging_algorithm_step(void)
467 /* Nothing to do */
470 static inline void charging_algorithm_close(void)
472 /* Nothing to do */
474 #elif CONFIG_CHARGING == CHARGING_MONITOR
476 * Monitor CHARGING/DISCHARGING state.
478 static inline void powermgmt_init_target(void)
480 /* Nothing to do */
483 static inline void charging_algorithm_step(void)
485 switch (charger_input_state)
487 case CHARGER_PLUGGED:
488 case CHARGER:
489 if (charging_state()) {
490 charge_state = CHARGING;
491 break;
493 /* Fallthrough */
494 case CHARGER_UNPLUGGED:
495 case NO_CHARGER:
496 charge_state = DISCHARGING;
497 break;
501 static inline void charging_algorithm_close(void)
503 /* Nothing to do */
505 #endif /* CONFIG_CHARGING == * */
507 #if CONFIG_CHARGING
508 /* Shortcut function calls - compatibility, simplicity. */
510 /* Returns true if any power input is capable of charging. */
511 bool charger_inserted(void)
513 return power_thread_inputs & POWER_INPUT_CHARGER;
516 /* Returns true if any power input is connected - charging-capable
517 * or not. */
518 bool power_input_present(void)
520 return power_thread_inputs & POWER_INPUT;
524 * Detect charger inserted. Return true if the state is transistional.
526 static inline bool detect_charger(unsigned int pwr)
529 * Detect charger plugged/unplugged transitions. On a plugged or
530 * unplugged event, we return immediately, run once through the main
531 * loop (including the subroutines), and end up back here where we
532 * transition to the appropriate steady state charger on/off state.
534 if (pwr & POWER_INPUT_CHARGER) {
535 switch (charger_input_state)
537 case NO_CHARGER:
538 case CHARGER_UNPLUGGED:
539 charger_input_state = CHARGER_PLUGGED;
540 break;
542 case CHARGER_PLUGGED:
543 queue_broadcast(SYS_CHARGER_CONNECTED, 0);
544 last_sent_battery_level = 0;
545 charger_input_state = CHARGER;
546 break;
548 case CHARGER:
549 /* Steady state */
550 return false;
553 else { /* charger not inserted */
554 switch (charger_input_state)
556 case NO_CHARGER:
557 /* Steady state */
558 return false;
560 case CHARGER_UNPLUGGED:
561 queue_broadcast(SYS_CHARGER_DISCONNECTED, 0);
562 last_sent_battery_level = 100;
563 charger_input_state = NO_CHARGER;
564 break;
566 case CHARGER_PLUGGED:
567 case CHARGER:
568 charger_input_state = CHARGER_UNPLUGGED;
569 break;
573 /* Transitional state */
574 return true;
576 #endif /* CONFIG_CHARGING */
579 * Monitor the presence of a charger and perform critical frequent steps
580 * such as running the battery voltage filter.
582 static inline void power_thread_step(void)
584 /* If the power off timeout expires, the main thread has failed
585 to shut down the system, and we need to force a power off */
586 if (shutdown_timeout) {
587 shutdown_timeout -= POWER_THREAD_STEP_TICKS;
589 if (shutdown_timeout <= 0)
590 power_off();
593 #ifdef HAVE_RTC_ALARM
594 power_thread_rtc_process();
595 #endif
598 * Do a digital exponential filter. We don't sample the battery if
599 * the disk is spinning unless we are in USB mode (the disk will most
600 * likely always be spinning in USB mode) or charging.
602 if (!storage_disk_is_active() || usb_inserted()
603 #if CONFIG_CHARGING >= CHARGING_MONITOR
604 || charger_input_state == CHARGER
605 #endif
607 avgbat += battery_adc_voltage() - avgbat / BATT_AVE_SAMPLES;
609 * battery_millivolts is the millivolt-scaled filtered battery value.
611 battery_millivolts = avgbat / BATT_AVE_SAMPLES;
613 /* update battery status every time an update is available */
614 battery_status_update();
616 else if (battery_percent < 8) {
618 * If battery is low, observe voltage during disk activity.
619 * Shut down if voltage drops below shutoff level and we are not
620 * using NiMH or Alkaline batteries.
622 battery_millivolts = (battery_adc_voltage() +
623 battery_millivolts + 1) / 2;
625 /* update battery status every time an update is available */
626 battery_status_update();
628 if (!shutdown_timeout && query_force_shutdown()) {
629 sys_poweroff();
631 else {
632 avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES;
635 } /* power_thread_step */
637 static void power_thread(void)
639 long next_power_hist;
641 /* Delay reading the first battery level */
642 #ifdef MROBE_100
643 while (battery_adc_voltage() > 4200) /* gives false readings initially */
644 #endif
646 sleep(HZ/100);
649 #if CONFIG_CHARGING
650 /* Initialize power input status before calling other routines. */
651 power_thread_inputs = power_input_status();
652 #endif
654 /* initialize the voltages for the exponential filter */
655 avgbat = battery_adc_voltage() + 15;
657 #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
658 /* The battery voltage is usually a little lower directly after
659 turning on, because the disk was used heavily. Raise it by 5% */
660 #if CONFIG_CHARGING
661 if (!charger_inserted()) /* only if charger not connected */
662 #endif
664 avgbat += (percent_to_volt_discharge[battery_type][6] -
665 percent_to_volt_discharge[battery_type][5]) / 2;
667 #endif /* HAVE_DISK_STORAGE */
669 avgbat = avgbat * BATT_AVE_SAMPLES;
670 battery_millivolts = avgbat / BATT_AVE_SAMPLES;
671 power_history[0] = battery_millivolts;
673 #if CONFIG_CHARGING
674 if (charger_inserted()) {
675 battery_percent = voltage_to_percent(battery_millivolts,
676 percent_to_volt_charge);
678 else
679 #endif
681 battery_percent = voltage_to_percent(battery_millivolts,
682 percent_to_volt_discharge[battery_type]);
683 battery_percent += battery_percent < 100;
686 powermgmt_init_target();
688 next_power_hist = current_tick + HZ*60;
690 while (1)
692 #if CONFIG_CHARGING
693 unsigned int pwr = power_input_status();
694 #ifdef HAVE_BATTERY_SWITCH
695 if ((pwr ^ power_thread_inputs) & POWER_INPUT_BATTERY) {
696 sleep(HZ/10);
697 reset_battery_filter(battery_adc_voltage());
699 #endif
700 power_thread_inputs = pwr;
702 if (!detect_charger(pwr))
703 #endif /* CONFIG_CHARGING */
705 /* Steady state */
706 sleep(POWER_THREAD_STEP_TICKS);
708 /* Do common power tasks */
709 power_thread_step();
712 /* Perform target tasks */
713 charging_algorithm_step();
715 if (TIME_BEFORE(current_tick, next_power_hist))
716 continue;
718 /* increment to ensure there is a record for every minute
719 * rather than go forward from the current tick */
720 next_power_hist += HZ*60;
722 /* rotate the power history */
723 memmove(&power_history[1], &power_history[0],
724 sizeof(power_history) - sizeof(power_history[0]));
726 /* insert new value at the start, in millivolts 8-) */
727 power_history[0] = battery_millivolts;
729 handle_auto_poweroff();
731 } /* power_thread */
733 void powermgmt_init(void)
735 /* init history to 0 */
736 memset(power_history, 0, sizeof(power_history));
737 create_thread(power_thread, power_stack, sizeof(power_stack), 0,
738 power_thread_name IF_PRIO(, PRIORITY_SYSTEM)
739 IF_COP(, CPU));
742 /* Various hardware housekeeping tasks relating to shutting down the player */
743 void shutdown_hw(void)
745 charging_algorithm_close();
746 audio_stop();
748 if (battery_level_safe()) { /* do not save on critical battery */
749 #ifdef HAVE_LCD_BITMAP
750 glyph_cache_save();
751 #endif
753 /* Commit pending writes if needed. Even though we don't do write caching,
754 things like flash translation layers may need this to commit scattered
755 pages to there final locations. So far only used for iPod Nano 2G. */
756 #ifdef HAVE_STORAGE_FLUSH
757 storage_flush();
758 #endif
760 if (storage_disk_is_active())
761 storage_spindown(1);
764 while (storage_disk_is_active())
765 sleep(HZ/10);
767 #if CONFIG_CODEC == SWCODEC
768 audiohw_close();
769 #else
770 mp3_shutdown();
771 #endif
773 /* If HD is still active we try to wait for spindown, otherwise the
774 shutdown_timeout in power_thread_step will force a power off */
775 while (storage_disk_is_active())
776 sleep(HZ/10);
778 #ifndef HAVE_LCD_COLOR
779 lcd_set_contrast(0);
780 #endif
781 #ifdef HAVE_REMOTE_LCD
782 lcd_remote_set_contrast(0);
783 #endif
784 #ifdef HAVE_LCD_SHUTDOWN
785 lcd_shutdown();
786 #endif
788 /* Small delay to make sure all HW gets time to flush. Especially
789 eeprom chips are quite slow and might be still writing the last
790 byte. */
791 sleep(HZ/4);
792 power_off();
795 void sys_poweroff(void)
797 #ifndef BOOTLOADER
798 logf("sys_poweroff()");
799 /* If the main thread fails to shut down the system, we will force a
800 power off after an 20 second timeout - 28 seconds if recording */
801 if (shutdown_timeout == 0) {
802 #if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(COWON_D2)
803 pcf50606_reset_timeout(); /* Reset timer on first attempt only */
804 #endif
805 #ifdef HAVE_RECORDING
806 if (audio_status() & AUDIO_STATUS_RECORD)
807 shutdown_timeout += HZ*8;
808 #endif
809 #ifdef IPOD_NANO2G
810 /* The FTL alone may take half a minute to shut down cleanly. */
811 shutdown_timeout += HZ*60;
812 #else
813 shutdown_timeout += HZ*20;
814 #endif
817 queue_broadcast(SYS_POWEROFF, 0);
818 #endif /* BOOTLOADER */
821 void cancel_shutdown(void)
823 logf("cancel_shutdown()");
825 #if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(COWON_D2)
826 /* TODO: Move some things to target/ tree */
827 if (shutdown_timeout)
828 pcf50606_reset_timeout();
829 #endif
831 shutdown_timeout = 0;
833 #endif /* SIMULATOR */
835 /* Send system battery level update events on reaching certain significant
836 levels. This must be called after battery_percent has been updated. */
837 void send_battery_level_event(void)
839 static const int levels[] = { 5, 15, 30, 50, 0 };
840 const int *level = levels;
842 while (*level)
844 if (battery_percent <= *level && last_sent_battery_level > *level) {
845 last_sent_battery_level = *level;
846 queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
847 break;
850 level++;