new release 1.2.0
[psensor-pkg-debian.git] / src / lib / nvidia.c
1 /*
2  * Copyright (C) 2010-2016 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 <nvidia.h>
35 #include <psensor.h>
36
37 static Display *display;
38
39 static const char *PROVIDER_NAME = "nvctrl";
40
41 static void set_nvidia_id(struct psensor *s, int id)
42 {
43         *(int *)s->provider_data = id;
44 }
45
46 static int get_nvidia_id(struct psensor *s)
47 {
48         return *(int *)s->provider_data;
49 }
50
51 static char *get_product_name(int id, int type)
52 {
53         char *name;
54         Bool res;
55
56         if (type & SENSOR_TYPE_FAN)
57                 return strdup("NVIDIA");
58
59         res = XNVCTRLQueryTargetStringAttribute(display,
60                                                 NV_CTRL_TARGET_TYPE_GPU,
61                                                 id,
62                                                 0,
63                                                 NV_CTRL_STRING_PRODUCT_NAME,
64                                                 &name);
65         if (res == True) {
66                 if (strcmp(name, "Unknown"))
67                         return name;
68
69                 log_err(_("%s: Unknown NVIDIA product name for GPU %d"),
70                         PROVIDER_NAME,
71                         id);
72                 free(name);
73         } else {
74                 log_err(_("%s: "
75                           "Failed to retrieve NVIDIA product name for GPU %d"),
76                         PROVIDER_NAME,
77                         id);
78         }
79
80         return strdup("NVIDIA");
81 }
82
83 static double get_att(int target, int id, int att)
84 {
85         Bool res;
86         int temp;
87
88         res = XNVCTRLQueryTargetAttribute(display, target, id, 0, att, &temp);
89
90         if (res == True)
91                 return temp;
92
93         return UNKNOWN_DBL_VALUE;
94 }
95
96 static double get_usage_att(char *atts, const char *att)
97 {
98         char *c, *key, *strv, *s;
99         size_t n;
100         double v;
101
102         c = atts;
103
104         v = UNKNOWN_DBL_VALUE;
105         while (*c) {
106                 s = c;
107                 n = 0;
108                 while (*c) {
109                         if (*c == '=')
110                                 break;
111                         c++;
112                         n++;
113                 }
114
115                 key = strndup(s, n);
116
117                 if (*c)
118                         c++;
119
120                 n = 0;
121                 s = c;
122                 while (*c) {
123                         if (*c == ',')
124                                 break;
125                         c++;
126                         n++;
127                 }
128
129                 strv = strndup(s, n);
130                 if (!strcmp(key, att))
131                         v = atoi(strv);
132
133                 free(key);
134                 free(strv);
135
136                 if (v != UNKNOWN_DBL_VALUE)
137                         break;
138
139                 while (*c && (*c == ' ' || *c == ','))
140                         c++;
141         }
142
143         return v;
144 }
145
146 static const char *get_nvidia_type_str(int type)
147 {
148         if (type & SENSOR_TYPE_GRAPHICS)
149                 return "graphics";
150
151         if (type & SENSOR_TYPE_VIDEO)
152                 return "video";
153
154         if (type & SENSOR_TYPE_MEMORY)
155                 return "memory";
156
157         if (type & SENSOR_TYPE_PCIE)
158                 return "PCIe";
159
160         if (type & SENSOR_TYPE_AMBIENT)
161                 return "ambient";
162
163         if (type & SENSOR_TYPE_TEMP)
164                 return "temp";
165
166         if (type & SENSOR_TYPE_FAN) {
167                 if (type & SENSOR_TYPE_RPM)
168                         return "fan rpm";
169
170                 return "fan level";
171         }
172
173         return "unknown";
174 }
175
176 static double get_usage(int id, int type)
177 {
178         const char *stype;
179         char *atts;
180         double v;
181         Bool res;
182
183         stype = get_nvidia_type_str(type);
184
185         if (!stype)
186                 return UNKNOWN_DBL_VALUE;
187
188         res = XNVCTRLQueryTargetStringAttribute(display,
189                                                 NV_CTRL_TARGET_TYPE_GPU,
190                                                 id,
191                                                 0,
192                                                 NV_CTRL_STRING_GPU_UTILIZATION,
193                                                 &atts);
194
195         if (res != True)
196                 return UNKNOWN_DBL_VALUE;
197
198         v = get_usage_att(atts, stype);
199
200         free(atts);
201
202         return v;
203 }
204
205 static double get_value(int id, int type)
206 {
207         int att;
208
209         if (type & SENSOR_TYPE_TEMP) {
210                 if (type & SENSOR_TYPE_AMBIENT)
211                         att = NV_CTRL_AMBIENT_TEMPERATURE;
212                 else
213                         att = NV_CTRL_GPU_CORE_TEMPERATURE;
214
215                 return get_att(NV_CTRL_TARGET_TYPE_GPU, id, att);
216         } else if (type & SENSOR_TYPE_FAN) {
217                 if (type & SENSOR_TYPE_RPM)
218                         return get_att(NV_CTRL_TARGET_TYPE_COOLER,
219                                        id,
220                                        NV_CTRL_THERMAL_COOLER_SPEED);
221                 else /* SENSOR_TYPE_PERCENT */
222                         return get_att(NV_CTRL_TARGET_TYPE_COOLER,
223                                        id,
224                                        NV_CTRL_THERMAL_COOLER_LEVEL);
225         } else { /* SENSOR_TYPE_PERCENT */
226                 return get_usage(id, type);
227         }
228 }
229
230 static void update(struct psensor *sensor)
231 {
232         double v;
233         int id;
234
235         id = get_nvidia_id(sensor);
236
237         v = get_value(id, sensor->type);
238
239         if (v == UNKNOWN_DBL_VALUE)
240                 log_err(_("%s: Failed to retrieve measure of type %x "
241                           "for NVIDIA GPU %d"),
242                         PROVIDER_NAME,
243                         sensor->type,
244                         id);
245         psensor_set_current_value(sensor, v);
246 }
247
248 static int check_sensor(int id, int type)
249 {
250         return get_value(id, type) != UNKNOWN_DBL_VALUE;
251 }
252
253 static char *i2str(int i)
254 {
255         char *str;
256         size_t n;
257
258         /* second +1 to avoid issue about the conversion of a double
259          * to a lower int
260          */
261         n = 1 + (ceil(log10(INT_MAX)) + 1) + 1;
262
263         str = malloc(n);
264         snprintf(str, n, "%d", i);
265
266         return str;
267 }
268
269 static struct psensor *create_nvidia_sensor(int id, int subtype, int value_len)
270 {
271         char *pname, *name, *strnid, *sid;
272         const char *stype;
273         int type;
274         size_t n;
275         struct psensor *s;
276         double v;
277
278         type = SENSOR_TYPE_NVCTRL | subtype;
279
280         if (!check_sensor(id, type))
281                 return NULL;
282
283         pname = get_product_name(id, type);
284         strnid = i2str(id);
285         stype = get_nvidia_type_str(type);
286
287         n = strlen(pname) + 1 + strlen(strnid) + 1 + strlen(stype) + 1;
288         name = malloc(n);
289         sprintf(name, "%s %s %s", pname, strnid, stype);
290
291         sid = malloc(strlen(PROVIDER_NAME) + 1 + strlen(name) + 1);
292         sprintf(sid, "%s %s", PROVIDER_NAME, name);
293
294         s = psensor_create(sid, name, pname, type, value_len);
295         s->provider_data = malloc(sizeof(int));
296         set_nvidia_id(s, id);
297
298         if ((type & SENSOR_TYPE_GPU) && (type & SENSOR_TYPE_TEMP)) {
299                 v = get_att(NV_CTRL_TARGET_TYPE_GPU,
300                             id,
301                             NV_CTRL_GPU_CORE_THRESHOLD);
302                 s->max = v;
303         }
304
305         free(strnid);
306
307         return s;
308 }
309
310 static int init(void)
311 {
312         int evt, err;
313
314         display = XOpenDisplay(NULL);
315
316         if (!display) {
317                 log_err(_("%s: Cannot open connection to X11 server."),
318                         PROVIDER_NAME);
319                 return 0;
320         }
321
322         if (XNVCTRLQueryExtension(display, &evt, &err))
323                 return 1;
324
325         log_err(_("%s: Failed to retrieve NVIDIA information."),
326                 PROVIDER_NAME);
327
328         return 0;
329 }
330
331 void nvidia_psensor_list_update(struct psensor **sensors)
332 {
333         struct psensor *s;
334
335         while (*sensors) {
336                 s = *sensors;
337
338                 if (!(s->type & SENSOR_TYPE_REMOTE)
339                     && s->type & SENSOR_TYPE_NVCTRL)
340                         update(s);
341
342                 sensors++;
343         }
344 }
345
346 static void add(struct psensor ***sensors, int id, int type, int values_len)
347 {
348         struct psensor *s;
349
350         s = create_nvidia_sensor(id, type, values_len);
351
352         if (s)
353                 psensor_list_append(sensors, s);
354 }
355
356 void nvidia_psensor_list_append(struct psensor ***ss, int values_len)
357 {
358         int i, n, utype;
359         Bool ret;
360
361         if (!init())
362                 return;
363
364         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_GPU, &n);
365         if (ret == True) {
366                 for (i = 0; i < n; i++) {
367                         add(ss,
368                             i,
369                             SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP,
370                             values_len);
371
372                         utype = SENSOR_TYPE_GPU | SENSOR_TYPE_PERCENT;
373                         add(ss, i, utype | SENSOR_TYPE_AMBIENT, values_len);
374                         add(ss, i, utype | SENSOR_TYPE_GRAPHICS, values_len);
375                         add(ss, i, utype | SENSOR_TYPE_VIDEO, values_len);
376                         add(ss, i, utype | SENSOR_TYPE_MEMORY, values_len);
377                         add(ss, i, utype | SENSOR_TYPE_PCIE, values_len);
378                 }
379         }
380
381         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_COOLER, &n);
382         if (ret == True) {
383                 log_fct("%s: Number of fans: %d", PROVIDER_NAME, n);
384                 for (i = 0; i < n; i++) {
385                         utype = SENSOR_TYPE_FAN | SENSOR_TYPE_RPM;
386                         if (check_sensor(i, utype))
387                                 add(ss, i, utype, values_len);
388
389                         utype = SENSOR_TYPE_FAN | SENSOR_TYPE_PERCENT;
390                         if (check_sensor(i, utype))
391                                 add(ss, i, utype, values_len);
392                 }
393         } else {
394                 log_err(_("%s: Failed to retrieve number of fans."),
395                         PROVIDER_NAME);
396         }
397 }
398
399 void nvidia_cleanup(void)
400 {
401         if (display) {
402                 XCloseDisplay(display);
403                 display = NULL;
404         }
405 }