Home | History | Annotate | Download | only in hud
      1 /**************************************************************************
      2  *
      3  * Copyright (C) 2016 Steven Toth <stoth (at) 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 "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 pipe_static_mutex(gsensor_temp_mutex);
     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 }
    143 
    144 static struct sensors_temp_info *
    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
    157 query_sti_load(struct hud_graph *gr)
    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, (uint64_t) sti->current);
    169             break;
    170          case SENSORS_TEMP_CRITICAL:
    171             hud_graph_add_value(gr, (uint64_t) sti->critical);
    172             break;
    173          case SENSORS_VOLTAGE_CURRENT:
    174             hud_graph_add_value(gr, (uint64_t)(sti->current * 1000));
    175             break;
    176          case SENSORS_CURRENT_CURRENT:
    177             hud_graph_add_value(gr, (uint64_t) sti->current);
    178             break;
    179          case SENSORS_POWER_CURRENT:
    180             hud_graph_add_value(gr, (uint64_t) 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
    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
    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
    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
    324 hud_get_num_sensors(bool displayhelp)
    325 {
    326    /* Return the number of sensors detected. */
    327    pipe_mutex_lock(gsensor_temp_mutex);
    328    if (gsensors_temp_count) {
    329       pipe_mutex_unlock(gsensor_temp_mutex);
    330       return gsensors_temp_count;
    331    }
    332 
    333    int ret = sensors_init(NULL);
    334    if (ret) {
    335       pipe_mutex_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    pipe_mutex_unlock(gsensor_temp_mutex);
    372    return gsensors_temp_count;
    373 }
    374 
    375 #endif /* HAVE_LIBSENSORS */
    376