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