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