d3bbb582c691cd0f9fb9de129fbc42b2272404ec
[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 <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         n = 1 + (ceil(log10(INT_MAX)) + 1) + 1;
261
262         str = malloc(n);
263         snprintf(str, n, "%d", i);
264
265         return str;
266 }
267
268 static struct psensor *create_nvidia_sensor(int id, int subtype, int value_len)
269 {
270         char *pname, *name, *strnid, *sid;
271         const char *stype;
272         int type;
273         size_t n;
274         struct psensor *s;
275
276         type = SENSOR_TYPE_NVCTRL | subtype;
277
278         if (!check_sensor(id, type))
279                 return NULL;
280
281         pname = get_product_name(id, type);
282         strnid = i2str(id);
283         stype = get_nvidia_type_str(type);
284
285         n = strlen(pname) + 1 + strlen(strnid) + 1 + strlen(stype) + 1;
286         name = malloc(n);
287         sprintf(name, "%s %s %s", pname, strnid, stype);
288
289         sid = malloc(strlen(PROVIDER_NAME) + 1 + strlen(name) + 1);
290         sprintf(sid, "%s %s", PROVIDER_NAME, name);
291
292         s = psensor_create(sid, name, pname, type, value_len);
293         s->provider_data = malloc(sizeof(int));
294         set_nvidia_id(s, id);
295
296         free(strnid);
297
298         return s;
299 }
300
301 static int init(void)
302 {
303         int evt, err;
304
305         display = XOpenDisplay(NULL);
306
307         if (!display) {
308                 log_err(_("%s: Cannot open connection to X11 server."),
309                         PROVIDER_NAME);
310                 return 0;
311         }
312
313         if (XNVCTRLQueryExtension(display, &evt, &err))
314                 return 1;
315
316         log_err(_("%s: Failed to retrieve NVIDIA information."),
317                 PROVIDER_NAME);
318
319         return 0;
320 }
321
322 void nvidia_psensor_list_update(struct psensor **sensors)
323 {
324         struct psensor *s;
325
326         while (*sensors) {
327                 s = *sensors;
328
329                 if (!(s->type & SENSOR_TYPE_REMOTE)
330                     && s->type & SENSOR_TYPE_NVCTRL)
331                         update(s);
332
333                 sensors++;
334         }
335 }
336
337 static void add(struct psensor ***sensors, int id, int type, int values_len)
338 {
339         struct psensor *s;
340
341         s = create_nvidia_sensor(id, type, values_len);
342
343         if (s)
344                 psensor_list_append(sensors, s);
345 }
346
347 void nvidia_psensor_list_append(struct psensor ***ss, int values_len)
348 {
349         int i, n, utype;
350         Bool ret;
351
352         if (!init())
353                 return;
354
355         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_GPU, &n);
356         if (ret == True) {
357                 for (i = 0; i < n; i++) {
358                         add(ss,
359                             i,
360                             SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP,
361                             values_len);
362
363                         utype = SENSOR_TYPE_GPU | SENSOR_TYPE_PERCENT;
364                         add(ss, i, utype | SENSOR_TYPE_AMBIENT, values_len);
365                         add(ss, i, utype | SENSOR_TYPE_GRAPHICS, values_len);
366                         add(ss, i, utype | SENSOR_TYPE_VIDEO, values_len);
367                         add(ss, i, utype | SENSOR_TYPE_MEMORY, values_len);
368                         add(ss, i, utype | SENSOR_TYPE_PCIE, values_len);
369                 }
370         }
371
372         ret = XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_COOLER, &n);
373         if (ret == True) {
374                 log_fct("%s: Number of fans: %d", PROVIDER_NAME, n);
375                 for (i = 0; i < n; i++) {
376                         utype = SENSOR_TYPE_FAN | SENSOR_TYPE_RPM;
377                         if (check_sensor(i, utype))
378                                 add(ss, i, utype, values_len);
379
380                         utype = SENSOR_TYPE_FAN | SENSOR_TYPE_PERCENT;
381                         if (check_sensor(i, utype))
382                                 add(ss, i, utype, values_len);
383                 }
384         } else {
385                 log_err(_("%s: Failed to retrieve number of fans."),
386                         PROVIDER_NAME);
387         }
388 }
389
390 void nvidia_cleanup(void)
391 {
392         if (display) {
393                 XCloseDisplay(display);
394                 display = NULL;
395         }
396 }