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 */
52 static int gsensors_temp_count = 0;
53 static struct list_head gsensors_temp_list;
54 static mtx_t gsensor_temp_mutex = _MTX_INITIALIZER_NP;
55
56 struct 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
76 static double
get_value(const sensors_chip_name * name,const sensors_subfeature * sub)77 get_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
90 static void
get_sensor_values(struct sensors_temp_info * sti)91 get_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
148 static struct sensors_temp_info *
find_sti_by_name(const char * n,unsigned int mode)149 find_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
160 static void
query_sti_load(struct hud_graph * gr,struct pipe_context * pipe)161 query_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 */
204 void
hud_sensors_temp_graph_install(struct hud_pane * pane,const char * dev_name,unsigned int mode)205 hud_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
253 static void
create_object(const char * chipname,const char * featurename,const sensors_chip_name * chip,const sensors_feature * feature,int mode)254 create_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
272 static void
build_sensor_list(void)273 build_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 */
327 int
hud_get_num_sensors(bool displayhelp)328 hud_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