Fix yellow.
[kugel-rb.git] / firmware / powermgmt.c
blob3f2b3c0f8511f78305a6b59b7ed8cf41c00b91a2
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 > percent_to_volt_discharge[0][0]) {
286 /* linear extrapolation */
287 powermgmt_est_runningtime_min = (level + battery_percent)*60
288 * battery_capacity / 200 / runcurrent();
290 if (0 > powermgmt_est_runningtime_min) {
291 powermgmt_est_runningtime_min = 0;
294 battery_percent = level;
295 send_battery_level_event();
299 * We shut off in the following cases:
300 * 1) The unit is idle, not playing music
301 * 2) The unit is playing music, but is paused
302 * 3) The battery level has reached shutdown limit
304 * We do not shut off in the following cases:
305 * 1) The USB is connected
306 * 2) The charger is connected
307 * 3) We are recording, or recording with pause
308 * 4) The radio is playing
310 static void handle_auto_poweroff(void)
312 long timeout = poweroff_timeout*60*HZ;
313 int audio_stat = audio_status();
314 long tick = current_tick;
316 #if CONFIG_CHARGING
318 * Inhibit shutdown as long as the charger is plugged in. If it is
319 * unplugged, wait for a timeout period and then shut down.
321 if (charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) {
322 last_event_tick = current_tick;
324 #endif
326 if (!shutdown_timeout && query_force_shutdown()) {
327 backlight_on();
328 sys_poweroff();
331 if (timeout &&
332 #if CONFIG_TUNER
333 !(get_radio_status() & FMRADIO_PLAYING) &&
334 #endif
335 !usb_inserted() &&
336 (audio_stat == 0 ||
337 (audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE) &&
338 !sleeptimer_active))) {
340 if (TIME_AFTER(tick, last_event_tick + timeout) &&
341 TIME_AFTER(tick, storage_last_disk_activity() + timeout)) {
342 sys_poweroff();
345 else if (sleeptimer_active) {
346 /* Handle sleeptimer */
347 if (TIME_AFTER(tick, sleeptimer_endtick)) {
348 audio_stop();
350 if (usb_inserted()
351 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
352 || charger_input_state != NO_CHARGER
353 #endif
355 DEBUGF("Sleep timer timeout. Stopping...\n");
356 set_sleep_timer(0);
357 backlight_off(); /* Nighty, nighty... */
359 else {
360 DEBUGF("Sleep timer timeout. Shutting off...\n");
361 sys_poweroff();
368 * Estimate how much current we are drawing just to run.
370 static int runcurrent(void)
372 int current;
374 #if MEM == 8 && !(defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM))
375 /* assuming 192 kbps, the running time is 22% longer with 8MB */
376 current = CURRENT_NORMAL*100 / 122;
377 #else
378 current = CURRENT_NORMAL;
379 #endif /* MEM == 8 */
381 #ifndef BOOTLOADER
382 if (usb_inserted()
383 #ifdef HAVE_USB_POWER
384 #if (CURRENT_USB < CURRENT_NORMAL)
385 || usb_powered()
386 #else
387 && !usb_powered()
388 #endif
389 #endif
391 current = CURRENT_USB;
394 #if defined(HAVE_BACKLIGHT)
395 if (backlight_get_current_timeout() == 0) /* LED always on */
396 current += CURRENT_BACKLIGHT;
397 #endif
399 #if defined(HAVE_RECORDING) && defined(CURRENT_RECORD)
400 if (audio_status() & AUDIO_STATUS_RECORD)
401 current += CURRENT_RECORD;
402 #endif
404 #ifdef HAVE_SPDIF_POWER
405 if (spdif_powered())
406 current += CURRENT_SPDIF_OUT;
407 #endif
409 #ifdef HAVE_REMOTE_LCD
410 if (remote_detect())
411 current += CURRENT_REMOTE;
412 #endif
413 #endif /* BOOTLOADER */
415 return current;
419 /* Check to see whether or not we've received an alarm in the last second */
420 #ifdef HAVE_RTC_ALARM
421 static void power_thread_rtc_process(void)
423 if (rtc_check_alarm_flag())
424 rtc_enable_alarm(false);
426 #endif
428 /* switch off unit if battery level is too low for reliable operation */
429 bool query_force_shutdown(void)
431 #if defined(NO_LOW_BATTERY_SHUTDOWN)
432 return false;
433 #elif defined(HAVE_BATTERY_SWITCH)
434 /* Cannot rely upon the battery reading to be valid and the
435 * device could be powered externally. */
436 return input_millivolts() < battery_level_shutoff[battery_type];
437 #else
438 return battery_millivolts < battery_level_shutoff[battery_type];
439 #endif
442 #if defined(HAVE_BATTERY_SWITCH) || defined(HAVE_RESET_BATTERY_FILTER)
444 * Reset the battery voltage filter to a new value and update the
445 * status.
447 void reset_battery_filter(int millivolts)
449 avgbat = millivolts * BATT_AVE_SAMPLES;
450 battery_millivolts = millivolts;
451 battery_status_update();
453 #endif /* HAVE_BATTERY_SWITCH */
455 /** Generic charging algorithms for common charging types **/
456 #if CONFIG_CHARGING == 0 || CONFIG_CHARGING == CHARGING_SIMPLE
457 static inline void powermgmt_init_target(void)
459 /* Nothing to do */
462 static inline void charging_algorithm_step(void)
464 /* Nothing to do */
467 static inline void charging_algorithm_close(void)
469 /* Nothing to do */
471 #elif CONFIG_CHARGING == CHARGING_MONITOR
473 * Monitor CHARGING/DISCHARGING state.
475 static inline void powermgmt_init_target(void)
477 /* Nothing to do */
480 static inline void charging_algorithm_step(void)
482 switch (charger_input_state)
484 case CHARGER_PLUGGED:
485 case CHARGER:
486 if (charging_state()) {
487 charge_state = CHARGING;
488 break;
490 /* Fallthrough */
491 case CHARGER_UNPLUGGED:
492 case NO_CHARGER:
493 charge_state = DISCHARGING;
494 break;
498 static inline void charging_algorithm_close(void)
500 /* Nothing to do */
502 #endif /* CONFIG_CHARGING == * */
504 #if CONFIG_CHARGING
505 /* Shortcut function calls - compatibility, simplicity. */
507 /* Returns true if any power input is capable of charging. */
508 bool charger_inserted(void)
510 return power_thread_inputs & POWER_INPUT_CHARGER;
513 /* Returns true if any power input is connected - charging-capable
514 * or not. */
515 bool power_input_present(void)
517 return power_thread_inputs & POWER_INPUT;
521 * Detect charger inserted. Return true if the state is transistional.
523 static inline bool detect_charger(unsigned int pwr)
526 * Detect charger plugged/unplugged transitions. On a plugged or
527 * unplugged event, we return immediately, run once through the main
528 * loop (including the subroutines), and end up back here where we
529 * transition to the appropriate steady state charger on/off state.
531 if (pwr & POWER_INPUT_CHARGER) {
532 switch (charger_input_state)
534 case NO_CHARGER:
535 case CHARGER_UNPLUGGED:
536 charger_input_state = CHARGER_PLUGGED;
537 break;
539 case CHARGER_PLUGGED:
540 queue_broadcast(SYS_CHARGER_CONNECTED, 0);
541 last_sent_battery_level = 0;
542 charger_input_state = CHARGER;
543 break;
545 case CHARGER:
546 /* Steady state */
547 return false;
550 else { /* charger not inserted */
551 switch (charger_input_state)
553 case NO_CHARGER:
554 /* Steady state */
555 return false;
557 case CHARGER_UNPLUGGED:
558 queue_broadcast(SYS_CHARGER_DISCONNECTED, 0);
559 last_sent_battery_level = 100;
560 charger_input_state = NO_CHARGER;
561 break;
563 case CHARGER_PLUGGED:
564 case CHARGER:
565 charger_input_state = CHARGER_UNPLUGGED;
566 break;
570 /* Transitional state */
571 return true;
573 #endif /* CONFIG_CHARGING */
576 * Monitor the presence of a charger and perform critical frequent steps
577 * such as running the battery voltage filter.
579 static inline void power_thread_step(void)
581 /* If the power off timeout expires, the main thread has failed
582 to shut down the system, and we need to force a power off */
583 if (shutdown_timeout) {
584 shutdown_timeout -= POWER_THREAD_STEP_TICKS;
586 if (shutdown_timeout <= 0)
587 power_off();
590 #ifdef HAVE_RTC_ALARM
591 power_thread_rtc_process();
592 #endif
595 * Do a digital exponential filter. We don't sample the battery if
596 * the disk is spinning unless we are in USB mode (the disk will most
597 * likely always be spinning in USB mode) or charging.
599 if (!storage_disk_is_active() || usb_inserted()
600 #if CONFIG_CHARGING >= CHARGING_MONITOR
601 || charger_input_state == CHARGER
602 #endif
604 avgbat += battery_adc_voltage() - avgbat / BATT_AVE_SAMPLES;
606 * battery_millivolts is the millivolt-scaled filtered battery value.
608 battery_millivolts = avgbat / BATT_AVE_SAMPLES;
610 /* update battery status every time an update is available */
611 battery_status_update();
613 else if (battery_percent < 8) {
615 * If battery is low, observe voltage during disk activity.
616 * Shut down if voltage drops below shutoff level and we are not
617 * using NiMH or Alkaline batteries.
619 battery_millivolts = (battery_adc_voltage() +
620 battery_millivolts + 1) / 2;
622 /* update battery status every time an update is available */
623 battery_status_update();
625 if (!shutdown_timeout && query_force_shutdown()) {
626 sys_poweroff();
628 else {
629 avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES;
632 } /* power_thread_step */
634 static void power_thread(void)
636 long next_power_hist;
638 /* Delay reading the first battery level */
639 #ifdef MROBE_100
640 while (battery_adc_voltage() > 4200) /* gives false readings initially */
641 #endif
643 sleep(HZ/100);
646 #if CONFIG_CHARGING
647 /* Initialize power input status before calling other routines. */
648 power_thread_inputs = power_input_status();
649 #endif
651 /* initialize the voltages for the exponential filter */
652 avgbat = battery_adc_voltage() + 15;
654 #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
655 /* The battery voltage is usually a little lower directly after
656 turning on, because the disk was used heavily. Raise it by 5% */
657 #if CONFIG_CHARGING
658 if (!charger_inserted()) /* only if charger not connected */
659 #endif
661 avgbat += (percent_to_volt_discharge[battery_type][6] -
662 percent_to_volt_discharge[battery_type][5]) / 2;
664 #endif /* HAVE_DISK_STORAGE */
666 avgbat = avgbat * BATT_AVE_SAMPLES;
667 battery_millivolts = avgbat / BATT_AVE_SAMPLES;
668 power_history[0] = battery_millivolts;
670 #if CONFIG_CHARGING
671 if (charger_inserted()) {
672 battery_percent = voltage_to_percent(battery_millivolts,
673 percent_to_volt_charge);
675 else
676 #endif
678 battery_percent = voltage_to_percent(battery_millivolts,
679 percent_to_volt_discharge[battery_type]);
680 battery_percent += battery_percent < 100;
683 powermgmt_init_target();
685 next_power_hist = current_tick + HZ*60;
687 while (1)
689 #if CONFIG_CHARGING
690 unsigned int pwr = power_input_status();
691 #ifdef HAVE_BATTERY_SWITCH
692 if ((pwr ^ power_thread_inputs) & POWER_INPUT_BATTERY) {
693 sleep(HZ/10);
694 reset_battery_filter(battery_adc_voltage());
696 #endif
697 power_thread_inputs = pwr;
699 if (!detect_charger(pwr))
700 #endif /* CONFIG_CHARGING */
702 /* Steady state */
703 sleep(POWER_THREAD_STEP_TICKS);
705 /* Do common power tasks */
706 power_thread_step();
709 /* Perform target tasks */
710 charging_algorithm_step();
712 if (TIME_BEFORE(current_tick, next_power_hist))
713 continue;
715 /* increment to ensure there is a record for every minute
716 * rather than go forward from the current tick */
717 next_power_hist += HZ*60;
719 /* rotate the power history */
720 memmove(&power_history[1], &power_history[0],
721 sizeof(power_history) - sizeof(power_history[0]));
723 /* insert new value at the start, in millivolts 8-) */
724 power_history[0] = battery_millivolts;
726 handle_auto_poweroff();
728 } /* power_thread */
730 void powermgmt_init(void)
732 /* init history to 0 */
733 memset(power_history, 0, sizeof(power_history));
734 create_thread(power_thread, power_stack, sizeof(power_stack), 0,
735 power_thread_name IF_PRIO(, PRIORITY_SYSTEM)
736 IF_COP(, CPU));
739 /* Various hardware housekeeping tasks relating to shutting down the player */
740 void shutdown_hw(void)
742 charging_algorithm_close();
743 audio_stop();
745 if (battery_level_safe()) { /* do not save on critical battery */
746 #ifdef HAVE_LCD_BITMAP
747 glyph_cache_save(NULL);
748 #endif
750 /* Commit pending writes if needed. Even though we don't do write caching,
751 things like flash translation layers may need this to commit scattered
752 pages to there final locations. So far only used for iPod Nano 2G. */
753 #ifdef HAVE_STORAGE_FLUSH
754 storage_flush();
755 #endif
757 if (storage_disk_is_active())
758 storage_spindown(1);
761 while (storage_disk_is_active())
762 sleep(HZ/10);
764 #if CONFIG_CODEC == SWCODEC
765 audiohw_close();
766 #else
767 mp3_shutdown();
768 #endif
770 /* If HD is still active we try to wait for spindown, otherwise the
771 shutdown_timeout in power_thread_step will force a power off */
772 while (storage_disk_is_active())
773 sleep(HZ/10);
775 #ifndef HAVE_LCD_COLOR
776 lcd_set_contrast(0);
777 #endif
778 #ifdef HAVE_REMOTE_LCD
779 lcd_remote_set_contrast(0);
780 #endif
781 #ifdef HAVE_LCD_SHUTDOWN
782 lcd_shutdown();
783 #endif
785 /* Small delay to make sure all HW gets time to flush. Especially
786 eeprom chips are quite slow and might be still writing the last
787 byte. */
788 sleep(HZ/4);
789 power_off();
792 void sys_poweroff(void)
794 #ifndef BOOTLOADER
795 logf("sys_poweroff()");
796 /* If the main thread fails to shut down the system, we will force a
797 power off after an 20 second timeout - 28 seconds if recording */
798 if (shutdown_timeout == 0) {
799 #if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(COWON_D2)
800 pcf50606_reset_timeout(); /* Reset timer on first attempt only */
801 #endif
802 #ifdef HAVE_RECORDING
803 if (audio_status() & AUDIO_STATUS_RECORD)
804 shutdown_timeout += HZ*8;
805 #endif
806 #ifdef IPOD_NANO2G
807 /* The FTL alone may take half a minute to shut down cleanly. */
808 shutdown_timeout += HZ*60;
809 #else
810 shutdown_timeout += HZ*20;
811 #endif
814 queue_broadcast(SYS_POWEROFF, 0);
815 #endif /* BOOTLOADER */
818 void cancel_shutdown(void)
820 logf("cancel_shutdown()");
822 #if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(COWON_D2)
823 /* TODO: Move some things to target/ tree */
824 if (shutdown_timeout)
825 pcf50606_reset_timeout();
826 #endif
828 shutdown_timeout = 0;
830 #endif /* SIMULATOR */
832 /* Send system battery level update events on reaching certain significant
833 levels. This must be called after battery_percent has been updated. */
834 void send_battery_level_event(void)
836 static const int levels[] = { 5, 15, 30, 50, 0 };
837 const int *level = levels;
839 while (*level)
841 if (battery_percent <= *level && last_sent_battery_level > *level) {
842 last_sent_battery_level = *level;
843 queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
844 break;
847 level++;