/************************************************************************** * * Copyright (C) 2016 Steven Toth * 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 #include #include #include #include #include #include #include #include #include /* 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) sf = sensors_get_subfeature(sti->chip, sti->feature, SENSORS_SUBFEATURE_POWER_AVERAGE); 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; snprintf(sti->chipname, sizeof(sti->chipname), "%s", chipname); snprintf(sti->featurename, sizeof(sti->featurename), "%s", 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 */