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