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.
This commit is contained in:
Carsten Haitzler 2017-07-10 09:05:12 +09:00
parent b88059b91a
commit 7caf5c8ff4
5 changed files with 139 additions and 24 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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);

View File

@ -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