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