From 7caf5c8ff40ef0f2423f7f80a83686f56a331f80 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Mon, 10 Jul 2017 09:05:12 +0900 Subject: [PATCH] add new powersave features for supporting a live sleep mode so new laptops now seem to no longer support S3 sleep. sleeping is done basically by going as idle as possible. you can ask the kernel to freeze execution BUT this seems to use about the same power as staying alive in my tests. to support this add 2 things: 1. a FREEZE powersave mode which implies we're alive but want to really stay as idle as absolutely possible. 2. powersave aware sleep functions that replace the usleeps in threads so they can switch from being super sleepy when in freeze mode to normal. --- src/bin/e_powersave.c | 124 +++++++++++++++++++++++---- src/bin/e_powersave.h | 10 ++- src/modules/cpufreq/e_mod_main.c | 20 ++++- src/modules/temperature/e_mod_main.c | 8 +- src/modules/temperature/e_mod_main.h | 1 + 5 files changed, 139 insertions(+), 24 deletions(-) diff --git a/src/bin/e_powersave.c b/src/bin/e_powersave.c index be622b577..03a1b91ea 100644 --- a/src/bin/e_powersave.c +++ b/src/bin/e_powersave.c @@ -8,9 +8,12 @@ struct _E_Powersave_Deferred_Action }; /* local subsystem functions */ +static void _e_powersave_sleeper_cb_dummy(void *data EINA_UNUSED, void *buffer EINA_UNUSED, unsigned int bytes EINA_UNUSED); static Eina_Bool _e_powersave_cb_deferred_timer(void *data); static void _e_powersave_mode_eval(void); static void _e_powersave_event_update_free(void *data EINA_UNUSED, void *event); +static void _e_powersave_event_change_send(E_Powersave_Mode mode); +static void _e_powersave_sleepers_wake(void); /* local subsystem globals */ E_API int E_EVENT_POWERSAVE_UPDATE = 0; @@ -19,7 +22,10 @@ static int walking_deferred_actions = 0; static Eina_List *deferred_actions = NULL; static Ecore_Timer *deferred_timer = NULL; static E_Powersave_Mode powersave_mode = E_POWERSAVE_MODE_LOW; +static E_Powersave_Mode powersave_mode_force = E_POWERSAVE_MODE_NONE; static double defer_time = 5.0; +static Eina_Bool powersave_force = EINA_FALSE; +static Eina_List *powersave_sleepers = NULL; /* externally accessible functions */ EINTERN int @@ -46,8 +52,8 @@ e_powersave_deferred_action_add(void (*func)(void *data), const void *data) if (!pa) return NULL; if (deferred_timer) ecore_timer_del(deferred_timer); deferred_timer = ecore_timer_loop_add(defer_time, - _e_powersave_cb_deferred_timer, - NULL); + _e_powersave_cb_deferred_timer, + NULL); pa->func = func; pa->data = data; deferred_actions = eina_list_append(deferred_actions, pa); @@ -80,41 +86,101 @@ e_powersave_deferred_action_del(E_Powersave_Deferred_Action *pa) E_API void e_powersave_mode_set(E_Powersave_Mode mode) { - E_Event_Powersave_Update *ev; - if (mode < e_config->powersave.min) mode = e_config->powersave.min; - else if (mode > e_config->powersave.max) - mode = e_config->powersave.max; + else if (mode > e_config->powersave.max) mode = e_config->powersave.max; + if (powersave_mode == mode) return; - printf("CHANGE PW SAVE MODE TO %i / %i\n", (int)mode, E_POWERSAVE_MODE_EXTREME); powersave_mode = mode; - ev = E_NEW(E_Event_Powersave_Update, 1); - ev->mode = mode; - ecore_event_add(E_EVENT_POWERSAVE_UPDATE, ev, _e_powersave_event_update_free, NULL); + if (powersave_force) return; + _e_powersave_event_change_send(powersave_mode); _e_powersave_mode_eval(); } E_API E_Powersave_Mode e_powersave_mode_get(void) { + if (powersave_force) return powersave_mode_force; return powersave_mode; } -E_API E_Powersave_Mode -e_powersave_mode_min_get(void) +E_API void +e_powersave_mode_force(E_Powersave_Mode mode) { - return e_config->powersave.min; + if (mode == powersave_mode_force) return; + powersave_force = EINA_TRUE; + powersave_mode_force = mode; + _e_powersave_event_change_send(powersave_mode_force); + _e_powersave_mode_eval(); } -E_API E_Powersave_Mode -e_powersave_mode_max_get(void) +E_API void +e_powersave_mode_unforce(void) { - return e_config->powersave.max; + if (!powersave_force) return; + powersave_force = EINA_FALSE; + if (powersave_mode_force != powersave_mode) + { + _e_powersave_event_change_send(powersave_mode); + _e_powersave_mode_eval(); + } + powersave_mode_force = E_POWERSAVE_MODE_NONE; +} + +E_API E_Powersave_Sleeper * +e_powersave_sleeper_new(void) +{ + Ecore_Pipe *pipe; + + pipe = ecore_pipe_add(_e_powersave_sleeper_cb_dummy, NULL); + powersave_sleepers = eina_list_append(powersave_sleepers, pipe); + return (E_Powersave_Sleeper *)pipe; +} + +E_API void +e_powersave_sleeper_free(E_Powersave_Sleeper *sleeper) +{ + Ecore_Pipe *pipe = (Ecore_Pipe *)sleeper; + + if (!pipe) return; + powersave_sleepers = eina_list_remove(powersave_sleepers, pipe); + ecore_pipe_del(pipe); +} + +E_API void +e_powersave_sleeper_sleep(E_Powersave_Sleeper *sleeper, int poll_interval) +{ + Ecore_Pipe *pipe = (Ecore_Pipe *)sleeper; + double tim, now; + + if (!pipe) return; + if (e_powersave_mode_get() == E_POWERSAVE_MODE_FREEZE) tim = 3600; + else tim = (double)poll_interval / 8.0; + now = ecore_time_get(); + tim = fmod(now, tim); + ecore_pipe_wait(pipe, 1, tim); } /* local subsystem functions */ +static void +_e_powersave_sleepers_wake(void) +{ + Ecore_Pipe *pipe; + Eina_List *l; + char buf[1] = { 1 }; + + EINA_LIST_FOREACH(powersave_sleepers, l, pipe) + { + ecore_pipe_write(pipe, buf, 1); + } +} + +static void +_e_powersave_sleeper_cb_dummy(void *data EINA_UNUSED, void *buffer EINA_UNUSED, unsigned int bytes EINA_UNUSED) +{ +} + static Eina_Bool _e_powersave_cb_deferred_timer(void *data EINA_UNUSED) { @@ -135,8 +201,12 @@ static void _e_powersave_mode_eval(void) { double t = 0.0; + E_Powersave_Mode mode; - switch (powersave_mode) + if (powersave_force) mode = powersave_mode_force; + else mode = powersave_mode; + + switch (mode) { case E_POWERSAVE_MODE_NONE: t = e_config->powersave.none; /* time to defer "power expensive" activities */ @@ -158,6 +228,10 @@ _e_powersave_mode_eval(void) t = e_config->powersave.extreme; break; + case E_POWERSAVE_MODE_FREEZE: + t = 3600; + break; + default: return; break; @@ -166,8 +240,8 @@ _e_powersave_mode_eval(void) { if (deferred_timer) ecore_timer_del(deferred_timer); deferred_timer = ecore_timer_loop_add(defer_time, - _e_powersave_cb_deferred_timer, - NULL); + _e_powersave_cb_deferred_timer, + NULL); defer_time = t; } } @@ -178,3 +252,15 @@ _e_powersave_event_update_free(void *data EINA_UNUSED, void *event) free(event); } +static void +_e_powersave_event_change_send(E_Powersave_Mode mode) +{ + E_Event_Powersave_Update *ev; + + printf("CHANGE PW SAVE MODE TO %i / %i\n", + (int)mode, E_POWERSAVE_MODE_EXTREME); + ev = E_NEW(E_Event_Powersave_Update, 1); + ev->mode = mode; + ecore_event_add(E_EVENT_POWERSAVE_UPDATE, ev, _e_powersave_event_update_free, NULL); + _e_powersave_sleepers_wake(); +} diff --git a/src/bin/e_powersave.h b/src/bin/e_powersave.h index 97791a4d3..1cf20678a 100644 --- a/src/bin/e_powersave.h +++ b/src/bin/e_powersave.h @@ -6,11 +6,13 @@ typedef enum _E_Powersave_Mode E_POWERSAVE_MODE_LOW, E_POWERSAVE_MODE_MEDIUM, E_POWERSAVE_MODE_HIGH, - E_POWERSAVE_MODE_EXTREME + E_POWERSAVE_MODE_EXTREME, + E_POWERSAVE_MODE_FREEZE } E_Powersave_Mode; typedef struct _E_Powersave_Deferred_Action E_Powersave_Deferred_Action; typedef struct _E_Event_Powersave_Update E_Event_Powersave_Update; +typedef struct _E_Powersave_Sleeper E_Powersave_Sleeper; #else #ifndef E_POWERSAVE_H @@ -31,6 +33,12 @@ E_API E_Powersave_Deferred_Action *e_powersave_deferred_action_add(void (*func) E_API void e_powersave_deferred_action_del(E_Powersave_Deferred_Action *pa); E_API void e_powersave_mode_set(E_Powersave_Mode mode); E_API E_Powersave_Mode e_powersave_mode_get(void); +E_API void e_powersave_mode_force(E_Powersave_Mode mode); +E_API void e_powersave_mode_unforce(void); +E_API E_Powersave_Sleeper *e_powersave_sleeper_new(void); +E_API void e_powersave_sleeper_free(E_Powersave_Sleeper *sleeper); +// the below function is INTENDED to be called from a thread +E_API void e_powersave_sleeper_sleep(E_Powersave_Sleeper *sleeper, int poll_interval); /* FIXME: in the powersave system add things like pre-loading entire files * int memory for pre-caching to avoid disk spinup, when in an appropriate diff --git a/src/modules/cpufreq/e_mod_main.c b/src/modules/cpufreq/e_mod_main.c index e18959797..d5251acd1 100644 --- a/src/modules/cpufreq/e_mod_main.c +++ b/src/modules/cpufreq/e_mod_main.c @@ -1154,7 +1154,9 @@ _cpufreq_event_cb_powersave(void *data EINA_UNUSED, int type, void *event) break; } + // fallthrough is intended case E_POWERSAVE_MODE_EXTREME: + default: if (has_powersave) _cpufreq_set_governor("powersave"); break; @@ -1281,6 +1283,7 @@ typedef struct _Thread_Config Thread_Config; struct _Thread_Config { int interval; + E_Powersave_Sleeper *sleeper; }; static void @@ -1298,9 +1301,8 @@ _cpufreq_cb_frequency_check_main(void *data, Ecore_Thread *th) else _cpufreq_status_free(status); if (ecore_thread_check(th)) break; - usleep((1000000.0 / 8.0) * (double)thc->interval); + e_powersave_sleeper_sleep(thc->sleeper, thc->interval); } - free(thc); } static void @@ -1360,6 +1362,15 @@ _cpufreq_cb_frequency_check_notify(void *data EINA_UNUSED, } } +static void +_cpufreq_cb_frequency_check_done(void *data, + Ecore_Thread *th EINA_UNUSED) +{ + Thread_Config *thc = data; + e_powersave_sleeper_free(thc->sleeper); + free(thc); +} + void _cpufreq_poll_interval_update(void) { @@ -1374,10 +1385,13 @@ _cpufreq_poll_interval_update(void) if (thc) { thc->interval = cpufreq_config->poll_interval; + thc->sleeper = e_powersave_sleeper_new(); cpufreq_config->frequency_check_thread = ecore_thread_feedback_run(_cpufreq_cb_frequency_check_main, _cpufreq_cb_frequency_check_notify, - NULL, NULL, thc, EINA_TRUE); + _cpufreq_cb_frequency_check_done, + _cpufreq_cb_frequency_check_done, + thc, EINA_TRUE); } e_config_save_queue(); } diff --git a/src/modules/temperature/e_mod_main.c b/src/modules/temperature/e_mod_main.c index 61f6f1872..5e76d5919 100644 --- a/src/modules/temperature/e_mod_main.c +++ b/src/modules/temperature/e_mod_main.c @@ -57,6 +57,7 @@ _temperature_thread_free(Tempthread *tth) #ifdef HAVE_EEZE EINA_LIST_FREE(tth->tempdevs, s) eina_stringshare_del(s); #endif + e_powersave_sleeper_free(tth->sleeper); free(tth->extn); free(tth); } @@ -328,7 +329,11 @@ _temperature_check_main(void *data, Ecore_Thread *th) temp = temperature_tempget_get(tth); if (ptemp != temp) ecore_thread_feedback(th, (void *)((long)temp)); ptemp = temp; - usleep((1000000.0 / 8.0) * (double)tth->poll_interval); + e_powersave_sleeper_sleep(tth->sleeper, tth->poll_interval); + if (e_powersave_mode_get() == E_POWERSAVE_MODE_FREEZE) + usleep((1000000.0 / 800.0) * (double)tth->poll_interval); + else + usleep((1000000.0 / 8.0) * (double)tth->poll_interval); if (ecore_thread_check(th)) break; } } @@ -361,6 +366,7 @@ temperature_face_update_config(Config_Face *inst) tth->poll_interval = inst->poll_interval; tth->sensor_type = inst->sensor_type; tth->inst = inst; + tth->sleeper = e_powersave_sleeper_new(); if (inst->sensor_name) tth->sensor_name = eina_stringshare_add(inst->sensor_name); diff --git a/src/modules/temperature/e_mod_main.h b/src/modules/temperature/e_mod_main.h index 52077405c..b9cf67187 100644 --- a/src/modules/temperature/e_mod_main.h +++ b/src/modules/temperature/e_mod_main.h @@ -42,6 +42,7 @@ struct _Tempthread const char *sensor_name; const char *sensor_path; void *extn; + E_Powersave_Sleeper *sleeper; #ifdef HAVE_EEZE Eina_List *tempdevs; #endif