added debug information about VCSC information
[psensor.git] / src / lib / nvidia.c
1 /*
2  * Copyright (C) 2010-2014 jeanfi@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA
18  */
19 #include <locale.h>
20 #include <libintl.h>
21 #define _(str) gettext(str)
22
23 #include <limits.h>
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <X11/Xlib.h>
30
31 #include <NVCtrl/NVCtrl.h>
32 #include <NVCtrl/NVCtrlLib.h>
33
34 #include <psensor.h>
35
36 Display *display;
37
38 static char *get_product_name(int id, int type)
39 {
40         char *name;
41         Bool res;
42
43         if (type & SENSOR_TYPE_FAN)
44                 return strdup("NVIDIA");
45
46         res = XNVCTRLQueryTargetStringAttribute(display,
47                                                 NV_CTRL_TARGET_TYPE_GPU,
48                                                 id,
49                                                 0,
50                                                 NV_CTRL_STRING_PRODUCT_NAME,
51                                                 &name);
52         if (res == True) {
53                 if (strcmp(name, "Unknown")) {
54                         return name;
55                 } else {
56                         log_err(_("Unknown NVIDIA product name for GPU %d"),
57                                 id);
58                         free(name);
59                 }
60         } else {
61                 log_err(_("Failed to retrieve NVIDIA product name for GPU %d"),
62                         id);
63         }
64
65         return strdup("NVIDIA");
66 }
67
68 static double get_att(int target, int id, int att)
69 {
70         Bool res;
71         int temp;
72
73         res = XNVCTRLQueryTargetAttribute(display, target, id, 0, att, &temp);
74
75         if (res == True)
76                 return temp;
77         else
78                 return UNKNOWN_DBL_VALUE;
79 }
80
81 static double get_usage_att(char *atts, const char *att)
82 {
83         char *c, *key, *strv, *s;
84         size_t n;
85         double v;
86
87         c = atts;
88
89         v = UNKNOWN_DBL_VALUE;
90         while (*c) {
91                 s = c;
92                 n = 0;
93                 while (*c) {
94                         if (*c == '=')
95                                 break;
96                         c++;
97                         n++;
98                 }
99
100                 key = strndup(s, n);
101
102                 if (*c)
103                         c++;
104
105                 n = 0;
106                 s = c;
107                 while (*c) {
108                         if (*c == ',')
109                                 break;
110                         c++;
111                         n++;
112                 }
113
114                 strv = strndup(s, n);
115                 if (!strcmp(key, att))
116                         v = atoi(strv);
117
118                 free(key);
119                 free(strv);
120
121                 if (v != UNKNOWN_DBL_VALUE)
122                         break;
123
124                 while (*c && (*c == ' ' || *c == ','))
125                         c++;
126         }
127
128         return v;
129 }
130
131 static const char *get_nvidia_type_str(int type)
132 {
133         if (type & SENSOR_TYPE_GRAPHICS)
134                 return "graphics";
135         else if (type & SENSOR_TYPE_VIDEO)
136                 return "video";
137         else if (type & SENSOR_TYPE_MEMORY)
138                 return "memory";
139         else if (type & SENSOR_TYPE_PCIE)
140                 return "PCIe";
141         else if (type & SENSOR_TYPE_AMBIENT)
142                 return "ambient";
143         else if (type & SENSOR_TYPE_TEMP)
144                 return "temp";
145         else if (type & SENSOR_TYPE_FAN)
146                 return "fan";
147         else
148                 return "unknown";
149 }
150
151 static double get_usage(int id, int type)
152 {
153         const char *stype;
154         char *atts;
155         double v;
156         Bool res;
157
158         stype = get_nvidia_type_str(type);
159
160         if (!stype)
161                 return UNKNOWN_DBL_VALUE;
162
163         res = XNVCTRLQueryTargetStringAttribute(display,
164                                                 NV_CTRL_TARGET_TYPE_GPU,
165                                                 id,
166                                                 0,
167                                                 NV_CTRL_STRING_GPU_UTILIZATION,
168                                                 &atts);
169
170         if (res != True)
171                 return UNKNOWN_DBL_VALUE;
172
173         v = get_usage_att(atts, stype);
174
175         free(atts);
176
177         return v;
178 }
179
180 static double get_value(int id, int type)
181 {
182         int att;
183
184         if (type & SENSOR_TYPE_TEMP) {
185                 if (type & SENSOR_TYPE_AMBIENT)
186                         att = NV_CTRL_AMBIENT_TEMPERATURE;
187                 else
188                         att = NV_CTRL_GPU_CORE_TEMPERATURE;
189
190                 return get_att(NV_CTRL_TARGET_TYPE_GPU, id, att);
191         } else if (type & SENSOR_TYPE_FAN) {
192                 return get_att(NV_CTRL_TARGET_TYPE_COOLER,
193                                id,
194                                NV_CTRL_THERMAL_COOLER_SPEED);
195         } else { /* SENSOR_TYPE_USAGE */
196                 return get_usage(id, type);
197         }
198 }
199
200 static void update(struct psensor *sensor)
201 {
202         double v;
203
204         v = get_value(sensor->nvidia_id, sensor->type);
205
206         if (v == UNKNOWN_DBL_VALUE)
207                 log_err(_("Failed to retrieve measure of type %x "
208                           "for NVIDIA GPU %d"),
209                         sensor->type,
210                         sensor->nvidia_id);
211         psensor_set_current_value(sensor, v);
212 }
213
214 static int check_sensor(int id, int type)
215 {
216         return get_value(id, type) != UNKNOWN_DBL_VALUE;
217 }
218
219 static char *i2str(int i)
220 {
221         char *str;
222         size_t n;
223
224         /* second +1 to avoid issue about the conversion of a double
225          * to a lower int */
226         n = 1 + (ceil(log10(INT_MAX)) + 1) + 1;
227
228         str = malloc(n);
229         snprintf(str, n, "%d", i);
230
231         return str;
232 }
233
234 static struct psensor *create_nvidia_sensor(int id, int subtype, int value_len)
235 {
236         char *pname, *name, *strnid, *sid;
237         const char *stype;
238         int type;
239         size_t n;
240         struct psensor *s;
241
242         type = SENSOR_TYPE_NVCTRL | subtype;
243
244         if (!check_sensor(id, type))
245                 return NULL;
246
247         pname = get_product_name(id, type);
248         strnid = i2str(id);
249         stype = get_nvidia_type_str(type);
250
251         n = strlen(pname) + 1 + strlen(strnid) + 1 + strlen(stype) + 1;
252
253         name = malloc(n);
254         sprintf(name, "%s %s %s", pname, strnid, stype);
255
256         sid = malloc(strlen("nvidia") + 1 + strlen(name) + 1);
257         sprintf(sid, "nvidia %s", name);
258
259         s = psensor_create(sid, name, pname, type, value_len);
260         s->nvidia_id = id;
261
262         free(strnid);
263
264         return s;
265 }
266
267 static int init()
268 {
269         int evt, err;
270
271         display = XOpenDisplay(NULL);
272
273         if (!display) {
274                 log_err(_("Cannot open connection to X11 server."));
275                 return 0;
276         }
277
278         if (XNVCTRLQueryExtension(display, &evt, &err))
279                 return 1;
280
281         log_err(_("Failed to retrieve NVIDIA information."));
282
283         return 0;
284 }
285
286 void nvidia_psensor_list_update(struct psensor **sensors)
287 {
288         struct psensor **ss, *s;
289
290         ss = sensors;
291         while (*ss) {
292                 s = *ss;
293
294                 if (s->type & SENSOR_TYPE_NVCTRL)
295                         update(s);
296
297                 ss++;
298         }
299 }
300
301 static void add(struct psensor ***sensors, int id, int type, int values_len)
302 {
303         struct psensor **tmp, *s;
304
305         s = create_nvidia_sensor(id, type, values_len);
306
307         if (s) {
308                 tmp = psensor_list_add(*sensors, s);
309                 free(*sensors);
310                 *sensors = tmp;
311         }
312 }
313
314 struct psensor **
315 nvidia_psensor_list_add(struct psensor **ss, int values_len)
316 {
317         int i, n, utype, rpm;
318         Bool ret;
319         char *str;
320
321         if (!init())
322                 return ss;
323
324         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_GPU, &n);
325         if (ret == True) {
326                 for (i = 0; i < n; i++) {
327                         add(&ss,
328                             i,
329                             SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP,
330                             values_len);
331
332                         utype = SENSOR_TYPE_GPU | SENSOR_TYPE_USAGE;
333                         add(&ss, i, utype | SENSOR_TYPE_AMBIENT, values_len);
334                         add(&ss, i, utype | SENSOR_TYPE_GRAPHICS, values_len);
335                         add(&ss, i, utype | SENSOR_TYPE_VIDEO, values_len);
336                         add(&ss, i, utype | SENSOR_TYPE_MEMORY, values_len);
337                         add(&ss, i, utype | SENSOR_TYPE_PCIE, values_len);
338                 }
339         }
340
341         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_COOLER, &n);
342         if (ret == True) {
343                 log_debug("NVIDIA: number of fans: %d", n);
344                 for (i = 0; i < n; i++) {
345                         ret = XNVCTRLQueryTargetAttribute
346                                 (display,
347                                  NV_CTRL_TARGET_TYPE_COOLER,
348                                  i,
349                                  0,
350                                  NV_CTRL_THERMAL_COOLER_SPEED, &rpm);
351                         if (ret == True)
352                                 log_debug("NVIDIA: fan speed %d %d", i, rpm);
353                         else
354                                 log_err(_("NVIDIA: "
355                                           "failed to retrieve fan speed %d"),
356                                         i);
357
358                         ret = XNVCTRLQueryTargetAttribute
359                                 (display,
360                                  NV_CTRL_TARGET_TYPE_COOLER,
361                                  i,
362                                  0,
363                                  NV_CTRL_THERMAL_COOLER_LEVEL, &rpm);
364                         if (ret == True)
365                                 log_debug("NVIDIA: fan level %d %d", i, rpm);
366                         else
367                                 log_err(_("NVIDIA: "
368                                           "failed to retrieve fan level %d"),
369                                         i);
370
371
372                         add(&ss, i, SENSOR_TYPE_FAN, values_len);
373                 }
374         } else {
375                 log_err(_("NVIDIA: failed to retrieve number of fans."));
376         }
377
378         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_VCSC, &n);
379         if (ret == True) {
380                 log_debug("NVIDIA: number of VCSC: %d", n);
381                 for (i = 0; i < n; i++) {
382                         ret = XNVCTRLQueryTargetStringAttribute
383                                 (display,
384                                  NV_CTRL_TARGET_TYPE_VCSC,
385                                  i,
386                                  0,
387                                  NV_CTRL_STRING_VCSC_FAN_STATUS, &str);
388                         if (ret == True)
389                                 log_debug("NVIDIA: vcsc fan %d %s", i, str);
390                         else
391                                 log_err(_("NVIDIA: "
392                                           "failed to retrieve vcsc fan info %d"),
393                                         i);
394
395                         add(&ss, i, SENSOR_TYPE_FAN, values_len);
396                 }
397         }
398
399         return ss;
400 }
401
402 void nvidia_cleanup()
403 {
404         if (display) {
405                 XCloseDisplay(display);
406                 display = NULL;
407         }
408 }