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