X-Git-Url: https://git.wpitchoune.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fnvidia.c;h=fa5a898a728f284799706a93ec42e72399d4372e;hb=ba0ded662ced0e4334ac0652d8f7991f485f0899;hp=2f28114497136a9c4d616bfc1b735ef371ec030f;hpb=c2ef6f041c69ee186a81b9f71b50c69807ba4627;p=psensor.git diff --git a/src/lib/nvidia.c b/src/lib/nvidia.c index 2f28114..fa5a898 100644 --- a/src/lib/nvidia.c +++ b/src/lib/nvidia.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 jeanfi@gmail.com + * Copyright (C) 2010-2014 jeanfi@gmail.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,6 +20,8 @@ #include #define _(str) gettext(str) +#include +#include #include #include #include @@ -29,76 +31,269 @@ #include #include -#include "psensor.h" +#include Display *display; -/* - Returns the temperature (Celcius) of a NVidia GPU. -*/ -static int get_temp(struct psensor *sensor) +static char *get_product_name(int id, int type) +{ + char *name; + Bool res; + + if (type & SENSOR_TYPE_FAN) + return strdup("NVIDIA"); + + res = XNVCTRLQueryTargetStringAttribute(display, + NV_CTRL_TARGET_TYPE_GPU, + id, + 0, + NV_CTRL_STRING_PRODUCT_NAME, + &name); + if (res == True) { + if (strcmp(name, "Unknown")) { + return name; + } else { + log_err(_("Unknown NVIDIA product name for GPU %d"), + id); + free(name); + } + } else { + log_err(_("Failed to retrieve NVIDIA product name for GPU %d"), + id); + } + + return strdup("NVIDIA"); +} + +static double get_att(int target, int id, int att) { - int temp; Bool res; + int temp; - res = XNVCTRLQueryTargetAttribute(display, - NV_CTRL_TARGET_TYPE_GPU, - sensor->nvidia_id, - 0, - NV_CTRL_GPU_CORE_TEMPERATURE, - &temp); + res = XNVCTRLQueryTargetAttribute(display, target, id, 0, att, &temp); if (res == True) return temp; + else + return UNKNOWN_DBL_VALUE; +} - fprintf(stderr, _("ERROR: failed to retrieve nvidia temperature\n")); - return 0; +static double get_usage_att(char *atts, const char *att) +{ + char *c, *key, *strv, *s; + size_t n; + double v; + + c = atts; + + v = UNKNOWN_DBL_VALUE; + while (*c) { + s = c; + n = 0; + while (*c) { + if (*c == '=') + break; + c++; + n++; + } + + key = strndup(s, n); + + if (*c) + c++; + + n = 0; + s = c; + while (*c) { + if (*c == ',') + break; + c++; + n++; + } + + strv = strndup(s, n); + if (!strcmp(key, att)) + v = atoi(strv); + + free(key); + free(strv); + + if (v != UNKNOWN_DBL_VALUE) + break; + + while (*c && (*c == ' ' || *c == ',')) + c++; + } + + return v; } -static struct psensor *create_sensor(int id, int values_len) +static const char *get_nvidia_type_str(int type) { - char name[200]; - char *sid; + if (type & SENSOR_TYPE_GRAPHICS) + return "graphics"; + + if (type & SENSOR_TYPE_VIDEO) + return "video"; + + if (type & SENSOR_TYPE_MEMORY) + return "memory"; + + if (type & SENSOR_TYPE_PCIE) + return "PCIe"; + + if (type & SENSOR_TYPE_AMBIENT) + return "ambient"; + + if (type & SENSOR_TYPE_TEMP) + return "temp"; + + if (type & SENSOR_TYPE_FAN) { + if (type & SENSOR_TYPE_RPM) + return "fan rpm"; + else + return "fan level"; + } + + return "unknown"; +} + +static double get_usage(int id, int type) +{ + const char *stype; + char *atts; + double v; + Bool res; + + stype = get_nvidia_type_str(type); + + if (!stype) + return UNKNOWN_DBL_VALUE; + + res = XNVCTRLQueryTargetStringAttribute(display, + NV_CTRL_TARGET_TYPE_GPU, + id, + 0, + NV_CTRL_STRING_GPU_UTILIZATION, + &atts); + + if (res != True) + return UNKNOWN_DBL_VALUE; + + v = get_usage_att(atts, stype); + + free(atts); + + return v; +} + +static double get_value(int id, int type) +{ + int att; + + if (type & SENSOR_TYPE_TEMP) { + if (type & SENSOR_TYPE_AMBIENT) + att = NV_CTRL_AMBIENT_TEMPERATURE; + else + att = NV_CTRL_GPU_CORE_TEMPERATURE; + + return get_att(NV_CTRL_TARGET_TYPE_GPU, id, att); + } else if (type & SENSOR_TYPE_FAN) { + if (type & SENSOR_TYPE_RPM) + return get_att(NV_CTRL_TARGET_TYPE_COOLER, + id, + NV_CTRL_THERMAL_COOLER_SPEED); + else /* SENSOR_TYPE_PERCENT */ + return get_att(NV_CTRL_TARGET_TYPE_COOLER, + id, + NV_CTRL_THERMAL_COOLER_LEVEL); + } else { /* SENSOR_TYPE_PERCENT */ + return get_usage(id, type); + } +} + +static void update(struct psensor *sensor) +{ + double v; + + v = get_value(sensor->nvidia_id, sensor->type); + + if (v == UNKNOWN_DBL_VALUE) + log_err(_("Failed to retrieve measure of type %x " + "for NVIDIA GPU %d"), + sensor->type, + sensor->nvidia_id); + psensor_set_current_value(sensor, v); +} + +static int check_sensor(int id, int type) +{ + return get_value(id, type) != UNKNOWN_DBL_VALUE; +} + +static char *i2str(int i) +{ + char *str; + size_t n; + + /* second +1 to avoid issue about the conversion of a double + * to a lower int */ + n = 1 + (ceil(log10(INT_MAX)) + 1) + 1; + + str = malloc(n); + snprintf(str, n, "%d", i); + + return str; +} + +static struct psensor *create_nvidia_sensor(int id, int subtype, int value_len) +{ + char *pname, *name, *strnid, *sid; + const char *stype; + int type; + size_t n; struct psensor *s; - sprintf(name, "GPU%d", id); + type = SENSOR_TYPE_NVCTRL | subtype; + + if (!check_sensor(id, type)) + return NULL; + + pname = get_product_name(id, type); + strnid = i2str(id); + stype = get_nvidia_type_str(type); + + n = strlen(pname) + 1 + strlen(strnid) + 1 + strlen(stype) + 1; + + name = malloc(n); + sprintf(name, "%s %s %s", pname, strnid, stype); sid = malloc(strlen("nvidia") + 1 + strlen(name) + 1); sprintf(sid, "nvidia %s", name); - s = psensor_create(sid, - strdup(name), - SENSOR_TYPE_NVIDIA_TEMP, - values_len); - + s = psensor_create(sid, name, pname, type, value_len); s->nvidia_id = id; + free(strnid); + return s; } -/* - Opens connection to X server and returns the number - of NVidia GPUs. - - Return 0 if no NVidia gpus or cannot get information. -*/ static int init() { - int evt, err, n; + int evt, err; display = XOpenDisplay(NULL); if (!display) { - fprintf(stderr, - _("ERROR: Cannot open connection to X Server\n")); + log_err(_("Cannot open connection to X11 server.")); return 0; } - if (XNVCTRLQueryExtension(display, &evt, &err) && - XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_GPU, &n)) - return n; + if (XNVCTRLQueryExtension(display, &evt, &err)) + return 1; - fprintf(stderr, _("ERROR: Cannot retrieve NVidia information\n")); + log_err(_("Failed to retrieve NVIDIA information.")); return 0; } @@ -111,31 +306,63 @@ void nvidia_psensor_list_update(struct psensor **sensors) while (*ss) { s = *ss; - if (s->type == SENSOR_TYPE_NVIDIA_TEMP) - psensor_set_current_value(s, get_temp(s)); + if (s->type & SENSOR_TYPE_NVCTRL) + update(s); ss++; } } -struct psensor **nvidia_psensor_list_add(struct psensor **sensors, - int values_len) +static void add(struct psensor ***sensors, int id, int type, int values_len) { - int i, n; - struct psensor **tmp, **ss, *s; - - n = init(); + struct psensor *s; - ss = sensors; - for (i = 0; i < n; i++) { - s = create_sensor(i, values_len); + s = create_nvidia_sensor(id, type, values_len); - tmp = psensor_list_add(ss, s); + if (s) + psensor_list_append(sensors, s); +} - if (ss != tmp) - free(ss); +struct psensor ** +nvidia_psensor_list_add(struct psensor **ss, int values_len) +{ + int i, n, utype; + Bool ret; + + if (!init()) + return ss; + + ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_GPU, &n); + if (ret == True) { + for (i = 0; i < n; i++) { + add(&ss, + i, + SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP, + values_len); + + utype = SENSOR_TYPE_GPU | SENSOR_TYPE_PERCENT; + add(&ss, i, utype | SENSOR_TYPE_AMBIENT, values_len); + add(&ss, i, utype | SENSOR_TYPE_GRAPHICS, values_len); + add(&ss, i, utype | SENSOR_TYPE_VIDEO, values_len); + add(&ss, i, utype | SENSOR_TYPE_MEMORY, values_len); + add(&ss, i, utype | SENSOR_TYPE_PCIE, values_len); + } + } - ss = tmp; + ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_COOLER, &n); + if (ret == True) { + log_debug("NVIDIA: number of fans: %d", n); + for (i = 0; i < n; i++) { + utype = SENSOR_TYPE_FAN | SENSOR_TYPE_RPM; + if (check_sensor(i, utype)) + add(&ss, i, utype, values_len); + + utype = SENSOR_TYPE_FAN | SENSOR_TYPE_PERCENT; + if (check_sensor(i, utype)) + add(&ss, i, utype, values_len); + } + } else { + log_err(_("NVIDIA: failed to retrieve number of fans.")); } return ss;