/**************************************************************************
|
*
|
* Copyright (C) 2016 Steven Toth <stoth@kernellabs.com>
|
* Copyright (C) 2016 Zodiac Inflight Innovations
|
* All Rights Reserved.
|
*
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
* copy of this software and associated documentation files (the
|
* "Software"), to deal in the Software without restriction, including
|
* without limitation the rights to use, copy, modify, merge, publish,
|
* distribute, sub license, and/or sell copies of the Software, and to
|
* permit persons to whom the Software is furnished to do so, subject to
|
* the following conditions:
|
*
|
* The above copyright notice and this permission notice (including the
|
* next paragraph) shall be included in all copies or substantial portions
|
* of the Software.
|
*
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
* IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
*
|
**************************************************************************/
|
|
#ifdef HAVE_LIBSENSORS
|
/* Purpose: Extract lm-sensors data, expose temperature, power, voltage. */
|
|
#include "hud/hud_private.h"
|
#include "util/list.h"
|
#include "util/os_time.h"
|
#include "os/os_thread.h"
|
#include "util/u_memory.h"
|
#include <stdio.h>
|
#include <unistd.h>
|
#include <dirent.h>
|
#include <stdlib.h>
|
#include <unistd.h>
|
#include <inttypes.h>
|
#include <sys/types.h>
|
#include <sys/stat.h>
|
#include <unistd.h>
|
#include <sensors/sensors.h>
|
|
/* TODO: We don't handle dynamic sensor discovery / arrival or removal.
|
* Static globals specific to this HUD category.
|
*/
|
static int gsensors_temp_count = 0;
|
static struct list_head gsensors_temp_list;
|
static mtx_t gsensor_temp_mutex = _MTX_INITIALIZER_NP;
|
|
struct sensors_temp_info
|
{
|
struct list_head list;
|
|
/* Combined chip and feature name, human readable. */
|
char name[64];
|
|
/* The type of measurement, critical or current. */
|
unsigned int mode;
|
|
uint64_t last_time;
|
|
char chipname[64];
|
char featurename[128];
|
|
sensors_chip_name *chip;
|
const sensors_feature *feature;
|
double current, min, max, critical;
|
};
|
|
static double
|
get_value(const sensors_chip_name *name, const sensors_subfeature *sub)
|
{
|
double val;
|
int err;
|
|
err = sensors_get_value(name, sub->number, &val);
|
if (err) {
|
fprintf(stderr, "ERROR: Can't get value of subfeature %s\n", sub->name);
|
val = 0;
|
}
|
return val;
|
}
|
|
static void
|
get_sensor_values(struct sensors_temp_info *sti)
|
{
|
const sensors_subfeature *sf;
|
|
switch(sti->mode) {
|
case SENSORS_VOLTAGE_CURRENT:
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_IN_INPUT);
|
if (sf)
|
sti->current = get_value(sti->chip, sf);
|
break;
|
case SENSORS_CURRENT_CURRENT:
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_CURR_INPUT);
|
if (sf) {
|
/* Sensors API returns in AMPs, even though driver is reporting mA,
|
* convert back to mA */
|
sti->current = get_value(sti->chip, sf) * 1000;
|
}
|
break;
|
case SENSORS_TEMP_CURRENT:
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_TEMP_INPUT);
|
if (sf)
|
sti->current = get_value(sti->chip, sf);
|
break;
|
case SENSORS_TEMP_CRITICAL:
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_TEMP_CRIT);
|
if (sf)
|
sti->critical = get_value(sti->chip, sf);
|
break;
|
case SENSORS_POWER_CURRENT:
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_POWER_INPUT);
|
if (sf) {
|
/* Sensors API returns in WATTs, even though driver is reporting mW,
|
* convert back to mW */
|
sti->current = get_value(sti->chip, sf) * 1000;
|
}
|
break;
|
}
|
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_TEMP_MIN);
|
if (sf)
|
sti->min = get_value(sti->chip, sf);
|
|
sf = sensors_get_subfeature(sti->chip, sti->feature,
|
SENSORS_SUBFEATURE_TEMP_MAX);
|
if (sf)
|
sti->max = get_value(sti->chip, sf);
|
}
|
|
static struct sensors_temp_info *
|
find_sti_by_name(const char *n, unsigned int mode)
|
{
|
list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) {
|
if (sti->mode != mode)
|
continue;
|
if (strcasecmp(sti->name, n) == 0)
|
return sti;
|
}
|
return 0;
|
}
|
|
static void
|
query_sti_load(struct hud_graph *gr, struct pipe_context *pipe)
|
{
|
struct sensors_temp_info *sti = gr->query_data;
|
uint64_t now = os_time_get();
|
|
if (sti->last_time) {
|
if (sti->last_time + gr->pane->period <= now) {
|
get_sensor_values(sti);
|
|
switch (sti->mode) {
|
case SENSORS_TEMP_CURRENT:
|
hud_graph_add_value(gr, sti->current);
|
break;
|
case SENSORS_TEMP_CRITICAL:
|
hud_graph_add_value(gr, sti->critical);
|
break;
|
case SENSORS_VOLTAGE_CURRENT:
|
hud_graph_add_value(gr, sti->current * 1000);
|
break;
|
case SENSORS_CURRENT_CURRENT:
|
hud_graph_add_value(gr, sti->current);
|
break;
|
case SENSORS_POWER_CURRENT:
|
hud_graph_add_value(gr, sti->current);
|
break;
|
}
|
|
sti->last_time = now;
|
}
|
}
|
else {
|
/* initialize */
|
get_sensor_values(sti);
|
sti->last_time = now;
|
}
|
}
|
|
/**
|
* Create and initialize a new object for a specific sensor interface dev.
|
* \param pane parent context.
|
* \param dev_name device name, EG. 'coretemp-isa-0000.Core 1'
|
* \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics.
|
*/
|
void
|
hud_sensors_temp_graph_install(struct hud_pane *pane, const char *dev_name,
|
unsigned int mode)
|
{
|
struct hud_graph *gr;
|
struct sensors_temp_info *sti;
|
|
int num_devs = hud_get_num_sensors(0);
|
if (num_devs <= 0)
|
return;
|
|
sti = find_sti_by_name(dev_name, mode);
|
if (!sti)
|
return;
|
|
gr = CALLOC_STRUCT(hud_graph);
|
if (!gr)
|
return;
|
|
snprintf(gr->name, sizeof(gr->name), "%.6s..%s (%s)",
|
sti->chipname,
|
sti->featurename,
|
sti->mode == SENSORS_VOLTAGE_CURRENT ? "Volts" :
|
sti->mode == SENSORS_CURRENT_CURRENT ? "Amps" :
|
sti->mode == SENSORS_TEMP_CURRENT ? "Curr" :
|
sti->mode == SENSORS_POWER_CURRENT ? "Pow" :
|
sti->mode == SENSORS_TEMP_CRITICAL ? "Crit" : "Unkn");
|
|
gr->query_data = sti;
|
gr->query_new_value = query_sti_load;
|
|
hud_pane_add_graph(pane, gr);
|
switch (sti->mode) {
|
case SENSORS_TEMP_CURRENT:
|
case SENSORS_TEMP_CRITICAL:
|
hud_pane_set_max_value(pane, 120);
|
break;
|
case SENSORS_VOLTAGE_CURRENT:
|
hud_pane_set_max_value(pane, 12);
|
break;
|
case SENSORS_CURRENT_CURRENT:
|
hud_pane_set_max_value(pane, 5000);
|
break;
|
case SENSORS_POWER_CURRENT:
|
hud_pane_set_max_value(pane, 5000 /* mW */);
|
break;
|
}
|
}
|
|
static void
|
create_object(const char *chipname, const char *featurename,
|
const sensors_chip_name *chip, const sensors_feature *feature,
|
int mode)
|
{
|
struct sensors_temp_info *sti = CALLOC_STRUCT(sensors_temp_info);
|
|
sti->mode = mode;
|
sti->chip = (sensors_chip_name *) chip;
|
sti->feature = feature;
|
strcpy(sti->chipname, chipname);
|
strcpy(sti->featurename, featurename);
|
snprintf(sti->name, sizeof(sti->name), "%s.%s", sti->chipname,
|
sti->featurename);
|
|
list_addtail(&sti->list, &gsensors_temp_list);
|
gsensors_temp_count++;
|
}
|
|
static void
|
build_sensor_list(void)
|
{
|
const sensors_chip_name *chip;
|
const sensors_chip_name *match = 0;
|
const sensors_feature *feature;
|
int chip_nr = 0;
|
|
char name[256];
|
while ((chip = sensors_get_detected_chips(match, &chip_nr))) {
|
sensors_snprintf_chip_name(name, sizeof(name), chip);
|
|
/* Get all features and filter accordingly. */
|
int fnr = 0;
|
while ((feature = sensors_get_features(chip, &fnr))) {
|
char *featurename = sensors_get_label(chip, feature);
|
if (!featurename)
|
continue;
|
|
/* Create a 'current' and 'critical' object pair.
|
* Ignore sensor if its not temperature based.
|
*/
|
switch(feature->type) {
|
case SENSORS_FEATURE_TEMP:
|
create_object(name, featurename, chip, feature,
|
SENSORS_TEMP_CURRENT);
|
create_object(name, featurename, chip, feature,
|
SENSORS_TEMP_CRITICAL);
|
break;
|
case SENSORS_FEATURE_IN:
|
create_object(name, featurename, chip, feature,
|
SENSORS_VOLTAGE_CURRENT);
|
break;
|
case SENSORS_FEATURE_CURR:
|
create_object(name, featurename, chip, feature,
|
SENSORS_CURRENT_CURRENT);
|
break;
|
case SENSORS_FEATURE_POWER:
|
create_object(name, featurename, chip, feature,
|
SENSORS_POWER_CURRENT);
|
break;
|
default:
|
break;
|
}
|
free(featurename);
|
}
|
}
|
}
|
|
/**
|
* Initialize internal object arrays and display lmsensors HUD help.
|
* \param displayhelp true if the list of detected devices should be
|
displayed on the console.
|
* \return number of detected lmsensor devices.
|
*/
|
int
|
hud_get_num_sensors(bool displayhelp)
|
{
|
/* Return the number of sensors detected. */
|
mtx_lock(&gsensor_temp_mutex);
|
if (gsensors_temp_count) {
|
mtx_unlock(&gsensor_temp_mutex);
|
return gsensors_temp_count;
|
}
|
|
int ret = sensors_init(NULL);
|
if (ret) {
|
mtx_unlock(&gsensor_temp_mutex);
|
return 0;
|
}
|
|
list_inithead(&gsensors_temp_list);
|
|
/* Scan /sys/block, for every object type we support, create and
|
* persist an object to represent its different statistics.
|
*/
|
build_sensor_list();
|
|
if (displayhelp) {
|
list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) {
|
char line[64];
|
switch (sti->mode) {
|
case SENSORS_TEMP_CURRENT:
|
snprintf(line, sizeof(line), " sensors_temp_cu-%s", sti->name);
|
break;
|
case SENSORS_TEMP_CRITICAL:
|
snprintf(line, sizeof(line), " sensors_temp_cr-%s", sti->name);
|
break;
|
case SENSORS_VOLTAGE_CURRENT:
|
snprintf(line, sizeof(line), " sensors_volt_cu-%s", sti->name);
|
break;
|
case SENSORS_CURRENT_CURRENT:
|
snprintf(line, sizeof(line), " sensors_curr_cu-%s", sti->name);
|
break;
|
case SENSORS_POWER_CURRENT:
|
snprintf(line, sizeof(line), " sensors_pow_cu-%s", sti->name);
|
break;
|
}
|
|
puts(line);
|
}
|
}
|
|
mtx_unlock(&gsensor_temp_mutex);
|
return gsensors_temp_count;
|
}
|
|
#endif /* HAVE_LIBSENSORS */
|