diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 3bf3c39fea..813955d3b0 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -1757,3 +1757,8 @@ id: LANG_PLUGINS desc: in main_menu() eng: "Plugins" new: + +id: LANG_CAR_ADAPTER_MODE +desc: Displayed for setting car adapter mode to on/off +eng: "Car Adapter Mode" +new: diff --git a/apps/settings.c b/apps/settings.c index e31a2d47d3..9b8f1b35e1 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -133,6 +133,8 @@ Rest of config block, only saved to disk: 0xAC Max number of files in dir (50-10000) 0xAE fade on pause/unpause/stop setting (bit 0) caption backlight (bit 1) + car adapter mode (bit 2) +0xAF [available/unused] 0xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7) 0xB1 peak meter release step size, peak_meter_dbfs (bit 7) 0xB2 peak meter min either in -db or in percent @@ -402,7 +404,8 @@ int settings_save( void ) (global_settings.max_files_in_dir >> 8) & 0xff; config_block[0xae] = (unsigned char) ((global_settings.fade_on_stop & 1) | - ((global_settings.caption_backlight & 1) << 1)); + ((global_settings.caption_backlight & 1) << 1) | + ((global_settings.car_adapter_mode & 1) << 2)); config_block[0xb0] = (unsigned char)global_settings.peak_meter_clip_hold | (global_settings.peak_meter_performance ? 0x80 : 0); config_block[0xb1] = global_settings.peak_meter_release | @@ -542,6 +545,8 @@ void settings_apply(void) global_settings.lang_file); lang_load(buf); } + + set_car_adapter_mode(global_settings.car_adapter_mode); } /* @@ -684,6 +689,7 @@ void settings_load(void) if (config_block[0xae] != 0xff) { global_settings.fade_on_stop = config_block[0xae] & 1; global_settings.caption_backlight = (config_block[0xae] >> 1) & 1; + global_settings.car_adapter_mode = (config_block[0xae] >> 2) & 1; } if(config_block[0xb0] != 0xff) { @@ -1070,6 +1076,8 @@ bool settings_load_config(char* file) else if (!strcasecmp(name, "max files in playlist")) set_cfg_int(&global_settings.max_files_in_playlist, value, 1000, 20000); + else if (!strcasecmp(name, "car adapter mode")) + set_cfg_bool(&global_settings.car_adapter_mode, value); else if (!strcasecmp(name, "recursive directory insert")) { static char* options[] = {"off", "on", "ask"}; set_cfg_option(&global_settings.recursive_dir_insert, value, @@ -1330,6 +1338,9 @@ bool settings_save_config(void) options[global_settings.poweroff]); } + fprintf(fd, "car adapter mode: %s\r\n", + boolopt[global_settings.car_adapter_mode]); + #ifdef HAVE_MAS3587F fprintf(fd, "#\r\n# Recording\r\n#\r\n"); fprintf(fd, "rec quality: %d\r\n", global_settings.rec_quality); @@ -1456,6 +1467,7 @@ void settings_reset(void) { global_settings.topruntime = 0; global_settings.fade_on_stop = true; global_settings.caption_backlight = false; + global_settings.car_adapter_mode = false; global_settings.max_files_in_dir = 400; global_settings.max_files_in_playlist = 10000; global_settings.show_icons = true; diff --git a/apps/settings.h b/apps/settings.h index c3772ad7ff..635834cac0 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -135,6 +135,7 @@ struct user_settings bool peak_meter_performance; /* true: high performance, else save energy*/ int peak_meter_min; /* range minimum */ int peak_meter_max; /* range maximum */ + bool car_adapter_mode; /* 0=off 1=on */ /* show status bar */ bool statusbar; /* 0=hide, 1=show */ diff --git a/apps/settings_menu.c b/apps/settings_menu.c index f12000c1ef..7af267389a 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -44,6 +44,15 @@ #endif #include "lang.h" +static bool car_adapter_mode(void) +{ + return set_bool_options( str(LANG_CAR_ADAPTER_MODE), + &global_settings.car_adapter_mode, + str(LANG_SET_BOOL_YES), + str(LANG_SET_BOOL_NO), + set_car_adapter_mode); +} + static bool contrast(void) { return set_int( str(LANG_CONTRAST), "", &global_settings.contrast, @@ -901,6 +910,7 @@ static bool system_settings_menu(void) { str(LANG_TIMEFORMAT), timeformat_set }, #endif { str(LANG_POWEROFF_IDLE), poweroff_idle_timer }, + { str(LANG_CAR_ADAPTER_MODE), car_adapter_mode }, { str(LANG_RESET), reset_settings }, }; diff --git a/apps/status.c b/apps/status.c index 6f412d81b1..f0445c3901 100644 --- a/apps/status.c +++ b/apps/status.c @@ -119,6 +119,7 @@ void status_draw(bool force_redraw) info.shuffle = global_settings.playlist_shuffle; info.keylock = keys_locked; info.repeat = global_settings.repeat_mode; + info.playmode = current_mode; /* only redraw if forced to, or info has changed */ if (force_redraw || diff --git a/apps/wps.c b/apps/wps.c index f34ec81d9c..c76d7ade26 100644 --- a/apps/wps.c +++ b/apps/wps.c @@ -653,14 +653,22 @@ int wps_show(void) restore = true; } - if (mpeg_status() & MPEG_STATUS_PAUSE) { - paused = true; - } else { - paused = false; - } - while ( 1 ) { + /* did someone else (i.e power thread) change mpeg pause mode? */ + if (paused != (mpeg_status() & MPEG_STATUS_PAUSE)) { + paused = mpeg_status() & MPEG_STATUS_PAUSE; + status_set_playmode(paused ? STATUS_PAUSE : STATUS_PLAY); + + /* if another thread paused mpeg, we are probably in car mode, + about to shut down. lets save the settings. */ + if (paused && global_settings.resume) { + settings_save(); +#ifndef HAVE_RTC + ata_flush(); +#endif + } + } #ifdef HAVE_LCD_BITMAP /* when the peak meter is enabled we want to have a diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h index 4b4d040dcd..f6b1a64b68 100644 --- a/firmware/export/powermgmt.h +++ b/firmware/export/powermgmt.h @@ -96,5 +96,6 @@ void set_battery_capacity(int capacity); /* set local battery capacity value */ void set_sleep_timer(int seconds); int get_sleep_timer(void); +void set_car_adapter_mode(bool setting); #endif diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index 21ccddf301..cca44e01bd 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c @@ -64,11 +64,18 @@ void set_battery_capacity(int capacity) { (void)capacity; } + +void set_car_adapter_mode(bool setting) +{ + (void)setting; +} + #else /* not SIMULATOR */ int battery_capacity = 1500; /* only a default value */ int battery_level_cached = -1; /* battery level of this minute, updated once per minute */ +static bool car_adapter_mode_enabled = false; static int poweroff_idle_timeout_value[15] = { @@ -306,8 +313,8 @@ static void handle_auto_poweroff(void) #endif !usb_inserted() && (mpeg_stat == 0 || - (mpeg_stat == (MPEG_STATUS_PLAY | MPEG_STATUS_PAUSE)) && - !sleeptimer_active)) + ((mpeg_stat == (MPEG_STATUS_PLAY | MPEG_STATUS_PAUSE)) && + !sleeptimer_active))) { if(TIME_AFTER(current_tick, last_keypress + timeout) && TIME_AFTER(current_tick, last_disk_activity + timeout)) @@ -346,6 +353,78 @@ static void handle_auto_poweroff(void) } } +void set_car_adapter_mode(bool setting) +{ + car_adapter_mode_enabled = setting; +} + +static void car_adapter_mode_processing(void) +{ + static bool charger_power_is_on = false; + static bool waiting_to_resume_play = false; + static long play_resume_time; + + if (car_adapter_mode_enabled) { + + if (waiting_to_resume_play) { + if (TIME_AFTER(current_tick, play_resume_time)) { + if (mpeg_status() & MPEG_STATUS_PAUSE) { + mpeg_resume(); + } + waiting_to_resume_play = false; + } + } + else { + if (charger_power_is_on) { + + /* if external power was turned off */ + if (!charger_inserted()) { + + charger_power_is_on = false; + + /* if playing */ + if ((mpeg_status() & MPEG_STATUS_PLAY) && + !(mpeg_status() & MPEG_STATUS_PAUSE)) { + mpeg_pause(); + } + } + } + else { + /* if external power was turned on */ + if (charger_inserted()) { + + charger_power_is_on = true; + + /* if paused */ + if (mpeg_status() & MPEG_STATUS_PAUSE) { + /* delay resume a bit while the engine is cranking */ + play_resume_time = current_tick + HZ*5; + waiting_to_resume_play = true; + } + } + } + } + } +} + +/* + * 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 + */ +static void power_thread_sleep(int ticks) +{ + while (ticks > 0) { + int small_ticks = MIN(HZ/2, ticks); + sleep(small_ticks); + ticks -= small_ticks; + + car_adapter_mode_processing(); + } +} + + /* * This power thread maintains a history of battery voltage * and implements a charging algorithm. @@ -389,7 +468,7 @@ static void power_thread(void) avg += adc_read(ADC_UNREG_POWER); ok_samples++; } - sleep(HZ*POWER_AVG_SLEEP); + power_thread_sleep(HZ*POWER_AVG_SLEEP); } avg = avg / ((ok_samples) ? ok_samples : spin_samples); @@ -580,7 +659,7 @@ static void power_thread(void) /* charge the calculated amount of seconds */ charger_enable(true); - sleep(HZ * trickle_sec); + power_thread_sleep(HZ * trickle_sec); charger_enable(false); /* trickle charging long enough? */ @@ -686,7 +765,7 @@ static void power_thread(void) i = 60 - POWER_AVG_N * POWER_AVG_SLEEP; #endif if (i > 0) - sleep(HZ*(i)); + power_thread_sleep(HZ*(i)); handle_auto_poweroff(); }