1/************************************************************************** 2 * 3 * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com> 4 * Copyright (C) 2016 Zodiac Inflight Innovations 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sub license, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the 16 * next paragraph) shall be included in all copies or substantial portions 17 * of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22 * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 **************************************************************************/ 28 29#ifdef HAVE_LIBSENSORS 30/* Purpose: Extract lm-sensors data, expose temperature, power, voltage. */ 31 32#include "hud/hud_private.h" 33#include "util/list.h" 34#include "util/os_time.h" 35#include "os/os_thread.h" 36#include "util/u_memory.h" 37#include "util/u_string.h" 38#include <stdio.h> 39#include <unistd.h> 40#include <dirent.h> 41#include <stdlib.h> 42#include <unistd.h> 43#include <inttypes.h> 44#include <sys/types.h> 45#include <sys/stat.h> 46#include <unistd.h> 47#include <sensors/sensors.h> 48 49/* TODO: We don't handle dynamic sensor discovery / arrival or removal. 50 * Static globals specific to this HUD category. 51 */ 52static int gsensors_temp_count = 0; 53static struct list_head gsensors_temp_list; 54static mtx_t gsensor_temp_mutex = _MTX_INITIALIZER_NP; 55 56struct sensors_temp_info 57{ 58 struct list_head list; 59 60 /* Combined chip and feature name, human readable. */ 61 char name[64]; 62 63 /* The type of measurement, critical or current. */ 64 unsigned int mode; 65 66 uint64_t last_time; 67 68 char chipname[64]; 69 char featurename[128]; 70 71 sensors_chip_name *chip; 72 const sensors_feature *feature; 73 double current, min, max, critical; 74}; 75 76static double 77get_value(const sensors_chip_name *name, const sensors_subfeature *sub) 78{ 79 double val; 80 int err; 81 82 err = sensors_get_value(name, sub->number, &val); 83 if (err) { 84 fprintf(stderr, "ERROR: Can't get value of subfeature %s\n", sub->name); 85 val = 0; 86 } 87 return val; 88} 89 90static void 91get_sensor_values(struct sensors_temp_info *sti) 92{ 93 const sensors_subfeature *sf; 94 95 switch(sti->mode) { 96 case SENSORS_VOLTAGE_CURRENT: 97 sf = sensors_get_subfeature(sti->chip, sti->feature, 98 SENSORS_SUBFEATURE_IN_INPUT); 99 if (sf) 100 sti->current = get_value(sti->chip, sf); 101 break; 102 case SENSORS_CURRENT_CURRENT: 103 sf = sensors_get_subfeature(sti->chip, sti->feature, 104 SENSORS_SUBFEATURE_CURR_INPUT); 105 if (sf) { 106 /* Sensors API returns in AMPs, even though driver is reporting mA, 107 * convert back to mA */ 108 sti->current = get_value(sti->chip, sf) * 1000; 109 } 110 break; 111 case SENSORS_TEMP_CURRENT: 112 sf = sensors_get_subfeature(sti->chip, sti->feature, 113 SENSORS_SUBFEATURE_TEMP_INPUT); 114 if (sf) 115 sti->current = get_value(sti->chip, sf); 116 break; 117 case SENSORS_TEMP_CRITICAL: 118 sf = sensors_get_subfeature(sti->chip, sti->feature, 119 SENSORS_SUBFEATURE_TEMP_CRIT); 120 if (sf) 121 sti->critical = get_value(sti->chip, sf); 122 break; 123 case SENSORS_POWER_CURRENT: 124 sf = sensors_get_subfeature(sti->chip, sti->feature, 125 SENSORS_SUBFEATURE_POWER_INPUT); 126 if (!sf) 127 sf = sensors_get_subfeature(sti->chip, sti->feature, 128 SENSORS_SUBFEATURE_POWER_AVERAGE); 129 if (sf) { 130 /* Sensors API returns in WATTs, even though driver is reporting mW, 131 * convert back to mW */ 132 sti->current = get_value(sti->chip, sf) * 1000; 133 } 134 break; 135 } 136 137 sf = sensors_get_subfeature(sti->chip, sti->feature, 138 SENSORS_SUBFEATURE_TEMP_MIN); 139 if (sf) 140 sti->min = get_value(sti->chip, sf); 141 142 sf = sensors_get_subfeature(sti->chip, sti->feature, 143 SENSORS_SUBFEATURE_TEMP_MAX); 144 if (sf) 145 sti->max = get_value(sti->chip, sf); 146} 147 148static struct sensors_temp_info * 149find_sti_by_name(const char *n, unsigned int mode) 150{ 151 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) { 152 if (sti->mode != mode) 153 continue; 154 if (strcasecmp(sti->name, n) == 0) 155 return sti; 156 } 157 return 0; 158} 159 160static void 161query_sti_load(struct hud_graph *gr, struct pipe_context *pipe) 162{ 163 struct sensors_temp_info *sti = gr->query_data; 164 uint64_t now = os_time_get(); 165 166 if (sti->last_time) { 167 if (sti->last_time + gr->pane->period <= now) { 168 get_sensor_values(sti); 169 170 switch (sti->mode) { 171 case SENSORS_TEMP_CURRENT: 172 hud_graph_add_value(gr, sti->current); 173 break; 174 case SENSORS_TEMP_CRITICAL: 175 hud_graph_add_value(gr, sti->critical); 176 break; 177 case SENSORS_VOLTAGE_CURRENT: 178 hud_graph_add_value(gr, sti->current * 1000); 179 break; 180 case SENSORS_CURRENT_CURRENT: 181 hud_graph_add_value(gr, sti->current); 182 break; 183 case SENSORS_POWER_CURRENT: 184 hud_graph_add_value(gr, sti->current); 185 break; 186 } 187 188 sti->last_time = now; 189 } 190 } 191 else { 192 /* initialize */ 193 get_sensor_values(sti); 194 sti->last_time = now; 195 } 196} 197 198/** 199 * Create and initialize a new object for a specific sensor interface dev. 200 * \param pane parent context. 201 * \param dev_name device name, EG. 'coretemp-isa-0000.Core 1' 202 * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics. 203 */ 204void 205hud_sensors_temp_graph_install(struct hud_pane *pane, const char *dev_name, 206 unsigned int mode) 207{ 208 struct hud_graph *gr; 209 struct sensors_temp_info *sti; 210 211 int num_devs = hud_get_num_sensors(0); 212 if (num_devs <= 0) 213 return; 214 215 sti = find_sti_by_name(dev_name, mode); 216 if (!sti) 217 return; 218 219 gr = CALLOC_STRUCT(hud_graph); 220 if (!gr) 221 return; 222 223 snprintf(gr->name, sizeof(gr->name), "%.6s..%s (%s)", 224 sti->chipname, 225 sti->featurename, 226 sti->mode == SENSORS_VOLTAGE_CURRENT ? "Volts" : 227 sti->mode == SENSORS_CURRENT_CURRENT ? "Amps" : 228 sti->mode == SENSORS_TEMP_CURRENT ? "Curr" : 229 sti->mode == SENSORS_POWER_CURRENT ? "Pow" : 230 sti->mode == SENSORS_TEMP_CRITICAL ? "Crit" : "Unkn"); 231 232 gr->query_data = sti; 233 gr->query_new_value = query_sti_load; 234 235 hud_pane_add_graph(pane, gr); 236 switch (sti->mode) { 237 case SENSORS_TEMP_CURRENT: 238 case SENSORS_TEMP_CRITICAL: 239 hud_pane_set_max_value(pane, 120); 240 break; 241 case SENSORS_VOLTAGE_CURRENT: 242 hud_pane_set_max_value(pane, 12); 243 break; 244 case SENSORS_CURRENT_CURRENT: 245 hud_pane_set_max_value(pane, 5000); 246 break; 247 case SENSORS_POWER_CURRENT: 248 hud_pane_set_max_value(pane, 5000 /* mW */); 249 break; 250 } 251} 252 253static void 254create_object(const char *chipname, const char *featurename, 255 const sensors_chip_name *chip, const sensors_feature *feature, 256 int mode) 257{ 258 struct sensors_temp_info *sti = CALLOC_STRUCT(sensors_temp_info); 259 260 sti->mode = mode; 261 sti->chip = (sensors_chip_name *) chip; 262 sti->feature = feature; 263 snprintf(sti->chipname, sizeof(sti->chipname), "%s", chipname); 264 snprintf(sti->featurename, sizeof(sti->featurename), "%s", featurename); 265 snprintf(sti->name, sizeof(sti->name), "%s.%s", sti->chipname, 266 sti->featurename); 267 268 list_addtail(&sti->list, &gsensors_temp_list); 269 gsensors_temp_count++; 270} 271 272static void 273build_sensor_list(void) 274{ 275 const sensors_chip_name *chip; 276 const sensors_chip_name *match = 0; 277 const sensors_feature *feature; 278 int chip_nr = 0; 279 280 char name[256]; 281 while ((chip = sensors_get_detected_chips(match, &chip_nr))) { 282 sensors_snprintf_chip_name(name, sizeof(name), chip); 283 284 /* Get all features and filter accordingly. */ 285 int fnr = 0; 286 while ((feature = sensors_get_features(chip, &fnr))) { 287 char *featurename = sensors_get_label(chip, feature); 288 if (!featurename) 289 continue; 290 291 /* Create a 'current' and 'critical' object pair. 292 * Ignore sensor if its not temperature based. 293 */ 294 switch(feature->type) { 295 case SENSORS_FEATURE_TEMP: 296 create_object(name, featurename, chip, feature, 297 SENSORS_TEMP_CURRENT); 298 create_object(name, featurename, chip, feature, 299 SENSORS_TEMP_CRITICAL); 300 break; 301 case SENSORS_FEATURE_IN: 302 create_object(name, featurename, chip, feature, 303 SENSORS_VOLTAGE_CURRENT); 304 break; 305 case SENSORS_FEATURE_CURR: 306 create_object(name, featurename, chip, feature, 307 SENSORS_CURRENT_CURRENT); 308 break; 309 case SENSORS_FEATURE_POWER: 310 create_object(name, featurename, chip, feature, 311 SENSORS_POWER_CURRENT); 312 break; 313 default: 314 break; 315 } 316 free(featurename); 317 } 318 } 319} 320 321/** 322 * Initialize internal object arrays and display lmsensors HUD help. 323 * \param displayhelp true if the list of detected devices should be 324 displayed on the console. 325 * \return number of detected lmsensor devices. 326 */ 327int 328hud_get_num_sensors(bool displayhelp) 329{ 330 /* Return the number of sensors detected. */ 331 mtx_lock(&gsensor_temp_mutex); 332 if (gsensors_temp_count) { 333 mtx_unlock(&gsensor_temp_mutex); 334 return gsensors_temp_count; 335 } 336 337 int ret = sensors_init(NULL); 338 if (ret) { 339 mtx_unlock(&gsensor_temp_mutex); 340 return 0; 341 } 342 343 list_inithead(&gsensors_temp_list); 344 345 /* Scan /sys/block, for every object type we support, create and 346 * persist an object to represent its different statistics. 347 */ 348 build_sensor_list(); 349 350 if (displayhelp) { 351 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) { 352 char line[64]; 353 switch (sti->mode) { 354 case SENSORS_TEMP_CURRENT: 355 snprintf(line, sizeof(line), " sensors_temp_cu-%s", sti->name); 356 break; 357 case SENSORS_TEMP_CRITICAL: 358 snprintf(line, sizeof(line), " sensors_temp_cr-%s", sti->name); 359 break; 360 case SENSORS_VOLTAGE_CURRENT: 361 snprintf(line, sizeof(line), " sensors_volt_cu-%s", sti->name); 362 break; 363 case SENSORS_CURRENT_CURRENT: 364 snprintf(line, sizeof(line), " sensors_curr_cu-%s", sti->name); 365 break; 366 case SENSORS_POWER_CURRENT: 367 snprintf(line, sizeof(line), " sensors_pow_cu-%s", sti->name); 368 break; 369 } 370 371 puts(line); 372 } 373 } 374 375 mtx_unlock(&gsensor_temp_mutex); 376 return gsensors_temp_count; 377} 378 379#endif /* HAVE_LIBSENSORS */ 380