diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-12-24 16:58:41 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-12-24 16:58:41 +0000 |
commit | 3157e1395674a930c74e2ef4cc4ce78dffea8569 (patch) | |
tree | 5b2a9befc3b051caf0806995ebd32a5ab3bcf5ff /firmware/powermgmt.c | |
parent | 0f9729739f2fd90759c1caeca86e487c36f98834 (diff) | |
download | rockbox-3157e1395674a930c74e2ef4cc4ce78dffea8569.tar.gz rockbox-3157e1395674a930c74e2ef4cc4ce78dffea8569.zip |
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
Diffstat (limited to 'firmware/powermgmt.c')
-rw-r--r-- | firmware/powermgmt.c | 1200 |
1 files changed, 376 insertions, 824 deletions
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index 0fed847973..5aa85c883d 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c | |||
@@ -20,18 +20,14 @@ | |||
20 | * | 20 | * |
21 | ****************************************************************************/ | 21 | ****************************************************************************/ |
22 | #include "config.h" | 22 | #include "config.h" |
23 | #include "cpu.h" | 23 | #include "system.h" |
24 | #include "kernel.h" | 24 | #include "kernel.h" |
25 | #include "thread.h" | 25 | #include "thread.h" |
26 | #include "system.h" | ||
27 | #include "debug.h" | 26 | #include "debug.h" |
28 | #include "panic.h" | ||
29 | #include "adc.h" | 27 | #include "adc.h" |
30 | #include "string.h" | 28 | #include "string.h" |
31 | #include "sprintf.h" | ||
32 | #include "storage.h" | 29 | #include "storage.h" |
33 | #include "power.h" | 30 | #include "power.h" |
34 | #include "button.h" | ||
35 | #include "audio.h" | 31 | #include "audio.h" |
36 | #include "mp3_playback.h" | 32 | #include "mp3_playback.h" |
37 | #include "usb.h" | 33 | #include "usb.h" |
@@ -57,166 +53,39 @@ | |||
57 | #include "lcd-remote-target.h" | 53 | #include "lcd-remote-target.h" |
58 | #endif | 54 | #endif |
59 | 55 | ||
60 | /* | 56 | /** Shared by sim **/ |
61 | * Define DEBUG_FILE to create a csv (spreadsheet) with battery information | 57 | int last_sent_battery_level = 100; |
62 | * in it (one sample per minute). This is only for very low level debug. | 58 | /* battery level (0-100%) */ |
63 | */ | 59 | int battery_percent = -1; |
64 | #undef DEBUG_FILE | 60 | void send_battery_level_event(void); |
65 | #if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL) | ||
66 | #include "file.h" | ||
67 | #define DEBUG_FILE_NAME "/powermgmt.csv" | ||
68 | #define DEBUG_MESSAGE_LEN 133 | ||
69 | static char debug_message[DEBUG_MESSAGE_LEN]; | ||
70 | #define DEBUG_STACK ((0x1000)/sizeof(long)) | ||
71 | static int fd = -1; /* write debug information to this file */ | ||
72 | static int wrcount = 0; | ||
73 | #else | ||
74 | #define DEBUG_STACK 0 | ||
75 | #endif | ||
76 | |||
77 | static int shutdown_timeout = 0; | ||
78 | #if CONFIG_CHARGING >= CHARGING_MONITOR | ||
79 | charge_state_type charge_state = DISCHARGING; /* charging mode */ | ||
80 | #endif | ||
81 | |||
82 | static void send_battery_level_event(void); | ||
83 | static int last_sent_battery_level = 100; | ||
84 | 61 | ||
85 | #if CONFIG_CHARGING | 62 | #if CONFIG_CHARGING |
86 | charger_input_state_type charger_input_state IDATA_ATTR; | 63 | /* State of the charger input as seen by the power thread */ |
87 | #endif | 64 | enum charger_input_state_type charger_input_state; |
88 | 65 | /* Power inputs as seen by the power thread */ | |
89 | #ifdef SIMULATOR /***********************************************************/ | 66 | unsigned int power_thread_inputs; |
90 | 67 | #if CONFIG_CHARGING >= CHARGING_MONITOR | |
91 | #define BATT_MINMVOLT 2500 /* minimum millivolts of battery */ | 68 | /* Charging state (mode) as seen by the power thread */ |
92 | #define BATT_MAXMVOLT 4500 /* maximum millivolts of battery */ | 69 | enum charge_state_type charge_state = DISCHARGING; |
93 | #define BATT_MAXRUNTIME (10 * 60) /* maximum runtime with full battery in minutes */ | ||
94 | |||
95 | static unsigned int battery_millivolts = (unsigned int)BATT_MAXMVOLT; | ||
96 | static int battery_percent = 100; /* battery capacity level in percent */ | ||
97 | static int powermgmt_est_runningtime_min = BATT_MAXRUNTIME; /* estimated remaining time in minutes */ | ||
98 | |||
99 | static void battery_status_update(void) | ||
100 | { | ||
101 | static time_t last_change = 0; | ||
102 | static bool charging = false; | ||
103 | time_t now; | ||
104 | |||
105 | time(&now); | ||
106 | if (last_change < now) | ||
107 | { | ||
108 | last_change = now; | ||
109 | |||
110 | /* change the values: */ | ||
111 | if (charging) | ||
112 | { | ||
113 | if (battery_millivolts >= BATT_MAXMVOLT) | ||
114 | { | ||
115 | /* Pretend the charger was disconnected */ | ||
116 | charging = false; | ||
117 | queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); | ||
118 | last_sent_battery_level = 100; | ||
119 | } | ||
120 | } | ||
121 | else | ||
122 | { | ||
123 | if (battery_millivolts <= BATT_MINMVOLT) | ||
124 | { | ||
125 | /* Pretend the charger was connected */ | ||
126 | charging = true; | ||
127 | queue_broadcast(SYS_CHARGER_CONNECTED, 0); | ||
128 | last_sent_battery_level = 0; | ||
129 | } | ||
130 | } | ||
131 | if (charging) | ||
132 | battery_millivolts += (BATT_MAXMVOLT - BATT_MINMVOLT) / 50; | ||
133 | else | ||
134 | battery_millivolts -= (BATT_MAXMVOLT - BATT_MINMVOLT) / 100; | ||
135 | |||
136 | battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) / | ||
137 | (BATT_MAXMVOLT - BATT_MINMVOLT); | ||
138 | powermgmt_est_runningtime_min = battery_percent * BATT_MAXRUNTIME / 100; | ||
139 | } | ||
140 | send_battery_level_event(); | ||
141 | } | ||
142 | |||
143 | void battery_read_info(int *voltage, int *level) | ||
144 | { | ||
145 | battery_status_update(); | ||
146 | |||
147 | if (voltage) | ||
148 | *voltage = battery_millivolts; | ||
149 | |||
150 | if (level) | ||
151 | *level = battery_percent; | ||
152 | } | ||
153 | |||
154 | unsigned int battery_voltage(void) | ||
155 | { | ||
156 | battery_status_update(); | ||
157 | return battery_millivolts; | ||
158 | } | ||
159 | |||
160 | int battery_level(void) | ||
161 | { | ||
162 | battery_status_update(); | ||
163 | return battery_percent; | ||
164 | } | ||
165 | |||
166 | int battery_time(void) | ||
167 | { | ||
168 | battery_status_update(); | ||
169 | return powermgmt_est_runningtime_min; | ||
170 | } | ||
171 | |||
172 | bool battery_level_safe(void) | ||
173 | { | ||
174 | return battery_level() >= 10; | ||
175 | } | ||
176 | |||
177 | void set_poweroff_timeout(int timeout) | ||
178 | { | ||
179 | (void)timeout; | ||
180 | } | ||
181 | |||
182 | void set_battery_capacity(int capacity) | ||
183 | { | ||
184 | (void)capacity; | ||
185 | } | ||
186 | |||
187 | #if BATTERY_TYPES_COUNT > 1 | ||
188 | void set_battery_type(int type) | ||
189 | { | ||
190 | (void)type; | ||
191 | } | ||
192 | #endif | ||
193 | |||
194 | void reset_poweroff_timer(void) | ||
195 | { | ||
196 | } | ||
197 | |||
198 | #ifdef HAVE_ACCESSORY_SUPPLY | ||
199 | void accessory_supply_set(bool enable) | ||
200 | { | ||
201 | (void)enable; | ||
202 | } | ||
203 | #endif | 70 | #endif |
71 | #endif /* CONFIG_CHARGING */ | ||
204 | 72 | ||
205 | #else /* not SIMULATOR ******************************************************/ | 73 | #ifndef SIMULATOR |
206 | 74 | static int shutdown_timeout = 0; | |
207 | /* | 75 | /* |
208 | * Average battery voltage and charger voltage, filtered via a digital | 76 | * Average battery voltage and charger voltage, filtered via a digital |
209 | * exponential filter (aka. exponential moving average, scaled): | 77 | * exponential filter (aka. exponential moving average, scaled): |
210 | * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N. | 78 | * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N. |
211 | */ | 79 | */ |
212 | static unsigned int avgbat; /* average battery voltage (filtering) */ | 80 | static unsigned int avgbat; |
213 | static unsigned int battery_millivolts;/* filtered battery voltage, millivolts */ | 81 | /* filtered battery voltage, millivolts */ |
82 | static unsigned int battery_millivolts; | ||
83 | /* default value, mAh */ | ||
84 | static int battery_capacity = BATTERY_CAPACITY_DEFAULT; | ||
85 | |||
214 | 86 | ||
215 | /* battery level (0-100%) of this minute, updated once per minute */ | ||
216 | static int battery_percent = -1; | ||
217 | static int battery_capacity = BATTERY_CAPACITY_DEFAULT; /* default value, mAh */ | ||
218 | #if BATTERY_TYPES_COUNT > 1 | 87 | #if BATTERY_TYPES_COUNT > 1 |
219 | static int battery_type = 0; | 88 | static int battery_type = 0; |
220 | #else | 89 | #else |
221 | #define battery_type 0 | 90 | #define battery_type 0 |
222 | #endif | 91 | #endif |
@@ -224,7 +93,7 @@ static int battery_type = 0; | |||
224 | /* Power history: power_history[0] is the newest sample */ | 93 | /* Power history: power_history[0] is the newest sample */ |
225 | unsigned short power_history[POWER_HISTORY_LEN]; | 94 | unsigned short power_history[POWER_HISTORY_LEN]; |
226 | 95 | ||
227 | static char power_stack[DEFAULT_STACK_SIZE/2 + DEBUG_STACK]; | 96 | static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK]; |
228 | static const char power_thread_name[] = "power"; | 97 | static const char power_thread_name[] = "power"; |
229 | 98 | ||
230 | static int poweroff_timeout = 0; | 99 | static int poweroff_timeout = 0; |
@@ -239,19 +108,6 @@ static int voltage_to_battery_level(int battery_millivolts); | |||
239 | static void battery_status_update(void); | 108 | static void battery_status_update(void); |
240 | static int runcurrent(void); | 109 | static int runcurrent(void); |
241 | 110 | ||
242 | #ifndef TARGET_POWERMGMT_FILTER_CHARGE_STATE | ||
243 | static inline int powermgmt_filter_charge_state(void) | ||
244 | { | ||
245 | #if CONFIG_CHARGING >= CHARGING_MONITOR | ||
246 | /* No adjustment of state */ | ||
247 | return charge_state; | ||
248 | #else | ||
249 | /* Always discharging */ | ||
250 | return DISCHARGING; | ||
251 | #endif | ||
252 | } | ||
253 | #endif /* TARGET_POWERMGMT_FILTER_CHARGE_STATE */ | ||
254 | |||
255 | void battery_read_info(int *voltage, int *level) | 111 | void battery_read_info(int *voltage, int *level) |
256 | { | 112 | { |
257 | int millivolts = battery_adc_voltage(); | 113 | int millivolts = battery_adc_voltage(); |
@@ -272,20 +128,30 @@ void reset_poweroff_timer(void) | |||
272 | void set_battery_type(int type) | 128 | void set_battery_type(int type) |
273 | { | 129 | { |
274 | if (type != battery_type) { | 130 | if (type != battery_type) { |
131 | if ((unsigned)type >= BATTERY_TYPES_COUNT) | ||
132 | type = 0; | ||
133 | |||
275 | battery_type = type; | 134 | battery_type = type; |
276 | battery_status_update(); /* recalculate the battery status */ | 135 | battery_status_update(); /* recalculate the battery status */ |
277 | } | 136 | } |
278 | } | 137 | } |
279 | #endif | 138 | #endif |
280 | 139 | ||
281 | void set_battery_capacity(int capacity) | 140 | void set_battery_capacity(int capacity) |
282 | { | 141 | { |
142 | if (capacity > BATTERY_CAPACITY_MAX) | ||
143 | capacity = BATTERY_CAPACITY_MAX; | ||
144 | if (capacity < BATTERY_CAPACITY_MIN) | ||
145 | capacity = BATTERY_CAPACITY_MIN; | ||
146 | |||
283 | battery_capacity = capacity; | 147 | battery_capacity = capacity; |
284 | if (battery_capacity > BATTERY_CAPACITY_MAX) | 148 | |
285 | battery_capacity = BATTERY_CAPACITY_MAX; | 149 | battery_status_update(); /* recalculate the battery status */ |
286 | if (battery_capacity < BATTERY_CAPACITY_MIN) | 150 | } |
287 | battery_capacity = BATTERY_CAPACITY_MIN; | 151 | |
288 | battery_status_update(); /* recalculate the battery status */ | 152 | int get_battery_capacity(void) |
153 | { | ||
154 | return battery_capacity; | ||
289 | } | 155 | } |
290 | 156 | ||
291 | int battery_time(void) | 157 | int battery_time(void) |
@@ -309,13 +175,19 @@ unsigned int battery_voltage(void) | |||
309 | return battery_millivolts; | 175 | return battery_millivolts; |
310 | } | 176 | } |
311 | 177 | ||
312 | #ifndef TARGET_BATTERY_LEVEL_SAFE | ||
313 | /* Tells if the battery level is safe for disk writes */ | 178 | /* Tells if the battery level is safe for disk writes */ |
314 | bool battery_level_safe(void) | 179 | bool battery_level_safe(void) |
315 | { | 180 | { |
181 | #if defined(NO_LOW_BATTERY_SHUTDOWN) | ||
182 | return true; | ||
183 | #elif defined(HAVE_BATTERY_SWITCH) | ||
184 | /* Cannot rely upon the battery reading to be valid and the | ||
185 | * device could be powered externally. */ | ||
186 | return input_millivolts() > battery_level_dangerous[battery_type]; | ||
187 | #else | ||
316 | return battery_millivolts > battery_level_dangerous[battery_type]; | 188 | return battery_millivolts > battery_level_dangerous[battery_type]; |
317 | } | ||
318 | #endif | 189 | #endif |
190 | } | ||
319 | 191 | ||
320 | void set_poweroff_timeout(int timeout) | 192 | void set_poweroff_timeout(int timeout) |
321 | { | 193 | { |
@@ -324,7 +196,7 @@ void set_poweroff_timeout(int timeout) | |||
324 | 196 | ||
325 | void set_sleep_timer(int seconds) | 197 | void set_sleep_timer(int seconds) |
326 | { | 198 | { |
327 | if(seconds) { | 199 | if (seconds) { |
328 | sleeptimer_active = true; | 200 | sleeptimer_active = true; |
329 | sleeptimer_endtick = current_tick + seconds * HZ; | 201 | sleeptimer_endtick = current_tick + seconds * HZ; |
330 | } | 202 | } |
@@ -336,7 +208,7 @@ void set_sleep_timer(int seconds) | |||
336 | 208 | ||
337 | int get_sleep_timer(void) | 209 | int get_sleep_timer(void) |
338 | { | 210 | { |
339 | if(sleeptimer_active) | 211 | if (sleeptimer_active) |
340 | return (sleeptimer_endtick - current_tick) / HZ; | 212 | return (sleeptimer_endtick - current_tick) / HZ; |
341 | else | 213 | else |
342 | return 0; | 214 | return 0; |
@@ -345,45 +217,46 @@ int get_sleep_timer(void) | |||
345 | /* look into the percent_to_volt_* table and get a realistic battery level */ | 217 | /* look into the percent_to_volt_* table and get a realistic battery level */ |
346 | static int voltage_to_percent(int voltage, const short* table) | 218 | static int voltage_to_percent(int voltage, const short* table) |
347 | { | 219 | { |
348 | if (voltage <= table[0]) | 220 | if (voltage <= table[0]) { |
349 | return 0; | 221 | return 0; |
350 | else | 222 | } |
351 | if (voltage >= table[10]) | 223 | else if (voltage >= table[10]) { |
352 | return 100; | 224 | return 100; |
353 | else { | 225 | } |
354 | /* search nearest value */ | 226 | else { |
355 | int i = 0; | 227 | /* search nearest value */ |
356 | while ((i < 10) && (table[i+1] < voltage)) | 228 | int i = 0; |
357 | i++; | 229 | |
358 | /* interpolate linear between the smaller and greater value */ | 230 | while (i < 10 && table[i+1] < voltage) |
359 | return (i * 10) /* Tens digit, 10% per entry */ | 231 | i++; |
360 | + (((voltage - table[i]) * 10) | 232 | |
361 | / (table[i+1] - table[i])); /* Ones digit: interpolated */ | 233 | /* interpolate linear between the smaller and greater value */ |
362 | } | 234 | /* Tens digit, 10% per entry, ones digit: interpolated */ |
235 | return i*10 + (voltage - table[i])*10 / (table[i+1] - table[i]); | ||
236 | } | ||
363 | } | 237 | } |
364 | 238 | ||
365 | /* update battery level and estimated runtime, called once per minute or | 239 | /* update battery level and estimated runtime, called once per minute or |
366 | * when battery capacity / type settings are changed */ | 240 | * when battery capacity / type settings are changed */ |
367 | static int voltage_to_battery_level(int battery_millivolts) | 241 | static int voltage_to_battery_level(int battery_millivolts) |
368 | { | 242 | { |
369 | const int state = powermgmt_filter_charge_state(); | ||
370 | int level; | 243 | int level; |
371 | 244 | ||
372 | if (state == DISCHARGING) { | ||
373 | level = voltage_to_percent(battery_millivolts, | ||
374 | percent_to_volt_discharge[battery_type]); | ||
375 | } | ||
376 | #if CONFIG_CHARGING >= CHARGING_MONITOR | 245 | #if CONFIG_CHARGING >= CHARGING_MONITOR |
377 | else if (state == CHARGING) { | 246 | if (charging_state()) { |
378 | /* battery level is defined to be < 100% until charging is finished */ | 247 | /* battery level is defined to be < 100% until charging is finished */ |
379 | level = MIN(voltage_to_percent(battery_millivolts, | 248 | level = voltage_to_percent(battery_millivolts, |
380 | percent_to_volt_charge), 99); | 249 | percent_to_volt_charge); |
250 | if (level > 99) | ||
251 | level = 99; | ||
381 | } | 252 | } |
382 | else { | 253 | else |
383 | /* in topoff/trickle charge, battery is by definition 100% full */ | 254 | #endif /* CONFIG_CHARGING >= CHARGING_MONITOR */ |
384 | level = 100; | 255 | { |
256 | /* DISCHARGING or error state */ | ||
257 | level = voltage_to_percent(battery_millivolts, | ||
258 | percent_to_volt_discharge[battery_type]); | ||
385 | } | 259 | } |
386 | #endif | ||
387 | 260 | ||
388 | return level; | 261 | return level; |
389 | } | 262 | } |
@@ -393,26 +266,25 @@ static void battery_status_update(void) | |||
393 | int level = voltage_to_battery_level(battery_millivolts); | 266 | int level = voltage_to_battery_level(battery_millivolts); |
394 | 267 | ||
395 | /* calculate estimated remaining running time */ | 268 | /* calculate estimated remaining running time */ |
396 | /* discharging: remaining running time */ | ||
397 | /* charging: remaining charging time */ | ||
398 | #if CONFIG_CHARGING >= CHARGING_MONITOR | 269 | #if CONFIG_CHARGING >= CHARGING_MONITOR |
399 | if (powermgmt_filter_charge_state() == CHARGING) { | 270 | if (charging_state()) { |
400 | powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60 | 271 | /* charging: remaining charging time */ |
401 | / 100 / (CURRENT_MAX_CHG - runcurrent()); | 272 | powermgmt_est_runningtime_min = (100 - level)*battery_capacity*60 |
273 | / 100 / (CURRENT_MAX_CHG - runcurrent()); | ||
402 | } | 274 | } |
403 | else | 275 | else |
404 | #endif | 276 | #endif |
405 | { | 277 | /* discharging: remaining running time */ |
406 | if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0]) | 278 | if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0]) { |
407 | powermgmt_est_runningtime_min = (level + battery_percent) * 60 * | 279 | powermgmt_est_runningtime_min = (level + battery_percent)*60 |
408 | battery_capacity / 200 / runcurrent(); | 280 | * battery_capacity / 200 / runcurrent(); |
409 | 281 | } | |
410 | else if (battery_millivolts <= battery_level_shutoff[0]) | 282 | else if (battery_millivolts <= battery_level_shutoff[0]) { |
411 | powermgmt_est_runningtime_min = 0; | 283 | powermgmt_est_runningtime_min = 0; |
412 | 284 | } | |
413 | else | 285 | else { |
414 | powermgmt_est_runningtime_min = (battery_millivolts - | 286 | powermgmt_est_runningtime_min = |
415 | battery_level_shutoff[0]) / 2; | 287 | (battery_millivolts - battery_level_shutoff[0]) / 2; |
416 | } | 288 | } |
417 | 289 | ||
418 | battery_percent = level; | 290 | battery_percent = level; |
@@ -434,62 +306,55 @@ static void battery_status_update(void) | |||
434 | static void handle_auto_poweroff(void) | 306 | static void handle_auto_poweroff(void) |
435 | { | 307 | { |
436 | long timeout = poweroff_timeout*60*HZ; | 308 | long timeout = poweroff_timeout*60*HZ; |
437 | int audio_stat = audio_status(); | 309 | int audio_stat = audio_status(); |
310 | long tick = current_tick; | ||
438 | 311 | ||
439 | #if CONFIG_CHARGING | 312 | #if CONFIG_CHARGING |
440 | /* | 313 | /* |
441 | * Inhibit shutdown as long as the charger is plugged in. If it is | 314 | * Inhibit shutdown as long as the charger is plugged in. If it is |
442 | * unplugged, wait for a timeout period and then shut down. | 315 | * unplugged, wait for a timeout period and then shut down. |
443 | */ | 316 | */ |
444 | if(charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) { | 317 | if (charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) { |
445 | last_event_tick = current_tick; | 318 | last_event_tick = current_tick; |
446 | } | 319 | } |
447 | #endif | 320 | #endif |
448 | 321 | ||
449 | if( !shutdown_timeout && query_force_shutdown()) { | 322 | if (!shutdown_timeout && query_force_shutdown()) { |
450 | backlight_on(); | 323 | backlight_on(); |
451 | sys_poweroff(); | 324 | sys_poweroff(); |
452 | } | 325 | } |
453 | 326 | ||
454 | if(timeout && | 327 | if (timeout && |
455 | #if CONFIG_TUNER && !defined(BOOTLOADER) | 328 | #if CONFIG_TUNER |
456 | (!(get_radio_status() & FMRADIO_PLAYING)) && | 329 | !(get_radio_status() & FMRADIO_PLAYING) && |
457 | #endif | 330 | #endif |
458 | !usb_inserted() && | 331 | !usb_inserted() && |
459 | ((audio_stat == 0) || | 332 | (audio_stat == 0 || |
460 | ((audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE)) && | 333 | (audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE) && |
461 | !sleeptimer_active))) | 334 | !sleeptimer_active))) { |
462 | { | 335 | |
463 | if(TIME_AFTER(current_tick, last_event_tick + timeout) && | 336 | if (TIME_AFTER(tick, last_event_tick + timeout) && |
464 | TIME_AFTER(current_tick, storage_last_disk_activity() + timeout)) | 337 | TIME_AFTER(tick, storage_last_disk_activity() + timeout)) { |
465 | { | ||
466 | sys_poweroff(); | 338 | sys_poweroff(); |
467 | } | 339 | } |
468 | } | 340 | } |
469 | else | 341 | else if (sleeptimer_active) { |
470 | { | ||
471 | /* Handle sleeptimer */ | 342 | /* Handle sleeptimer */ |
472 | if(sleeptimer_active) | 343 | if (TIME_AFTER(tick, sleeptimer_endtick)) { |
473 | { | 344 | audio_stop(); |
474 | if(TIME_AFTER(current_tick, sleeptimer_endtick)) | 345 | |
475 | { | 346 | if (usb_inserted() |
476 | audio_stop(); | ||
477 | if (usb_inserted() | ||
478 | #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) | 347 | #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) |
479 | || ((charger_input_state == CHARGER) || | 348 | || charger_input_state != NO_CHARGER |
480 | (charger_input_state == CHARGER_PLUGGED)) | ||
481 | #endif | 349 | #endif |
482 | ) | 350 | ) { |
483 | { | 351 | DEBUGF("Sleep timer timeout. Stopping...\n"); |
484 | DEBUGF("Sleep timer timeout. Stopping...\n"); | 352 | set_sleep_timer(0); |
485 | set_sleep_timer(0); | 353 | backlight_off(); /* Nighty, nighty... */ |
486 | backlight_off(); /* Nighty, nighty... */ | 354 | } |
487 | } | 355 | else { |
488 | else | 356 | DEBUGF("Sleep timer timeout. Shutting off...\n"); |
489 | { | 357 | sys_poweroff(); |
490 | DEBUGF("Sleep timer timeout. Shutting off...\n"); | ||
491 | sys_poweroff(); | ||
492 | } | ||
493 | } | 358 | } |
494 | } | 359 | } |
495 | } | 360 | } |
@@ -504,25 +369,24 @@ static int runcurrent(void) | |||
504 | 369 | ||
505 | #if MEM == 8 && !(defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM)) | 370 | #if MEM == 8 && !(defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM)) |
506 | /* assuming 192 kbps, the running time is 22% longer with 8MB */ | 371 | /* assuming 192 kbps, the running time is 22% longer with 8MB */ |
507 | current = (CURRENT_NORMAL*100/122); | 372 | current = CURRENT_NORMAL*100 / 122; |
508 | #else | 373 | #else |
509 | current = CURRENT_NORMAL; | 374 | current = CURRENT_NORMAL; |
510 | #endif /* MEM == 8 */ | 375 | #endif /* MEM == 8 */ |
511 | 376 | ||
512 | if(usb_inserted() | 377 | if (usb_inserted() |
513 | #if defined(HAVE_USB_POWER) | 378 | #ifdef HAVE_USB_POWER |
514 | #if (CURRENT_USB < CURRENT_NORMAL) | 379 | #if (CURRENT_USB < CURRENT_NORMAL) |
515 | || usb_powered() | 380 | || usb_powered() |
516 | #else | 381 | #else |
517 | && !usb_powered() | 382 | && !usb_powered() |
518 | #endif | 383 | #endif |
519 | #endif | 384 | #endif |
520 | ) | 385 | ) { |
521 | { | ||
522 | current = CURRENT_USB; | 386 | current = CURRENT_USB; |
523 | } | 387 | } |
524 | 388 | ||
525 | #if defined(HAVE_BACKLIGHT) && !defined(BOOTLOADER) | 389 | #if defined(HAVE_BACKLIGHT) |
526 | if (backlight_get_current_timeout() == 0) /* LED always on */ | 390 | if (backlight_get_current_timeout() == 0) /* LED always on */ |
527 | current += CURRENT_BACKLIGHT; | 391 | current += CURRENT_BACKLIGHT; |
528 | #endif | 392 | #endif |
@@ -542,7 +406,7 @@ static int runcurrent(void) | |||
542 | current += CURRENT_REMOTE; | 406 | current += CURRENT_REMOTE; |
543 | #endif | 407 | #endif |
544 | 408 | ||
545 | return(current); | 409 | return current; |
546 | } | 410 | } |
547 | 411 | ||
548 | 412 | ||
@@ -550,424 +414,76 @@ static int runcurrent(void) | |||
550 | #ifdef HAVE_RTC_ALARM | 414 | #ifdef HAVE_RTC_ALARM |
551 | static void power_thread_rtc_process(void) | 415 | static void power_thread_rtc_process(void) |
552 | { | 416 | { |
553 | if (rtc_check_alarm_flag()) { | 417 | if (rtc_check_alarm_flag()) |
554 | rtc_enable_alarm(false); | 418 | rtc_enable_alarm(false); |
555 | } | ||
556 | } | 419 | } |
557 | #endif | 420 | #endif |
558 | 421 | ||
559 | #ifndef TARGET_QUERY_FORCE_SHUTDOWN | 422 | /* switch off unit if battery level is too low for reliable operation */ |
560 | bool query_force_shutdown(void) | 423 | bool query_force_shutdown(void) |
561 | { | 424 | { |
562 | #ifndef NO_LOW_BATTERY_SHUTDOWN | 425 | #if defined(NO_LOW_BATTERY_SHUTDOWN) |
563 | /* switch off unit if battery level is too low for reliable operation */ | ||
564 | return battery_millivolts < battery_level_shutoff[battery_type]; | ||
565 | #else | ||
566 | return false; | 426 | return false; |
427 | #elif defined(HAVE_BATTERY_SWITCH) | ||
428 | /* Cannot rely upon the battery reading to be valid and the | ||
429 | * device could be powered externally. */ | ||
430 | return input_millivolts() < battery_level_shutoff[battery_type]; | ||
431 | #else | ||
432 | return battery_millivolts < battery_level_shutoff[battery_type]; | ||
567 | #endif | 433 | #endif |
568 | } | 434 | } |
569 | #endif /* TARGET_QUERY_FORCE_SHUTDOWN */ | ||
570 | |||
571 | /* | ||
572 | * This power thread maintains a history of battery voltage | ||
573 | * and implements a charging algorithm. | ||
574 | */ | ||
575 | #if CONFIG_CHARGING == CHARGING_CONTROL | ||
576 | #define BATT_AVE_SAMPLES 32 /* filter constant / @ 2Hz sample rate */ | ||
577 | 435 | ||
436 | #ifdef HAVE_BATTERY_SWITCH | ||
578 | /* | 437 | /* |
579 | * For a complete description of the charging algorithm read | 438 | * Reset the battery voltage filter to a new value and update the |
580 | * docs/CHARGING_ALGORITHM. | 439 | * status. |
581 | */ | 440 | */ |
582 | int long_delta; /* long term delta battery voltage */ | 441 | void reset_battery_filter(int millivolts) |
583 | int short_delta; /* short term delta battery voltage */ | ||
584 | bool disk_activity_last_cycle = false; /* flag set to aid charger time | ||
585 | * calculation */ | ||
586 | char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in | ||
587 | debug menu */ | ||
588 | /* percentage at which charging | ||
589 | starts */ | ||
590 | int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the | ||
591 | charging started or | ||
592 | stopped? */ | ||
593 | int powermgmt_last_cycle_level = 0; /* which level had the | ||
594 | batteries at this time? */ | ||
595 | int trickle_sec = 0; /* how many seconds should the | ||
596 | charger be enabled per | ||
597 | minute for trickle | ||
598 | charging? */ | ||
599 | int pid_p = 0; /* PID proportional term */ | ||
600 | int pid_i = 0; /* PID integral term */ | ||
601 | |||
602 | static inline void charging_algorithm_small_step(void) | ||
603 | { | 442 | { |
604 | if (storage_disk_is_active()) { | 443 | avgbat = millivolts * BATT_AVE_SAMPLES; |
605 | /* flag hdd use for charging calculation */ | 444 | battery_millivolts = millivolts; |
606 | disk_activity_last_cycle = true; | 445 | battery_status_update(); |
607 | } | ||
608 | |||
609 | #if defined(DEBUG_FILE) | ||
610 | /* | ||
611 | * If we have a lot of pending writes or if the disk is spining, | ||
612 | * fsync the debug log file. | ||
613 | */ | ||
614 | if((wrcount > 10) || ((wrcount > 0) && storage_disk_is_active())) { | ||
615 | fsync(fd); | ||
616 | wrcount = 0; | ||
617 | } | ||
618 | #endif /* defined(DEBUG_FILE) */ | ||
619 | } | 446 | } |
447 | #endif /* HAVE_BATTERY_SWITCH */ | ||
620 | 448 | ||
621 | static inline void charging_algorithm_big_step(void) | 449 | /** Generic charging algorithms for common charging types **/ |
450 | #if CONFIG_CHARGING == CHARGING_SIMPLE | ||
451 | static inline void charging_algorithm_step(void) | ||
622 | { | 452 | { |
623 | static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle | 453 | /* Nothing to do */ |
624 | * voltage level */ | ||
625 | static int charge_max_time_idle = 0; /* max. charging duration, calculated at | ||
626 | * beginning of charging */ | ||
627 | static int charge_max_time_now = 0; /* max. charging duration including | ||
628 | * hdd activity */ | ||
629 | static int minutes_disk_activity = 0; /* count minutes of hdd use during | ||
630 | * charging */ | ||
631 | static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */ | ||
632 | int i; | ||
633 | |||
634 | if (charger_input_state == CHARGER_PLUGGED) { | ||
635 | pid_p = 0; | ||
636 | pid_i = 0; | ||
637 | snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in"); | ||
638 | /* | ||
639 | * The charger was just plugged in. If the battery level is | ||
640 | * nearly charged, just trickle. If the battery is low, start | ||
641 | * a full charge cycle. If the battery level is in between, | ||
642 | * top-off and then trickle. | ||
643 | */ | ||
644 | if(battery_percent > START_TOPOFF_CHG) { | ||
645 | powermgmt_last_cycle_level = battery_percent; | ||
646 | powermgmt_last_cycle_startstop_min = 0; | ||
647 | if(battery_percent >= START_TRICKLE_CHG) { | ||
648 | charge_state = TRICKLE; | ||
649 | target_voltage = TRICKLE_VOLTAGE; | ||
650 | } else { | ||
651 | charge_state = TOPOFF; | ||
652 | target_voltage = TOPOFF_VOLTAGE; | ||
653 | } | ||
654 | } else { | ||
655 | /* | ||
656 | * Start the charger full strength | ||
657 | */ | ||
658 | i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500; | ||
659 | charge_max_time_idle = | ||
660 | i * (100 + 35 - battery_percent) / 100; | ||
661 | if (charge_max_time_idle > i) { | ||
662 | charge_max_time_idle = i; | ||
663 | } | ||
664 | charge_max_time_now = charge_max_time_idle; | ||
665 | |||
666 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
667 | "ChgAt %d%% max %dm", battery_level(), | ||
668 | charge_max_time_now); | ||
669 | |||
670 | /* enable the charger after the max time calc is done, | ||
671 | because battery_level depends on if the charger is | ||
672 | on */ | ||
673 | DEBUGF("power: charger inserted and battery" | ||
674 | " not full, charging\n"); | ||
675 | powermgmt_last_cycle_level = battery_percent; | ||
676 | powermgmt_last_cycle_startstop_min = 0; | ||
677 | trickle_sec = 60; | ||
678 | long_delta = short_delta = 999999; | ||
679 | charge_state = CHARGING; | ||
680 | } | ||
681 | } | ||
682 | |||
683 | if (charge_state == CHARGING) { | ||
684 | /* alter charge time max length with extra disk use */ | ||
685 | if (disk_activity_last_cycle) { | ||
686 | minutes_disk_activity++; | ||
687 | charge_max_time_now = charge_max_time_idle + | ||
688 | (minutes_disk_activity * 2 / 5); | ||
689 | disk_activity_last_cycle = false; | ||
690 | last_disk_activity = 0; | ||
691 | } else { | ||
692 | last_disk_activity++; | ||
693 | } | ||
694 | /* | ||
695 | * Check the delta voltage over the last X minutes so we can do | ||
696 | * our end-of-charge logic based on the battery level change. | ||
697 | *(no longer use minimum time as logic for charge end has 50 | ||
698 | * minutes minimum charge built in) | ||
699 | */ | ||
700 | if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) { | ||
701 | short_delta = power_history[0] - | ||
702 | power_history[CHARGE_END_SHORTD - 1]; | ||
703 | } | ||
704 | |||
705 | if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) { | ||
706 | /* | ||
707 | * Scan the history: the points where measurement is taken need to | ||
708 | * be fairly static. (check prior to short delta 'area') | ||
709 | * (also only check first and last 10 cycles - delta in middle OK) | ||
710 | */ | ||
711 | long_delta = power_history[0] - | ||
712 | power_history[CHARGE_END_LONGD - 1]; | ||
713 | |||
714 | for(i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++) { | ||
715 | if(((power_history[i] - power_history[i+1]) > 50) || | ||
716 | ((power_history[i] - power_history[i+1]) < -50)) { | ||
717 | long_delta = 777777; | ||
718 | break; | ||
719 | } | ||
720 | } | ||
721 | for(i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++) { | ||
722 | if(((power_history[i] - power_history[i+1]) > 50) || | ||
723 | ((power_history[i] - power_history[i+1]) < -50)) { | ||
724 | long_delta = 888888; | ||
725 | break; | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | |||
730 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
731 | "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min, | ||
732 | charge_max_time_now); | ||
733 | /* | ||
734 | * End of charge criteria (any qualify): | ||
735 | * 1) Charged a long time | ||
736 | * 2) DeltaV went negative for a short time ( & long delta static) | ||
737 | * 3) DeltaV was negative over a longer period (no disk use only) | ||
738 | * Note: short_delta and long_delta are millivolts | ||
739 | */ | ||
740 | if ((powermgmt_last_cycle_startstop_min >= charge_max_time_now) || | ||
741 | (short_delta <= -50 && long_delta < 50 ) || (long_delta < -20 && | ||
742 | last_disk_activity > CHARGE_END_LONGD)) { | ||
743 | if (powermgmt_last_cycle_startstop_min > charge_max_time_now) { | ||
744 | DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, " | ||
745 | "enough!\n"); | ||
746 | /* | ||
747 | *have charged too long and deltaV detection did not | ||
748 | *work! | ||
749 | */ | ||
750 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
751 | "Chg tmout %d min", charge_max_time_now); | ||
752 | /* | ||
753 | * Switch to trickle charging. We skip the top-off | ||
754 | * since we've effectively done the top-off operation | ||
755 | * already since we charged for the maximum full | ||
756 | * charge time. | ||
757 | */ | ||
758 | powermgmt_last_cycle_level = battery_percent; | ||
759 | powermgmt_last_cycle_startstop_min = 0; | ||
760 | charge_state = TRICKLE; | ||
761 | |||
762 | /* | ||
763 | * set trickle charge target to a relative voltage instead | ||
764 | * of an arbitrary value - the fully charged voltage may | ||
765 | * vary according to ambient temp, battery condition etc | ||
766 | * trickle target is -0.15v from full voltage acheived | ||
767 | * topup target is -0.05v from full voltage | ||
768 | */ | ||
769 | target_voltage = power_history[0] - 150; | ||
770 | |||
771 | } else { | ||
772 | if(short_delta <= -5) { | ||
773 | DEBUGF("power: short-term negative" | ||
774 | " delta, enough!\n"); | ||
775 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
776 | "end negd %d %dmin", short_delta, | ||
777 | powermgmt_last_cycle_startstop_min); | ||
778 | target_voltage = power_history[CHARGE_END_SHORTD - 1] | ||
779 | - 50; | ||
780 | } else { | ||
781 | DEBUGF("power: long-term small " | ||
782 | "positive delta, enough!\n"); | ||
783 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
784 | "end lowd %d %dmin", long_delta, | ||
785 | powermgmt_last_cycle_startstop_min); | ||
786 | target_voltage = power_history[CHARGE_END_LONGD - 1] | ||
787 | - 50; | ||
788 | } | ||
789 | /* | ||
790 | * Switch to top-off charging. | ||
791 | */ | ||
792 | powermgmt_last_cycle_level = battery_percent; | ||
793 | powermgmt_last_cycle_startstop_min = 0; | ||
794 | charge_state = TOPOFF; | ||
795 | } | ||
796 | } | ||
797 | } | ||
798 | else if (charge_state != DISCHARGING) /* top off or trickle */ | ||
799 | { | ||
800 | /* | ||
801 | *Time to switch from topoff to trickle? | ||
802 | */ | ||
803 | if ((charge_state == TOPOFF) && | ||
804 | (powermgmt_last_cycle_startstop_min > TOPOFF_MAX_TIME)) | ||
805 | { | ||
806 | powermgmt_last_cycle_level = battery_percent; | ||
807 | powermgmt_last_cycle_startstop_min = 0; | ||
808 | charge_state = TRICKLE; | ||
809 | target_voltage = target_voltage - 100; | ||
810 | } | ||
811 | /* | ||
812 | * Adjust trickle charge time (proportional and integral terms). | ||
813 | * Note: I considered setting the level higher if the USB is | ||
814 | * plugged in, but it doesn't appear to be necessary and will | ||
815 | * generate more heat [gvb]. | ||
816 | */ | ||
817 | |||
818 | pid_p = ((signed)target_voltage - (signed)battery_millivolts) / 5; | ||
819 | if((pid_p <= PID_DEADZONE) && (pid_p >= -PID_DEADZONE)) | ||
820 | pid_p = 0; | ||
821 | |||
822 | if((unsigned) battery_millivolts < target_voltage) { | ||
823 | if(pid_i < 60) { | ||
824 | pid_i++; /* limit so it doesn't "wind up" */ | ||
825 | } | ||
826 | } else { | ||
827 | if(pid_i > 0) { | ||
828 | pid_i--; /* limit so it doesn't "wind up" */ | ||
829 | } | ||
830 | } | ||
831 | |||
832 | trickle_sec = pid_p + pid_i; | ||
833 | |||
834 | if(trickle_sec > 60) { | ||
835 | trickle_sec = 60; | ||
836 | } | ||
837 | if(trickle_sec < 0) { | ||
838 | trickle_sec = 0; | ||
839 | } | ||
840 | |||
841 | } else if (charge_state == DISCHARGING) { | ||
842 | trickle_sec = 0; | ||
843 | /* | ||
844 | * The charger is enabled here only in one case: if it was | ||
845 | * turned on at boot time (power_init). Turn it off now. | ||
846 | */ | ||
847 | if (charger_enabled) | ||
848 | charger_enable(false); | ||
849 | } | ||
850 | |||
851 | if (charger_input_state == CHARGER_UNPLUGGED) { | ||
852 | /* | ||
853 | * The charger was just unplugged. | ||
854 | */ | ||
855 | DEBUGF("power: charger disconnected, disabling\n"); | ||
856 | |||
857 | charger_enable(false); | ||
858 | powermgmt_last_cycle_level = battery_percent; | ||
859 | powermgmt_last_cycle_startstop_min = 0; | ||
860 | trickle_sec = 0; | ||
861 | pid_p = 0; | ||
862 | pid_i = 0; | ||
863 | charge_state = DISCHARGING; | ||
864 | snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge"); | ||
865 | } | ||
866 | |||
867 | /* sleep for a minute */ | ||
868 | if(trickle_sec > 0) { | ||
869 | charger_enable(true); | ||
870 | power_thread_sleep(HZ * trickle_sec); | ||
871 | } | ||
872 | if(trickle_sec < 60) | ||
873 | charger_enable(false); | ||
874 | power_thread_sleep(HZ * (60 - trickle_sec)); | ||
875 | |||
876 | #if defined(DEBUG_FILE) | ||
877 | if(usb_inserted()) { | ||
878 | if(fd >= 0) { | ||
879 | /* It is probably too late to close the file but we can try...*/ | ||
880 | close(fd); | ||
881 | fd = -1; | ||
882 | } | ||
883 | } else { | ||
884 | if(fd < 0) { | ||
885 | fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT); | ||
886 | if(fd >= 0) { | ||
887 | snprintf(debug_message, DEBUG_MESSAGE_LEN, | ||
888 | "cycle_min, bat_millivolts, bat_percent, chgr_state" | ||
889 | " ,charge_state, pid_p, pid_i, trickle_sec\n"); | ||
890 | write(fd, debug_message, strlen(debug_message)); | ||
891 | wrcount = 99; /* force a flush */ | ||
892 | } | ||
893 | } | ||
894 | if(fd >= 0) { | ||
895 | snprintf(debug_message, DEBUG_MESSAGE_LEN, | ||
896 | "%d, %d, %d, %d, %d, %d, %d, %d\n", | ||
897 | powermgmt_last_cycle_startstop_min, battery_millivolts, | ||
898 | battery_percent, charger_input_state, charge_state, | ||
899 | pid_p, pid_i, trickle_sec); | ||
900 | write(fd, debug_message, strlen(debug_message)); | ||
901 | wrcount++; | ||
902 | } | ||
903 | } | ||
904 | #endif /* defined(DEBUG_FILE) */ | ||
905 | |||
906 | powermgmt_last_cycle_startstop_min++; | ||
907 | } | 454 | } |
908 | 455 | ||
909 | /* | ||
910 | * Prepare charging for poweroff | ||
911 | */ | ||
912 | static inline void charging_algorithm_close(void) | 456 | static inline void charging_algorithm_close(void) |
913 | { | 457 | { |
914 | #if defined(DEBUG_FILE) | 458 | /* Nothing to do */ |
915 | if(fd >= 0) { | ||
916 | close(fd); | ||
917 | fd = -1; | ||
918 | } | ||
919 | #endif | ||
920 | } | ||
921 | #elif CONFIG_CHARGING == CHARGING_TARGET | ||
922 | extern void charging_algorithm_big_step(void); | ||
923 | extern void charging_algorithm_small_step(void); | ||
924 | extern void charging_algorithm_close(void); | ||
925 | |||
926 | void set_filtered_battery_voltage(int millivolts) | ||
927 | { | ||
928 | avgbat = millivolts * BATT_AVE_SAMPLES; | ||
929 | battery_millivolts = millivolts; | ||
930 | battery_status_update(); | ||
931 | } | 459 | } |
932 | 460 | #elif CONFIG_CHARGING == CHARGING_MONITOR | |
933 | #else | 461 | /* |
934 | #define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */ | 462 | * Monitor CHARGING/DISCHARGING state. |
935 | 463 | */ | |
936 | static inline void charging_algorithm_small_step(void) | 464 | static inline void charging_algorithm_step(void) |
937 | { | 465 | { |
938 | #if CONFIG_CHARGING == CHARGING_MONITOR | ||
939 | switch (charger_input_state) | 466 | switch (charger_input_state) |
940 | { | 467 | { |
941 | case CHARGER_UNPLUGGED: | 468 | case CHARGER_PLUGGED: |
942 | case NO_CHARGER: | 469 | case CHARGER: |
943 | charge_state = DISCHARGING; | 470 | if (charging_state()) { |
944 | break; | 471 | charge_state = CHARGING; |
945 | case CHARGER_PLUGGED: | ||
946 | case CHARGER: | ||
947 | if (charging_state()) { | ||
948 | charge_state = CHARGING; | ||
949 | } else { | ||
950 | charge_state = DISCHARGING; | ||
951 | } | ||
952 | break; | 472 | break; |
473 | } | ||
474 | /* Fallthrough */ | ||
475 | case CHARGER_UNPLUGGED: | ||
476 | case NO_CHARGER: | ||
477 | charge_state = DISCHARGING; | ||
478 | break; | ||
953 | } | 479 | } |
954 | #endif /* CONFIG_CHARGING == CHARGING_MONITOR */ | ||
955 | } | 480 | } |
956 | 481 | ||
957 | static inline void charging_algorithm_big_step(void) | ||
958 | { | ||
959 | /* sleep for a minute */ | ||
960 | power_thread_sleep(HZ * 60); | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | * Prepare charging for poweroff | ||
965 | */ | ||
966 | static inline void charging_algorithm_close(void) | 482 | static inline void charging_algorithm_close(void) |
967 | { | 483 | { |
968 | /* Nothing to do */ | 484 | /* Nothing to do */ |
969 | } | 485 | } |
970 | #endif /* CONFIG_CHARGING == CHARGING_CONTROL */ | 486 | #endif /* CONFIG_CHARGING == * */ |
971 | 487 | ||
972 | #if CONFIG_CHARGING | 488 | #if CONFIG_CHARGING |
973 | /* Shortcut function calls - compatibility, simplicity. */ | 489 | /* Shortcut function calls - compatibility, simplicity. */ |
@@ -975,270 +491,271 @@ static inline void charging_algorithm_close(void) | |||
975 | /* Returns true if any power input is capable of charging. */ | 491 | /* Returns true if any power input is capable of charging. */ |
976 | bool charger_inserted(void) | 492 | bool charger_inserted(void) |
977 | { | 493 | { |
978 | return power_input_status() & POWER_INPUT_CHARGER; | 494 | return power_thread_inputs & POWER_INPUT_CHARGER; |
979 | } | 495 | } |
980 | 496 | ||
981 | /* Returns true if any power input is connected - charging-capable | 497 | /* Returns true if any power input is connected - charging-capable |
982 | * or not. */ | 498 | * or not. */ |
983 | bool power_input_present(void) | 499 | bool power_input_present(void) |
984 | { | 500 | { |
985 | return power_input_status() & POWER_INPUT; | 501 | return power_thread_inputs & POWER_INPUT; |
986 | } | 502 | } |
987 | #endif /* CONFIG_CHARGING */ | ||
988 | 503 | ||
989 | /* | 504 | /* |
990 | * This function is called to do the relativly long sleep waits from within the | 505 | * Detect charger inserted. Return true if the state is transistional. |
991 | * main power_thread loop while at the same time servicing any other periodic | ||
992 | * functions in the power thread which need to be called at a faster periodic | ||
993 | * rate than the slow periodic rate of the main power_thread loop. | ||
994 | * | ||
995 | * While we are waiting for the time to expire, we average the battery | ||
996 | * voltages. | ||
997 | */ | 506 | */ |
998 | void power_thread_sleep(int ticks) | 507 | static inline bool detect_charger(unsigned int pwr) |
999 | { | 508 | { |
1000 | long tick_return = current_tick + ticks; | 509 | /* |
510 | * Detect charger plugged/unplugged transitions. On a plugged or | ||
511 | * unplugged event, we return immediately, run once through the main | ||
512 | * loop (including the subroutines), and end up back here where we | ||
513 | * transition to the appropriate steady state charger on/off state. | ||
514 | */ | ||
515 | if (pwr & POWER_INPUT_CHARGER) { | ||
516 | switch (charger_input_state) | ||
517 | { | ||
518 | case NO_CHARGER: | ||
519 | case CHARGER_UNPLUGGED: | ||
520 | charger_input_state = CHARGER_PLUGGED; | ||
521 | break; | ||
1001 | 522 | ||
1002 | do | 523 | case CHARGER_PLUGGED: |
1003 | { | 524 | queue_broadcast(SYS_CHARGER_CONNECTED, 0); |
1004 | #if CONFIG_CHARGING | 525 | last_sent_battery_level = 0; |
1005 | /* | 526 | charger_input_state = CHARGER; |
1006 | * Detect charger plugged/unplugged transitions. On a plugged or | 527 | break; |
1007 | * unplugged event, we return immediately, run once through the main | 528 | |
1008 | * loop (including the subroutines), and end up back here where we | 529 | case CHARGER: |
1009 | * transition to the appropriate steady state charger on/off state. | 530 | /* Steady state */ |
1010 | */ | 531 | return false; |
1011 | if(power_input_status() & POWER_INPUT_CHARGER) { | ||
1012 | switch(charger_input_state) { | ||
1013 | case NO_CHARGER: | ||
1014 | case CHARGER_UNPLUGGED: | ||
1015 | charger_input_state = CHARGER_PLUGGED; | ||
1016 | tick_return = current_tick; | ||
1017 | goto do_small_step; /* Algorithm should see transition */ | ||
1018 | case CHARGER_PLUGGED: | ||
1019 | queue_broadcast(SYS_CHARGER_CONNECTED, 0); | ||
1020 | last_sent_battery_level = 0; | ||
1021 | charger_input_state = CHARGER; | ||
1022 | break; | ||
1023 | case CHARGER: | ||
1024 | break; | ||
1025 | } | ||
1026 | } else { /* charger not inserted */ | ||
1027 | switch(charger_input_state) { | ||
1028 | case NO_CHARGER: | ||
1029 | break; | ||
1030 | case CHARGER_UNPLUGGED: | ||
1031 | queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); | ||
1032 | last_sent_battery_level = 100; | ||
1033 | charger_input_state = NO_CHARGER; | ||
1034 | break; | ||
1035 | case CHARGER_PLUGGED: | ||
1036 | case CHARGER: | ||
1037 | charger_input_state = CHARGER_UNPLUGGED; | ||
1038 | tick_return = current_tick; | ||
1039 | goto do_small_step; /* Algorithm should see transition */ | ||
1040 | } | ||
1041 | } | 532 | } |
1042 | #endif /* CONFIG_CHARGING */ | 533 | } |
534 | else { /* charger not inserted */ | ||
535 | switch (charger_input_state) | ||
536 | { | ||
537 | case NO_CHARGER: | ||
538 | /* Steady state */ | ||
539 | return false; | ||
1043 | 540 | ||
1044 | ticks = tick_return - current_tick; | 541 | case CHARGER_UNPLUGGED: |
542 | queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); | ||
543 | last_sent_battery_level = 100; | ||
544 | charger_input_state = NO_CHARGER; | ||
545 | break; | ||
1045 | 546 | ||
1046 | if (ticks > 0) { | 547 | case CHARGER_PLUGGED: |
1047 | ticks = MIN(HZ/2, ticks); | 548 | case CHARGER: |
1048 | sleep(ticks); | 549 | charger_input_state = CHARGER_UNPLUGGED; |
550 | break; | ||
1049 | } | 551 | } |
552 | } | ||
1050 | 553 | ||
1051 | /* If the power off timeout expires, the main thread has failed | 554 | /* Transitional state */ |
1052 | to shut down the system, and we need to force a power off */ | 555 | return true; |
1053 | if(shutdown_timeout) { | 556 | } |
1054 | shutdown_timeout -= MAX(ticks, 1); | 557 | #endif /* CONFIG_CHARGING */ |
1055 | if(shutdown_timeout <= 0) | 558 | |
1056 | power_off(); | 559 | /* |
1057 | } | 560 | * Monitor the presence of a charger and perform critical frequent steps |
561 | * such as running the battery voltage filter. | ||
562 | */ | ||
563 | static inline void power_thread_step(void) | ||
564 | { | ||
565 | /* If the power off timeout expires, the main thread has failed | ||
566 | to shut down the system, and we need to force a power off */ | ||
567 | if (shutdown_timeout) { | ||
568 | shutdown_timeout -= POWER_THREAD_STEP_TICKS; | ||
569 | |||
570 | if (shutdown_timeout <= 0) | ||
571 | power_off(); | ||
572 | } | ||
1058 | 573 | ||
1059 | #ifdef HAVE_RTC_ALARM | 574 | #ifdef HAVE_RTC_ALARM |
1060 | power_thread_rtc_process(); | 575 | power_thread_rtc_process(); |
1061 | #endif | 576 | #endif |
1062 | 577 | ||
1063 | /* | 578 | /* |
1064 | * Do a digital exponential filter. We don't sample the battery if | 579 | * Do a digital exponential filter. We don't sample the battery if |
1065 | * the disk is spinning unless we are in USB mode (the disk will most | 580 | * the disk is spinning unless we are in USB mode (the disk will most |
1066 | * likely always be spinning in USB mode) or charging. | 581 | * likely always be spinning in USB mode) or charging. |
1067 | */ | 582 | */ |
1068 | if (!storage_disk_is_active() || usb_inserted() | 583 | if (!storage_disk_is_active() || usb_inserted() |
1069 | #if CONFIG_CHARGING >= CHARGING_MONITOR | 584 | #if CONFIG_CHARGING >= CHARGING_MONITOR |
1070 | || charger_input_state == CHARGER | 585 | || charger_input_state == CHARGER |
1071 | #endif | 586 | #endif |
1072 | ) { | 587 | ) { |
1073 | avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES); | 588 | avgbat += battery_adc_voltage() - avgbat / BATT_AVE_SAMPLES; |
1074 | /* | 589 | /* |
1075 | * battery_millivolts is the millivolt-scaled filtered battery value. | 590 | * battery_millivolts is the millivolt-scaled filtered battery value. |
1076 | */ | 591 | */ |
1077 | battery_millivolts = avgbat / BATT_AVE_SAMPLES; | 592 | battery_millivolts = avgbat / BATT_AVE_SAMPLES; |
1078 | 593 | ||
1079 | /* update battery status every time an update is available */ | 594 | /* update battery status every time an update is available */ |
1080 | battery_status_update(); | 595 | battery_status_update(); |
596 | } | ||
597 | else if (battery_percent < 8) { | ||
598 | /* | ||
599 | * If battery is low, observe voltage during disk activity. | ||
600 | * Shut down if voltage drops below shutoff level and we are not | ||
601 | * using NiMH or Alkaline batteries. | ||
602 | */ | ||
603 | battery_millivolts = (battery_adc_voltage() + | ||
604 | battery_millivolts + 1) / 2; | ||
605 | |||
606 | /* update battery status every time an update is available */ | ||
607 | battery_status_update(); | ||
608 | |||
609 | if (!shutdown_timeout && query_force_shutdown()) { | ||
610 | sys_poweroff(); | ||
1081 | } | 611 | } |
1082 | else if (battery_percent < 8) { | 612 | else { |
1083 | /* If battery is low, observe voltage during disk activity. | 613 | avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES; |
1084 | * Shut down if voltage drops below shutoff level and we are not | ||
1085 | * using NiMH or Alkaline batteries. | ||
1086 | */ | ||
1087 | battery_millivolts = (battery_adc_voltage() + | ||
1088 | battery_millivolts + 1) / 2; | ||
1089 | |||
1090 | /* update battery status every time an update is available */ | ||
1091 | battery_status_update(); | ||
1092 | |||
1093 | if (!shutdown_timeout && query_force_shutdown()) { | ||
1094 | sys_poweroff(); | ||
1095 | } | ||
1096 | else { | ||
1097 | avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES); | ||
1098 | } | ||
1099 | } | 614 | } |
1100 | |||
1101 | #if CONFIG_CHARGING | ||
1102 | do_small_step: | ||
1103 | #endif | ||
1104 | charging_algorithm_small_step(); | ||
1105 | } | 615 | } |
1106 | while (TIME_BEFORE(current_tick, tick_return)); | 616 | } /* power_thread_step */ |
1107 | } | ||
1108 | 617 | ||
1109 | static void power_thread(void) | 618 | static void power_thread(void) |
1110 | { | 619 | { |
620 | long next_power_hist; | ||
621 | |||
1111 | /* Delay reading the first battery level */ | 622 | /* Delay reading the first battery level */ |
1112 | #ifdef MROBE_100 | 623 | #ifdef MROBE_100 |
1113 | while(battery_adc_voltage()>4200) /* gives false readings initially */ | 624 | while (battery_adc_voltage() > 4200) /* gives false readings initially */ |
625 | #endif | ||
626 | { | ||
627 | sleep(HZ/100); | ||
628 | } | ||
629 | |||
630 | #if CONFIG_CHARGING | ||
631 | /* Initialize power input status before calling other routines. */ | ||
632 | power_thread_inputs = power_input_status(); | ||
1114 | #endif | 633 | #endif |
1115 | sleep(HZ/100); | ||
1116 | 634 | ||
1117 | /* initialize the voltages for the exponential filter */ | 635 | /* initialize the voltages for the exponential filter */ |
1118 | avgbat = battery_adc_voltage() + 15; | 636 | avgbat = battery_adc_voltage() + 15; |
1119 | 637 | ||
1120 | #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */ | 638 | #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */ |
1121 | /* The battery voltage is usually a little lower directly after | 639 | /* The battery voltage is usually a little lower directly after |
1122 | turning on, because the disk was used heavily. Raise it by 5% */ | 640 | turning on, because the disk was used heavily. Raise it by 5% */ |
1123 | #if CONFIG_CHARGING | 641 | #if CONFIG_CHARGING |
1124 | if(!charger_inserted()) /* only if charger not connected */ | 642 | if (!charger_inserted()) /* only if charger not connected */ |
1125 | #endif | 643 | #endif |
644 | { | ||
1126 | avgbat += (percent_to_volt_discharge[battery_type][6] - | 645 | avgbat += (percent_to_volt_discharge[battery_type][6] - |
1127 | percent_to_volt_discharge[battery_type][5]) / 2; | 646 | percent_to_volt_discharge[battery_type][5]) / 2; |
647 | } | ||
1128 | #endif /* HAVE_DISK_STORAGE */ | 648 | #endif /* HAVE_DISK_STORAGE */ |
1129 | 649 | ||
1130 | avgbat = avgbat * BATT_AVE_SAMPLES; | 650 | avgbat = avgbat * BATT_AVE_SAMPLES; |
1131 | battery_millivolts = avgbat / BATT_AVE_SAMPLES; | 651 | battery_millivolts = avgbat / BATT_AVE_SAMPLES; |
652 | power_history[0] = battery_millivolts; | ||
1132 | 653 | ||
1133 | #if CONFIG_CHARGING | 654 | #if CONFIG_CHARGING |
1134 | if(charger_inserted()) { | 655 | if (charger_inserted()) { |
1135 | battery_percent = voltage_to_percent(battery_millivolts, | 656 | battery_percent = voltage_to_percent(battery_millivolts, |
1136 | percent_to_volt_charge); | 657 | percent_to_volt_charge); |
1137 | } else | 658 | } |
659 | else | ||
1138 | #endif | 660 | #endif |
1139 | { battery_percent = voltage_to_percent(battery_millivolts, | 661 | { |
1140 | percent_to_volt_discharge[battery_type]); | 662 | battery_percent = voltage_to_percent(battery_millivolts, |
1141 | battery_percent += (battery_percent < 100); | 663 | percent_to_volt_discharge[battery_type]); |
664 | battery_percent += battery_percent < 100; | ||
1142 | } | 665 | } |
1143 | 666 | ||
1144 | #if CONFIG_CHARGING == CHARGING_TARGET | 667 | #if CONFIG_CHARGING == CHARGING_TARGET |
1145 | powermgmt_init_target(); | 668 | powermgmt_init_target(); |
1146 | #endif | 669 | #endif |
1147 | 670 | ||
671 | next_power_hist = current_tick + HZ*60; | ||
672 | |||
1148 | while (1) | 673 | while (1) |
1149 | { | 674 | { |
675 | #if CONFIG_CHARGING >= CHARGING_MONITOR | ||
676 | unsigned int pwr = power_input_status(); | ||
677 | #ifdef HAVE_BATTERY_SWITCH | ||
678 | if ((pwr ^ power_thread_inputs) & POWER_INPUT_BATTERY) { | ||
679 | sleep(HZ/10); | ||
680 | reset_battery_filter(battery_adc_voltage()); | ||
681 | } | ||
682 | #endif | ||
683 | power_thread_inputs = pwr; | ||
684 | |||
685 | if (!detect_charger(pwr)) | ||
686 | #endif /* CONFIG_CHARGING */ | ||
687 | { | ||
688 | /* Steady state */ | ||
689 | sleep(POWER_THREAD_STEP_TICKS); | ||
690 | |||
691 | /* Do common power tasks */ | ||
692 | power_thread_step(); | ||
693 | } | ||
694 | |||
695 | /* Perform target tasks */ | ||
696 | charging_algorithm_step(); | ||
697 | |||
698 | if (TIME_BEFORE(current_tick, next_power_hist)) | ||
699 | continue; | ||
700 | |||
701 | /* increment to ensure there is a record for every minute | ||
702 | * rather than go forward from the current tick */ | ||
703 | next_power_hist += HZ*60; | ||
704 | |||
1150 | /* rotate the power history */ | 705 | /* rotate the power history */ |
1151 | memmove(power_history + 1, power_history, | 706 | memmove(&power_history[1], &power_history[0], |
1152 | sizeof(power_history) - sizeof(power_history[0])); | 707 | sizeof(power_history) - sizeof(power_history[0])); |
1153 | 708 | ||
1154 | /* insert new value at the start, in millivolts 8-) */ | 709 | /* insert new value at the start, in millivolts 8-) */ |
1155 | power_history[0] = battery_millivolts; | 710 | power_history[0] = battery_millivolts; |
1156 | 711 | ||
1157 | charging_algorithm_big_step(); | ||
1158 | |||
1159 | handle_auto_poweroff(); | 712 | handle_auto_poweroff(); |
1160 | } | 713 | } |
1161 | } | 714 | } /* power_thread */ |
1162 | 715 | ||
1163 | void powermgmt_init(void) | 716 | void powermgmt_init(void) |
1164 | { | 717 | { |
1165 | /* init history to 0 */ | 718 | /* init history to 0 */ |
1166 | memset(power_history, 0x00, sizeof(power_history)); | 719 | memset(power_history, 0, sizeof(power_history)); |
1167 | create_thread(power_thread, power_stack, sizeof(power_stack), 0, | 720 | create_thread(power_thread, power_stack, sizeof(power_stack), 0, |
1168 | power_thread_name IF_PRIO(, PRIORITY_SYSTEM) | 721 | power_thread_name IF_PRIO(, PRIORITY_SYSTEM) |
1169 | IF_COP(, CPU)); | 722 | IF_COP(, CPU)); |
1170 | } | 723 | } |
1171 | 724 | ||
1172 | #endif /* SIMULATOR */ | 725 | /* Various hardware housekeeping tasks relating to shutting down the player */ |
1173 | |||
1174 | void sys_poweroff(void) | ||
1175 | { | ||
1176 | #ifndef BOOTLOADER | ||
1177 | logf("sys_poweroff()"); | ||
1178 | /* If the main thread fails to shut down the system, we will force a | ||
1179 | power off after an 20 second timeout - 28 seconds if recording */ | ||
1180 | if (shutdown_timeout == 0) | ||
1181 | { | ||
1182 | #if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR) | ||
1183 | pcf50606_reset_timeout(); /* Reset timer on first attempt only */ | ||
1184 | #endif | ||
1185 | #if defined(HAVE_RECORDING) && !defined(BOOTLOADER) | ||
1186 | if (audio_status() & AUDIO_STATUS_RECORD) | ||
1187 | shutdown_timeout += HZ*8; | ||
1188 | #endif | ||
1189 | shutdown_timeout += HZ*20; | ||
1190 | } | ||
1191 | |||
1192 | queue_broadcast(SYS_POWEROFF, 0); | ||
1193 | #endif /* BOOTLOADER */ | ||
1194 | } | ||
1195 | |||
1196 | void cancel_shutdown(void) | ||
1197 | { | ||
1198 | logf("sys_cancel_shutdown()"); | ||
1199 | |||
1200 | #if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR) | ||
1201 | /* TODO: Move some things to target/ tree */ | ||
1202 | if (shutdown_timeout) | ||
1203 | pcf50606_reset_timeout(); | ||
1204 | #endif | ||
1205 | |||
1206 | shutdown_timeout = 0; | ||
1207 | } | ||
1208 | |||
1209 | /* Various hardware housekeeping tasks relating to shutting down the jukebox */ | ||
1210 | void shutdown_hw(void) | 726 | void shutdown_hw(void) |
1211 | { | 727 | { |
1212 | #ifndef SIMULATOR | ||
1213 | charging_algorithm_close(); | 728 | charging_algorithm_close(); |
1214 | audio_stop(); | 729 | audio_stop(); |
730 | |||
1215 | if (battery_level_safe()) { /* do not save on critical battery */ | 731 | if (battery_level_safe()) { /* do not save on critical battery */ |
1216 | #ifdef HAVE_LCD_BITMAP | 732 | #ifdef HAVE_LCD_BITMAP |
1217 | glyph_cache_save(); | 733 | glyph_cache_save(); |
1218 | #endif | 734 | #endif |
1219 | if(storage_disk_is_active()) | 735 | if (storage_disk_is_active()) |
1220 | storage_spindown(1); | 736 | storage_spindown(1); |
1221 | } | 737 | } |
1222 | while(storage_disk_is_active()) | 738 | |
739 | while (storage_disk_is_active()) | ||
1223 | sleep(HZ/10); | 740 | sleep(HZ/10); |
1224 | 741 | ||
1225 | #if CONFIG_CODEC != SWCODEC | 742 | #if CONFIG_CODEC == SWCODEC |
1226 | mp3_shutdown(); | ||
1227 | #else | ||
1228 | audiohw_close(); | 743 | audiohw_close(); |
744 | #else | ||
745 | mp3_shutdown(); | ||
1229 | #endif | 746 | #endif |
1230 | 747 | ||
1231 | /* If HD is still active we try to wait for spindown, otherwise the | 748 | /* If HD is still active we try to wait for spindown, otherwise the |
1232 | shutdown_timeout in power_thread_sleep will force a power off */ | 749 | shutdown_timeout in power_thread_step will force a power off */ |
1233 | while(storage_disk_is_active()) | 750 | while (storage_disk_is_active()) |
1234 | sleep(HZ/10); | 751 | sleep(HZ/10); |
752 | |||
1235 | #ifndef HAVE_LCD_COLOR | 753 | #ifndef HAVE_LCD_COLOR |
1236 | lcd_set_contrast(0); | 754 | lcd_set_contrast(0); |
1237 | #endif | 755 | #endif |
1238 | #ifdef HAVE_REMOTE_LCD | 756 | #ifdef HAVE_REMOTE_LCD |
1239 | lcd_remote_set_contrast(0); | 757 | lcd_remote_set_contrast(0); |
1240 | #endif | 758 | #endif |
1241 | |||
1242 | #ifdef HAVE_LCD_SHUTDOWN | 759 | #ifdef HAVE_LCD_SHUTDOWN |
1243 | lcd_shutdown(); | 760 | lcd_shutdown(); |
1244 | #endif | 761 | #endif |
@@ -1248,23 +765,58 @@ void shutdown_hw(void) | |||
1248 | byte. */ | 765 | byte. */ |
1249 | sleep(HZ/4); | 766 | sleep(HZ/4); |
1250 | power_off(); | 767 | power_off(); |
1251 | #endif /* #ifndef SIMULATOR */ | ||
1252 | } | 768 | } |
1253 | 769 | ||
770 | void sys_poweroff(void) | ||
771 | { | ||
772 | #ifndef BOOTLOADER | ||
773 | logf("sys_poweroff()"); | ||
774 | /* If the main thread fails to shut down the system, we will force a | ||
775 | power off after an 20 second timeout - 28 seconds if recording */ | ||
776 | if (shutdown_timeout == 0) { | ||
777 | #if defined(IAUDIO_X5) || defined(IAUDIO_M5) | ||
778 | pcf50606_reset_timeout(); /* Reset timer on first attempt only */ | ||
779 | #endif | ||
780 | #ifdef HAVE_RECORDING | ||
781 | if (audio_status() & AUDIO_STATUS_RECORD) | ||
782 | shutdown_timeout += HZ*8; | ||
783 | #endif | ||
784 | shutdown_timeout += HZ*20; | ||
785 | } | ||
786 | |||
787 | queue_broadcast(SYS_POWEROFF, 0); | ||
788 | #endif /* BOOTLOADER */ | ||
789 | } | ||
790 | |||
791 | void cancel_shutdown(void) | ||
792 | { | ||
793 | logf("cancel_shutdown()"); | ||
794 | |||
795 | #if defined(IAUDIO_X5) || defined(IAUDIO_M5) | ||
796 | /* TODO: Move some things to target/ tree */ | ||
797 | if (shutdown_timeout) | ||
798 | pcf50606_reset_timeout(); | ||
799 | #endif | ||
800 | |||
801 | shutdown_timeout = 0; | ||
802 | } | ||
803 | #endif /* SIMULATOR */ | ||
804 | |||
1254 | /* Send system battery level update events on reaching certain significant | 805 | /* Send system battery level update events on reaching certain significant |
1255 | levels. This must be called after battery_percent has been updated. */ | 806 | levels. This must be called after battery_percent has been updated. */ |
1256 | static void send_battery_level_event(void) | 807 | void send_battery_level_event(void) |
1257 | { | 808 | { |
1258 | static const int levels[] = { 5, 15, 30, 50, 0 }; | 809 | static const int levels[] = { 5, 15, 30, 50, 0 }; |
1259 | const int *level = levels; | 810 | const int *level = levels; |
811 | |||
1260 | while (*level) | 812 | while (*level) |
1261 | { | 813 | { |
1262 | if (battery_percent <= *level && last_sent_battery_level > *level) | 814 | if (battery_percent <= *level && last_sent_battery_level > *level) { |
1263 | { | ||
1264 | last_sent_battery_level = *level; | 815 | last_sent_battery_level = *level; |
1265 | queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level); | 816 | queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level); |
1266 | break; | 817 | break; |
1267 | } | 818 | } |
819 | |||
1268 | level++; | 820 | level++; |
1269 | } | 821 | } |
1270 | } | 822 | } |