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