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