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 <stdio.h> 38#include <unistd.h> 39#include <dirent.h> 40#include <stdlib.h> 41#include <unistd.h> 42#include <inttypes.h> 43#include <sys/types.h> 44#include <sys/stat.h> 45#include <unistd.h> 46#include <sensors/sensors.h> 47 48/* TODO: We don't handle dynamic sensor discovery / arrival or removal. 49 * Static globals specific to this HUD category. 50 */ 51static int gsensors_temp_count = 0; 52static struct list_head gsensors_temp_list; 53static mtx_t gsensor_temp_mutex = _MTX_INITIALIZER_NP; 54 55struct sensors_temp_info 56{ 57 struct list_head list; 58 59 /* Combined chip and feature name, human readable. */ 60 char name[64]; 61 62 /* The type of measurement, critical or current. */ 63 unsigned int mode; 64 65 uint64_t last_time; 66 67 char chipname[64]; 68 char featurename[128]; 69 70 sensors_chip_name *chip; 71 const sensors_feature *feature; 72 double current, min, max, critical; 73}; 74 75static double 76get_value(const sensors_chip_name *name, const sensors_subfeature *sub) 77{ 78 double val; 79 int err; 80 81 err = sensors_get_value(name, sub->number, &val); 82 if (err) { 83 fprintf(stderr, "ERROR: Can't get value of subfeature %s\n", sub->name); 84 val = 0; 85 } 86 return val; 87} 88 89static void 90get_sensor_values(struct sensors_temp_info *sti) 91{ 92 const sensors_subfeature *sf; 93 94 switch(sti->mode) { 95 case SENSORS_VOLTAGE_CURRENT: 96 sf = sensors_get_subfeature(sti->chip, sti->feature, 97 SENSORS_SUBFEATURE_IN_INPUT); 98 if (sf) 99 sti->current = get_value(sti->chip, sf); 100 break; 101 case SENSORS_CURRENT_CURRENT: 102 sf = sensors_get_subfeature(sti->chip, sti->feature, 103 SENSORS_SUBFEATURE_CURR_INPUT); 104 if (sf) { 105 /* Sensors API returns in AMPs, even though driver is reporting mA, 106 * convert back to mA */ 107 sti->current = get_value(sti->chip, sf) * 1000; 108 } 109 break; 110 case SENSORS_TEMP_CURRENT: 111 sf = sensors_get_subfeature(sti->chip, sti->feature, 112 SENSORS_SUBFEATURE_TEMP_INPUT); 113 if (sf) 114 sti->current = get_value(sti->chip, sf); 115 break; 116 case SENSORS_TEMP_CRITICAL: 117 sf = sensors_get_subfeature(sti->chip, sti->feature, 118 SENSORS_SUBFEATURE_TEMP_CRIT); 119 if (sf) 120 sti->critical = get_value(sti->chip, sf); 121 break; 122 case SENSORS_POWER_CURRENT: 123 sf = sensors_get_subfeature(sti->chip, sti->feature, 124 SENSORS_SUBFEATURE_POWER_INPUT); 125 if (!sf) 126 sf = sensors_get_subfeature(sti->chip, sti->feature, 127 SENSORS_SUBFEATURE_POWER_AVERAGE); 128 if (sf) { 129 /* Sensors API returns in WATTs, even though driver is reporting mW, 130 * convert back to mW */ 131 sti->current = get_value(sti->chip, sf) * 1000; 132 } 133 break; 134 } 135 136 sf = sensors_get_subfeature(sti->chip, sti->feature, 137 SENSORS_SUBFEATURE_TEMP_MIN); 138 if (sf) 139 sti->min = get_value(sti->chip, sf); 140 141 sf = sensors_get_subfeature(sti->chip, sti->feature, 142 SENSORS_SUBFEATURE_TEMP_MAX); 143 if (sf) 144 sti->max = get_value(sti->chip, sf); 145} 146 147static struct sensors_temp_info * 148find_sti_by_name(const char *n, unsigned int mode) 149{ 150 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) { 151 if (sti->mode != mode) 152 continue; 153 if (strcasecmp(sti->name, n) == 0) 154 return sti; 155 } 156 return 0; 157} 158 159static void 160query_sti_load(struct hud_graph *gr, struct pipe_context *pipe) 161{ 162 struct sensors_temp_info *sti = gr->query_data; 163 uint64_t now = os_time_get(); 164 165 if (sti->last_time) { 166 if (sti->last_time + gr->pane->period <= now) { 167 get_sensor_values(sti); 168 169 switch (sti->mode) { 170 case SENSORS_TEMP_CURRENT: 171 hud_graph_add_value(gr, sti->current); 172 break; 173 case SENSORS_TEMP_CRITICAL: 174 hud_graph_add_value(gr, sti->critical); 175 break; 176 case SENSORS_VOLTAGE_CURRENT: 177 hud_graph_add_value(gr, sti->current * 1000); 178 break; 179 case SENSORS_CURRENT_CURRENT: 180 hud_graph_add_value(gr, sti->current); 181 break; 182 case SENSORS_POWER_CURRENT: 183 hud_graph_add_value(gr, sti->current); 184 break; 185 } 186 187 sti->last_time = now; 188 } 189 } 190 else { 191 /* initialize */ 192 get_sensor_values(sti); 193 sti->last_time = now; 194 } 195} 196 197/** 198 * Create and initialize a new object for a specific sensor interface dev. 199 * \param pane parent context. 200 * \param dev_name device name, EG. 'coretemp-isa-0000.Core 1' 201 * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics. 202 */ 203void 204hud_sensors_temp_graph_install(struct hud_pane *pane, const char *dev_name, 205 unsigned int mode) 206{ 207 struct hud_graph *gr; 208 struct sensors_temp_info *sti; 209 210 int num_devs = hud_get_num_sensors(0); 211 if (num_devs <= 0) 212 return; 213 214 sti = find_sti_by_name(dev_name, mode); 215 if (!sti) 216 return; 217 218 gr = CALLOC_STRUCT(hud_graph); 219 if (!gr) 220 return; 221 222 snprintf(gr->name, sizeof(gr->name), "%.6s..%s (%s)", 223 sti->chipname, 224 sti->featurename, 225 sti->mode == SENSORS_VOLTAGE_CURRENT ? "Volts" : 226 sti->mode == SENSORS_CURRENT_CURRENT ? "Amps" : 227 sti->mode == SENSORS_TEMP_CURRENT ? "Curr" : 228 sti->mode == SENSORS_POWER_CURRENT ? "Pow" : 229 sti->mode == SENSORS_TEMP_CRITICAL ? "Crit" : "Unkn"); 230 231 gr->query_data = sti; 232 gr->query_new_value = query_sti_load; 233 234 hud_pane_add_graph(pane, gr); 235 switch (sti->mode) { 236 case SENSORS_TEMP_CURRENT: 237 case SENSORS_TEMP_CRITICAL: 238 hud_pane_set_max_value(pane, 120); 239 break; 240 case SENSORS_VOLTAGE_CURRENT: 241 hud_pane_set_max_value(pane, 12); 242 break; 243 case SENSORS_CURRENT_CURRENT: 244 hud_pane_set_max_value(pane, 5000); 245 break; 246 case SENSORS_POWER_CURRENT: 247 hud_pane_set_max_value(pane, 5000 /* mW */); 248 break; 249 } 250} 251 252static void 253create_object(const char *chipname, const char *featurename, 254 const sensors_chip_name *chip, const sensors_feature *feature, 255 int mode) 256{ 257 struct sensors_temp_info *sti = CALLOC_STRUCT(sensors_temp_info); 258 259 sti->mode = mode; 260 sti->chip = (sensors_chip_name *) chip; 261 sti->feature = feature; 262 snprintf(sti->chipname, sizeof(sti->chipname), "%s", chipname); 263 snprintf(sti->featurename, sizeof(sti->featurename), "%s", featurename); 264 snprintf(sti->name, sizeof(sti->name), "%s.%s", sti->chipname, 265 sti->featurename); 266 267 list_addtail(&sti->list, &gsensors_temp_list); 268 gsensors_temp_count++; 269} 270 271static void 272build_sensor_list(void) 273{ 274 const sensors_chip_name *chip; 275 const sensors_chip_name *match = 0; 276 const sensors_feature *feature; 277 int chip_nr = 0; 278 279 char name[256]; 280 while ((chip = sensors_get_detected_chips(match, &chip_nr))) { 281 sensors_snprintf_chip_name(name, sizeof(name), chip); 282 283 /* Get all features and filter accordingly. */ 284 int fnr = 0; 285 while ((feature = sensors_get_features(chip, &fnr))) { 286 char *featurename = sensors_get_label(chip, feature); 287 if (!featurename) 288 continue; 289 290 /* Create a 'current' and 'critical' object pair. 291 * Ignore sensor if its not temperature based. 292 */ 293 switch(feature->type) { 294 case SENSORS_FEATURE_TEMP: 295 create_object(name, featurename, chip, feature, 296 SENSORS_TEMP_CURRENT); 297 create_object(name, featurename, chip, feature, 298 SENSORS_TEMP_CRITICAL); 299 break; 300 case SENSORS_FEATURE_IN: 301 create_object(name, featurename, chip, feature, 302 SENSORS_VOLTAGE_CURRENT); 303 break; 304 case SENSORS_FEATURE_CURR: 305 create_object(name, featurename, chip, feature, 306 SENSORS_CURRENT_CURRENT); 307 break; 308 case SENSORS_FEATURE_POWER: 309 create_object(name, featurename, chip, feature, 310 SENSORS_POWER_CURRENT); 311 break; 312 default: 313 break; 314 } 315 free(featurename); 316 } 317 } 318} 319 320/** 321 * Initialize internal object arrays and display lmsensors HUD help. 322 * \param displayhelp true if the list of detected devices should be 323 displayed on the console. 324 * \return number of detected lmsensor devices. 325 */ 326int 327hud_get_num_sensors(bool displayhelp) 328{ 329 /* Return the number of sensors detected. */ 330 mtx_lock(&gsensor_temp_mutex); 331 if (gsensors_temp_count) { 332 mtx_unlock(&gsensor_temp_mutex); 333 return gsensors_temp_count; 334 } 335 336 int ret = sensors_init(NULL); 337 if (ret) { 338 mtx_unlock(&gsensor_temp_mutex); 339 return 0; 340 } 341 342 list_inithead(&gsensors_temp_list); 343 344 /* Scan /sys/block, for every object type we support, create and 345 * persist an object to represent its different statistics. 346 */ 347 build_sensor_list(); 348 349 if (displayhelp) { 350 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) { 351 char line[64]; 352 switch (sti->mode) { 353 case SENSORS_TEMP_CURRENT: 354 snprintf(line, sizeof(line), " sensors_temp_cu-%s", sti->name); 355 break; 356 case SENSORS_TEMP_CRITICAL: 357 snprintf(line, sizeof(line), " sensors_temp_cr-%s", sti->name); 358 break; 359 case SENSORS_VOLTAGE_CURRENT: 360 snprintf(line, sizeof(line), " sensors_volt_cu-%s", sti->name); 361 break; 362 case SENSORS_CURRENT_CURRENT: 363 snprintf(line, sizeof(line), " sensors_curr_cu-%s", sti->name); 364 break; 365 case SENSORS_POWER_CURRENT: 366 snprintf(line, sizeof(line), " sensors_pow_cu-%s", sti->name); 367 break; 368 } 369 370 puts(line); 371 } 372 } 373 374 mtx_unlock(&gsensor_temp_mutex); 375 return gsensors_temp_count; 376} 377 378#endif /* HAVE_LIBSENSORS */ 379