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