xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/hud/hud_sensors_temp.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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