• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
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
get_value(const sensors_chip_name * name,const sensors_subfeature * sub)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
get_sensor_values(struct sensors_temp_info * sti)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 *
find_sti_by_name(const char * n,unsigned int mode)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
query_sti_load(struct hud_graph * gr,struct pipe_context * pipe)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
hud_sensors_temp_graph_install(struct hud_pane * pane,const char * dev_name,unsigned int mode)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
create_object(const char * chipname,const char * featurename,const sensors_chip_name * chip,const sensors_feature * feature,int mode)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
build_sensor_list(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
hud_get_num_sensors(bool displayhelp)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