gallium/hud: Add power sensor support
[mesa.git] / src / gallium / auxiliary / hud / hud_sensors_temp.c
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 #if 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 "os/os_time.h"
35 #include "util/u_memory.h"
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <dirent.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <inttypes.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <sensors/sensors.h>
46
47 #define LOCAL_DEBUG 0
48
49 /* TODO: We don't handle dynamic sensor discovery / arrival or removal.
50 * Static globals specific to this HUD category.
51 */
52 static int gsensors_temp_count = 0;
53 static struct list_head gsensors_temp_list;
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 #if LOCAL_DEBUG
143 printf("%s.%s.current = %.1f\n", sti->chipname, sti->featurename,
144 sti->current);
145 printf("%s.%s.critical = %.1f\n", sti->chipname, sti->featurename,
146 sti->critical);
147 #endif
148 }
149
150 static struct sensors_temp_info *
151 find_sti_by_name(const char *n, unsigned int mode)
152 {
153 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) {
154 if (sti->mode != mode)
155 continue;
156 if (strcasecmp(sti->name, n) == 0)
157 return sti;
158 }
159 return 0;
160 }
161
162 static void
163 query_sti_load(struct hud_graph *gr)
164 {
165 struct sensors_temp_info *sti = gr->query_data;
166 uint64_t now = os_time_get();
167
168 if (sti->last_time) {
169 if (sti->last_time + gr->pane->period <= now) {
170 get_sensor_values(sti);
171
172 switch (sti->mode) {
173 case SENSORS_TEMP_CURRENT:
174 hud_graph_add_value(gr, (uint64_t) sti->current);
175 break;
176 case SENSORS_TEMP_CRITICAL:
177 hud_graph_add_value(gr, (uint64_t) sti->critical);
178 break;
179 case SENSORS_VOLTAGE_CURRENT:
180 hud_graph_add_value(gr, (uint64_t)(sti->current * 1000));
181 break;
182 case SENSORS_CURRENT_CURRENT:
183 hud_graph_add_value(gr, (uint64_t) sti->current);
184 break;
185 case SENSORS_POWER_CURRENT:
186 hud_graph_add_value(gr, (uint64_t) sti->current);
187 break;
188 }
189
190 sti->last_time = now;
191 }
192 }
193 else {
194 /* initialize */
195 get_sensor_values(sti);
196 sti->last_time = now;
197 }
198 }
199
200 static void
201 free_query_data(void *p)
202 {
203 struct sensors_temp_info *sti = (struct sensors_temp_info *) p;
204 list_del(&sti->list);
205 if (sti->chip)
206 sensors_free_chip_name(sti->chip);
207 FREE(sti);
208 sensors_cleanup();
209 }
210
211 /**
212 * Create and initialize a new object for a specific sensor interface dev.
213 * \param pane parent context.
214 * \param dev_name device name, EG. 'coretemp-isa-0000.Core 1'
215 * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics.
216 */
217 void
218 hud_sensors_temp_graph_install(struct hud_pane *pane, const char *dev_name,
219 unsigned int mode)
220 {
221 struct hud_graph *gr;
222 struct sensors_temp_info *sti;
223
224 int num_devs = hud_get_num_sensors(0);
225 if (num_devs <= 0)
226 return;
227 #if LOCAL_DEBUG
228 printf("%s(%s, %s) - Creating HUD object\n", __func__, dev_name,
229 mode == SENSORS_VOLTAGE_CURRENT ? "VOLTS" :
230 mode == SENSORS_CURRENT_CURRENT ? "AMPS" :
231 mode == SENSORS_TEMP_CURRENT ? "CU" :
232 mode == SENSORS_POWER_CURRENT ? "POWER" :
233 mode == SENSORS_TEMP_CRITICAL ? "CR" : "UNDEFINED");
234 #endif
235
236 sti = find_sti_by_name(dev_name, mode);
237 if (!sti)
238 return;
239
240 gr = CALLOC_STRUCT(hud_graph);
241 if (!gr)
242 return;
243
244 snprintf(gr->name, sizeof(gr->name), "%.6s..%s (%s)",
245 sti->chipname,
246 sti->featurename,
247 sti->mode == SENSORS_VOLTAGE_CURRENT ? "Volts" :
248 sti->mode == SENSORS_CURRENT_CURRENT ? "Amps" :
249 sti->mode == SENSORS_TEMP_CURRENT ? "Curr" :
250 sti->mode == SENSORS_POWER_CURRENT ? "Pow" :
251 sti->mode == SENSORS_TEMP_CRITICAL ? "Crit" : "Unkn");
252
253 gr->query_data = sti;
254 gr->query_new_value = query_sti_load;
255
256 /* Don't use free() as our callback as that messes up Gallium's
257 * memory debugger. Use simple free_query_data() wrapper.
258 */
259 gr->free_query_data = free_query_data;
260
261 hud_pane_add_graph(pane, gr);
262 switch (sti->mode) {
263 case SENSORS_TEMP_CURRENT:
264 case SENSORS_TEMP_CRITICAL:
265 hud_pane_set_max_value(pane, 120);
266 break;
267 case SENSORS_VOLTAGE_CURRENT:
268 hud_pane_set_max_value(pane, 12);
269 break;
270 case SENSORS_CURRENT_CURRENT:
271 hud_pane_set_max_value(pane, 5000);
272 break;
273 case SENSORS_POWER_CURRENT:
274 hud_pane_set_max_value(pane, 5000 /* mW */);
275 break;
276 }
277 }
278
279 static void
280 create_object(const char *chipname, const char *featurename,
281 const sensors_chip_name *chip, const sensors_feature *feature,
282 int mode)
283 {
284 #if LOCAL_DEBUG
285 printf("%03d: %s.%s\n", gsensors_temp_count, chipname, featurename);
286 #endif
287 struct sensors_temp_info *sti = CALLOC_STRUCT(sensors_temp_info);
288
289 sti->mode = mode;
290 sti->chip = (sensors_chip_name *) chip;
291 sti->feature = feature;
292 strcpy(sti->chipname, chipname);
293 strcpy(sti->featurename, featurename);
294 snprintf(sti->name, sizeof(sti->name), "%s.%s", sti->chipname,
295 sti->featurename);
296
297 list_addtail(&sti->list, &gsensors_temp_list);
298 gsensors_temp_count++;
299 }
300
301 static void
302 build_sensor_list(void)
303 {
304 const sensors_chip_name *chip;
305 const sensors_chip_name *match = 0;
306 const sensors_feature *feature;
307 int chip_nr = 0;
308
309 char name[256];
310 while ((chip = sensors_get_detected_chips(match, &chip_nr))) {
311 sensors_snprintf_chip_name(name, sizeof(name), chip);
312
313 /* Get all features and filter accordingly. */
314 int fnr = 0;
315 while ((feature = sensors_get_features(chip, &fnr))) {
316 char *featurename = sensors_get_label(chip, feature);
317 if (!featurename)
318 continue;
319
320 /* Create a 'current' and 'critical' object pair.
321 * Ignore sensor if its not temperature based.
322 */
323 switch(feature->type) {
324 case SENSORS_FEATURE_TEMP:
325 create_object(name, featurename, chip, feature,
326 SENSORS_TEMP_CURRENT);
327 create_object(name, featurename, chip, feature,
328 SENSORS_TEMP_CRITICAL);
329 break;
330 case SENSORS_FEATURE_IN:
331 create_object(name, featurename, chip, feature,
332 SENSORS_VOLTAGE_CURRENT);
333 break;
334 case SENSORS_FEATURE_CURR:
335 create_object(name, featurename, chip, feature,
336 SENSORS_CURRENT_CURRENT);
337 break;
338 case SENSORS_FEATURE_POWER:
339 create_object(name, featurename, chip, feature,
340 SENSORS_POWER_CURRENT);
341 break;
342 default:
343 break;
344 }
345 free(featurename);
346 }
347 }
348 }
349
350 /**
351 * Initialize internal object arrays and display lmsensors HUD help.
352 * \param displayhelp true if the list of detected devices should be
353 displayed on the console.
354 * \return number of detected lmsensor devices.
355 */
356 int
357 hud_get_num_sensors(bool displayhelp)
358 {
359 /* Return the number of sensors detected. */
360 if (gsensors_temp_count)
361 return gsensors_temp_count;
362
363 int ret = sensors_init(NULL);
364 if (ret)
365 return 0;
366
367 list_inithead(&gsensors_temp_list);
368
369 /* Scan /sys/block, for every object type we support, create and
370 * persist an object to represent its different statistics.
371 */
372 build_sensor_list();
373
374 if (displayhelp) {
375 list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) {
376 char line[64];
377 switch (sti->mode) {
378 case SENSORS_TEMP_CURRENT:
379 snprintf(line, sizeof(line), " sensors_temp_cu-%s", sti->name);
380 break;
381 case SENSORS_TEMP_CRITICAL:
382 snprintf(line, sizeof(line), " sensors_temp_cr-%s", sti->name);
383 break;
384 case SENSORS_VOLTAGE_CURRENT:
385 snprintf(line, sizeof(line), " sensors_volt_cu-%s", sti->name);
386 break;
387 case SENSORS_CURRENT_CURRENT:
388 snprintf(line, sizeof(line), " sensors_curr_cu-%s", sti->name);
389 break;
390 case SENSORS_POWER_CURRENT:
391 snprintf(line, sizeof(line), " sensors_pow_cu-%s", sti->name);
392 break;
393 }
394
395 puts(line);
396 }
397 }
398
399 return gsensors_temp_count;
400 }
401
402 #endif /* HAVE_LIBSENSORS */