1 /************************************************************************** 2 * 3 * Copyright (C) 2016 Steven Toth <stoth (at) 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 */ 51 static int gsensors_temp_count = 0; 52 static struct list_head gsensors_temp_list; 53 static mtx_t gsensor_temp_mutex = _MTX_INITIALIZER_NP; 54 55 struct 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 75 static double 76 get_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 89 static void 90 get_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 /* Sensors API returns in WATTs, even though driver is reporting mW, 127 * convert back to mW */ 128 sti->current = get_value(sti->chip, sf) * 1000; 129 } 130 break; 131 } 132 133 sf = sensors_get_subfeature(sti->chip, sti->feature, 134 SENSORS_SUBFEATURE_TEMP_MIN); 135 if (sf) 136 sti->min = get_value(sti->chip, sf); 137 138 sf = sensors_get_subfeature(sti->chip, sti->feature, 139 SENSORS_SUBFEATURE_TEMP_MAX); 140 if (sf) 141 sti->max = get_value(sti->chip, sf); 142 } 143 144 static struct sensors_temp_info * 145 find_sti_by_name(const char *n, unsigned int mode) 146 { 147 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) { 148 if (sti->mode != mode) 149 continue; 150 if (strcasecmp(sti->name, n) == 0) 151 return sti; 152 } 153 return 0; 154 } 155 156 static void 157 query_sti_load(struct hud_graph *gr, struct pipe_context *pipe) 158 { 159 struct sensors_temp_info *sti = gr->query_data; 160 uint64_t now = os_time_get(); 161 162 if (sti->last_time) { 163 if (sti->last_time + gr->pane->period <= now) { 164 get_sensor_values(sti); 165 166 switch (sti->mode) { 167 case SENSORS_TEMP_CURRENT: 168 hud_graph_add_value(gr, sti->current); 169 break; 170 case SENSORS_TEMP_CRITICAL: 171 hud_graph_add_value(gr, sti->critical); 172 break; 173 case SENSORS_VOLTAGE_CURRENT: 174 hud_graph_add_value(gr, sti->current * 1000); 175 break; 176 case SENSORS_CURRENT_CURRENT: 177 hud_graph_add_value(gr, sti->current); 178 break; 179 case SENSORS_POWER_CURRENT: 180 hud_graph_add_value(gr, sti->current); 181 break; 182 } 183 184 sti->last_time = now; 185 } 186 } 187 else { 188 /* initialize */ 189 get_sensor_values(sti); 190 sti->last_time = now; 191 } 192 } 193 194 /** 195 * Create and initialize a new object for a specific sensor interface dev. 196 * \param pane parent context. 197 * \param dev_name device name, EG. 'coretemp-isa-0000.Core 1' 198 * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics. 199 */ 200 void 201 hud_sensors_temp_graph_install(struct hud_pane *pane, const char *dev_name, 202 unsigned int mode) 203 { 204 struct hud_graph *gr; 205 struct sensors_temp_info *sti; 206 207 int num_devs = hud_get_num_sensors(0); 208 if (num_devs <= 0) 209 return; 210 211 sti = find_sti_by_name(dev_name, mode); 212 if (!sti) 213 return; 214 215 gr = CALLOC_STRUCT(hud_graph); 216 if (!gr) 217 return; 218 219 snprintf(gr->name, sizeof(gr->name), "%.6s..%s (%s)", 220 sti->chipname, 221 sti->featurename, 222 sti->mode == SENSORS_VOLTAGE_CURRENT ? "Volts" : 223 sti->mode == SENSORS_CURRENT_CURRENT ? "Amps" : 224 sti->mode == SENSORS_TEMP_CURRENT ? "Curr" : 225 sti->mode == SENSORS_POWER_CURRENT ? "Pow" : 226 sti->mode == SENSORS_TEMP_CRITICAL ? "Crit" : "Unkn"); 227 228 gr->query_data = sti; 229 gr->query_new_value = query_sti_load; 230 231 hud_pane_add_graph(pane, gr); 232 switch (sti->mode) { 233 case SENSORS_TEMP_CURRENT: 234 case SENSORS_TEMP_CRITICAL: 235 hud_pane_set_max_value(pane, 120); 236 break; 237 case SENSORS_VOLTAGE_CURRENT: 238 hud_pane_set_max_value(pane, 12); 239 break; 240 case SENSORS_CURRENT_CURRENT: 241 hud_pane_set_max_value(pane, 5000); 242 break; 243 case SENSORS_POWER_CURRENT: 244 hud_pane_set_max_value(pane, 5000 /* mW */); 245 break; 246 } 247 } 248 249 static void 250 create_object(const char *chipname, const char *featurename, 251 const sensors_chip_name *chip, const sensors_feature *feature, 252 int mode) 253 { 254 struct sensors_temp_info *sti = CALLOC_STRUCT(sensors_temp_info); 255 256 sti->mode = mode; 257 sti->chip = (sensors_chip_name *) chip; 258 sti->feature = feature; 259 strcpy(sti->chipname, chipname); 260 strcpy(sti->featurename, featurename); 261 snprintf(sti->name, sizeof(sti->name), "%s.%s", sti->chipname, 262 sti->featurename); 263 264 list_addtail(&sti->list, &gsensors_temp_list); 265 gsensors_temp_count++; 266 } 267 268 static void 269 build_sensor_list(void) 270 { 271 const sensors_chip_name *chip; 272 const sensors_chip_name *match = 0; 273 const sensors_feature *feature; 274 int chip_nr = 0; 275 276 char name[256]; 277 while ((chip = sensors_get_detected_chips(match, &chip_nr))) { 278 sensors_snprintf_chip_name(name, sizeof(name), chip); 279 280 /* Get all features and filter accordingly. */ 281 int fnr = 0; 282 while ((feature = sensors_get_features(chip, &fnr))) { 283 char *featurename = sensors_get_label(chip, feature); 284 if (!featurename) 285 continue; 286 287 /* Create a 'current' and 'critical' object pair. 288 * Ignore sensor if its not temperature based. 289 */ 290 switch(feature->type) { 291 case SENSORS_FEATURE_TEMP: 292 create_object(name, featurename, chip, feature, 293 SENSORS_TEMP_CURRENT); 294 create_object(name, featurename, chip, feature, 295 SENSORS_TEMP_CRITICAL); 296 break; 297 case SENSORS_FEATURE_IN: 298 create_object(name, featurename, chip, feature, 299 SENSORS_VOLTAGE_CURRENT); 300 break; 301 case SENSORS_FEATURE_CURR: 302 create_object(name, featurename, chip, feature, 303 SENSORS_CURRENT_CURRENT); 304 break; 305 case SENSORS_FEATURE_POWER: 306 create_object(name, featurename, chip, feature, 307 SENSORS_POWER_CURRENT); 308 break; 309 default: 310 break; 311 } 312 free(featurename); 313 } 314 } 315 } 316 317 /** 318 * Initialize internal object arrays and display lmsensors HUD help. 319 * \param displayhelp true if the list of detected devices should be 320 displayed on the console. 321 * \return number of detected lmsensor devices. 322 */ 323 int 324 hud_get_num_sensors(bool displayhelp) 325 { 326 /* Return the number of sensors detected. */ 327 mtx_lock(&gsensor_temp_mutex); 328 if (gsensors_temp_count) { 329 mtx_unlock(&gsensor_temp_mutex); 330 return gsensors_temp_count; 331 } 332 333 int ret = sensors_init(NULL); 334 if (ret) { 335 mtx_unlock(&gsensor_temp_mutex); 336 return 0; 337 } 338 339 list_inithead(&gsensors_temp_list); 340 341 /* Scan /sys/block, for every object type we support, create and 342 * persist an object to represent its different statistics. 343 */ 344 build_sensor_list(); 345 346 if (displayhelp) { 347 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) { 348 char line[64]; 349 switch (sti->mode) { 350 case SENSORS_TEMP_CURRENT: 351 snprintf(line, sizeof(line), " sensors_temp_cu-%s", sti->name); 352 break; 353 case SENSORS_TEMP_CRITICAL: 354 snprintf(line, sizeof(line), " sensors_temp_cr-%s", sti->name); 355 break; 356 case SENSORS_VOLTAGE_CURRENT: 357 snprintf(line, sizeof(line), " sensors_volt_cu-%s", sti->name); 358 break; 359 case SENSORS_CURRENT_CURRENT: 360 snprintf(line, sizeof(line), " sensors_curr_cu-%s", sti->name); 361 break; 362 case SENSORS_POWER_CURRENT: 363 snprintf(line, sizeof(line), " sensors_pow_cu-%s", sti->name); 364 break; 365 } 366 367 puts(line); 368 } 369 } 370 371 mtx_unlock(&gsensor_temp_mutex); 372 return gsensors_temp_count; 373 } 374 375 #endif /* HAVE_LIBSENSORS */ 376