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