914 lines
26 KiB
C
914 lines
26 KiB
C
|
#include "cpuclock.h"
|
||
|
|
||
|
typedef struct _Thread_Config Thread_Config;
|
||
|
|
||
|
struct _Thread_Config
|
||
|
{
|
||
|
int interval;
|
||
|
Instance *inst;
|
||
|
};
|
||
|
|
||
|
typedef struct _Pstate_Config Pstate_Config;
|
||
|
|
||
|
struct _Pstate_Config
|
||
|
{
|
||
|
Instance *inst;
|
||
|
int min;
|
||
|
int max;
|
||
|
int turbo;
|
||
|
};
|
||
|
|
||
|
static Cpu_Status *
|
||
|
_cpuclock_status_new(void)
|
||
|
{
|
||
|
Cpu_Status *s;
|
||
|
|
||
|
s = E_NEW(Cpu_Status, 1);
|
||
|
if (!s) return NULL;
|
||
|
s->active = -1;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_status_free(Cpu_Status *s)
|
||
|
{
|
||
|
Eina_List *l;
|
||
|
|
||
|
if (s->frequencies) eina_list_free(s->frequencies);
|
||
|
if (s->governors)
|
||
|
{
|
||
|
for (l = s->governors; l; l = l->next)
|
||
|
E_FREE_FUNC(l->data, free);
|
||
|
eina_list_free(s->governors);
|
||
|
}
|
||
|
E_FREE_FUNC(s->cur_governor, free);
|
||
|
if (s->orig_governor) eina_stringshare_del(s->orig_governor);
|
||
|
E_FREE_FUNC(s, free);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
_cpuclock_cb_sort(const void *item1, const void *item2)
|
||
|
{
|
||
|
int a, b;
|
||
|
|
||
|
a = (long)item1;
|
||
|
b = (long)item2;
|
||
|
if (a < b) return -1;
|
||
|
else if (a > b)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_set_thread_governor(void *data, Ecore_Thread *th EINA_UNUSED)
|
||
|
{
|
||
|
const char *governor = data;
|
||
|
|
||
|
_cpuclock_sysfs_setall("scaling_governor", governor);
|
||
|
if (!strcmp(governor, "ondemand"))
|
||
|
_cpuclock_sysfs_set("ondemand/ignore_nice_load", "0");
|
||
|
else if (!strcmp(governor, "conservative"))
|
||
|
_cpuclock_sysfs_set("conservative/ignore_nice_load", "0");
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_set_thread_frequency(void *data, Ecore_Thread *th EINA_UNUSED)
|
||
|
{
|
||
|
const char *freq = data;
|
||
|
|
||
|
#if defined __FreeBSD__ || defined __OpenBSD__
|
||
|
_cpuclock_sysctl_set("frequency", freq);
|
||
|
return;
|
||
|
#endif
|
||
|
_cpuclock_sysfs_setall("scaling_setspeed", freq);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_set_thread_pstate(void *data, Ecore_Thread *th EINA_UNUSED)
|
||
|
{
|
||
|
Pstate_Config *pc = data;
|
||
|
|
||
|
_cpuclock_sysfs_pstate(pc->min, pc->max, pc->turbo);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_set_thread_done(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_set_thread_pstate_done(void *data, Ecore_Thread *th EINA_UNUSED)
|
||
|
{
|
||
|
Pstate_Config *pc = data;
|
||
|
|
||
|
E_FREE_FUNC(pc, free);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
_cpuclock_set_governor(const char *governor)
|
||
|
{
|
||
|
#if defined __FreeBSD__ || defined __OpenBSD__
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
ecore_thread_run(_cpuclock_set_thread_governor, _cpuclock_set_thread_done, NULL, governor);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_set_frequency(int frequency)
|
||
|
{
|
||
|
char buf[4096];
|
||
|
const char *freq;
|
||
|
|
||
|
#ifdef __FreeBSD__
|
||
|
frequency /= 1000;
|
||
|
#endif
|
||
|
|
||
|
snprintf(buf, sizeof(buf), "%i", frequency);
|
||
|
freq = eina_stringshare_add(buf);
|
||
|
|
||
|
ecore_thread_run(_cpuclock_set_thread_frequency, _cpuclock_set_thread_done, NULL, freq);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
_cpuclock_set_pstate(int min, int max, int turbo)
|
||
|
{
|
||
|
#if defined __FreeBSD__ || defined __OpenBSD__
|
||
|
return;
|
||
|
#endif
|
||
|
Pstate_Config *pc;
|
||
|
|
||
|
pc = E_NEW(Pstate_Config, 1);
|
||
|
if (!pc) return;
|
||
|
|
||
|
pc->turbo = turbo;
|
||
|
pc->min = min;
|
||
|
pc->max = max;
|
||
|
|
||
|
ecore_thread_run(_cpuclock_set_thread_pstate, _cpuclock_set_thread_pstate_done, NULL, pc);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_face_cb_set_frequency(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *src EINA_UNUSED)
|
||
|
{
|
||
|
Eina_List *l;
|
||
|
int next_frequency = 0;
|
||
|
Instance *inst = data;
|
||
|
|
||
|
for (l = inst->cfg->cpuclock.status->frequencies; l; l = l->next)
|
||
|
{
|
||
|
if (inst->cfg->cpuclock.status->cur_frequency == (long)l->data)
|
||
|
{
|
||
|
if (!strcmp(emission, "e,action,frequency,increase"))
|
||
|
{
|
||
|
if (l->next) next_frequency = (long)l->next->data;
|
||
|
break;
|
||
|
}
|
||
|
else if (!strcmp(emission, "e,action,frequency,decrease"))
|
||
|
{
|
||
|
if (l->prev) next_frequency = (long)l->prev->data;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (inst->cfg->cpuclock.status->can_set_frequency && next_frequency)
|
||
|
_cpuclock_set_frequency(next_frequency);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_face_cb_set_governor(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *src EINA_UNUSED)
|
||
|
{
|
||
|
Eina_List *l;
|
||
|
char *next_governor = NULL;
|
||
|
Instance *inst = data;
|
||
|
|
||
|
for (l = inst->cfg->cpuclock.status->governors; l; l = l->next)
|
||
|
{
|
||
|
if (!strcmp(l->data, inst->cfg->cpuclock.status->cur_governor))
|
||
|
{
|
||
|
if (l->next)
|
||
|
next_governor = l->next->data;
|
||
|
else
|
||
|
next_governor = inst->cfg->cpuclock.status->governors->data;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (next_governor) _cpuclock_set_governor(next_governor);
|
||
|
}
|
||
|
|
||
|
static Eina_Bool
|
||
|
_cpuclock_event_cb_powersave(void *data, int type, void *event)
|
||
|
{
|
||
|
Instance *inst = data;
|
||
|
E_Event_Powersave_Update *ev;
|
||
|
Eina_List *l;
|
||
|
Eina_Bool has_powersave = EINA_FALSE;
|
||
|
Eina_Bool has_conservative = EINA_FALSE;
|
||
|
|
||
|
if (type != E_EVENT_POWERSAVE_UPDATE) return ECORE_CALLBACK_PASS_ON;
|
||
|
if (!inst->cfg->cpuclock.auto_powersave) return ECORE_CALLBACK_PASS_ON;
|
||
|
|
||
|
ev = event;
|
||
|
if (!inst->cfg->cpuclock.status->orig_governor)
|
||
|
inst->cfg->cpuclock.status->orig_governor = eina_stringshare_add(inst->cfg->cpuclock.status->cur_governor);
|
||
|
|
||
|
for (l = inst->cfg->cpuclock.status->governors; l; l = l->next)
|
||
|
{
|
||
|
if (!strcmp(l->data, "conservative"))
|
||
|
has_conservative = EINA_TRUE;
|
||
|
else if (!strcmp(l->data, "powersave"))
|
||
|
has_powersave = EINA_TRUE;
|
||
|
else if (!strcmp(l->data, "interactive"))
|
||
|
has_powersave = EINA_TRUE;
|
||
|
}
|
||
|
|
||
|
switch (ev->mode)
|
||
|
{
|
||
|
case E_POWERSAVE_MODE_NONE:
|
||
|
case E_POWERSAVE_MODE_LOW:
|
||
|
_cpuclock_set_governor(inst->cfg->cpuclock.status->orig_governor);
|
||
|
eina_stringshare_del(inst->cfg->cpuclock.status->orig_governor);
|
||
|
inst->cfg->cpuclock.status->orig_governor = NULL;
|
||
|
break;
|
||
|
|
||
|
case E_POWERSAVE_MODE_MEDIUM:
|
||
|
case E_POWERSAVE_MODE_HIGH:
|
||
|
if ((inst->cfg->cpuclock.powersave_governor) || (has_conservative))
|
||
|
{
|
||
|
if (inst->cfg->cpuclock.powersave_governor)
|
||
|
_cpuclock_set_governor(inst->cfg->cpuclock.powersave_governor);
|
||
|
else
|
||
|
_cpuclock_set_governor("conservative");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case E_POWERSAVE_MODE_EXTREME:
|
||
|
if (has_powersave)
|
||
|
_cpuclock_set_governor("powersave");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ECORE_CALLBACK_PASS_ON;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
_cpuclock_config_updated(Instance *inst)
|
||
|
{
|
||
|
Edje_Message_Int_Set *frequency_msg;
|
||
|
Edje_Message_String_Set *governor_msg;
|
||
|
Eina_List *l;
|
||
|
int i;
|
||
|
unsigned int count;
|
||
|
|
||
|
if (inst->cfg->cpuclock.status->frequencies)
|
||
|
{
|
||
|
count = eina_list_count(inst->cfg->cpuclock.status->frequencies);
|
||
|
frequency_msg = malloc(sizeof(Edje_Message_Int_Set) + (count - 1) * sizeof(int));
|
||
|
EINA_SAFETY_ON_NULL_RETURN(frequency_msg);
|
||
|
frequency_msg->count = count;
|
||
|
for (l = inst->cfg->cpuclock.status->frequencies, i = 0; l; l = l->next, i++)
|
||
|
frequency_msg->val[i] = (long)l->data;
|
||
|
edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_INT_SET, 1, frequency_msg);
|
||
|
free(frequency_msg);
|
||
|
}
|
||
|
|
||
|
if (inst->cfg->cpuclock.status->governors)
|
||
|
{
|
||
|
count = eina_list_count(inst->cfg->cpuclock.status->governors);
|
||
|
governor_msg = malloc(sizeof(Edje_Message_String_Set) + (count - 1) * sizeof(char *));
|
||
|
governor_msg->count = count;
|
||
|
for (l = inst->cfg->cpuclock.status->governors, i = 0; l; l = l->next, i++)
|
||
|
governor_msg->str[i] = (char *)l->data;
|
||
|
edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_STRING_SET, 2, governor_msg);
|
||
|
free(governor_msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_face_update_current(Instance *inst)
|
||
|
{
|
||
|
Edje_Message_Int_Set *frequency_msg;
|
||
|
Edje_Message_String governor_msg;
|
||
|
|
||
|
frequency_msg = malloc(sizeof(Edje_Message_Int_Set) + (sizeof(int) * 4));
|
||
|
EINA_SAFETY_ON_NULL_RETURN(frequency_msg);
|
||
|
frequency_msg->count = 5;
|
||
|
frequency_msg->val[0] = inst->cfg->cpuclock.status->cur_frequency;
|
||
|
frequency_msg->val[1] = inst->cfg->cpuclock.status->can_set_frequency;
|
||
|
frequency_msg->val[2] = inst->cfg->cpuclock.status->cur_min_frequency;
|
||
|
frequency_msg->val[3] = inst->cfg->cpuclock.status->cur_max_frequency;
|
||
|
frequency_msg->val[4] = 0; // pad
|
||
|
edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_INT_SET, 3,
|
||
|
frequency_msg);
|
||
|
free(frequency_msg);
|
||
|
|
||
|
/* BSD crashes here without the if-condition
|
||
|
* since it has no governors (yet) */
|
||
|
if (inst->cfg->cpuclock.status->cur_governor)
|
||
|
{
|
||
|
governor_msg.str = inst->cfg->cpuclock.status->cur_governor;
|
||
|
edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_STRING, 4,
|
||
|
&governor_msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_status_check_available(Cpu_Status *s)
|
||
|
{
|
||
|
char buf[4096];
|
||
|
Eina_List *l;
|
||
|
// FIXME: this assumes all cores accept the same freqs/ might be wrong
|
||
|
|
||
|
#if defined (__OpenBSD__)
|
||
|
int p;
|
||
|
|
||
|
if (s->frequencies)
|
||
|
{
|
||
|
eina_list_free(s->frequencies);
|
||
|
s->frequencies = NULL;
|
||
|
}
|
||
|
|
||
|
/* storing percents */
|
||
|
p = 100;
|
||
|
s->frequencies = eina_list_append(s->frequencies, (void *)p);
|
||
|
p = 75;
|
||
|
s->frequencies = eina_list_append(s->frequencies, (void *)p);
|
||
|
p = 50;
|
||
|
s->frequencies = eina_list_append(s->frequencies, (void *)p);
|
||
|
p = 25;
|
||
|
s->frequencies = eina_list_append(s->frequencies, (void *)p);
|
||
|
#elif defined (__FreeBSD__)
|
||
|
int freq;
|
||
|
size_t len = sizeof(buf);
|
||
|
char *pos, *q;
|
||
|
|
||
|
/* read freq_levels sysctl and store it in freq */
|
||
|
if (sysctlbyname("dev.cpu.0.freq_levels", buf, &len, NULL, 0) == 0)
|
||
|
{
|
||
|
/* sysctl returns 0 on success */
|
||
|
if (s->frequencies)
|
||
|
{
|
||
|
eina_list_free(s->frequencies);
|
||
|
s->frequencies = NULL;
|
||
|
}
|
||
|
|
||
|
/* parse freqs and store the frequencies in s->frequencies */
|
||
|
pos = buf;
|
||
|
while (pos)
|
||
|
{
|
||
|
q = strchr(pos, '/');
|
||
|
if (!q) break;
|
||
|
|
||
|
*q = '\0';
|
||
|
freq = atoi(pos);
|
||
|
freq *= 1000;
|
||
|
s->frequencies = eina_list_append(s->frequencies, (void *)freq);
|
||
|
|
||
|
pos = q + 1;
|
||
|
pos = strchr(pos, ' ');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* sort is not necessary because freq_levels is already sorted */
|
||
|
/* freebsd doesn't have governors */
|
||
|
if (s->governors)
|
||
|
{
|
||
|
for (l = s->governors; l; l = l->next)
|
||
|
free(l->data);
|
||
|
eina_list_free(s->governors);
|
||
|
s->governors = NULL;
|
||
|
}
|
||
|
#else
|
||
|
FILE *f;
|
||
|
|
||
|
f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
char *freq;
|
||
|
|
||
|
if (s->frequencies)
|
||
|
{
|
||
|
eina_list_free(s->frequencies);
|
||
|
s->frequencies = NULL;
|
||
|
}
|
||
|
|
||
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
||
|
{
|
||
|
fclose(f);
|
||
|
return;
|
||
|
}
|
||
|
fclose(f);
|
||
|
|
||
|
freq = strtok(buf, " ");
|
||
|
do
|
||
|
{
|
||
|
if (atoi(freq) != 0)
|
||
|
{
|
||
|
s->frequencies = eina_list_append(s->frequencies,
|
||
|
(void *)(long)atoi(freq));
|
||
|
}
|
||
|
freq = strtok(NULL, " ");
|
||
|
}
|
||
|
while (freq);
|
||
|
|
||
|
s->frequencies = eina_list_sort(s->frequencies,
|
||
|
eina_list_count(s->frequencies),
|
||
|
_cpuclock_cb_sort);
|
||
|
}
|
||
|
else
|
||
|
do
|
||
|
{
|
||
|
#define CPUFREQ_SYSFSDIR "/sys/devices/system/cpu/cpu0/cpufreq"
|
||
|
f = fopen(CPUFREQ_SYSFSDIR "/scaling_cur_freq", "r");
|
||
|
if (!f) break;
|
||
|
fclose(f);
|
||
|
|
||
|
f = fopen(CPUFREQ_SYSFSDIR "/scaling_driver", "r");
|
||
|
if (!f) break;
|
||
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
||
|
{
|
||
|
fclose(f);
|
||
|
break;
|
||
|
}
|
||
|
fclose(f);
|
||
|
if (strcmp(buf, "intel_pstate\n")) break;
|
||
|
|
||
|
if (s->frequencies)
|
||
|
{
|
||
|
eina_list_free(s->frequencies);
|
||
|
s->frequencies = NULL;
|
||
|
}
|
||
|
#define CPUFREQ_ADDF(filename) \
|
||
|
f = fopen(CPUFREQ_SYSFSDIR filename, "r"); \
|
||
|
if (f) \
|
||
|
{ \
|
||
|
if (fgets(buf, sizeof(buf), f) != NULL) \
|
||
|
s->frequencies = eina_list_append(s->frequencies, \
|
||
|
(void *)(long)(atoi(buf))); \
|
||
|
fclose(f); \
|
||
|
}
|
||
|
CPUFREQ_ADDF("/cpuinfo_min_freq");
|
||
|
CPUFREQ_ADDF("/cpuinfo_max_freq");
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
char *gov;
|
||
|
int len;
|
||
|
|
||
|
if (s->governors)
|
||
|
{
|
||
|
for (l = s->governors; l; l = l->next)
|
||
|
free(l->data);
|
||
|
eina_list_free(s->governors);
|
||
|
s->governors = NULL;
|
||
|
}
|
||
|
|
||
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
||
|
{
|
||
|
fclose(f);
|
||
|
return;
|
||
|
}
|
||
|
fclose(f);
|
||
|
len = strlen(buf);
|
||
|
if (len > 0)
|
||
|
{
|
||
|
gov = buf + len - 1;
|
||
|
while ((gov > buf) && (isspace(*gov)))
|
||
|
{
|
||
|
*gov = 0;
|
||
|
gov--;
|
||
|
}
|
||
|
}
|
||
|
gov = strtok(buf, " ");
|
||
|
do
|
||
|
{
|
||
|
while ((*gov) && (isspace(*gov)))
|
||
|
gov++;
|
||
|
if (strlen(gov) != 0)
|
||
|
s->governors = eina_list_append(s->governors, strdup(gov));
|
||
|
gov = strtok(NULL, " ");
|
||
|
}
|
||
|
while (gov);
|
||
|
|
||
|
s->governors =
|
||
|
eina_list_sort(s->governors, eina_list_count(s->governors),
|
||
|
(int (*)(const void *, const void *))strcmp);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
_cpuclock_status_check_current(Cpu_Status *s)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int frequency = 0;
|
||
|
|
||
|
#if defined (__OpenBSD__)
|
||
|
size_t len = sizeof(frequency);
|
||
|
int percent, mib[] = {CTL_HW, HW_CPUSPEED};
|
||
|
s->active = 0;
|
||
|
|
||
|
_cpuclock_status_check_available(s);
|
||
|
|
||
|
if (sysctl(mib, 2, &frequency, &len, NULL, 0) == 0)
|
||
|
{
|
||
|
frequency *= 1000;
|
||
|
if (frequency != s->cur_frequency) ret = 1;
|
||
|
s->cur_frequency = frequency;
|
||
|
s->active = 1;
|
||
|
}
|
||
|
|
||
|
mib[1] = HW_SETPERF;
|
||
|
|
||
|
if (sysctl(mib, 2, &percent, &len, NULL, 0) == 0)
|
||
|
{
|
||
|
s->cur_percent = percent;
|
||
|
}
|
||
|
|
||
|
s->can_set_frequency = 1;
|
||
|
s->cur_governor = NULL;
|
||
|
|
||
|
#elif defined (__FreeBSD__)
|
||
|
size_t len = sizeof(frequency);
|
||
|
s->active = 0;
|
||
|
|
||
|
/* frequency is stored in dev.cpu.0.freq */
|
||
|
if (sysctlbyname("dev.cpu.0.freq", &frequency, &len, NULL, 0) == 0)
|
||
|
{
|
||
|
frequency *= 1000;
|
||
|
if (frequency != s->cur_frequency) ret = 1;
|
||
|
s->cur_frequency = frequency;
|
||
|
s->active = 1;
|
||
|
}
|
||
|
|
||
|
/* hardcoded for testing */
|
||
|
s->can_set_frequency = 1;
|
||
|
s->cur_governor = NULL;
|
||
|
|
||
|
#else
|
||
|
char buf[4096];
|
||
|
FILE *f;
|
||
|
int frequency_min = 0x7fffffff;
|
||
|
int frequency_max = 0;
|
||
|
int freqtot = 0;
|
||
|
int i;
|
||
|
|
||
|
s->active = 0;
|
||
|
|
||
|
_cpuclock_status_check_available(s);
|
||
|
// average out frequencies of all cores
|
||
|
for (i = 0; i < 64; i++)
|
||
|
{
|
||
|
snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
|
||
|
f = fopen(buf, "r");
|
||
|
if (f)
|
||
|
{
|
||
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
||
|
{
|
||
|
fclose(f);
|
||
|
continue;
|
||
|
}
|
||
|
fclose(f);
|
||
|
|
||
|
frequency = atoi(buf);
|
||
|
if (frequency > frequency_max) frequency_max = frequency;
|
||
|
if (frequency < frequency_min) frequency_min = frequency;
|
||
|
freqtot += frequency;
|
||
|
s->active = 1;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
if (i < 1) i = 1;
|
||
|
frequency = freqtot / i;
|
||
|
if (frequency != s->cur_frequency) ret = 1;
|
||
|
if (frequency_min != s->cur_min_frequency) ret = 1;
|
||
|
if (frequency_max != s->cur_max_frequency) ret = 1;
|
||
|
s->cur_frequency = frequency;
|
||
|
s->cur_min_frequency = frequency_min;
|
||
|
s->cur_max_frequency = frequency_max;
|
||
|
|
||
|
// printf("%i | %i %i\n", frequency, frequency_min, frequency_max);
|
||
|
|
||
|
// FIXME: this assumes all cores are on the same governor
|
||
|
f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
s->can_set_frequency = 1;
|
||
|
fclose(f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s->can_set_frequency = 0;
|
||
|
}
|
||
|
|
||
|
f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
if (fgets(buf, sizeof(buf), f) == NULL)
|
||
|
{
|
||
|
fclose(f);
|
||
|
return ret;
|
||
|
}
|
||
|
fclose(f);
|
||
|
|
||
|
for (p = buf; (*p != 0) && (isalnum(*p)); p++) ;
|
||
|
*p = 0;
|
||
|
|
||
|
if ((!s->cur_governor) || (strcmp(buf, s->cur_governor)))
|
||
|
{
|
||
|
ret = 1;
|
||
|
|
||
|
free(s->cur_governor);
|
||
|
s->cur_governor = strdup(buf);
|
||
|
|
||
|
for (i = strlen(s->cur_governor) - 1; i >= 0; i--)
|
||
|
{
|
||
|
if (isspace(s->cur_governor[i]))
|
||
|
s->cur_governor[i] = 0;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
f = fopen("/sys/devices/system/cpu/intel_pstate/min_perf_pct", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
if (fgets(buf, sizeof(buf), f) != NULL)
|
||
|
{
|
||
|
s->pstate_min = atoi(buf);
|
||
|
s->pstate = 1;
|
||
|
}
|
||
|
fclose(f);
|
||
|
}
|
||
|
f = fopen("/sys/devices/system/cpu/intel_pstate/max_perf_pct", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
if (fgets(buf, sizeof(buf), f) != NULL)
|
||
|
{
|
||
|
s->pstate_max = atoi(buf);
|
||
|
s->pstate = 1;
|
||
|
}
|
||
|
fclose(f);
|
||
|
}
|
||
|
f = fopen("/sys/devices/system/cpu/intel_pstate/no_turbo", "r");
|
||
|
if (f)
|
||
|
{
|
||
|
if (fgets(buf, sizeof(buf), f) != NULL)
|
||
|
{
|
||
|
s->pstate_turbo = atoi(buf);
|
||
|
if (s->pstate_turbo) s->pstate_turbo = 0;
|
||
|
else s->pstate_turbo = 1;
|
||
|
s->pstate = 1;
|
||
|
}
|
||
|
fclose(f);
|
||
|
}
|
||
|
#endif
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_cb_frequency_check_main(void *data, Ecore_Thread *th)
|
||
|
{
|
||
|
Thread_Config *thc = data;
|
||
|
for (;;)
|
||
|
{
|
||
|
Cpu_Status *status;
|
||
|
|
||
|
if (ecore_thread_check(th)) break;
|
||
|
status = _cpuclock_status_new();
|
||
|
if (_cpuclock_status_check_current(status))
|
||
|
ecore_thread_feedback(th, status);
|
||
|
else
|
||
|
_cpuclock_status_free(status);
|
||
|
if (ecore_thread_check(th)) break;
|
||
|
usleep((1000000.0 / 8.0) * (double)thc->interval);
|
||
|
}
|
||
|
E_FREE_FUNC(thc, free);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_cb_frequency_check_notify(void *data,
|
||
|
Ecore_Thread *th EINA_UNUSED,
|
||
|
void *msg)
|
||
|
{
|
||
|
Cpu_Status *status = msg;
|
||
|
Eina_Bool freq_changed = EINA_FALSE;
|
||
|
Thread_Config *thc = data;
|
||
|
Instance *inst = thc->inst;
|
||
|
|
||
|
if (inst->cfg->esm != E_SYSINFO_MODULE_CPUCLOCK && inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return;
|
||
|
if (!inst->cfg) return;
|
||
|
if ((inst->cfg->cpuclock.status) && (status) &&
|
||
|
(
|
||
|
#ifdef __OpenBSD__
|
||
|
(status->cur_percent != inst->cfg->cpuclock.status->cur_percent ) ||
|
||
|
#endif
|
||
|
(status->cur_frequency != inst->cfg->cpuclock.status->cur_frequency ) ||
|
||
|
(status->cur_min_frequency != inst->cfg->cpuclock.status->cur_min_frequency) ||
|
||
|
(status->cur_max_frequency != inst->cfg->cpuclock.status->cur_max_frequency) ||
|
||
|
(status->can_set_frequency != inst->cfg->cpuclock.status->can_set_frequency)))
|
||
|
freq_changed = EINA_TRUE;
|
||
|
_cpuclock_status_free(inst->cfg->cpuclock.status);
|
||
|
inst->cfg->cpuclock.status = status;
|
||
|
if (freq_changed)
|
||
|
{
|
||
|
_cpuclock_face_update_current(inst);
|
||
|
}
|
||
|
if (inst->cfg->cpuclock.status->active == 0)
|
||
|
elm_layout_signal_emit(inst->cfg->cpuclock.o_gadget, "e,state,disabled", "e");
|
||
|
else if (inst->cfg->cpuclock.status->active == 1)
|
||
|
elm_layout_signal_emit(inst->cfg->cpuclock.o_gadget, "e,state,enabled", "e");
|
||
|
|
||
|
_cpuclock_set_pstate(inst->cfg->cpuclock.pstate_min - 1,
|
||
|
inst->cfg->cpuclock.pstate_max - 1, inst->cfg->cpuclock.status->pstate_turbo);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
_cpuclock_poll_interval_update(Instance *inst)
|
||
|
{
|
||
|
Thread_Config *thc;
|
||
|
|
||
|
if (inst->cfg->cpuclock.frequency_check_thread)
|
||
|
{
|
||
|
ecore_thread_cancel(inst->cfg->cpuclock.frequency_check_thread);
|
||
|
inst->cfg->cpuclock.frequency_check_thread = NULL;
|
||
|
}
|
||
|
thc = E_NEW(Thread_Config, 1);
|
||
|
if (thc)
|
||
|
{
|
||
|
thc->inst = inst;
|
||
|
thc->interval = inst->cfg->cpuclock.poll_interval;
|
||
|
inst->cfg->cpuclock.frequency_check_thread =
|
||
|
ecore_thread_feedback_run(_cpuclock_cb_frequency_check_main,
|
||
|
_cpuclock_cb_frequency_check_notify,
|
||
|
NULL, NULL, thc, EINA_TRUE);
|
||
|
}
|
||
|
e_config_save_queue();
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data)
|
||
|
{
|
||
|
Instance *inst = data;
|
||
|
|
||
|
if (inst->o_main != event_data) return;
|
||
|
|
||
|
if (inst->cfg->cpuclock.handler)
|
||
|
ecore_event_handler_del(inst->cfg->cpuclock.handler);
|
||
|
|
||
|
if (inst->cfg->cpuclock.frequency_check_thread)
|
||
|
{
|
||
|
ecore_thread_cancel(inst->cfg->cpuclock.frequency_check_thread);
|
||
|
inst->cfg->cpuclock.frequency_check_thread = NULL;
|
||
|
}
|
||
|
|
||
|
if (inst->cfg->cpuclock.governor)
|
||
|
eina_stringshare_del(inst->cfg->cpuclock.governor);
|
||
|
if (inst->cfg->cpuclock.status) _cpuclock_status_free(inst->cfg->cpuclock.status);
|
||
|
|
||
|
sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg);
|
||
|
E_FREE(inst->cfg);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sysinfo_cpuclock_remove(Instance *inst)
|
||
|
{
|
||
|
if (inst->cfg->cpuclock.handler)
|
||
|
ecore_event_handler_del(inst->cfg->cpuclock.handler);
|
||
|
|
||
|
if (inst->cfg->cpuclock.frequency_check_thread)
|
||
|
{
|
||
|
ecore_thread_cancel(inst->cfg->cpuclock.frequency_check_thread);
|
||
|
inst->cfg->cpuclock.frequency_check_thread = NULL;
|
||
|
}
|
||
|
|
||
|
if (inst->cfg->cpuclock.governor)
|
||
|
eina_stringshare_del(inst->cfg->cpuclock.governor);
|
||
|
if (inst->cfg->cpuclock.status) _cpuclock_status_free(inst->cfg->cpuclock.status);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cpuclock_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED)
|
||
|
{
|
||
|
Instance *inst = data;
|
||
|
|
||
|
if (inst->cfg->cpuclock.pstate_min == 0) inst->cfg->cpuclock.pstate_min = 1;
|
||
|
if (inst->cfg->cpuclock.pstate_max == 0) inst->cfg->cpuclock.pstate_max = 101;
|
||
|
|
||
|
inst->cfg->cpuclock.o_gadget = elm_layout_add(inst->o_main);
|
||
|
e_theme_edje_object_set(inst->cfg->cpuclock.o_gadget, "base/theme/modules/cpufreq",
|
||
|
"e/modules/cpufreq/main");
|
||
|
E_EXPAND(inst->cfg->cpuclock.o_gadget);
|
||
|
E_FILL(inst->cfg->cpuclock.o_gadget);
|
||
|
edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,governor,next", "*",
|
||
|
_cpuclock_face_cb_set_governor, inst);
|
||
|
edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,increase", "*",
|
||
|
_cpuclock_face_cb_set_frequency, inst);
|
||
|
edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,decrease", "*",
|
||
|
_cpuclock_face_cb_set_frequency, inst);
|
||
|
elm_box_pack_end(inst->o_main, inst->cfg->cpuclock.o_gadget);
|
||
|
evas_object_show(inst->cfg->cpuclock.o_gadget);
|
||
|
evas_object_smart_callback_del_full(obj, "gadget_created", _cpuclock_created_cb, data);
|
||
|
inst->cfg->cpuclock.status = _cpuclock_status_new();
|
||
|
_cpuclock_status_check_available(inst->cfg->cpuclock.status);
|
||
|
_cpuclock_poll_interval_update(inst);
|
||
|
inst->cfg->cpuclock.handler = ecore_event_handler_add(E_EVENT_POWERSAVE_UPDATE,
|
||
|
_cpuclock_event_cb_powersave, inst);
|
||
|
_cpuclock_config_updated(inst);
|
||
|
}
|
||
|
|
||
|
Evas_Object *
|
||
|
sysinfo_cpuclock_create(Evas_Object *parent, Instance *inst)
|
||
|
{
|
||
|
if (inst->cfg->cpuclock.pstate_min == 0) inst->cfg->cpuclock.pstate_min = 1;
|
||
|
if (inst->cfg->cpuclock.pstate_max == 0) inst->cfg->cpuclock.pstate_max = 101;
|
||
|
|
||
|
inst->cfg->cpuclock.o_gadget = elm_layout_add(parent);
|
||
|
e_theme_edje_object_set(inst->cfg->cpuclock.o_gadget, "base/theme/modules/cpufreq",
|
||
|
"e/modules/cpufreq/main");
|
||
|
E_EXPAND(inst->cfg->cpuclock.o_gadget);
|
||
|
E_FILL(inst->cfg->cpuclock.o_gadget);
|
||
|
edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,governor,next", "*",
|
||
|
_cpuclock_face_cb_set_governor, inst);
|
||
|
edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,increase", "*",
|
||
|
_cpuclock_face_cb_set_frequency, inst);
|
||
|
edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,decrease", "*",
|
||
|
_cpuclock_face_cb_set_frequency, inst);
|
||
|
evas_object_show(inst->cfg->cpuclock.o_gadget);
|
||
|
inst->cfg->cpuclock.status = _cpuclock_status_new();
|
||
|
_cpuclock_status_check_available(inst->cfg->cpuclock.status);
|
||
|
_cpuclock_poll_interval_update(inst);
|
||
|
inst->cfg->cpuclock.handler = ecore_event_handler_add(E_EVENT_POWERSAVE_UPDATE,
|
||
|
_cpuclock_event_cb_powersave, inst);
|
||
|
_cpuclock_config_updated(inst);
|
||
|
|
||
|
return inst->cfg->cpuclock.o_gadget;
|
||
|
}
|
||
|
|
||
|
static Config_Item *
|
||
|
_conf_item_get(int *id)
|
||
|
{
|
||
|
Config_Item *ci;
|
||
|
Eina_List *l;
|
||
|
|
||
|
if (*id > 0)
|
||
|
{
|
||
|
EINA_LIST_FOREACH(sysinfo_config->items, l, ci)
|
||
|
if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_CPUCLOCK) return ci;
|
||
|
}
|
||
|
|
||
|
ci = E_NEW(Config_Item, 1);
|
||
|
|
||
|
if (*id != -1)
|
||
|
ci->id = eina_list_count(sysinfo_config->items)+1;
|
||
|
else
|
||
|
ci->id = -1;
|
||
|
|
||
|
ci->esm = E_SYSINFO_MODULE_CPUCLOCK;
|
||
|
ci->cpuclock.poll_interval = 32;
|
||
|
ci->cpuclock.restore_governor = 0;
|
||
|
ci->cpuclock.auto_powersave = 1;
|
||
|
ci->cpuclock.powersave_governor = NULL;
|
||
|
ci->cpuclock.governor = NULL;
|
||
|
ci->cpuclock.pstate_min = 1;
|
||
|
ci->cpuclock.pstate_max = 101;
|
||
|
|
||
|
sysinfo_config->items = eina_list_append(sysinfo_config->items, ci);
|
||
|
|
||
|
return ci;
|
||
|
}
|
||
|
|
||
|
Evas_Object *
|
||
|
cpuclock_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED)
|
||
|
{
|
||
|
Instance *inst;
|
||
|
|
||
|
inst = E_NEW(Instance, 1);
|
||
|
inst->cfg = _conf_item_get(id);
|
||
|
*id = inst->cfg->id;
|
||
|
inst->o_main = elm_box_add(parent);
|
||
|
E_EXPAND(inst->o_main);
|
||
|
evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
|
||
|
evas_object_smart_callback_add(parent, "gadget_created", _cpuclock_created_cb, inst);
|
||
|
evas_object_smart_callback_add(parent, "gadget_removed", _cpuclock_removed_cb, inst);
|
||
|
evas_object_show(inst->o_main);
|
||
|
|
||
|
if (inst->cfg->id < 0) return inst->o_main;
|
||
|
|
||
|
sysinfo_instances =
|
||
|
eina_list_append(sysinfo_instances, inst);
|
||
|
|
||
|
return inst->o_main;
|
||
|
}
|