Simplify powermgmt thread loops so it calls functions turn (no more power_thread_sleep). Do other target-friendly simplifications, generic battery switch handling and split sim-specific code. Whoever can, please verify charging on the Archos Recorder (due to change in the charger duty cycle code).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19579 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index beee39a..abb6018 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1603,10 +1603,10 @@
                 lcd_puts(0, 2, buf);
 #endif
 #if CONFIG_CHARGING
-#if CONFIG_CHARGING == CHARGING_CONTROL
+#if defined ARCHOS_RECORDER
                 snprintf(buf, 30, "Chgr: %s %s",
                          charger_inserted() ? "present" : "absent",
-                         charger_enabled ? "on" : "off");
+                         charger_enabled() ? "on" : "off");
                 lcd_puts(0, 3, buf);
                 snprintf(buf, 30, "short delta: %d", short_delta);
                 lcd_puts(0, 5, buf);
@@ -1616,13 +1616,11 @@
                 snprintf(buf, 30, "USB Inserted: %s",
                          usb_inserted() ? "yes" : "no");
                 lcd_puts(0, 8, buf);
-#if defined IRIVER_H300_SERIES
+#elif defined IRIVER_H300_SERIES
                 snprintf(buf, 30, "USB Charging Enabled: %s",
                          usb_charging_enabled() ? "yes" : "no");
                 lcd_puts(0, 9, buf);
-#endif
-#else /* CONFIG_CHARGING != CHARGING_CONTROL */
-#if defined IPOD_NANO || defined IPOD_VIDEO
+#elif defined IPOD_NANO || defined IPOD_VIDEO
                 int usb_pwr  = (GPIOL_INPUT_VAL & 0x10)?true:false;
                 int ext_pwr  = (GPIOL_INPUT_VAL & 0x08)?false:true;
                 int dock     = (GPIOA_INPUT_VAL & 0x10)?true:false;
@@ -1644,12 +1642,8 @@
                 snprintf(buf, 30, "Headphone: %s",
                          headphone ? "connected" : "disconnected");
                 lcd_puts(0, 7, buf);
-#else
-                snprintf(buf, 30, "Charger: %s",
-                         charger_inserted() ? "present" : "absent");
-                lcd_puts(0, 3, buf);
-#if defined TOSHIBA_GIGABEAT_S
-                int line = 4;
+#elif defined TOSHIBA_GIGABEAT_S
+                int line = 3;
                 unsigned int st;
 
                 static const unsigned char * const chrgstate_strings[] =
@@ -1663,6 +1657,10 @@
                     "<unknown>",
                 };
 
+                snprintf(buf, 30, "Charger: %s",
+                         charger_inserted() ? "present" : "absent");
+                lcd_puts(0, line++, buf);
+
                 st = power_input_status() &
                      (POWER_INPUT_CHARGER | POWER_INPUT_BATTERY);
                 snprintf(buf, 30, "%s%s",
@@ -1730,9 +1728,11 @@
                 }
                     
                 lcd_puts(0, line++, buf);
-#endif /* defined TOSHIBA_GIGABEAT_S */
-#endif /* defined IPOD_NANO || defined IPOD_VIDEO */
-#endif /* CONFIG_CHARGING != CHARGING_CONTROL */
+#else
+                snprintf(buf, 30, "Charger: %s",
+                         charger_inserted() ? "present" : "absent");
+                lcd_puts(0, 3, buf);
+#endif /* target type */
 #endif /* CONFIG_CHARGING */
                 break;
 
@@ -1750,7 +1750,7 @@
 
             case 3: /* remaining time estimation: */
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
+#ifdef ARCHOS_RECORDER
                 snprintf(buf, 30, "charge_state: %d", charge_state);
                 lcd_puts(0, 0, buf);
 
@@ -1765,7 +1765,7 @@
 
                 snprintf(buf, 30, "Trickle sec: %d/60", trickle_sec);
                 lcd_puts(0, 4, buf);
-#endif /* CONFIG_CHARGING == CHARGING_CONTROL */
+#endif /* ARCHOS_RECORDER */
 
                 snprintf(buf, 30, "Last PwrHist: %d.%03dV",
                     power_history[0] / 1000,
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index 4c15b1d..b22824d 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -27,6 +27,7 @@
 #include "lang.h"
 #include "action.h"
 #include "settings.h"
+#include "power.h"
 #include "powermgmt.h"
 #include "menu.h"
 #include "misc.h"
@@ -199,15 +200,14 @@
             if (charge_state == CHARGING)
                 return (char *)str(LANG_BATTERY_CHARGE);
             else
-#if CONFIG_CHARGING == CHARGING_CONTROL
+#ifdef ARCHOS_RECORDER
             if (charge_state == TOPOFF)
                 return (char *)str(LANG_BATTERY_TOPOFF_CHARGE);
-            else
-#endif
-            if (charge_state == TRICKLE)
+            else if (charge_state == TRICKLE)
                 return (char *)str(LANG_BATTERY_TRICKLE_CHARGE);
             else
-#endif
+#endif /* ARCHOS_RECORDER */
+#endif /* CONFIG_CHARGING = */
             if (battery_level() >= 0)
                 snprintf(buffer, buffer_len, (char *)str(LANG_BATTERY_TIME),
                          battery_level(), battery_time() / 60, battery_time() % 60);
@@ -282,22 +282,21 @@
         }
         case INFO_BATTERY: /* battery */
 #if CONFIG_CHARGING == CHARGING_SIMPLE
-            if (charger_input_state == CHARGER)
+            if (charger_inserted())
                 talk_id(LANG_BATTERY_CHARGE, true);
             else
 #elif CONFIG_CHARGING >= CHARGING_MONITOR
             if (charge_state == CHARGING)
                 talk_id(LANG_BATTERY_CHARGE, true);
             else
-#if CONFIG_CHARGING == CHARGING_CONTROL
+#ifdef ARCHOS_RECORDER
             if (charge_state == TOPOFF)
                 talk_id(LANG_BATTERY_TOPOFF_CHARGE, true);
-            else
-#endif
-            if (charge_state == TRICKLE)
+            else if (charge_state == TRICKLE)
                 talk_id(LANG_BATTERY_TRICKLE_CHARGE, true);
             else
-#endif
+#endif /* ARCHOS_RECORDER */
+#endif /* CONFIG_CHARGING = */
             if (battery_level() >= 0)
             {
                 talk_id(LANG_BATTERY_TIME, false);
diff --git a/apps/screens.c b/apps/screens.c
index 992c740..230e9ae 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -209,8 +209,7 @@
         lcd_puts(0, 7, buf);
     }
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-
+#ifdef ARCHOS_RECORER
     snprintf(buf, 32, "Charge mode:");
     lcd_puts(0, 2, buf);
 
@@ -224,10 +223,9 @@
         snprintf(buf, 32, "not charging");
 
     lcd_puts(0, 3, buf);
-    if (!charger_enabled)
+    if (!charger_enabled())
         animate = false;
-#endif /* CONFIG_CHARGING == CHARGING_CONTROL */
-
+#endif /* ARCHOS_RECORER */
 
     /* middle part */
     memset(charging_logo+3, 0x00, 32);
diff --git a/firmware/export/config-c200.h b/firmware/export/config-c200.h
index e169606..ac64f20 100644
--- a/firmware/export/config-c200.h
+++ b/firmware/export/config-c200.h
@@ -129,6 +129,11 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+/* define current usage levels */
+#define CURRENT_NORMAL     45  /* Should be nearly identical to E200 */
+#define CURRENT_BACKLIGHT  40  /* Screen is about 20, blue LEDs are another 20, so 40 if both */
+#define CURRENT_RECORD     40  /* flash player, so this is just unboosted current*/
+
 /** Non-simulator section **/
 #ifndef SIMULATOR
 
diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h
index 3cae14c..b290a24 100644
--- a/firmware/export/config-e200.h
+++ b/firmware/export/config-e200.h
@@ -123,6 +123,11 @@
 /* Hardware controlled charging? FIXME */
 #define CONFIG_CHARGING CHARGING_SIMPLE
 
+/* define current usage levels */
+#define CURRENT_NORMAL     45  /* Mike's measurements in Jan 2008  */
+#define CURRENT_BACKLIGHT  40  /* Screen is about 20, blue LEDs are another 20, so 40 if both */
+#define CURRENT_RECORD     40  /* flash player, so this is just unboosted current*/
+
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h
index 9c327f1..7f91e35 100644
--- a/firmware/export/config-h100.h
+++ b/firmware/export/config-h100.h
@@ -110,9 +110,16 @@
 #define BATTERY_TYPES_COUNT  1    /* only one type */
 
 /* Hardware controlled charging */
-//#define CONFIG_CHARGING CHARGING_SIMPLE
+
 #define CONFIG_CHARGING CHARGING_MONITOR /* FIXME: remove that once monitoring is fixed properly */
 
+/* define current usage levels */
+#define CURRENT_NORMAL     80  /* 16h playback on 1300mAh battery */
+#define CURRENT_BACKLIGHT  23  /* from IriverBattery twiki page */
+#define CURRENT_SPDIF_OUT  10  /* optical SPDIF output on */
+#define CURRENT_RECORD    105  /* additional current while recording */
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+
 #ifndef SIMULATOR
 
 /* Define this if you have a Motorola SCF5249 */
diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h
index d81fa07..b4a687b 100644
--- a/firmware/export/config-h120.h
+++ b/firmware/export/config-h120.h
@@ -110,9 +110,15 @@
 #define BATTERY_TYPES_COUNT  1    /* only one type */
 
 /* Hardware controlled charging */
-//#define CONFIG_CHARGING CHARGING_SIMPLE
 #define CONFIG_CHARGING CHARGING_MONITOR /* FIXME: remove that once monitoring is fixed properly */
 
+/* define current usage levels */
+#define CURRENT_NORMAL     80  /* 16h playback on 1300mAh battery */
+#define CURRENT_BACKLIGHT  23  /* from IriverBattery twiki page */
+#define CURRENT_SPDIF_OUT  10  /* optical SPDIF output on */
+#define CURRENT_RECORD    105  /* additional current while recording */
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+
 #ifndef SIMULATOR
 
 /* Define this if you have a Motorola SCF5249 */
diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h
index fa0046a..05d75fc 100644
--- a/firmware/export/config-h300.h
+++ b/firmware/export/config-h300.h
@@ -112,6 +112,13 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+/* define current usage levels */
+#define CURRENT_NORMAL     80  /* 16h playback on 1300mAh battery from IriverRuntime wiki page */
+#define CURRENT_BACKLIGHT  23  /* FIXME: This needs to be measured, copied from H100 */
+#define CURRENT_RECORD    110  /* additional current while recording */
+#define CURRENT_MAX_CHG   650  /* maximum charging current */
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+
 /* define this if the unit can have USB charging disabled by user -
  * if USB/MAIN power is discernable and hardware doesn't compel charging */
 #define HAVE_USB_CHARGING_ENABLE
diff --git a/firmware/export/config-iaudiom3.h b/firmware/export/config-iaudiom3.h
index f4ced65..a6e1028 100644
--- a/firmware/export/config-iaudiom3.h
+++ b/firmware/export/config-iaudiom3.h
@@ -102,6 +102,9 @@
 /* Hardware controlled charging? FIXME */
 #define CONFIG_CHARGING CHARGING_SIMPLE
 
+/* define current usage levels */
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+
 #ifndef SIMULATOR
 
 /* Define this if your LCD can set contrast */
diff --git a/firmware/export/config-iaudiom5.h b/firmware/export/config-iaudiom5.h
index 5ec6b77..26cc9c5 100644
--- a/firmware/export/config-iaudiom5.h
+++ b/firmware/export/config-iaudiom5.h
@@ -105,6 +105,9 @@
 /* Hardware controlled charging? FIXME */
 #define CONFIG_CHARGING CHARGING_SIMPLE
 
+/* define current usage levels */
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+
 #ifndef SIMULATOR
 
 /* Define this if your LCD can set contrast */
diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h
index 03b8460..8b1f908 100644
--- a/firmware/export/config-iaudiox5.h
+++ b/firmware/export/config-iaudiox5.h
@@ -122,6 +122,9 @@
 /* Hardware controlled charging? FIXME */
 #define CONFIG_CHARGING CHARGING_SIMPLE
 
+/* define current usage levels */
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+
 #ifndef SIMULATOR
 
 /* define this if the backlight thread is used for fade, not for sim, needs
diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h
index 4a2207d..89b14f9 100644
--- a/firmware/export/config-ipod4g.h
+++ b/firmware/export/config-ipod4g.h
@@ -120,6 +120,13 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+/* define current usage levels */
+#define CURRENT_NORMAL     100  /* MP3: ~10.5h out of 1100mAh battery  */
+#define CURRENT_BACKLIGHT  20  /* FIXME: this needs adjusting */
+#if defined(HAVE_RECORDING)
+#define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
+#endif
+
 #ifndef SIMULATOR
 
 /* Define this if you have a PortalPlayer PP5020 */
diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h
index 7d6515f..6017d0a 100644
--- a/firmware/export/config-ipodnano.h
+++ b/firmware/export/config-ipodnano.h
@@ -115,6 +115,12 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+#define CURRENT_NORMAL     32  /* MP3: ~9h playback out of 300mAh battery */
+#define CURRENT_BACKLIGHT  20  /* FIXME: this needs adjusting */
+#if defined(HAVE_RECORDING)
+#define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
+#endif
+
 #ifndef SIMULATOR
 
 /* Define this if you have a PortalPlayer PP5022 */
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h
index fa6ddd3..d04e562 100644
--- a/firmware/export/config-ipodvideo.h
+++ b/firmware/export/config-ipodvideo.h
@@ -123,6 +123,13 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+/* define current usage levels */
+#define CURRENT_NORMAL     35  /* MP3: ~11h out of 400mAh battery (30GB) or ~17h out of 600mAh (60GB) */
+#define CURRENT_BACKLIGHT  20  /* FIXME: this needs adjusting */
+#if defined(HAVE_RECORDING)
+#define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
+#endif
+
 #ifndef SIMULATOR
 
 /* Define this if you have a PortalPlayer PP5022 */
diff --git a/firmware/export/config-ondiofm.h b/firmware/export/config-ondiofm.h
index 634433f..8233728 100644
--- a/firmware/export/config-ondiofm.h
+++ b/firmware/export/config-ondiofm.h
@@ -69,6 +69,11 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+/* define current usage levels */
+#define CURRENT_NORMAL     95  /* average, nearly proportional to 1/U */
+#define CURRENT_USB         1  /* host powered in USB mode; avoid zero-div */
+#define CURRENT_BACKLIGHT   0  /* no backlight */
+
 #ifndef SIMULATOR
 
 /* Define this if you have a SH7034 */
diff --git a/firmware/export/config-ondiosp.h b/firmware/export/config-ondiosp.h
index 3443b83..5fb7806 100644
--- a/firmware/export/config-ondiosp.h
+++ b/firmware/export/config-ondiosp.h
@@ -58,6 +58,11 @@
 /* define this if the unit can be powered or charged via USB */
 #define HAVE_USB_POWER
 
+/* define current usage levels */
+#define CURRENT_NORMAL     95  /* average, nearly proportional to 1/U */
+#define CURRENT_USB         1  /* host powered in USB mode; avoid zero-div */
+#define CURRENT_BACKLIGHT   0  /* no backlight */
+
 #ifndef SIMULATOR
 
 /* Define this if you have a SH7034 */
diff --git a/firmware/export/config-recorder.h b/firmware/export/config-recorder.h
index e5fa336..75aa2cf 100644
--- a/firmware/export/config-recorder.h
+++ b/firmware/export/config-recorder.h
@@ -72,7 +72,7 @@
 #define NO_LOW_BATTERY_SHUTDOWN
 
 /* Software controlled charging */
-#define CONFIG_CHARGING CHARGING_CONTROL
+#define CONFIG_CHARGING CHARGING_TARGET
 
 #ifndef SIMULATOR
 
diff --git a/firmware/export/config.h b/firmware/export/config.h
index d8c14dd..1854f59 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -114,11 +114,13 @@
 #define X5_REMOTE   3
 
 /* CONFIG_CHARGING */
+
+/* Generic types */
 #define CHARGING_SIMPLE  1 /* Simple, hardware controlled charging */
 #define CHARGING_MONITOR 2 /* Hardware controlled charging with monitoring */
-#define CHARGING_CONTROL 3 /* Software controlled charging */
-#define CHARGING_TARGET  4 /* Anything the target implements that is not
-                              a generic implementation */
+
+/* Mostly target-specific code in the /target tree */
+#define CHARGING_TARGET  3
 
 /* CONFIG_LCD */
 #define LCD_SSD1815   1 /* as used by Archos Recorders and Ondios */
diff --git a/firmware/export/power.h b/firmware/export/power.h
index 7478879..edf43f8 100644
--- a/firmware/export/power.h
+++ b/firmware/export/power.h
@@ -21,11 +21,6 @@
 #ifndef _POWER_H_
 #define _POWER_H_
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-extern bool charger_enabled;
-void charger_enable(bool on);
-#endif
-
 #if CONFIG_CHARGING
 enum power_input_flags {
     /* No external power source? Default. */
@@ -82,9 +77,9 @@
 void power_off(void);
 void ide_power_enable(bool on);
 
-# if CONFIG_CHARGING == CHARGING_MONITOR
+#if CONFIG_CHARGING >= CHARGING_MONITOR
 bool charging_state(void);
-# endif
+#endif
 
 #ifndef SIMULATOR
 
@@ -102,4 +97,4 @@
 bool tuner_power(bool status);
 #endif
 
-#endif
+#endif /* _POWER_H_ */
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index c333795..5be3a39 100644
--- a/firmware/export/powermgmt.h
+++ b/firmware/export/powermgmt.h
@@ -25,149 +25,103 @@
 
 #define POWER_HISTORY_LEN 2*60   /* 2 hours of samples, one per minute */
 
-#define CHARGE_END_SHORTD  6     /* stop when N minutes have passed with
-                                  * avg delta being < -0.05 V */
-#define CHARGE_END_LONGD  50     /* stop when N minutes have passed with
-                                  * avg delta being < -0.02 V */
-
-typedef enum {       /* sorted by increasing charging current */
+enum charge_state_type
+{
+    /* sorted by increasing charging current */
 #if CONFIG_CHARGING >= CHARGING_MONITOR
-    CHARGE_STATE_DISABLED = -2, /* Disable charger use */
-    CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow
-                        further attempts without user intervention */
+    CHARGE_STATE_DISABLED = -2, /* Disable charger use (safety measure) */
+    CHARGE_STATE_ERROR = -1,    /* Some error occurred that should not allow
+                                   turning on the charger again by software
+                                   without user intervention (ie. replug) */
 #endif
     DISCHARGING = 0,
 #if CONFIG_CHARGING >= CHARGING_MONITOR
-    TRICKLE,         /* Can occur for CONFIG_CHARGING >= CHARGING_MONITOR */
-                     /* For LiIon, the low-current precharge mode if battery
-                        was very low */
-    TOPOFF,          /* Can occur for CONFIG_CHARGING == CHARGING_CONTROL */
+    TRICKLE,         /* For NiCd, battery maintenence phase */
+                     /* For LiIon, low-current precharge phase */
+    TOPOFF,          /* For NiCd, waiting for dead zone */
                      /* For LiIon, constant voltage phase */
-    CHARGING,        /* Can occur for all CONFIG_CHARGING options */
-                     /* For LiIon, the constant current phase */
+    CHARGING,        /* For NiCd, main charge phase */
+                     /* For LiIon, constant current phase */
 #endif
-} charge_state_type;
+};
 
 /* tells what the charger is doing */
-extern charge_state_type charge_state;
+extern enum charge_state_type charge_state;
 
 #ifdef CONFIG_CHARGING
 /*
  * Flag that the charger has been plugged in/removed: this is set for exactly
  * one time through the power loop when the charger has been plugged in.
  */
-typedef enum {
+enum charger_input_state_type
+{
     NO_CHARGER = 0,     /* No charger is present */
     CHARGER_UNPLUGGED,  /* Transitional state during CHARGER=>NO_CHARGER */
     CHARGER_PLUGGED,    /* Transitional state during NO_CHARGER=>CHARGER */
     CHARGER             /* Charger is present */
-} charger_input_state_type;
+};
 
 /* tells the state of the charge input */
-extern charger_input_state_type charger_input_state;
-#endif
+extern enum charger_input_state_type charger_input_state;
 
-#ifndef SIMULATOR
+/* Power input status saved on the power thread each loop */
+extern unsigned int power_thread_inputs;
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-#define START_TOPOFF_CHG    85  /* Battery % to start at top-off */
-#define START_TRICKLE_CHG   95  /* Battery % to start at trickle */
-
-#define POWER_MESSAGE_LEN 32     /* power thread status message */
-#define CHARGE_MAX_TIME_1500 450 /* minutes: maximum charging time for 1500 mAh batteries */
-                                 /* actual max time depends also on BATTERY_CAPACITY! */
-#define CHARGE_MIN_TIME   10     /* minutes: minimum charging time */
-#define TOPOFF_MAX_TIME   90     /* After charging, go to top off charge. How long should top off charge be? */
-#define TOPOFF_VOLTAGE    5650   /* which voltage is best? (millivolts) */
-#define TRICKLE_MAX_TIME  12*60  /* After top off charge, go to trickle charge. How long should trickle charge be? */
-#define TRICKLE_VOLTAGE   5450   /* which voltage is best? (millivolts) */
-
-#define START_TOPOFF_SEC    25   /* initial trickle_sec for topoff */
-#define START_TRICKLE_SEC   15   /* initial trickle_sec for trickle */
-
-#define PID_DEADZONE        4    /* PID proportional deadzone */
-
-extern char power_message[POWER_MESSAGE_LEN];
-
-extern int long_delta;          /* long term delta battery voltage */
-extern int short_delta;         /* short term delta battery voltage */
-
-extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was the charging started or stopped? */
-extern int powermgmt_last_cycle_level;         /* which level had the batteries at this time? */
-
-extern int pid_p;                /* PID proportional term */
-extern int pid_i;                /* PID integral term */
-extern int trickle_sec;          /* trickle charge: How many seconds per minute are we charging actually? */
-
-#endif /* CONFIG_CHARGING == CHARGING_CONTROL */
-
-#if defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM) /* Values for Ondio */
-# define CURRENT_NORMAL     95  /* average, nearly proportional to 1/U */
-# define CURRENT_USB         1  /* host powered in USB mode; avoid zero-div */
-# define CURRENT_BACKLIGHT   0  /* no backlight */
-#else            /* Values for HD based jukeboxes */
-#ifdef IRIVER_H100_SERIES
-# define CURRENT_NORMAL     80  /* 16h playback on 1300mAh battery */
-# define CURRENT_BACKLIGHT  23  /* from IriverBattery twiki page */
-# define CURRENT_SPDIF_OUT  10  /* optical SPDIF output on */
-# define CURRENT_RECORD    105  /* additional current while recording */
-#elif defined(IRIVER_H300_SERIES)
-# define CURRENT_NORMAL     80  /* 16h playback on 1300mAh battery from IriverRuntime wiki page */
-# define CURRENT_BACKLIGHT  23  /* FIXME: This needs to be measured, copied from H100 */
-# define CURRENT_RECORD    110  /* additional current while recording */
-#elif defined(IPOD_NANO)        /* iPOD Nano */
-# define CURRENT_NORMAL     32  /* MP3: ~9h playback out of 300mAh battery */
-# define CURRENT_BACKLIGHT  20  /* FIXME: this needs adjusting */
-#if defined(HAVE_RECORDING)
-# define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
-#endif
-#elif defined(IPOD_VIDEO)       /* iPOD Video */
-# define CURRENT_NORMAL     35  /* MP3: ~11h out of 400mAh battery (30GB) or ~17h out of 600mAh (60GB) */
-# define CURRENT_BACKLIGHT  20  /* FIXME: this needs adjusting */
-#if defined(HAVE_RECORDING)
-# define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
-#endif
-#elif defined(SANSA_E200)    /* Sandisk E200v1  */
-# define CURRENT_NORMAL     45  /* Mike's measurements in Jan 2008  */
-# define CURRENT_BACKLIGHT  40  /* Screen is about 20, blue LEDs are another 20, so 40 if both */
-# define CURRENT_RECORD     40  /* flash player, so this is just unboosted current*/
-#elif defined(SANSA_C200)    /* Sandisk C200v1 */
-# define CURRENT_NORMAL     45  /* Should be nearly identical to E200 */
-# define CURRENT_BACKLIGHT  40  /* Screen is about 20, blue LEDs are another 20, so 40 if both */
-# define CURRENT_RECORD     40  /* flash player, so this is just unboosted current*/
-#elif defined(IPOD_4G)       /* iPOD 4G */
-# define CURRENT_NORMAL     100  /* MP3: ~10.5h out of 1100mAh battery  */
-# define CURRENT_BACKLIGHT  20  /* FIXME: this needs adjusting */
-#if defined(HAVE_RECORDING)
-# define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
-#endif
-#else /* Not iriver H1x0, H3x0, nor Archos Ondio, nor iPod nano/Video/4G, nor Sansas */
-# define CURRENT_NORMAL    145  /* usual current in mA when using the AJB including some disk/backlight/... activity */
-# define CURRENT_BACKLIGHT  30  /* additional current when backlight always on */
-#if defined(HAVE_RECORDING)
-# define CURRENT_RECORD     35  /* FIXME: this needs adjusting */
-#endif
-#endif /* Not Archos Ondio */
-#define CURRENT_USB        500  /* usual current in mA in USB mode */
-#ifdef HAVE_REMOTE_LCD
-# define CURRENT_REMOTE      8  /* add. current when H100-remote connected */
-#endif /* HAVE_REMOTE_LCD */
-
-# define CURRENT_MIN_CHG    70  /* minimum charge current */
-# define MIN_CHG_V        8500  /* at 8.5v charger voltage get CURRENT_MIN_CHG */
-# ifdef IRIVER_H300_SERIES
-#  define CURRENT_MAX_CHG  650  /* maximum charging current */
-# else
-#  define CURRENT_MAX_CHG  350  /* maximum charging current */
-# endif
-# define MAX_CHG_V       10250  /* anything over 10.25v gives CURRENT_MAX_CHG */
-#endif /* not ONDIO */
+#endif /* CONFIG_CHARGING */
 
 #if CONFIG_CHARGING == CHARGING_TARGET
 /* Include target-specific definitions */
 #include "powermgmt-target.h"
 #endif
 
+#ifndef SIMULATOR
+
+/* Generic current values that are really rather meaningless - config header
+ * should define proper numbers. */
+#ifndef CURRENT_NORMAL
+#define CURRENT_NORMAL    145  /* usual current in mA */
+#endif
+
+#ifndef CURRENT_BACKLIGHT
+#define CURRENT_BACKLIGHT  30  /* additional current when backlight always on */
+#endif
+
+#ifdef HAVE_RECORDING
+#ifndef CURRENT_RECORD
+#define CURRENT_RECORD     35  /* additional recording current */
+#endif
+#endif /* HAVE_RECORDING */
+
+#ifndef CURRENT_USB
+#define CURRENT_USB       500 /* usual current in mA in USB mode */
+#endif
+
+#ifdef HAVE_REMOTE_LCD
+#define CURRENT_REMOTE      8  /* additional current when remote connected */
+#endif /* HAVE_REMOTE_LCD */
+
+#if CONFIG_CHARGING
+#ifndef CURRENT_MAX_CHG
+#define CURRENT_MAX_CHG   350  /* maximum charging current */
+#endif
+#endif /* CONFIG_CHARGING */
+
+#ifdef CHARGING_DEBUG_FILE
+#define POWERMGMT_DEBUG_STACK ((0x1000)/sizeof(long))
+#else
+#define POWERMGMT_DEBUG_STACK 0
+#endif
+
+#ifndef BATT_AVE_SAMPLES
+/* slw filter constant unless otherwise specified */
+#define BATT_AVE_SAMPLES 128
+#endif
+
+#ifndef POWER_THREAD_STEP_TICKS
+/* 2HZ sample rate unless otherwise specified */
+#define POWER_THREAD_STEP_TICKS (HZ/2)
+#endif
+
 extern unsigned short power_history[POWER_HISTORY_LEN];
 extern const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT];
 extern const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT];
@@ -179,12 +133,6 @@
 /* Start up power management thread */
 void powermgmt_init(void);
 
-/* Do target portion of init (for CHARGING_TARGET) - called on power thread */
-void powermgmt_init_target(void);
-
-/* Handle frequent tasks and call charging_algorithm_small_step */
-void power_thread_sleep(int ticks);
-
 #endif /* SIMULATOR */
 
 /* Returns battery statust */
@@ -193,9 +141,15 @@
 unsigned int battery_adc_voltage(void); /* voltage from ADC in millivolts */
 unsigned int battery_voltage(void); /* filtered batt. voltage in millivolts */
 
+#ifdef HAVE_BATTERY_SWITCH
+unsigned int input_millivolts(void); /* voltage that device is running from */
+
 /* Set the filtered battery voltage (to adjust it before beginning a charge
-   cycle for instance where old, loaded readings will likely be invalid). */
-void set_filtered_battery_voltage(int millivolts);
+ * cycle for instance where old, loaded readings will likely be invalid).
+ * Also readjust when battery switch is opened or closed.
+ */
+void reset_battery_filter(int millivolts);
+#endif /* HAVE_BATTERY_SWITCH */
 
 /* read unfiltered battery info */
 void battery_read_info(int *voltage, int *level);
@@ -203,13 +157,10 @@
 /* Tells if the battery level is safe for disk writes */
 bool battery_level_safe(void);
 
-#ifdef TARGET_POWERMGMT_FILTER_CHARGE_STATE
-int powermgmt_filter_charge_state(void);
-#endif
-
 void set_poweroff_timeout(int timeout);
 void set_battery_capacity(int capacity); /* set local battery capacity value */
-void set_battery_type(int type);         /* set local battery type */
+int  get_battery_capacity(void); /* get local battery capacity value */
+void set_battery_type(int type); /* set local battery type */
 
 void set_sleep_timer(int seconds);
 int get_sleep_timer(void);
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index 0fed847..5aa85c8 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -20,18 +20,14 @@
  *
  ****************************************************************************/
 #include "config.h"
-#include "cpu.h"
+#include "system.h"
 #include "kernel.h"
 #include "thread.h"
-#include "system.h"
 #include "debug.h"
-#include "panic.h"
 #include "adc.h"
 #include "string.h"
-#include "sprintf.h"
 #include "storage.h"
 #include "power.h"
-#include "button.h"
 #include "audio.h"
 #include "mp3_playback.h"
 #include "usb.h"
@@ -57,166 +53,39 @@
 #include "lcd-remote-target.h"
 #endif
 
-/*
- * Define DEBUG_FILE to create a csv (spreadsheet) with battery information
- * in it (one sample per minute).  This is only for very low level debug.
- */
-#undef  DEBUG_FILE
-#if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL)
-#include "file.h"
-#define DEBUG_FILE_NAME   "/powermgmt.csv"
-#define DEBUG_MESSAGE_LEN 133
-static char debug_message[DEBUG_MESSAGE_LEN];
-#define DEBUG_STACK ((0x1000)/sizeof(long))
-static int fd = -1;          /* write debug information to this file */
-static int wrcount = 0;
-#else
-#define DEBUG_STACK 0
-#endif
-
-static int shutdown_timeout = 0;
-#if CONFIG_CHARGING >= CHARGING_MONITOR
-charge_state_type charge_state = DISCHARGING;     /* charging mode */
-#endif
-
-static void send_battery_level_event(void);
-static int last_sent_battery_level = 100;
+/** Shared by sim **/
+int last_sent_battery_level = 100;
+/* battery level (0-100%) */
+int battery_percent = -1;
+void send_battery_level_event(void);
 
 #if CONFIG_CHARGING
-charger_input_state_type charger_input_state IDATA_ATTR;
+/* State of the charger input as seen by the power thread */
+enum charger_input_state_type charger_input_state;
+/* Power inputs as seen by the power thread */
+unsigned int power_thread_inputs;
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+/* Charging state (mode) as seen by the power thread */
+enum charge_state_type charge_state = DISCHARGING;
 #endif
+#endif /* CONFIG_CHARGING */
 
-#ifdef SIMULATOR /***********************************************************/
-
-#define BATT_MINMVOLT   2500            /* minimum millivolts of battery */
-#define BATT_MAXMVOLT   4500            /* maximum millivolts of battery */
-#define BATT_MAXRUNTIME (10 * 60)       /* maximum runtime with full battery in minutes */
-
-static unsigned int battery_millivolts = (unsigned int)BATT_MAXMVOLT;
-static int battery_percent = 100;       /* battery capacity level in percent */
-static int powermgmt_est_runningtime_min = BATT_MAXRUNTIME; /* estimated remaining time in minutes */
-
-static void battery_status_update(void)
-{
-    static time_t last_change = 0;
-    static bool charging = false;
-    time_t now;
-
-    time(&now);
-    if (last_change < now)
-    {
-        last_change = now;
-
-        /* change the values: */
-        if (charging)
-        {
-            if (battery_millivolts >= BATT_MAXMVOLT)
-            {
-                /* Pretend the charger was disconnected */
-                charging = false;
-                queue_broadcast(SYS_CHARGER_DISCONNECTED, 0);
-                last_sent_battery_level = 100;
-            }
-        }
-        else
-        {
-            if (battery_millivolts <= BATT_MINMVOLT)
-            {
-                /* Pretend the charger was connected */
-                charging = true;
-                queue_broadcast(SYS_CHARGER_CONNECTED, 0);
-                last_sent_battery_level = 0;
-            }
-        }
-        if (charging)
-            battery_millivolts += (BATT_MAXMVOLT - BATT_MINMVOLT) / 50;
-        else
-            battery_millivolts -= (BATT_MAXMVOLT - BATT_MINMVOLT) / 100;
-
-        battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) /
-                            (BATT_MAXMVOLT - BATT_MINMVOLT);
-        powermgmt_est_runningtime_min = battery_percent * BATT_MAXRUNTIME / 100;
-    }
-    send_battery_level_event();
-}
-
-void battery_read_info(int *voltage, int *level)
-{
-    battery_status_update();
-
-    if (voltage)
-        *voltage = battery_millivolts;
-
-    if (level)
-        *level = battery_percent;
-}
-
-unsigned int battery_voltage(void)
-{
-    battery_status_update();
-    return battery_millivolts;
-}
-
-int battery_level(void)
-{
-    battery_status_update();
-    return battery_percent;
-}
-
-int battery_time(void)
-{
-    battery_status_update();
-    return powermgmt_est_runningtime_min;
-}
-
-bool battery_level_safe(void)
-{
-    return battery_level() >= 10;
-}
-
-void set_poweroff_timeout(int timeout)
-{
-    (void)timeout;
-}
-
-void set_battery_capacity(int capacity)
-{
-  (void)capacity;
-}
-
-#if BATTERY_TYPES_COUNT > 1
-void set_battery_type(int type)
-{
-    (void)type;
-}
-#endif
-
-void reset_poweroff_timer(void)
-{
-}
-
-#ifdef HAVE_ACCESSORY_SUPPLY
-void accessory_supply_set(bool enable)
-{
-    (void)enable;
-}
-#endif
-
-#else /* not SIMULATOR ******************************************************/
-
+#ifndef SIMULATOR
+static int shutdown_timeout = 0;
 /*
  * Average battery voltage and charger voltage, filtered via a digital
  * exponential filter (aka. exponential moving average, scaled):
  * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N.
  */
-static unsigned int avgbat;     /* average battery voltage (filtering) */
-static unsigned int battery_millivolts;/* filtered battery voltage, millivolts */
+static unsigned int avgbat;
+/* filtered battery voltage, millivolts */
+static unsigned int battery_millivolts;
+/* default value, mAh */
+static int battery_capacity = BATTERY_CAPACITY_DEFAULT;
 
-/* battery level (0-100%) of this minute, updated once per minute */
-static int battery_percent  = -1;
-static int battery_capacity = BATTERY_CAPACITY_DEFAULT; /* default value, mAh */
+
 #if BATTERY_TYPES_COUNT > 1
-static int battery_type     = 0;
+static int battery_type = 0;
 #else
 #define battery_type 0
 #endif
@@ -224,7 +93,7 @@
 /* Power history: power_history[0] is the newest sample */
 unsigned short power_history[POWER_HISTORY_LEN];
 
-static char power_stack[DEFAULT_STACK_SIZE/2 + DEBUG_STACK];
+static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK];
 static const char power_thread_name[] = "power";
 
 static int poweroff_timeout = 0;
@@ -239,19 +108,6 @@
 static void battery_status_update(void);
 static int runcurrent(void);
 
-#ifndef TARGET_POWERMGMT_FILTER_CHARGE_STATE
-static inline int powermgmt_filter_charge_state(void)
-{
-#if CONFIG_CHARGING >= CHARGING_MONITOR
-    /* No adjustment of state */
-    return charge_state;
-#else
-    /* Always discharging */
-    return DISCHARGING;
-#endif
-}
-#endif /* TARGET_POWERMGMT_FILTER_CHARGE_STATE */
-
 void battery_read_info(int *voltage, int *level)
 {
     int millivolts = battery_adc_voltage();
@@ -272,20 +128,30 @@
 void set_battery_type(int type)
 {
     if (type != battery_type) {
+        if ((unsigned)type >= BATTERY_TYPES_COUNT)
+            type = 0;
+
         battery_type = type;
-        battery_status_update();     /* recalculate the battery status */
+        battery_status_update(); /* recalculate the battery status */
     }
 }
 #endif
 
 void set_battery_capacity(int capacity)
 {
+    if (capacity > BATTERY_CAPACITY_MAX)
+        capacity = BATTERY_CAPACITY_MAX;
+    if (capacity < BATTERY_CAPACITY_MIN)
+        capacity = BATTERY_CAPACITY_MIN;
+
     battery_capacity = capacity;
-    if (battery_capacity > BATTERY_CAPACITY_MAX)
-        battery_capacity = BATTERY_CAPACITY_MAX;
-    if (battery_capacity < BATTERY_CAPACITY_MIN)
-        battery_capacity = BATTERY_CAPACITY_MIN;
-    battery_status_update();     /* recalculate the battery status */
+
+    battery_status_update(); /* recalculate the battery status */
+}
+
+int get_battery_capacity(void)
+{
+    return battery_capacity;
 }
 
 int battery_time(void)
@@ -309,13 +175,19 @@
     return battery_millivolts;
 }
 
-#ifndef TARGET_BATTERY_LEVEL_SAFE
 /* Tells if the battery level is safe for disk writes */
 bool battery_level_safe(void)
 {
+#if defined(NO_LOW_BATTERY_SHUTDOWN)
+    return true;
+#elif defined(HAVE_BATTERY_SWITCH)
+    /* Cannot rely upon the battery reading to be valid and the
+     * device could be powered externally. */
+    return input_millivolts() > battery_level_dangerous[battery_type];
+#else
     return battery_millivolts > battery_level_dangerous[battery_type];
-}
 #endif
+}
 
 void set_poweroff_timeout(int timeout)
 {
@@ -324,7 +196,7 @@
 
 void set_sleep_timer(int seconds)
 {
-    if(seconds) {
+    if (seconds) {
         sleeptimer_active  = true;
         sleeptimer_endtick = current_tick + seconds * HZ;
     }
@@ -336,7 +208,7 @@
 
 int get_sleep_timer(void)
 {
-    if(sleeptimer_active)
+    if (sleeptimer_active)
         return (sleeptimer_endtick - current_tick) / HZ;
     else
         return 0;
@@ -345,45 +217,46 @@
 /* look into the percent_to_volt_* table and get a realistic battery level */
 static int voltage_to_percent(int voltage, const short* table)
 {
-    if (voltage <= table[0])
+    if (voltage <= table[0]) {
         return 0;
-    else
-        if (voltage >= table[10])
-            return 100;
-        else {
-            /* search nearest value */
-            int i = 0;
-            while ((i < 10) && (table[i+1] < voltage))
-                i++;
-            /* interpolate linear between the smaller and greater value */
-            return (i * 10) /* Tens digit, 10% per entry */
-                + (((voltage - table[i]) * 10)
-                   / (table[i+1] - table[i])); /* Ones digit: interpolated */
-        }
+    }
+    else if (voltage >= table[10]) {
+        return 100;
+    }
+    else {
+        /* search nearest value */
+        int i = 0;
+
+        while (i < 10 && table[i+1] < voltage)
+            i++;
+
+        /* interpolate linear between the smaller and greater value */
+        /* Tens digit, 10% per entry,  ones digit: interpolated */
+        return i*10 + (voltage - table[i])*10 / (table[i+1] - table[i]);
+    }
 }
 
 /* update battery level and estimated runtime, called once per minute or
  * when battery capacity / type settings are changed */
 static int voltage_to_battery_level(int battery_millivolts)
 {
-    const int state = powermgmt_filter_charge_state();
     int level;
 
-    if (state == DISCHARGING) {
-        level = voltage_to_percent(battery_millivolts,
-                    percent_to_volt_discharge[battery_type]);
-    }
 #if CONFIG_CHARGING >= CHARGING_MONITOR
-    else if (state == CHARGING) {
+    if (charging_state()) {
         /* battery level is defined to be < 100% until charging is finished */
-        level = MIN(voltage_to_percent(battery_millivolts,
-                    percent_to_volt_charge), 99);
+        level = voltage_to_percent(battery_millivolts,
+                                   percent_to_volt_charge);
+        if (level > 99)
+            level = 99;
     }
-    else {
-        /* in topoff/trickle charge, battery is by definition 100% full */
-        level = 100;
+    else
+#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */
+    {
+        /* DISCHARGING or error state */
+        level = voltage_to_percent(battery_millivolts,
+                                   percent_to_volt_discharge[battery_type]);
     }
-#endif
 
     return level;
 }
@@ -393,26 +266,25 @@
     int level = voltage_to_battery_level(battery_millivolts);
 
     /* calculate estimated remaining running time */
-    /* discharging: remaining running time */
-    /* charging:    remaining charging time */
 #if CONFIG_CHARGING >= CHARGING_MONITOR
-    if (powermgmt_filter_charge_state() == CHARGING) {
-        powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60
-                                      / 100 / (CURRENT_MAX_CHG - runcurrent());
+    if (charging_state()) {
+        /* charging: remaining charging time */
+        powermgmt_est_runningtime_min = (100 - level)*battery_capacity*60
+                / 100 / (CURRENT_MAX_CHG - runcurrent());
     }
     else
 #endif
-    {
-        if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0])
-            powermgmt_est_runningtime_min = (level + battery_percent) * 60 *
-                                         battery_capacity / 200 / runcurrent();
-
-        else if (battery_millivolts <= battery_level_shutoff[0])
-            powermgmt_est_runningtime_min = 0;
-
-        else
-            powermgmt_est_runningtime_min = (battery_millivolts -
-                                             battery_level_shutoff[0]) / 2;
+    /* discharging: remaining running time */
+    if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0]) {
+        powermgmt_est_runningtime_min = (level + battery_percent)*60
+                * battery_capacity / 200 / runcurrent();
+    }
+    else if (battery_millivolts <= battery_level_shutoff[0]) {
+        powermgmt_est_runningtime_min = 0;
+    }
+    else {
+        powermgmt_est_runningtime_min =
+            (battery_millivolts - battery_level_shutoff[0]) / 2;
     }
 
     battery_percent = level;
@@ -434,62 +306,55 @@
 static void handle_auto_poweroff(void)
 {
     long timeout = poweroff_timeout*60*HZ;
-    int  audio_stat = audio_status();
+    int audio_stat = audio_status();
+    long tick = current_tick;
 
 #if CONFIG_CHARGING
     /*
      * Inhibit shutdown as long as the charger is plugged in.  If it is
      * unplugged, wait for a timeout period and then shut down.
      */
-    if(charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) {
+    if (charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) {
         last_event_tick = current_tick;
     }
 #endif
 
-    if( !shutdown_timeout && query_force_shutdown()) {
+    if (!shutdown_timeout && query_force_shutdown()) {
         backlight_on();
         sys_poweroff();
     }
 
-    if(timeout &&
-#if CONFIG_TUNER && !defined(BOOTLOADER)
-       (!(get_radio_status() & FMRADIO_PLAYING)) &&
+    if (timeout &&
+#if CONFIG_TUNER
+        !(get_radio_status() & FMRADIO_PLAYING) &&
 #endif
-       !usb_inserted() &&
-       ((audio_stat == 0) ||
-        ((audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE)) &&
-         !sleeptimer_active)))
-    {
-        if(TIME_AFTER(current_tick, last_event_tick    + timeout) &&
-           TIME_AFTER(current_tick, storage_last_disk_activity() + timeout))
-        {
+        !usb_inserted() &&
+        (audio_stat == 0 ||
+         (audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE) &&
+          !sleeptimer_active))) {
+
+        if (TIME_AFTER(tick, last_event_tick + timeout) &&
+            TIME_AFTER(tick, storage_last_disk_activity() + timeout)) {
             sys_poweroff();
         }
     }
-    else
-    {
+    else if (sleeptimer_active) {
         /* Handle sleeptimer */
-        if(sleeptimer_active)
-        {
-            if(TIME_AFTER(current_tick, sleeptimer_endtick))
-            {
-                audio_stop();
-                if (usb_inserted()
+        if (TIME_AFTER(tick, sleeptimer_endtick)) {
+            audio_stop();
+
+            if (usb_inserted()
 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
-                    || ((charger_input_state == CHARGER) ||
-                    (charger_input_state == CHARGER_PLUGGED))
+                || charger_input_state != NO_CHARGER
 #endif
-                   )
-                {
-                    DEBUGF("Sleep timer timeout. Stopping...\n");
-                    set_sleep_timer(0);
-                    backlight_off(); /* Nighty, nighty... */
-                }
-                else
-                {
-                    DEBUGF("Sleep timer timeout. Shutting off...\n");
-                    sys_poweroff();
-                }
+            ) {
+                DEBUGF("Sleep timer timeout. Stopping...\n");
+                set_sleep_timer(0);
+                backlight_off(); /* Nighty, nighty... */
+            }
+            else {
+                DEBUGF("Sleep timer timeout. Shutting off...\n");
+                sys_poweroff();
             }
         }
     }
@@ -504,25 +369,24 @@
 
 #if MEM == 8 && !(defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM))
     /* assuming 192 kbps, the running time is 22% longer with 8MB */
-    current = (CURRENT_NORMAL*100/122);
+    current = CURRENT_NORMAL*100 / 122;
 #else
     current = CURRENT_NORMAL;
 #endif /* MEM == 8 */
 
-    if(usb_inserted()
-#if defined(HAVE_USB_POWER)
-  #if (CURRENT_USB < CURRENT_NORMAL)
+    if (usb_inserted()
+#ifdef HAVE_USB_POWER
+    #if (CURRENT_USB < CURRENT_NORMAL)
        || usb_powered()
-  #else
+    #else
        && !usb_powered()
-  #endif
+    #endif
 #endif
-    )
-    {
+    ) {
         current = CURRENT_USB;
     }
 
-#if defined(HAVE_BACKLIGHT) && !defined(BOOTLOADER)
+#if defined(HAVE_BACKLIGHT)
     if (backlight_get_current_timeout() == 0) /* LED always on */
         current += CURRENT_BACKLIGHT;
 #endif
@@ -542,7 +406,7 @@
         current += CURRENT_REMOTE;
 #endif
 
-    return(current);
+    return current;
 }
 
 
@@ -550,424 +414,76 @@
 #ifdef HAVE_RTC_ALARM
 static void power_thread_rtc_process(void)
 {
-    if (rtc_check_alarm_flag()) {
+    if (rtc_check_alarm_flag())
         rtc_enable_alarm(false);
-    }
 }
 #endif
 
-#ifndef TARGET_QUERY_FORCE_SHUTDOWN
+/* switch off unit if battery level is too low for reliable operation */
 bool query_force_shutdown(void)
 {
-#ifndef NO_LOW_BATTERY_SHUTDOWN
-    /* switch off unit if battery level is too low for reliable operation */
-    return battery_millivolts < battery_level_shutoff[battery_type];
-#else
+#if defined(NO_LOW_BATTERY_SHUTDOWN)
     return false;
+#elif defined(HAVE_BATTERY_SWITCH)
+    /* Cannot rely upon the battery reading to be valid and the
+     * device could be powered externally. */
+    return input_millivolts() < battery_level_shutoff[battery_type];
+#else
+    return battery_millivolts < battery_level_shutoff[battery_type];
 #endif
 }
-#endif /* TARGET_QUERY_FORCE_SHUTDOWN */
 
+#ifdef HAVE_BATTERY_SWITCH
 /*
- * This power thread maintains a history of battery voltage
- * and implements a charging algorithm.
+ * Reset the battery voltage filter to a new value and update the
+ * status.
  */
-#if CONFIG_CHARGING == CHARGING_CONTROL
-#define BATT_AVE_SAMPLES    32  /* filter constant / @ 2Hz sample rate */
-
-/*
- * For a complete description of the charging algorithm read
- * docs/CHARGING_ALGORITHM.
- */
-int long_delta;                     /* long term delta battery voltage */
-int short_delta;                    /* short term delta battery voltage */
-bool disk_activity_last_cycle = false;  /* flag set to aid charger time
-                                         * calculation */
-char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in
-                                           debug menu */
-                                        /* percentage at which charging
-                                           starts */
-int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the
-                                           charging started or
-                                           stopped? */
-int powermgmt_last_cycle_level = 0;     /* which level had the
-                                           batteries at this time? */
-int trickle_sec = 0;                    /* how many seconds should the
-                                           charger be enabled per
-                                           minute for trickle
-                                           charging? */
-int pid_p = 0;                      /* PID proportional term */
-int pid_i = 0;                      /* PID integral term */
-
-static inline void charging_algorithm_small_step(void)
-{
-    if (storage_disk_is_active()) {
-        /* flag hdd use for charging calculation */
-        disk_activity_last_cycle = true;
-    }
-
-#if defined(DEBUG_FILE)
-    /*
-     * If we have a lot of pending writes or if the disk is spining,
-     * fsync the debug log file.
-     */
-    if((wrcount > 10) || ((wrcount > 0) && storage_disk_is_active())) {
-        fsync(fd);
-        wrcount = 0;
-    }
-#endif /* defined(DEBUG_FILE) */
-}
-
-static inline void charging_algorithm_big_step(void)
-{
-    static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle
-                                                           * voltage level */
-    static int charge_max_time_idle = 0; /* max. charging duration, calculated at
-                                          * beginning of charging */
-    static int charge_max_time_now = 0; /* max. charging duration including
-                                         * hdd activity */
-    static int minutes_disk_activity = 0; /* count minutes of hdd use during
-                                           * charging */
-    static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */
-    int i;
-
-    if (charger_input_state == CHARGER_PLUGGED) {
-        pid_p = 0;
-        pid_i = 0;
-        snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in");
-        /*
-         * The charger was just plugged in.  If the battery level is
-         * nearly charged, just trickle.  If the battery is low, start
-         * a full charge cycle.  If the battery level is in between,
-         * top-off and then trickle.
-         */
-        if(battery_percent > START_TOPOFF_CHG) {
-            powermgmt_last_cycle_level = battery_percent;
-            powermgmt_last_cycle_startstop_min = 0;
-            if(battery_percent >= START_TRICKLE_CHG) {
-                charge_state = TRICKLE;
-                target_voltage = TRICKLE_VOLTAGE;
-            } else {
-                charge_state = TOPOFF;
-                target_voltage = TOPOFF_VOLTAGE;
-            }
-        } else {
-            /*
-             * Start the charger full strength
-             */
-            i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500;
-            charge_max_time_idle =
-                i * (100 + 35 - battery_percent) / 100;
-            if (charge_max_time_idle > i) {
-                charge_max_time_idle = i;
-            }
-            charge_max_time_now = charge_max_time_idle;
-
-            snprintf(power_message, POWER_MESSAGE_LEN,
-                     "ChgAt %d%% max %dm", battery_level(),
-                     charge_max_time_now);
-
-            /* enable the charger after the max time calc is done,
-               because battery_level depends on if the charger is
-               on */
-            DEBUGF("power: charger inserted and battery"
-                   " not full, charging\n");
-            powermgmt_last_cycle_level = battery_percent;
-            powermgmt_last_cycle_startstop_min = 0;
-            trickle_sec  = 60;
-            long_delta   = short_delta = 999999;
-            charge_state = CHARGING;
-        }
-    }
-
-    if (charge_state == CHARGING) {
-        /* alter charge time max length with extra disk use */
-        if (disk_activity_last_cycle) {
-            minutes_disk_activity++;
-            charge_max_time_now = charge_max_time_idle +
-                                 (minutes_disk_activity * 2 / 5);
-            disk_activity_last_cycle = false;
-            last_disk_activity = 0;
-        } else {
-            last_disk_activity++;
-        }
-        /*
-         * Check the delta voltage over the last X minutes so we can do
-         * our end-of-charge logic based on the battery level change.
-         *(no longer use minimum time as logic for charge end has 50
-         * minutes minimum charge built in)
-         */
-        if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) {
-            short_delta = power_history[0] -
-                          power_history[CHARGE_END_SHORTD - 1];
-        }
-
-        if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) {
-        /*
-         * Scan the history: the points where measurement is taken need to
-         * be fairly static. (check prior to short delta 'area')
-         * (also only check first and last 10 cycles - delta in middle OK)
-         */
-            long_delta = power_history[0] -
-                         power_history[CHARGE_END_LONGD - 1];
-
-            for(i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++) {
-                if(((power_history[i] - power_history[i+1]) >  50) ||
-                   ((power_history[i] - power_history[i+1]) < -50)) {
-                    long_delta = 777777;
-                    break;
-                }
-            }
-             for(i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++) {
-                if(((power_history[i] - power_history[i+1]) >  50) ||
-                   ((power_history[i] - power_history[i+1]) < -50)) {
-                    long_delta = 888888;
-                    break;
-                }
-            }
-        }
-
-        snprintf(power_message, POWER_MESSAGE_LEN,
-                 "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min,
-                 charge_max_time_now);
-        /*
-         * End of charge criteria (any qualify):
-         * 1) Charged a long time
-         * 2) DeltaV went negative for a short time ( & long delta static)
-         * 3) DeltaV was negative over a longer period (no disk use only)
-         * Note: short_delta and long_delta are millivolts
-         */
-        if ((powermgmt_last_cycle_startstop_min >= charge_max_time_now) ||
-            (short_delta <= -50 && long_delta < 50 ) || (long_delta  < -20 &&
-            last_disk_activity > CHARGE_END_LONGD)) {
-            if (powermgmt_last_cycle_startstop_min > charge_max_time_now) {
-                DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, "
-                       "enough!\n");
-                /*
-                 *have charged too long and deltaV detection did not
-                 *work!
-                 */
-                 snprintf(power_message, POWER_MESSAGE_LEN,
-                         "Chg tmout %d min", charge_max_time_now);
-                /*
-                 * Switch to trickle charging.  We skip the top-off
-                 * since we've effectively done the top-off operation
-                 * already since we charged for the maximum full
-                 * charge time.
-                 */
-                powermgmt_last_cycle_level = battery_percent;
-                powermgmt_last_cycle_startstop_min = 0;
-                charge_state = TRICKLE;
-
-                /*
-                 * set trickle charge target to a relative voltage instead
-                 * of an arbitrary value - the fully charged voltage may
-                 * vary according to ambient temp, battery condition etc
-                 * trickle target is -0.15v from full voltage acheived
-                 * topup target is -0.05v from full voltage
-                 */
-                target_voltage = power_history[0] - 150;
-
-            } else {
-                if(short_delta <= -5) {
-                    DEBUGF("power: short-term negative"
-                           " delta, enough!\n");
-                    snprintf(power_message, POWER_MESSAGE_LEN,
-                             "end negd %d %dmin", short_delta,
-                             powermgmt_last_cycle_startstop_min);
-                    target_voltage = power_history[CHARGE_END_SHORTD - 1]
-                                     - 50;
-                } else {
-                    DEBUGF("power: long-term small "
-                           "positive delta, enough!\n");
-                    snprintf(power_message, POWER_MESSAGE_LEN,
-                             "end lowd %d %dmin", long_delta,
-                             powermgmt_last_cycle_startstop_min);
-                    target_voltage = power_history[CHARGE_END_LONGD - 1]
-                                     - 50;
-                }
-                /*
-                 * Switch to top-off charging.
-                 */
-                powermgmt_last_cycle_level = battery_percent;
-                powermgmt_last_cycle_startstop_min = 0;
-                charge_state = TOPOFF;
-            }
-        }
-    }
-    else if (charge_state != DISCHARGING)  /* top off or trickle */
-    {
-        /*
-         *Time to switch from topoff to trickle?
-         */
-        if ((charge_state == TOPOFF) &&
-            (powermgmt_last_cycle_startstop_min > TOPOFF_MAX_TIME))
-        {
-            powermgmt_last_cycle_level = battery_percent;
-            powermgmt_last_cycle_startstop_min = 0;
-            charge_state = TRICKLE;
-            target_voltage = target_voltage - 100;
-        }
-        /*
-         * Adjust trickle charge time (proportional and integral terms).
-         * Note: I considered setting the level higher if the USB is
-         * plugged in, but it doesn't appear to be necessary and will
-         * generate more heat [gvb].
-         */
-
-        pid_p = ((signed)target_voltage - (signed)battery_millivolts) / 5;
-        if((pid_p <= PID_DEADZONE) && (pid_p >= -PID_DEADZONE))
-            pid_p = 0;
-
-        if((unsigned) battery_millivolts < target_voltage) {
-            if(pid_i < 60) {
-                pid_i++;        /* limit so it doesn't "wind up" */
-            }
-        } else {
-            if(pid_i > 0) {
-                pid_i--;        /* limit so it doesn't "wind up" */
-            }
-        }
-
-        trickle_sec = pid_p + pid_i;
-
-        if(trickle_sec > 60) {
-            trickle_sec = 60;
-        }
-        if(trickle_sec < 0) {
-            trickle_sec = 0;
-        }
-
-    } else if (charge_state == DISCHARGING) {
-        trickle_sec = 0;
-        /*
-         * The charger is enabled here only in one case: if it was
-         * turned on at boot time (power_init).  Turn it off now.
-         */
-        if (charger_enabled)
-            charger_enable(false);
-    }
-
-    if (charger_input_state == CHARGER_UNPLUGGED) {
-        /*
-         * The charger was just unplugged.
-         */
-        DEBUGF("power: charger disconnected, disabling\n");
-
-        charger_enable(false);
-        powermgmt_last_cycle_level = battery_percent;
-        powermgmt_last_cycle_startstop_min = 0;
-        trickle_sec  = 0;
-        pid_p        = 0;
-        pid_i        = 0;
-        charge_state = DISCHARGING;
-        snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge");
-    }
-
-    /* sleep for a minute */
-    if(trickle_sec > 0) {
-        charger_enable(true);
-        power_thread_sleep(HZ * trickle_sec);
-    }
-    if(trickle_sec < 60)
-        charger_enable(false);
-    power_thread_sleep(HZ * (60 - trickle_sec));
-
-#if defined(DEBUG_FILE)
-    if(usb_inserted()) {
-        if(fd >= 0) {
-            /* It is probably too late to close the file but we can try...*/
-            close(fd);
-            fd = -1;
-        }
-    } else {
-        if(fd < 0) {
-            fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT);
-            if(fd >= 0) {
-                snprintf(debug_message, DEBUG_MESSAGE_LEN,
-                "cycle_min, bat_millivolts, bat_percent, chgr_state"
-                " ,charge_state, pid_p, pid_i, trickle_sec\n");
-                write(fd, debug_message, strlen(debug_message));
-                wrcount = 99;   /* force a flush */
-            }
-        }
-        if(fd >= 0) {
-            snprintf(debug_message, DEBUG_MESSAGE_LEN,
-                    "%d, %d, %d, %d, %d, %d, %d, %d\n",
-                powermgmt_last_cycle_startstop_min, battery_millivolts,
-                battery_percent, charger_input_state, charge_state,
-                pid_p, pid_i, trickle_sec);
-            write(fd, debug_message, strlen(debug_message));
-            wrcount++;
-        }
-    }
-#endif /* defined(DEBUG_FILE) */
-
-    powermgmt_last_cycle_startstop_min++;
-}
-
-/*
- * Prepare charging for poweroff
- */
-static inline void charging_algorithm_close(void)
-{
-#if defined(DEBUG_FILE)
-    if(fd >= 0) {
-        close(fd);
-        fd = -1;
-    }
-#endif
-}
-#elif CONFIG_CHARGING == CHARGING_TARGET
-extern void charging_algorithm_big_step(void);
-extern void charging_algorithm_small_step(void);
-extern void charging_algorithm_close(void);
-
-void set_filtered_battery_voltage(int millivolts)
+void reset_battery_filter(int millivolts)
 {
     avgbat = millivolts * BATT_AVE_SAMPLES;
     battery_millivolts = millivolts;
     battery_status_update();
 }
+#endif /* HAVE_BATTERY_SWITCH */
 
-#else
-#define BATT_AVE_SAMPLES   128  /* slw filter constant for all others */
-
-static inline void charging_algorithm_small_step(void)
+/** Generic charging algorithms for common charging types **/
+#if CONFIG_CHARGING == CHARGING_SIMPLE
+static inline void charging_algorithm_step(void)
 {
-#if CONFIG_CHARGING == CHARGING_MONITOR
-    switch (charger_input_state)
-    {
-        case CHARGER_UNPLUGGED:
-        case NO_CHARGER:
-            charge_state = DISCHARGING;
-            break;
-        case CHARGER_PLUGGED:
-        case CHARGER:
-            if (charging_state()) {
-                charge_state = CHARGING;
-            } else {
-                charge_state = DISCHARGING;
-            }
-            break;
-    }
-#endif /* CONFIG_CHARGING == CHARGING_MONITOR */
+    /* Nothing to do */
 }
 
-static inline void charging_algorithm_big_step(void)
-{
-    /* sleep for a minute */
-    power_thread_sleep(HZ * 60);
-}
-
-/*
- * Prepare charging for poweroff
- */
 static inline void charging_algorithm_close(void)
 {
     /* Nothing to do */
 }
-#endif /* CONFIG_CHARGING == CHARGING_CONTROL */
+#elif CONFIG_CHARGING == CHARGING_MONITOR
+/*
+ * Monitor CHARGING/DISCHARGING state.
+ */
+static inline void charging_algorithm_step(void)
+{
+    switch (charger_input_state)
+    {
+    case CHARGER_PLUGGED:
+    case CHARGER:
+        if (charging_state()) {
+            charge_state = CHARGING;
+            break;
+        }
+    /* Fallthrough */
+    case CHARGER_UNPLUGGED:
+    case NO_CHARGER:
+        charge_state = DISCHARGING;
+        break;
+    }
+}
+
+static inline void charging_algorithm_close(void)
+{
+    /* Nothing to do */
+}
+#endif /* CONFIG_CHARGING == * */
 
 #if CONFIG_CHARGING
 /* Shortcut function calls - compatibility, simplicity. */
@@ -975,201 +491,281 @@
 /* Returns true if any power input is capable of charging. */
 bool charger_inserted(void)
 {
-    return power_input_status() & POWER_INPUT_CHARGER;
+    return power_thread_inputs & POWER_INPUT_CHARGER;
 }
 
 /* Returns true if any power input is connected - charging-capable
  * or not. */
 bool power_input_present(void)
 {
-    return power_input_status() & POWER_INPUT;
+    return power_thread_inputs & POWER_INPUT;
+}
+
+/*
+ * Detect charger inserted. Return true if the state is transistional.
+ */
+static inline bool detect_charger(unsigned int pwr)
+{
+    /*
+     * Detect charger plugged/unplugged transitions.  On a plugged or
+     * unplugged event, we return immediately, run once through the main
+     * loop (including the subroutines), and end up back here where we
+     * transition to the appropriate steady state charger on/off state.
+     */
+    if (pwr & POWER_INPUT_CHARGER) {
+        switch (charger_input_state)
+        {
+        case NO_CHARGER:
+        case CHARGER_UNPLUGGED:
+            charger_input_state = CHARGER_PLUGGED;
+            break;
+
+        case CHARGER_PLUGGED:
+            queue_broadcast(SYS_CHARGER_CONNECTED, 0);
+            last_sent_battery_level = 0;
+            charger_input_state = CHARGER;
+            break;
+
+        case CHARGER:
+            /* Steady state */
+            return false;
+        }
+    }
+    else {    /* charger not inserted */
+        switch (charger_input_state)
+        {
+        case NO_CHARGER:
+            /* Steady state */
+            return false;
+
+        case CHARGER_UNPLUGGED:
+            queue_broadcast(SYS_CHARGER_DISCONNECTED, 0);
+            last_sent_battery_level = 100;
+            charger_input_state = NO_CHARGER;
+            break;
+
+        case CHARGER_PLUGGED:
+        case CHARGER:
+            charger_input_state = CHARGER_UNPLUGGED;
+            break;
+        }
+    }
+
+    /* Transitional state */
+    return true;
 }
 #endif /* CONFIG_CHARGING */
 
 /*
- * This function is called to do the relativly long sleep waits from within the
- * main power_thread loop while at the same time servicing any other periodic
- * functions in the power thread which need to be called at a faster periodic
- * rate than the slow periodic rate of the main power_thread loop.
- *
- * While we are waiting for the time to expire, we average the battery
- * voltages.
+ * Monitor the presence of a charger and perform critical frequent steps
+ * such as running the battery voltage filter.
  */
-void power_thread_sleep(int ticks)
+static inline void power_thread_step(void)
 {
-    long tick_return = current_tick + ticks;
+    /* If the power off timeout expires, the main thread has failed
+       to shut down the system, and we need to force a power off */
+    if (shutdown_timeout) {
+        shutdown_timeout -= POWER_THREAD_STEP_TICKS;
 
-    do
-    {
-#if CONFIG_CHARGING
-        /*
-         * Detect charger plugged/unplugged transitions.  On a plugged or
-         * unplugged event, we return immediately, run once through the main
-         * loop (including the subroutines), and end up back here where we
-         * transition to the appropriate steady state charger on/off state.
-         */
-        if(power_input_status() & POWER_INPUT_CHARGER) {
-            switch(charger_input_state) {
-                case NO_CHARGER:
-                case CHARGER_UNPLUGGED:
-                    charger_input_state = CHARGER_PLUGGED;
-                    tick_return = current_tick;
-                    goto do_small_step; /* Algorithm should see transition */
-                case CHARGER_PLUGGED:
-                    queue_broadcast(SYS_CHARGER_CONNECTED, 0);
-                    last_sent_battery_level = 0;
-                    charger_input_state = CHARGER;
-                    break;
-                case CHARGER:
-                    break;
-            }
-        } else {    /* charger not inserted */
-            switch(charger_input_state) {
-                case NO_CHARGER:
-                    break;
-                case CHARGER_UNPLUGGED:
-                    queue_broadcast(SYS_CHARGER_DISCONNECTED, 0);
-                    last_sent_battery_level = 100;
-                    charger_input_state = NO_CHARGER;
-                    break;
-                case CHARGER_PLUGGED:
-                case CHARGER:
-                    charger_input_state = CHARGER_UNPLUGGED;
-                    tick_return = current_tick;
-                    goto do_small_step; /* Algorithm should see transition */
-            }
-        }
-#endif /* CONFIG_CHARGING */
-
-        ticks = tick_return - current_tick;
-
-        if (ticks > 0) {
-            ticks = MIN(HZ/2, ticks);
-            sleep(ticks);
-        }
-
-        /* If the power off timeout expires, the main thread has failed
-           to shut down the system, and we need to force a power off */
-        if(shutdown_timeout) {
-            shutdown_timeout -= MAX(ticks, 1);
-            if(shutdown_timeout <= 0)
-                power_off();
-        }
+        if (shutdown_timeout <= 0)
+            power_off();
+    }
 
 #ifdef HAVE_RTC_ALARM
-        power_thread_rtc_process();
+    power_thread_rtc_process();
 #endif
 
-        /*
-         * Do a digital exponential filter.  We don't sample the battery if
-         * the disk is spinning unless we are in USB mode (the disk will most
-         * likely always be spinning in USB mode) or charging.
-         */
-        if (!storage_disk_is_active() || usb_inserted()
+    /*
+     * Do a digital exponential filter.  We don't sample the battery if
+     * the disk is spinning unless we are in USB mode (the disk will most
+     * likely always be spinning in USB mode) or charging.
+     */
+    if (!storage_disk_is_active() || usb_inserted()
 #if CONFIG_CHARGING >= CHARGING_MONITOR
-                || charger_input_state == CHARGER
+            || charger_input_state == CHARGER
 #endif
-        ) {
-            avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES);
-            /*
-             * battery_millivolts is the millivolt-scaled filtered battery value.
-             */
-            battery_millivolts = avgbat / BATT_AVE_SAMPLES;
+    ) {
+        avgbat += battery_adc_voltage() - avgbat / BATT_AVE_SAMPLES;
+        /*
+         * battery_millivolts is the millivolt-scaled filtered battery value.
+         */
+        battery_millivolts = avgbat / BATT_AVE_SAMPLES;
 
-            /* update battery status every time an update is available */
-            battery_status_update();
-        }
-        else if (battery_percent < 8) {
-            /* If battery is low, observe voltage during disk activity.
-             * Shut down if voltage drops below shutoff level and we are not
-             * using NiMH or Alkaline batteries.
-             */
-            battery_millivolts = (battery_adc_voltage() +
-                                  battery_millivolts + 1) / 2;
-
-            /* update battery status every time an update is available */
-            battery_status_update();
-
-            if (!shutdown_timeout && query_force_shutdown()) {
-                sys_poweroff();
-            }
-            else {
-                avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES);
-            }
-        }
-
-#if CONFIG_CHARGING
-    do_small_step:
-#endif
-        charging_algorithm_small_step();
+        /* update battery status every time an update is available */
+        battery_status_update();
     }
-    while (TIME_BEFORE(current_tick, tick_return));
-}
+    else if (battery_percent < 8) {
+        /*
+         * If battery is low, observe voltage during disk activity.
+         * Shut down if voltage drops below shutoff level and we are not
+         * using NiMH or Alkaline batteries.
+         */
+        battery_millivolts = (battery_adc_voltage() +
+                              battery_millivolts + 1) / 2;
+
+        /* update battery status every time an update is available */
+        battery_status_update();
+
+        if (!shutdown_timeout && query_force_shutdown()) {
+            sys_poweroff();
+        }
+        else {
+            avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES;
+        }
+    }
+} /* power_thread_step */
 
 static void power_thread(void)
 {
+    long next_power_hist;
+
     /* Delay reading the first battery level */
 #ifdef MROBE_100
-    while(battery_adc_voltage()>4200)  /* gives false readings initially */
+    while (battery_adc_voltage() > 4200) /* gives false readings initially */
 #endif
-    sleep(HZ/100);
+    {
+        sleep(HZ/100);
+    }
+
+#if CONFIG_CHARGING
+    /* Initialize power input status before calling other routines. */
+    power_thread_inputs = power_input_status();
+#endif
 
     /* initialize the voltages for the exponential filter */
     avgbat = battery_adc_voltage() + 15;
 
 #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
-        /* The battery voltage is usually a little lower directly after
-           turning on, because the disk was used heavily. Raise it by 5% */
+    /* The battery voltage is usually a little lower directly after
+       turning on, because the disk was used heavily. Raise it by 5% */
 #if CONFIG_CHARGING
-    if(!charger_inserted()) /* only if charger not connected */
+    if (!charger_inserted()) /* only if charger not connected */
 #endif
+    {
         avgbat += (percent_to_volt_discharge[battery_type][6] -
                    percent_to_volt_discharge[battery_type][5]) / 2;
+    }
 #endif /* HAVE_DISK_STORAGE */
 
     avgbat = avgbat * BATT_AVE_SAMPLES;
     battery_millivolts = avgbat / BATT_AVE_SAMPLES;
+    power_history[0] = battery_millivolts;
 
 #if CONFIG_CHARGING
-    if(charger_inserted()) {
-        battery_percent  = voltage_to_percent(battery_millivolts,
-                           percent_to_volt_charge);
-    } else
+    if (charger_inserted()) {
+        battery_percent = voltage_to_percent(battery_millivolts,
+                            percent_to_volt_charge);
+    }
+    else
 #endif
-    {   battery_percent  = voltage_to_percent(battery_millivolts,
-                           percent_to_volt_discharge[battery_type]);
-        battery_percent += (battery_percent < 100);
+    {
+        battery_percent = voltage_to_percent(battery_millivolts,
+                            percent_to_volt_discharge[battery_type]);
+        battery_percent += battery_percent < 100;
     }
 
 #if CONFIG_CHARGING == CHARGING_TARGET
     powermgmt_init_target();
 #endif
 
+    next_power_hist = current_tick + HZ*60;
+
     while (1)
     {
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+        unsigned int pwr = power_input_status();
+#ifdef HAVE_BATTERY_SWITCH
+        if ((pwr ^ power_thread_inputs) & POWER_INPUT_BATTERY) {
+            sleep(HZ/10);
+            reset_battery_filter(battery_adc_voltage());
+        }
+#endif
+        power_thread_inputs = pwr;
+
+        if (!detect_charger(pwr))
+#endif /* CONFIG_CHARGING */
+        {
+            /* Steady state */
+            sleep(POWER_THREAD_STEP_TICKS);
+
+            /* Do common power tasks */
+            power_thread_step();
+        }
+
+        /* Perform target tasks */
+        charging_algorithm_step();
+
+        if (TIME_BEFORE(current_tick, next_power_hist))
+            continue;
+
+        /* increment to ensure there is a record for every minute
+         * rather than go forward from the current tick */
+        next_power_hist += HZ*60;
+
         /* rotate the power history */
-        memmove(power_history + 1, power_history,
+        memmove(&power_history[1], &power_history[0],
                 sizeof(power_history) - sizeof(power_history[0]));
 
         /* insert new value at the start, in millivolts 8-) */
         power_history[0] = battery_millivolts;
 
-        charging_algorithm_big_step();
-
         handle_auto_poweroff();
     }
-}
+} /* power_thread */
 
 void powermgmt_init(void)
 {
     /* init history to 0 */
-    memset(power_history, 0x00, sizeof(power_history));
+    memset(power_history, 0, sizeof(power_history));
     create_thread(power_thread, power_stack, sizeof(power_stack), 0,
                   power_thread_name IF_PRIO(, PRIORITY_SYSTEM)
                   IF_COP(, CPU));
 }
 
-#endif /* SIMULATOR */
+/* Various hardware housekeeping tasks relating to shutting down the player */
+void shutdown_hw(void)
+{
+    charging_algorithm_close();
+    audio_stop();
+
+    if (battery_level_safe()) { /* do not save on critical battery */
+#ifdef HAVE_LCD_BITMAP
+        glyph_cache_save();
+#endif
+        if (storage_disk_is_active())
+            storage_spindown(1);
+    }
+
+    while (storage_disk_is_active())
+        sleep(HZ/10);
+
+#if CONFIG_CODEC == SWCODEC
+    audiohw_close();
+#else
+    mp3_shutdown();
+#endif
+
+    /* If HD is still active we try to wait for spindown, otherwise the
+       shutdown_timeout in power_thread_step will force a power off */
+    while (storage_disk_is_active())
+        sleep(HZ/10);
+
+#ifndef HAVE_LCD_COLOR
+    lcd_set_contrast(0);
+#endif
+#ifdef HAVE_REMOTE_LCD
+    lcd_remote_set_contrast(0);
+#endif
+#ifdef HAVE_LCD_SHUTDOWN
+    lcd_shutdown();
+#endif
+
+    /* Small delay to make sure all HW gets time to flush. Especially
+       eeprom chips are quite slow and might be still writing the last
+       byte. */
+    sleep(HZ/4);
+    power_off();
+}
 
 void sys_poweroff(void)
 {
@@ -1177,12 +773,11 @@
     logf("sys_poweroff()");
     /* If the main thread fails to shut down the system, we will force a
        power off after an 20 second timeout - 28 seconds if recording */
-    if (shutdown_timeout == 0)
-    {
-#if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR)
+    if (shutdown_timeout == 0) {
+#if defined(IAUDIO_X5) || defined(IAUDIO_M5)
         pcf50606_reset_timeout(); /* Reset timer on first attempt only */
 #endif
-#if defined(HAVE_RECORDING) && !defined(BOOTLOADER)
+#ifdef HAVE_RECORDING
         if (audio_status() & AUDIO_STATUS_RECORD)
             shutdown_timeout += HZ*8;
 #endif
@@ -1195,9 +790,9 @@
 
 void cancel_shutdown(void)
 {
-    logf("sys_cancel_shutdown()");
+    logf("cancel_shutdown()");
 
-#if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR)
+#if defined(IAUDIO_X5) || defined(IAUDIO_M5)
     /* TODO: Move some things to target/ tree */
     if (shutdown_timeout)
         pcf50606_reset_timeout();
@@ -1205,66 +800,23 @@
 
     shutdown_timeout = 0;
 }
-
-/* Various hardware housekeeping tasks relating to shutting down the jukebox */
-void shutdown_hw(void)
-{
-#ifndef SIMULATOR
-    charging_algorithm_close();
-    audio_stop();
-    if (battery_level_safe()) { /* do not save on critical battery */
-#ifdef HAVE_LCD_BITMAP
-        glyph_cache_save();
-#endif
-        if(storage_disk_is_active())
-            storage_spindown(1);
-    }
-    while(storage_disk_is_active())
-        sleep(HZ/10);
-
-#if CONFIG_CODEC != SWCODEC
-    mp3_shutdown();
-#else
-    audiohw_close();
-#endif
-
-    /* If HD is still active we try to wait for spindown, otherwise the
-       shutdown_timeout in power_thread_sleep will force a power off */
-    while(storage_disk_is_active())
-        sleep(HZ/10);
-#ifndef HAVE_LCD_COLOR
-    lcd_set_contrast(0);
-#endif
-#ifdef HAVE_REMOTE_LCD
-    lcd_remote_set_contrast(0);
-#endif
-
-#ifdef HAVE_LCD_SHUTDOWN
-    lcd_shutdown();
-#endif
-
-    /* Small delay to make sure all HW gets time to flush. Especially
-       eeprom chips are quite slow and might be still writing the last
-       byte. */
-    sleep(HZ/4);
-    power_off();
-#endif /* #ifndef SIMULATOR */
-}
+#endif /* SIMULATOR */
 
 /* Send system battery level update events on reaching certain significant
-   levels.  This must be called after battery_percent has been updated. */
-static void send_battery_level_event(void)
+   levels. This must be called after battery_percent has been updated. */
+void send_battery_level_event(void)
 {
     static const int levels[] = { 5, 15, 30, 50, 0 };
     const int *level = levels;
+
     while (*level)
     {
-        if (battery_percent <= *level && last_sent_battery_level > *level)
-        { 
+        if (battery_percent <= *level && last_sent_battery_level > *level) {
             last_sent_battery_level = *level;
             queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
             break;
         }
+
         level++;
     }
 }
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
index fa9c7b0..b4a6c61 100644
--- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
@@ -168,7 +168,7 @@
 /* All code has a preference for the main charger being connected over
  * USB. USB is considered in the algorithm only if it is the sole source. */
 static uint32_t int_sense0 = 0;      /* Interrupt Sense 0 bits */
-static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */
+static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */
 static int charger_total_timer = 0;  /* Total allowed charging time */
 static int icharger_ave = 0;         /* Filtered charging current */
 static bool charger_close = false;   /* Shutdown notification */
@@ -181,7 +181,7 @@
 static int chgcurr_timer = 0;        /* Countdown to CHGCURR error */
 #define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */
 #define WATCHDOG_TIMEOUT       (10*2) /* If not serviced, poweroff in 10s */
-#define CHGCURR_TIMEOUT        (2*2)  /* 2s debounce */
+#define CHGCURR_TIMEOUT        (4*2)  /* 4s debounce */
 
 /* Temperature monitoring */
 static enum
@@ -217,7 +217,7 @@
 /* Return true if the main charger is connected. */
 static bool main_charger_connected(void)
 {
-    return (power_status &
+    return (last_inputs &
             POWER_INPUT_MAIN_CHARGER &
             POWER_INPUT_CHARGER) != 0;
 }
@@ -233,16 +233,14 @@
         return BATT_USB_VAUTO_RECHARGE;
 }
 
-#ifndef NO_LOW_BATTERY_SHUTDOWN
 /* Return greater of supply (BP) or filtered battery voltage. */
-static unsigned int input_millivolts(void)
+unsigned int input_millivolts(void)
 {
     unsigned int app_millivolts = application_supply_adc_voltage();
     unsigned int bat_millivolts = battery_voltage();
 
     return MAX(app_millivolts, bat_millivolts);
 }
-#endif
 
 /* Get smoothed readings for initializing filtered data. */
 static int stat_battery_reading(int type)
@@ -292,7 +290,7 @@
 
     if (millivolts != INT_MIN)
     {
-        set_filtered_battery_voltage(millivolts);
+        reset_battery_filter(millivolts);
         return true;
     }
 
@@ -357,13 +355,13 @@
     int usb_select;
     uint32_t i;
 
-    usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB)
+    usb_select = ((last_inputs & POWER_INPUT) == POWER_INPUT_USB)
                     ? 1 : 0;
 
     if (charge_state == DISCHARGING && usb_select == 1)
     {
         /* USB-only, DISCHARGING, = maintaining battery */
-        int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1;
+        int select = (last_inputs & POWER_INPUT_CHARGER) ? 0 : 1;
         charger_setting = charger_bits[CHARGING+1][select];
     }
     else
@@ -458,7 +456,7 @@
     if (ok)
     {
         /* Is the battery even connected? */
-        ok = (power_status & POWER_INPUT_BATTERY) != 0;
+        ok = (last_inputs & POWER_INPUT_BATTERY) != 0;
     }
 
     if (ok)
@@ -591,20 +589,6 @@
 #endif
 }
 
-/* Returns CHARGING or DISCHARGING since that's all we really do. */
-int powermgmt_filter_charge_state(void)
-{
-    switch(charge_state)
-    {
-    case TRICKLE:
-    case TOPOFF:
-    case CHARGING:
-        return CHARGING;
-    default:
-        return DISCHARGING;
-    }
-}
-
 /* Returns true if the unit is charging the batteries. */
 bool charging_state(void)
 {
@@ -625,24 +609,6 @@
     return icharger_ave / ICHARGER_AVE_SAMPLES;
 }
 
-bool query_force_shutdown(void)
-{
-#ifndef NO_LOW_BATTERY_SHUTDOWN
-    return input_millivolts() < battery_level_shutoff[0];
-#else
-    return false;
-#endif
-}
-
-bool battery_level_safe(void)
-{
-#ifndef NO_LOW_BATTERY_SHUTDOWN
-    return input_millivolts() > battery_level_dangerous[0];
-#else
-    return true;
-#endif
-}
-
 static void charger_plugged(void)
 {
     adc_enable_channel(ADC_BATTERY_TEMP, true);
@@ -662,7 +628,7 @@
     }
 
     /* Might need to reevaluate these bits in charger_none. */
-    power_status &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
+    last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
     temp_state = TEMP_STATE_NORMAL;
     autorecharge_counter = 0;
     chgcurr_timer = 0;
@@ -672,15 +638,11 @@
 
 static void charger_none(void)
 {
-    unsigned int pwr = power_input_status();
+    unsigned int pwr = power_thread_inputs;
 
-    if (power_status != pwr)
+    if (last_inputs != pwr)
     {
-        /* If battery switch state changed, reset filter. */
-        if ((power_status ^ pwr) & POWER_INPUT_BATTERY)
-            update_filtered_battery_voltage();
-
-        power_status = pwr;
+        last_inputs = pwr;
 
         if (charge_state == CHARGE_STATE_DISABLED)
             return;
@@ -696,7 +658,7 @@
         else
         {
             charger_unplugged();
-            power_status = pwr; /* Restore status */
+            last_inputs = pwr; /* Restore status */
         }
     }
     else if (charger_setting != 0)
@@ -716,17 +678,13 @@
 
 static void charger_control(void)
 {
-    unsigned int pwr = power_input_status();
+    unsigned int pwr = power_thread_inputs;
 
-    if (power_status != pwr)
+    if (last_inputs != pwr)
     {
-        unsigned int changed = power_status ^ pwr;
+        unsigned int changed = last_inputs ^ pwr;
 
-        power_status = pwr;
-
-        /* If battery switch state changed, reset filter. */
-        if (changed & POWER_INPUT_BATTERY)
-            update_filtered_battery_voltage();
+        last_inputs = pwr;
 
         if (charger_setting != 0)
             charger_setting = CHARGER_ADJUST;
@@ -771,12 +729,11 @@
     {
         /* Battery voltage may have dropped and a charge cycle should
          * start again. Debounced. */
-        if (autorecharge_counter < 0)
+        if (autorecharge_counter < 0 &&
+            battery_adc_voltage() < BATT_FULL_VOLTAGE)
         {
-            /* Try starting a cycle now regardless of battery level to
-             * allow user to ensure the battery is topped off. It
-             * will soon turn off if already full. */
-            autorecharge_counter = 0;
+            /* Try starting a cycle now if battery isn't already topped
+             * off to allow user to ensure the battery is full. */
         }
         else if (battery_voltage() > auto_recharge_voltage())
         {
@@ -791,6 +748,8 @@
             break;
         }
 
+        autorecharge_counter = 0;
+
         charging_set_thread_priority(true);
 
         if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
@@ -858,10 +817,12 @@
 }
 
 /* Main charging algorithm - called from powermgmt.c */
-void charging_algorithm_small_step(void)
+void charging_algorithm_step(void)
 {
+#ifdef IMX31_ALLOW_CHARGING
     if (service_wdt)
         watchdog_service();
+#endif
 
     /* Switch by input state */
     switch (charger_input_state)
@@ -909,12 +870,6 @@
     }
 }
 
-void charging_algorithm_big_step(void)
-{
-    /* Sleep for one minute */
-    power_thread_sleep(HZ*60);
-}
-
 /* Disable the charger and prepare for poweroff - called off-thread so we
  * signal the charging thread to prepare to quit. */
 void charging_algorithm_close(void)
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
index 8ad4af8..1b55a4e 100644
--- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
@@ -53,6 +53,7 @@
 #define BATT_VTRICKLE_CHARGE        2900 /* Must charge slowly */
 #define BATT_VSLOW_CHARGE           3500 /* Lower-current charge mode below
                                           * this level */
+#define BATT_FULL_VOLTAGE           4161 /* Battery already topped */
 #define BATT_VAUTO_RECHARGE         4100 /* When to begin another cycle */
 #define BATT_USB_VAUTO_RECHARGE     4000 /* When to cycle with USB only */
 #define BATT_USB_VSTOP              4140 /* When to "stop" when USB only */
@@ -92,23 +93,13 @@
 #define BATT_AVE_SAMPLES            32
 #define ICHARGER_AVE_SAMPLES        32
 
+void powermgmt_init_target(void);
+void charging_algorithm_step(void);
+void charging_algorithm_close(void);
+
 /* Provide filtered charge current */
 int battery_charge_current(void);
 
-#ifndef SIMULATOR
-/* Indicate various functions that require implementation at the target level.
- * This is because the battery could be low or the battery switch is off but
- * with the main charger attached which implies safe power for anything. The
- * true battery reading is always reported for voltage readings and not the
- * value at the application supply. */
-#define TARGET_QUERY_FORCE_SHUTDOWN
-
-/* For this the application supply is read out if the charger is attached or
- * the battery read if not (completely hardware selected at BP). */
-#define TARGET_BATTERY_LEVEL_SAFE
-
-/* The state should be adjusted to CHARGING or DISCHARGING */
-#define TARGET_POWERMGMT_FILTER_CHARGE_STATE
-#endif /* SIMULATOR */
+#define CURRENT_MAX_CHG battery_charge_current()
 
 #endif /* POWERMGMT_TARGET_H */
diff --git a/firmware/target/arm/iriver/h10/power-h10.c b/firmware/target/arm/iriver/h10/power-h10.c
index deca325..1a1f6af 100644
--- a/firmware/target/arm/iriver/h10/power-h10.c
+++ b/firmware/target/arm/iriver/h10/power-h10.c
@@ -33,10 +33,6 @@
 #include "logf.h"
 #include "usb.h"
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-bool charger_enabled;
-#endif
-
 #if CONFIG_TUNER
 
 bool tuner_power(bool status)
diff --git a/firmware/target/arm/olympus/mrobe-100/power-mr100.c b/firmware/target/arm/olympus/mrobe-100/power-mr100.c
index c3eb96b..46b99fb 100644
--- a/firmware/target/arm/olympus/mrobe-100/power-mr100.c
+++ b/firmware/target/arm/olympus/mrobe-100/power-mr100.c
@@ -29,10 +29,6 @@
 #include "logf.h"
 #include "usb.h"
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-bool charger_enabled;
-#endif
-
 void power_init(void)
 {
     /* Enable power-off bit */
diff --git a/firmware/target/arm/philips/hdd1630/power-hdd1630.c b/firmware/target/arm/philips/hdd1630/power-hdd1630.c
index 03a5794..4e7172e 100755
--- a/firmware/target/arm/philips/hdd1630/power-hdd1630.c
+++ b/firmware/target/arm/philips/hdd1630/power-hdd1630.c
@@ -29,10 +29,6 @@
 #include "logf.h"
 #include "usb.h"
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-bool charger_enabled;
-#endif
-
 void power_init(void)
 {
     /* power off bit */
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c
index 6522e65..49f7e2e 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c
@@ -19,9 +19,10 @@
  * KIND, either express or implied.
  *
  ****************************************************************************/
-
 #include "config.h"
+#include "system.h"
 #include "adc.h"
+#include "power.h"
 #include "powermgmt.h"
 
 const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
@@ -58,3 +59,17 @@
     return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
 }
 
+unsigned int input_millivolts(void)
+{
+
+    unsigned int batt_millivolts = battery_voltage();
+
+    if ((power_thread_inputs & POWER_INPUT_BATTERY) == 0) {
+        /* Just return a safe value if battery isn't connected */
+        return 4050;
+    }
+
+    return batt_millivolts;
+}
+
+
diff --git a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
index abf5790..fe5460d 100644
--- a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
+++ b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
@@ -33,10 +33,6 @@
 #include "logf.h"
 #include "usb.h"
 
-#if CONFIG_CHARGING == CHARGING_CONTROL
-bool charger_enabled;
-#endif
-
 void power_init(void)
 {
 }
diff --git a/firmware/target/sh/archos/recorder/power-recorder.c b/firmware/target/sh/archos/recorder/power-recorder.c
index d90c029..8d7ea5f 100644
--- a/firmware/target/sh/archos/recorder/power-recorder.c
+++ b/firmware/target/sh/archos/recorder/power-recorder.c
@@ -25,9 +25,10 @@
 #include "kernel.h"
 #include "system.h"
 #include "power.h"
+#include "powermgmt-target.h"
 #include "usb.h"
 
-bool charger_enabled;
+static bool charger_on;
 
 void power_init(void)
 {
@@ -48,13 +49,18 @@
     if(on)
     {
         and_b(~0x20, &PBDRL);
-        charger_enabled = 1;
     } 
     else 
     {
         or_b(0x20, &PBDRL);
-        charger_enabled = 0;
     }
+
+    charger_on = on;
+}
+
+bool charger_enabled(void)
+{
+    return charger_on;
 }
 
 void ide_power_enable(bool on)
diff --git a/firmware/target/sh/archos/recorder/powermgmt-recorder.c b/firmware/target/sh/archos/recorder/powermgmt-recorder.c
index 6de5cc8..7b18420 100644
--- a/firmware/target/sh/archos/recorder/powermgmt-recorder.c
+++ b/firmware/target/sh/archos/recorder/powermgmt-recorder.c
@@ -19,9 +19,13 @@
  * KIND, either express or implied.
  *
  ****************************************************************************/
-
 #include "config.h"
+#include "system.h"
+#include <sprintf.h>
+#include "debug.h"
+#include "storage.h"
 #include "adc.h"
+#include "power.h"
 #include "powermgmt.h"
 
 const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
@@ -60,3 +64,434 @@
 {
     return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
 }
+
+/** Charger control **/
+#ifdef CHARGING_DEBUG_FILE
+#include "file.h"
+#define DEBUG_FILE_NAME "/powermgmt.csv"
+#define DEBUG_MESSAGE_LEN 133
+static char debug_message[DEBUG_MESSAGE_LEN];
+static int fd = -1;          /* write debug information to this file */
+static int wrcount = 0;
+#endif /* CHARGING_DEBUG_FILE */
+
+/*
+ * For a complete description of the charging algorithm read
+ * docs/CHARGING_ALGORITHM.
+ */
+int long_delta;                     /* long term delta battery voltage */
+int short_delta;                    /* short term delta battery voltage */
+bool disk_activity_last_cycle = false;  /* flag set to aid charger time
+                                         * calculation */
+char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in
+                                           debug menu */
+                                        /* percentage at which charging
+                                           starts */
+int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the
+                                           charging started or
+                                           stopped? */
+int powermgmt_last_cycle_level = 0;     /* which level had the
+                                           batteries at this time? */
+int trickle_sec = 0;                    /* how many seconds should the
+                                           charger be enabled per
+                                           minute for trickle
+                                           charging? */
+int pid_p = 0;                      /* PID proportional term */
+int pid_i = 0;                      /* PID integral term */
+
+static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle
+                                                       * voltage level */
+static int charge_max_time_idle = 0; /* max. charging duration, calculated at
+                                      * beginning of charging */
+static int charge_max_time_now = 0; /* max. charging duration including
+                                     * hdd activity */
+static int minutes_disk_activity = 0; /* count minutes of hdd use during
+                                       * charging */
+static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */
+
+#ifdef CHARGING_DEBUG_FILE
+static void debug_file_close(void)
+{
+    if (fd >= 0) {
+        close(fd);
+        fd = -1;
+    }
+}
+
+static void debug_file_log(void)
+{
+    if (usb_inserted()) {
+        /* It is probably too late to close the file but we can try... */
+        debug_file_close();
+    }
+    else if (fd < 0) {
+        fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT);
+
+        if (fd >= 0) {
+            snprintf(debug_message, DEBUG_MESSAGE_LEN,
+            "cycle_min, bat_millivolts, bat_percent, chgr_state"
+            " ,charge_state, pid_p, pid_i, trickle_sec\n");
+            write(fd, debug_message, strlen(debug_message));
+            wrcount = 99;   /* force a flush */
+        }
+    }
+    else {
+        snprintf(debug_message, DEBUG_MESSAGE_LEN,
+                "%d, %d, %d, %d, %d, %d, %d, %d\n",
+            powermgmt_last_cycle_startstop_min, battery_voltage(),
+            battery_level(), charger_input_state, charge_state,
+            pid_p, pid_i, trickle_sec);
+        write(fd, debug_message, strlen(debug_message));
+        wrcount++;
+    }
+}
+
+static void debug_file_sync(void)
+{
+    /*
+     * If we have a lot of pending writes or if the disk is spining,
+     * fsync the debug log file.
+     */
+    if (wrcount > 10 || (wrcount > 0 && storage_disk_is_active())) {
+        if (fd >= 0)
+            fsync(fd);
+
+        wrcount = 0;
+    }
+}
+#else /* !CHARGING_DEBUG_FILE */
+#define debug_file_close()
+#define debug_file_log()
+#define debug_file_sync()
+#endif /* CHARGING_DEBUG_FILE */
+
+/*
+ * Do tasks that should be done every step.
+ */
+static void do_frequent_tasks(void)
+{
+    if (storage_disk_is_active()) {
+        /* flag hdd use for charging calculation */
+        disk_activity_last_cycle = true;
+    }
+
+    debug_file_sync();
+}
+
+/*
+ * The charger was just plugged in.  If the battery level is
+ * nearly charged, just trickle.  If the battery is low, start
+ * a full charge cycle.  If the battery level is in between,
+ * top-off and then trickle.
+ */
+static void charger_plugged(void)
+{
+    int battery_percent = battery_level();
+
+    pid_p = 0;
+    pid_i = 0;
+    powermgmt_last_cycle_level = battery_percent;
+    powermgmt_last_cycle_startstop_min = 0;
+
+    snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in");
+
+    if (battery_percent > START_TOPOFF_CHG) {
+
+        if (battery_percent >= START_TRICKLE_CHG) {
+            charge_state = TRICKLE;
+            target_voltage = TRICKLE_VOLTAGE;
+        }
+        else {
+            charge_state = TOPOFF;
+            target_voltage = TOPOFF_VOLTAGE;
+        }
+    }
+    else {
+        /*
+         * Start the charger full strength
+         */
+        int i = CHARGE_MAX_MIN_1500 * get_battery_capacity() / 1500;
+        charge_max_time_idle = i * (100 + 35 - battery_percent) / 100;
+
+        if (charge_max_time_idle > i)
+            charge_max_time_idle = i;
+
+        charge_max_time_now = charge_max_time_idle;
+
+        snprintf(power_message, POWER_MESSAGE_LEN,
+                 "ChgAt %d%% max %dm", battery_percent,
+                 charge_max_time_now);
+
+        /*
+         * Enable the charger after the max time calc is done,
+         * because battery_level depends on if the charger is
+         * on.
+         */
+        DEBUGF("power: charger inserted and battery"
+               " not full, charging\n");
+        trickle_sec  = 60;
+        long_delta   = short_delta = 999999;
+        charge_state = CHARGING;
+    }
+}
+
+/*
+ * The charger was just unplugged.
+ */
+static void charger_unplugged(void)
+{
+    DEBUGF("power: charger disconnected, disabling\n");
+
+    charger_enable(false);
+    powermgmt_last_cycle_level = battery_level();
+    powermgmt_last_cycle_startstop_min = 0;
+    trickle_sec  = 0;
+    pid_p        = 0;
+    pid_i        = 0;
+    charge_state = DISCHARGING;
+    snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge");
+}
+
+static void charging_step(void)
+{
+    int i;
+
+    /* alter charge time max length with extra disk use */
+    if (disk_activity_last_cycle) {
+        minutes_disk_activity++;
+        charge_max_time_now = charge_max_time_idle +
+                              minutes_disk_activity*2 / 5;
+        disk_activity_last_cycle = false;
+        last_disk_activity = 0;
+    }
+    else {
+        last_disk_activity++;
+    }
+
+    /*
+     * Check the delta voltage over the last X minutes so we can do
+     * our end-of-charge logic based on the battery level change
+     * (no longer use minimum time as logic for charge end has 50
+     * minutes minimum charge built in).
+     */
+    if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) {
+        short_delta = power_history[0] -
+                      power_history[CHARGE_END_SHORTD - 1];
+    }
+
+    if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) {
+        /*
+         * Scan the history: the points where measurement is taken need to
+         * be fairly static. Check prior to short delta 'area'. Also only
+         * check first and last 10 cycles (delta in middle OK).
+         */
+        long_delta = power_history[0] -
+                     power_history[CHARGE_END_LONGD - 1];
+
+        for (i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++)
+        {
+            if ((power_history[i] - power_history[i+1]) >  50 ||
+                (power_history[i] - power_history[i+1]) < -50) {
+                long_delta = 777777;
+                break;
+            }
+        }
+
+        for (i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++)
+        {
+            if ((power_history[i] - power_history[i+1]) >  50 ||
+                (power_history[i] - power_history[i+1]) < -50) {
+                long_delta = 888888;
+                break;
+            }
+        }
+    }
+
+    snprintf(power_message, POWER_MESSAGE_LEN,
+             "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min,
+             charge_max_time_now);
+
+    /*
+     * End of charge criteria (any qualify):
+     * 1) Charged a long time
+     * 2) DeltaV went negative for a short time ( & long delta static)
+     * 3) DeltaV was negative over a longer period (no disk use only)
+     *
+     * Note: short_delta and long_delta are millivolts
+     */
+    if (powermgmt_last_cycle_startstop_min >= charge_max_time_now ||
+        (short_delta <= -50 && long_delta < 50) ||
+        (long_delta  < -20 && last_disk_activity > CHARGE_END_LONGD)) {
+
+        int battery_percent = battery_level();
+
+        if (powermgmt_last_cycle_startstop_min > charge_max_time_now) {
+            DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, "
+                   "enough!\n");
+            /*
+             * Have charged too long and deltaV detection did not
+             * work!
+             */
+             snprintf(power_message, POWER_MESSAGE_LEN,
+                     "Chg tmout %d min", charge_max_time_now);
+            /*
+             * Switch to trickle charging.  We skip the top-off
+             * since we've effectively done the top-off operation
+             * already since we charged for the maximum full
+             * charge time.
+             */
+            powermgmt_last_cycle_level = battery_percent;
+            powermgmt_last_cycle_startstop_min = 0;
+            charge_state = TRICKLE;
+
+            /*
+             * Set trickle charge target to a relative voltage instead
+             * of an arbitrary value - the fully charged voltage may
+             * vary according to ambient temp, battery condition etc.
+             * Trickle target is -0.15v from full voltage acheived.
+             * Topup target is -0.05v from full voltage.
+             */
+            target_voltage = power_history[0] - 150;
+
+        }
+        else {
+            if(short_delta <= -5) {
+                DEBUGF("power: short-term negative"
+                       " delta, enough!\n");
+                snprintf(power_message, POWER_MESSAGE_LEN,
+                         "end negd %d %dmin", short_delta,
+                         powermgmt_last_cycle_startstop_min);
+                target_voltage = power_history[CHARGE_END_SHORTD - 1] - 50;
+            }
+            else {
+                DEBUGF("power: long-term small "
+                       "positive delta, enough!\n");
+                snprintf(power_message, POWER_MESSAGE_LEN,
+                         "end lowd %d %dmin", long_delta,
+                         powermgmt_last_cycle_startstop_min);
+                target_voltage = power_history[CHARGE_END_LONGD - 1] - 50;
+            }
+
+            /*
+             * Switch to top-off charging.
+             */
+            powermgmt_last_cycle_level = battery_percent;
+            powermgmt_last_cycle_startstop_min = 0;
+            charge_state = TOPOFF;
+        }
+    }
+}
+
+static void topoff_trickle_step(void)
+{
+    unsigned int millivolts;
+
+    /*
+     *Time to switch from topoff to trickle?
+     */
+    if (charge_state == TOPOFF &&
+        powermgmt_last_cycle_startstop_min > TOPOFF_MAX_MIN) {
+
+        powermgmt_last_cycle_level = battery_level();
+        powermgmt_last_cycle_startstop_min = 0;
+        charge_state = TRICKLE;
+        target_voltage = target_voltage - 100;
+    }
+    /*
+     * Adjust trickle charge time (proportional and integral terms).
+     * Note: I considered setting the level higher if the USB is
+     * plugged in, but it doesn't appear to be necessary and will
+     * generate more heat [gvb].
+     */
+    millivolts = battery_voltage();
+
+    pid_p = ((signed)target_voltage - (signed)millivolts) / 5;
+    if (pid_p <= PID_DEADZONE && pid_p >= -PID_DEADZONE)
+        pid_p = 0;
+
+    if ((unsigned)millivolts < target_voltage) {
+        if (pid_i < 60)
+            pid_i++; /* limit so it doesn't "wind up" */
+    }
+    else {
+        if (pid_i > 0)
+            pid_i--; /* limit so it doesn't "wind up" */
+    }
+
+    trickle_sec = pid_p + pid_i;
+
+    if (trickle_sec > 60)
+        trickle_sec = 60;
+
+    if (trickle_sec < 0)
+        trickle_sec = 0;
+}
+
+void charging_algorithm_step(void)
+{
+    static int pwm_counter = 0; /* PWM total cycle in steps */
+    static int pwm_duty = 0;    /* PWM duty cycle in steps */
+
+    switch (charger_input_state)
+    {
+    case CHARGER_PLUGGED:
+        charger_plugged();
+        break;
+
+    case CHARGER_UNPLUGGED:
+        charger_unplugged();
+        break;
+
+    case CHARGER:
+    case NO_CHARGER:
+        do_frequent_tasks();
+
+        if (pwm_counter > 0) {
+            if (pwm_duty > 0 && --pwm_duty <= 0)
+                charger_enable(false); /* Duty cycle expired */
+
+            if (--pwm_counter > 0)
+                return;
+
+            /* PWM cycle is complete */
+            powermgmt_last_cycle_startstop_min++;
+            debug_file_log();
+        }
+        break;
+    }
+
+    switch (charge_state)
+    {
+    case CHARGING:
+        charging_step();
+        break;
+
+    case TOPOFF:
+    case TRICKLE:
+        topoff_trickle_step();        
+        break;
+
+    case DISCHARGING:
+    default:
+        break;
+    }
+
+    /* If 100%, ensure pwm_on never expires and briefly disables the
+     * charger. */
+    pwm_duty = (trickle_sec < 60) ? trickle_sec*2 : 0;
+    pwm_counter = 60*2;
+    charger_enable(trickle_sec > 0);
+}
+
+#ifdef CHARGING_DEBUG_FILE
+void charging_algorithm_close(void)
+{
+    debug_file_close();
+}
+#endif /* CHARGING_DEBUG_FILE */
+
+/* Returns true if the unit is charging the batteries. */
+bool charging_state(void)
+{
+    return charge_state == CHARGING;
+}
diff --git a/firmware/target/sh/archos/recorder/powermgmt-target.h b/firmware/target/sh/archos/recorder/powermgmt-target.h
new file mode 100644
index 0000000..8fa2521
--- /dev/null
+++ b/firmware/target/sh/archos/recorder/powermgmt-target.h
@@ -0,0 +1,101 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
+ * Revisions copyright (C) 2005 by Gerald Van Baren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef POWERMGMT_TARGET_H
+#define POWERMGMT_TARGET_H
+
+/*
+ * Define CHARGING_DEBUG_FILE to create a csv (spreadsheet) with battery
+ * information in it (one sample per minute/connect/disconnect).
+ *
+ * This is only for very low level debug.
+ */
+#undef CHARGING_DEBUG_FILE
+
+
+/* stop when N minutes have passed with avg delta being < -0.05 V */
+#define CHARGE_END_SHORTD       6
+/* stop when N minutes have passed with avg delta being < -0.02 V */
+#define CHARGE_END_LONGD       50
+
+/* Battery % to start at top-off */
+#define START_TOPOFF_CHG       85
+/* Battery % to start at trickle */
+#define START_TRICKLE_CHG      95
+/* power thread status message */
+#define POWER_MESSAGE_LEN      32
+/* minutes: maximum charging time for 1500 mAh batteries
+ * actual max time depends also on BATTERY_CAPACITY! */
+#define CHARGE_MAX_MIN_1500   450
+/* minutes: minimum charging time */     
+#define CHARGE_MIN_MIN         10
+/* After charging, go to top off charge. How long should top off charge be? */
+#define TOPOFF_MAX_MIN         90
+/* which voltage is best? (millivolts) */
+#define TOPOFF_VOLTAGE       5650
+/* After top off charge, go to trickle harge. How long should trickle
+ * charge be? */
+#define TRICKLE_MAX_MIN       720 /* 12 hrs */
+/* which voltage is best? (millivolts) */
+#define TRICKLE_VOLTAGE      5450
+/* initial trickle_sec for topoff */
+#define START_TOPOFF_SEC       25
+/* initial trickle_sec for trickle */
+#define START_TRICKLE_SEC      15
+
+#define PID_DEADZONE            4    /* PID proportional deadzone */
+
+extern char power_message[POWER_MESSAGE_LEN];
+
+extern int long_delta;          /* long term delta battery voltage */
+extern int short_delta;         /* short term delta battery voltage */
+
+extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was
+                                                  the charging started or
+                                                  stopped? */
+extern int powermgmt_last_cycle_level;         /* which level had the batteries
+                                                  at this time? */
+
+extern int pid_p;                /* PID proportional term */
+extern int pid_i;                /* PID integral term */
+extern int trickle_sec;          /* how many seconds should the
+                                    charger be enabled per
+                                    minute for trickle
+                                    charging? */
+void charger_enable(bool on);
+bool charger_enabled(void);
+
+/* Battery filter lengths in samples */
+#define BATT_AVE_SAMPLES 32
+
+/* No init to do */
+static inline void powermgmt_init_target(void) {}
+void charging_algorithm_step(void);
+
+#ifdef CHARGING_DEBUG_FILE
+/* Need to flush and close debug file */
+void charging_algorithm_close(void);
+#else
+/* No poweroff operation to do */
+static inline void charging_algorithm_close(void) {}
+#endif
+
+#endif /* POWERMGMT_TARGET_H */
diff --git a/flash/bootbox/main.c b/flash/bootbox/main.c
index f53a5ed..e4be785 100644
--- a/flash/bootbox/main.c
+++ b/flash/bootbox/main.c
@@ -75,7 +75,7 @@
 
     do
     {
-#if CONFIG_CHARGING == CHARGING_CONTROL
+#ifdef ARCHOS_RECORDER
         if (charge_state == CHARGING)
             msg = "charging";
         else if (charge_state == TOPOFF)
@@ -84,7 +84,6 @@
             msg = "trickle charge";
         else
             msg = "not charging";
-
 #else
         msg = "charging";
 #endif
diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES
index 24dac31..881049e 100644
--- a/uisimulator/common/SOURCES
+++ b/uisimulator/common/SOURCES
@@ -7,3 +7,5 @@
 #endif
 sim_icons.c
 stubs.c
+powermgmt-sim.c
+
diff --git a/uisimulator/common/powermgmt-sim.c b/uisimulator/common/powermgmt-sim.c
new file mode 100644
index 0000000..c06f846
--- /dev/null
+++ b/uisimulator/common/powermgmt-sim.c
@@ -0,0 +1,159 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
+ * Revisions copyright (C) 2005 by Gerald Van Baren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include "system.h"
+#include <time.h>
+#include "kernel.h"
+#include "powermgmt.h"
+
+#define BATT_MINMVOLT   2500      /* minimum millivolts of battery */
+#define BATT_MAXMVOLT   4500      /* maximum millivolts of battery */
+#define BATT_MAXRUNTIME (10 * 60) /* maximum runtime with full battery in
+                                     minutes */
+
+extern void send_battery_level_event(void);
+extern int last_sent_battery_level;
+extern int battery_percent;
+
+static unsigned int battery_millivolts = BATT_MAXMVOLT;
+/* estimated remaining time in minutes */
+static int powermgmt_est_runningtime_min = BATT_MAXRUNTIME;
+
+static void battery_status_update(void)
+{
+    static time_t last_change = 0;
+    static bool charging = false;
+    time_t now;
+
+    time(&now);
+
+    if (last_change < now) {
+        last_change = now;
+
+        /* change the values: */
+        if (charging) {
+            if (battery_millivolts >= BATT_MAXMVOLT) {
+                /* Pretend the charger was disconnected */
+                charging = false;
+                queue_broadcast(SYS_CHARGER_DISCONNECTED, 0);
+                last_sent_battery_level = 100;
+            }
+        }
+        else {
+            if (battery_millivolts <= BATT_MINMVOLT) {
+                /* Pretend the charger was connected */
+                charging = true;
+                queue_broadcast(SYS_CHARGER_CONNECTED, 0);
+                last_sent_battery_level = 0;
+            }
+        }
+
+        if (charging) {
+            battery_millivolts += (BATT_MAXMVOLT - BATT_MINMVOLT) / 50;
+        }
+        else {
+            battery_millivolts -= (BATT_MAXMVOLT - BATT_MINMVOLT) / 100;
+        }
+
+        battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) /
+                            (BATT_MAXMVOLT - BATT_MINMVOLT);
+
+        powermgmt_est_runningtime_min =
+            battery_percent * BATT_MAXRUNTIME / 100;
+    }
+
+    send_battery_level_event();
+}
+
+void battery_read_info(int *voltage, int *level)
+{
+    battery_status_update();
+
+    if (voltage)
+        *voltage = battery_millivolts;
+
+    if (level)
+        *level = battery_percent;
+}
+
+unsigned int battery_voltage(void)
+{
+    battery_status_update();
+    return battery_millivolts;
+}
+
+int battery_level(void)
+{
+    battery_status_update();
+    return battery_percent;
+}
+
+int battery_time(void)
+{
+    battery_status_update();
+    return powermgmt_est_runningtime_min;
+}
+
+bool battery_level_safe(void)
+{
+    return battery_level() >= 10;
+}
+
+void set_poweroff_timeout(int timeout)
+{
+    (void)timeout;
+}
+
+void set_battery_capacity(int capacity)
+{
+  (void)capacity;
+}
+
+#if BATTERY_TYPES_COUNT > 1
+void set_battery_type(int type)
+{
+    (void)type;
+}
+#endif
+
+#ifdef HAVE_ACCESSORY_SUPPLY
+void accessory_supply_set(bool enable)
+{
+    (void)enable;
+}
+#endif
+
+void reset_poweroff_timer(void)
+{
+}
+
+void shutdown_hw(void)
+{
+}
+
+void sys_poweroff(void)
+{
+}
+
+void cancel_shutdown(void)
+{
+}