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