enlightenment/src/modules/temperature/e_mod_tempget.c

779 lines
25 KiB
C

#include "e.h"
#include "e_mod_main.h"
#if defined (__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
#else
// https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
typedef enum
{
TEMP_UNKNOWN,
TEMP_CPU, // CPU embedded diode
TEMP_TRANSISTOR, // 3904 transistor
TEMP_THERMAL, // thermal diode
TEMP_THERMISTOR, // thermistor
TEMP_AMD_AMDSI, // AMD AMDSI
TEMP_INTEL_PECI // Intel PECI
} Temp_Type;
typedef enum
{
PWM_OFF, // full speed
PWM_ON, // enabled
PWM_AUTO // automatic mode
} Pwm_Mode;
typedef struct
{
Temp_Type type;
const char *path;
const char *name;
const char *label;
int enable;
int fault;
double temp;
double min;
double max;
double crit;
double emergency;
double offset;
} Temp;
typedef struct
{
const char *path;
const char *name;
const char *label;
int enable;
int fault;
int rpm;
int min;
int max;
int target;
} Fan;
typedef struct
{
const char *path;
const char *name;
Pwm_Mode mode;
double val;
double min;
double max;
} Pwm;
typedef struct
{
const char *path;
const char *name;
const char *label;
double mhz;
} Freq;
typedef struct
{
const char *path;
const char *name;
int enable;
double watts;
double min;
double max;
double cap;
double cap_min;
double cap_max;
double crit;
} Power;
typedef struct
{
const char *path;
const char *name;
const char *label;
int enable;
double volts;
double min;
double max;
double crit_min;
double crit;
} Volt;
typedef struct
{
const char *path;
const char *name;
int enable;
double amps;
double min;
double max;
double crit_min;
double crit;
} Amp;
typedef struct
{
const char *path;
const char *name;
int enable;
double joules;
} Energy;
typedef struct
{
const char *path;
const char *name;
int enable;
double percent;
} Humid;
typedef struct
{
const char *path;
const char *name;
int alarm;
} Intrusion;
typedef struct
{
const char *path;
const char *name;
const char *label;
Eina_List *temps;
Eina_List *fans;
Eina_List *pwms;
Eina_List *freqs;
Eina_List *powers;
Eina_List *volts;
Eina_List *amps;
Eina_List *energies;
Eina_List *humids;
Eina_List *intrusions;
} Mon;
static Eina_Lock mons_lock;
static Eina_List *mons = NULL;
static char *
_file_str_get(const char *path)
{
int fd = open(path, O_RDONLY);
char buf[4096], *str = NULL, *s;
ssize_t sz;
if (fd < 0) return NULL;
sz = read(fd, buf, sizeof(buf) - 1);
if (sz < 1) goto err;
buf[sz] = '\0';
for (s = buf; s[0] != '\0'; s++)
{
if ((s[0] == '\n') || (s[0] == '\r'))
{
s[0] = '\0';
break;
}
}
str = strdup(buf);
err:
close(fd);
return str;
}
#define NOVAL -999999999
static int
_file_int_get(const char *path)
{
char *str = _file_str_get(path);
if (!str) return NOVAL;
int val = atoi(str);
free(str);
return val;
}
#define GETVAL_STR(_base, _name, _field) do { char *_s; char _b[1024 + 512]; \
snprintf(_b, sizeof(_b), "%s_%s", _base, _name); \
if ((_s = _file_str_get(_b))) { \
_field = eina_stringshare_add(_s); \
free(_s); \
} \
} while (0)
#define GETVAL_INT(_base, _name, _field) do { int _v; char _b[1024 + 512]; \
snprintf(_b, sizeof(_b), "%s_%s", _base, _name); \
if ((_v = _file_int_get(_b)) >= 0) _field = _v; \
} while (0)
#define GETVAL_DBL(_base, _name, _field, _div) do { int _v; char _b[1024 + 512]; \
snprintf(_b, sizeof(_b), "%s_%s", _base, _name); \
if ((_v = _file_int_get(_b)) >= 0) _field = (double)_v / (_div); \
} while (0)
static void
_hwmon_init(void)
{
Eina_List *list;
char *s, *str, buf[1024], buf2[1024 + 256];
int i, x;
eina_lock_take(&mons_lock);
list = ecore_file_ls("/sys/class/hwmon");
EINA_LIST_FREE(list, s)
{
Mon *mon = calloc(1, sizeof(Mon));
if (mon)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s", s);
mon->path = eina_stringshare_add(buf);
mon->name = eina_stringshare_add(s);
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/name", s);
str = _file_str_get(buf);
if (str)
{
mon->label = eina_stringshare_add(str);
free(str);
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/temp%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x == NOVAL) break;
Temp *t = calloc(1, sizeof(Temp));
if (t)
{
t->temp = (double)x / 1000.0;
t->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "temp%i", i);
t->name = eina_stringshare_add(buf2);
GETVAL_STR(buf, "label", t->label);
t->enable = 1;
GETVAL_INT(buf, "enable", t->enable);
GETVAL_INT(buf, "fault", t->fault);
GETVAL_INT(buf, "type", t->type);
GETVAL_DBL(buf, "min", t->min, 1000.0);
GETVAL_DBL(buf, "max", t->max, 1000.0);
GETVAL_DBL(buf, "crit", t->crit, 1000.0);
GETVAL_DBL(buf, "emergency", t->emergency, 1000.0);
mon->temps = eina_list_append(mon->temps, t);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/fan%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x == NOVAL) break;
Fan *f = calloc(1, sizeof(Fan));
if (f)
{
f->rpm = x;
f->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "fan%i", i);
f->name = eina_stringshare_add(buf2);
GETVAL_STR(buf, "label", f->label);
f->enable = 1;
GETVAL_INT(buf, "enable", f->enable);
GETVAL_INT(buf, "fault", f->fault);
GETVAL_INT(buf, "min", f->min);
GETVAL_INT(buf, "max", f->max);
GETVAL_INT(buf, "target", f->target);
mon->fans = eina_list_append(mon->fans, f);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/pwm%i", s, i);
x = _file_int_get(buf);
if (x == NOVAL) break;
Pwm *p = calloc(1, sizeof(Pwm));
if (p)
{
p->val = (double)x / 255.0;
p->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "pwm%i", i);
p->name = eina_stringshare_add(buf2);
GETVAL_INT(buf, "enable", p->mode);
GETVAL_DBL(buf, "min", p->min, 255.0);
GETVAL_DBL(buf, "max", p->max, 255.0);
mon->pwms = eina_list_append(mon->pwms, p);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/freq%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x == NOVAL) break;
Freq *f = calloc(1, sizeof(Freq));
if (f)
{
f->mhz = (double)x / 1000000.0;
f->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "freq%i", i);
f->name = eina_stringshare_add(buf2);
GETVAL_STR(buf, "label", f->label);
mon->freqs = eina_list_append(mon->freqs, f);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/power%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x < 0)
{
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "average");
x = _file_int_get(buf2);
}
if (x == NOVAL) break;
Power *p = calloc(1, sizeof(Power));
if (p)
{
p->watts = (double)x / 1000000.0;
p->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "power%i", i);
p->name = eina_stringshare_add(buf2);
p->enable = 1;
GETVAL_INT(buf, "enable", p->enable);
GETVAL_DBL(buf, "min", p->min, 1000000.0);
GETVAL_DBL(buf, "max", p->max, 1000000.0);
if (p->min <= 0.0)
GETVAL_DBL(buf, "average_min", p->min, 1000000.0);
if (p->max <= 0.0)
GETVAL_DBL(buf, "average_max", p->max, 1000000.0);
GETVAL_DBL(buf, "cap", p->cap, 1000000.0);
GETVAL_DBL(buf, "cap_min", p->cap_min, 1000000.0);
GETVAL_DBL(buf, "cap_max", p->cap_max, 1000000.0);
GETVAL_DBL(buf, "crit", p->crit, 1000000.0);
mon->powers = eina_list_append(mon->powers, p);
}
}
for (i = 0; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/in%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x < 0)
{
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "average");
x = _file_int_get(buf2);
}
if (x == NOVAL) break;
Volt *v = calloc(1, sizeof(Volt));
if (v)
{
v->volts = (double)x / 1000.0;
v->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "in%i", i);
v->name = eina_stringshare_add(buf2);
GETVAL_STR(buf, "label", v->label);
v->enable = 1;
GETVAL_INT(buf, "enable", v->enable);
GETVAL_DBL(buf, "min", v->min, 1000.0);
GETVAL_DBL(buf, "max", v->max, 1000.0);
GETVAL_DBL(buf, "lcrit", v->crit_min, 1000.0);
GETVAL_DBL(buf, "crit", v->crit, 1000.0);
mon->volts = eina_list_append(mon->volts, v);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/curr%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x < 0)
{
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "average");
x = _file_int_get(buf2);
}
if (x == NOVAL) break;
Amp *a = calloc(1, sizeof(Amp));
if (a)
{
a->amps = (double)x / 1000.0;
a->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "curr%i", i);
a->name = eina_stringshare_add(buf2);
a->enable = 1;
GETVAL_INT(buf, "enable", a->enable);
GETVAL_DBL(buf, "min", a->min, 1000.0);
GETVAL_DBL(buf, "max", a->max, 1000.0);
GETVAL_DBL(buf, "lcrit", a->crit_min, 1000.0);
GETVAL_DBL(buf, "crit", a->crit, 1000.0);
mon->amps = eina_list_append(mon->amps, a);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/energy%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x == NOVAL) break;
Energy *e = calloc(1, sizeof(Energy));
if (e)
{
e->joules = (double)x / 1000000.0;
e->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "energy%i", i);
e->name = eina_stringshare_add(buf2);
e->enable = 1;
GETVAL_INT(buf, "enable", e->enable);
mon->energies = eina_list_append(mon->energies, e);
}
}
for (i = 1; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/humidity%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "input");
x = _file_int_get(buf2);
if (x == NOVAL) break;
Humid *h = calloc(1, sizeof(Humid));
if (h)
{
h->percent = (double)x / 1000.0;
h->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "humidity%i", i);
h->name = eina_stringshare_add(buf2);
h->enable = 1;
GETVAL_INT(buf, "enable", h->enable);
mon->humids = eina_list_append(mon->humids, h);
}
}
for (i = 0; ; i++)
{
snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s/intrusion%i", s, i);
snprintf(buf2, sizeof(buf2), "%s_%s", buf, "alarm");
x = _file_int_get(buf2);
if (x == NOVAL) break;
Intrusion *n = calloc(1, sizeof(Intrusion));
if (n)
{
n->alarm = x;
n->path = eina_stringshare_add(buf);
snprintf(buf2, sizeof(buf2), "intrusion%i", i);
n->name = eina_stringshare_add(buf2);
mon->intrusions = eina_list_append(mon->intrusions, n);
}
}
mons = eina_list_append(mons, mon);
}
free(s);
}
eina_lock_release(&mons_lock);
}
static void
_hwmon_update(void)
{
Eina_List *l, *ll;
Mon *mon;
Temp *temp;
Fan *fan;
Pwm *pwm;
Freq *freq;
Power *power;
Volt *volt;
Amp *amp;
Energy *energy;
Humid *humid;
Intrusion *intrusion;
int x;
char buf[4096];
eina_lock_take(&mons_lock);
EINA_LIST_FOREACH(mons, l, mon)
{
EINA_LIST_FOREACH(mon->temps, ll, temp)
{
snprintf(buf, sizeof(buf), "%s_input", temp->path);
temp->temp = (double)_file_int_get(buf) / 1000.0;
snprintf(buf, sizeof(buf), "%s_enable", temp->path);
x = _file_int_get(buf);
if (x != NOVAL) temp->enable = x;
snprintf(buf, sizeof(buf), "%s_fault", temp->path);
temp->fault = _file_int_get(buf);
}
EINA_LIST_FOREACH(mon->fans, ll, fan)
{
snprintf(buf, sizeof(buf), "%s_input", fan->path);
fan->rpm = _file_int_get(buf);
snprintf(buf, sizeof(buf), "%s_target", fan->path);
fan->target = _file_int_get(buf);
snprintf(buf, sizeof(buf), "%s_enable", fan->path);
x = _file_int_get(buf);
if (x != NOVAL) fan->enable = x;
snprintf(buf, sizeof(buf), "%s_fault", fan->path);
fan->fault = _file_int_get(buf);
}
EINA_LIST_FOREACH(mon->pwms, ll, pwm)
{
snprintf(buf, sizeof(buf), "%s", pwm->path);
pwm->val = (double)_file_int_get(buf) / 255.0;
snprintf(buf, sizeof(buf), "%s_enable", pwm->path);
x = _file_int_get(buf);
if (x != NOVAL) pwm->mode = x;
}
EINA_LIST_FOREACH(mon->freqs, ll, freq)
{
snprintf(buf, sizeof(buf), "%s_input", freq->path);
freq->mhz = (double)_file_int_get(buf) / 1000000.0;
}
EINA_LIST_FOREACH(mon->powers, ll, power)
{
snprintf(buf, sizeof(buf), "%s_input", power->path);
x = _file_int_get(buf);
if (x == NOVAL)
{
snprintf(buf, sizeof(buf), "%s_average", power->path);
x = _file_int_get(buf);
}
power->watts = (double)x / 1000000.0;
snprintf(buf, sizeof(buf), "%s_enable", power->path);
x = _file_int_get(buf);
if (x != NOVAL) power->enable = x;
}
EINA_LIST_FOREACH(mon->volts, ll, volt)
{
snprintf(buf, sizeof(buf), "%s_input", volt->path);
x = _file_int_get(buf);
if (x == NOVAL)
{
snprintf(buf, sizeof(buf), "%s_average", volt->path);
x = _file_int_get(buf);
}
volt->volts = (double)x / 1000.0;
snprintf(buf, sizeof(buf), "%s_enable", volt->path);
x = _file_int_get(buf);
if (x != NOVAL) volt->enable = x;
}
EINA_LIST_FOREACH(mon->amps, ll, amp)
{
snprintf(buf, sizeof(buf), "%s_input", amp->path);
x = _file_int_get(buf);
if (x == NOVAL)
{
snprintf(buf, sizeof(buf), "%s_average", amp->path);
x = _file_int_get(buf);
}
amp->amps = (double)x / 1000.0;
snprintf(buf, sizeof(buf), "%s_enable", amp->path);
x = _file_int_get(buf);
if (x != NOVAL) amp->enable = x;
}
EINA_LIST_FOREACH(mon->energies, ll, energy)
{
snprintf(buf, sizeof(buf), "%s_input", energy->path);
energy->joules = (double)_file_int_get(buf) / 1000000.0;
snprintf(buf, sizeof(buf), "%s_enable", energy->path);
x = _file_int_get(buf);
if (x != NOVAL) energy->enable = x;
}
EINA_LIST_FOREACH(mon->humids, ll, humid)
{
snprintf(buf, sizeof(buf), "%s_input", humid->path);
humid->percent = (double)_file_int_get(buf) / 1000.0;
snprintf(buf, sizeof(buf), "%s_enable", humid->path);
x = _file_int_get(buf);
if (x != NOVAL) humid->enable = x;
}
EINA_LIST_FOREACH(mon->intrusions, ll, intrusion)
{
snprintf(buf, sizeof(buf), "%s_alarm", intrusion->path);
intrusion->alarm = _file_int_get(buf);
}
}
eina_lock_release(&mons_lock);
}
static void
init(Tempthread *tth)
{
if (tth->initted) return;
tth->initted = EINA_TRUE;
if (((!tth->sensor_name) || (tth->sensor_name[0] == 0)))
{
eina_stringshare_replace(&(tth->sensor_name), NULL);
if (!tth->sensor_name)
{
Eina_List *l, *ll;
Mon *mon;
Temp *temp;
eina_lock_take(&mons_lock);
EINA_LIST_FOREACH(mons, l, mon)
{
EINA_LIST_FOREACH(mon->temps, ll, temp)
{
tth->sensor_name = eina_stringshare_add(temp->path);
break;
}
}
eina_lock_release(&mons_lock);
}
}
// we couldn't find the sensor - so init failed - try again next time
if (((!tth->sensor_name) || (tth->sensor_name[0] == 0)))
tth->initted = EINA_FALSE;
}
static int
check(Tempthread *tth)
{
Eina_List *l, *ll;
Mon *mon;
Temp *temp;
_hwmon_update();
if (tth->sensor_name)
{
double t = 0.0;
eina_lock_take(&mons_lock);
EINA_LIST_FOREACH(mons, l, mon)
{
EINA_LIST_FOREACH(mon->temps, ll, temp)
{
if (!strcmp(tth->sensor_name, temp->path))
{
t = temp->temp;
break;
}
}
}
eina_lock_release(&mons_lock);
return t;
}
return -999;
}
int
temperature_tempget_get(Tempthread *tth)
{
init(tth);
return check(tth);
}
Eina_List *
temperature_tempget_sensor_list(void)
{
Eina_List *sensors = NULL, *l, *ll;
Sensor *sen;
Mon *mon;
Temp *temp;
char buf[256];
EINA_LIST_FOREACH(mons, l, mon)
{
EINA_LIST_FOREACH(mon->temps, ll, temp)
{
sen = calloc(1, sizeof(Sensor));
sen->name = eina_stringshare_add(temp->path);
snprintf(buf, sizeof(buf), "%s - %s",
mon->label ? mon->label : mon->name,
temp->label ? temp->label : temp->name);
sen->label = eina_stringshare_add(buf);
sensors = eina_list_append(sensors, sen);
}
}
return sensors;
}
void
temperature_tempget_setup(void)
{
eina_lock_new(&mons_lock);
_hwmon_init();
}
void
temperature_tempget_clear(void)
{
Mon *mon;
Temp *temp;
Fan *fan;
Pwm *pwm;
Freq *freq;
Power *power;
Volt *volt;
Amp *amp;
Energy *energy;
Humid *humid;
Intrusion *intrusion;
eina_lock_take(&mons_lock);
EINA_LIST_FREE(mons, mon)
{
eina_stringshare_replace(&(mon->path), NULL);
eina_stringshare_replace(&(mon->name), NULL);
eina_stringshare_replace(&(mon->label), NULL);
EINA_LIST_FREE(mon->temps, temp)
{
eina_stringshare_replace(&(temp->path), NULL);
eina_stringshare_replace(&(temp->name), NULL);
eina_stringshare_replace(&(temp->label), NULL);
free(temp);
}
EINA_LIST_FREE(mon->fans, fan)
{
eina_stringshare_replace(&(fan->path), NULL);
eina_stringshare_replace(&(fan->name), NULL);
eina_stringshare_replace(&(fan->label), NULL);
free(fan);
}
EINA_LIST_FREE(mon->pwms, pwm)
{
eina_stringshare_replace(&(pwm->path), NULL);
eina_stringshare_replace(&(pwm->name), NULL);
free(fan);
}
EINA_LIST_FREE(mon->freqs, freq)
{
eina_stringshare_replace(&(freq->path), NULL);
eina_stringshare_replace(&(freq->name), NULL);
eina_stringshare_replace(&(freq->label), NULL);
free(freq);
}
EINA_LIST_FREE(mon->powers, power)
{
eina_stringshare_replace(&(power->path), NULL);
eina_stringshare_replace(&(power->name), NULL);
free(power);
}
EINA_LIST_FREE(mon->volts, volt)
{
eina_stringshare_replace(&(volt->path), NULL);
eina_stringshare_replace(&(volt->name), NULL);
eina_stringshare_replace(&(volt->label), NULL);
free(volt);
}
EINA_LIST_FREE(mon->amps, amp)
{
eina_stringshare_replace(&(amp->path), NULL);
eina_stringshare_replace(&(amp->name), NULL);
free(amp);
}
EINA_LIST_FREE(mon->energies, energy)
{
eina_stringshare_replace(&(energy->path), NULL);
eina_stringshare_replace(&(energy->name), NULL);
free(energy);
}
EINA_LIST_FREE(mon->humids, humid)
{
eina_stringshare_replace(&(humid->path), NULL);
eina_stringshare_replace(&(humid->name), NULL);
free(humid);
}
EINA_LIST_FREE(mon->intrusions, intrusion)
{
eina_stringshare_replace(&(intrusion->path), NULL);
eina_stringshare_replace(&(intrusion->name), NULL);
free(intrusion);
}
free(mon);
}
eina_lock_release(&mons_lock);
// extra lock take to cover race cond for thread
eina_lock_take(&mons_lock);
eina_lock_release(&mons_lock);
eina_lock_free(&mons_lock);
}
#endif