Added NVidia GPU usage.
[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 <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <X11/Xlib.h>
28
29 #include <NVCtrl/NVCtrl.h>
30 #include <NVCtrl/NVCtrlLib.h>
31
32 #include "psensor.h"
33
34 Display *display;
35
36 /* Returns the temperature (Celsius) of a NVIDIA GPU. */
37 static int get_temp(struct psensor *sensor)
38 {
39         int temp;
40         Bool res;
41
42         res = XNVCTRLQueryTargetAttribute(display,
43                                           NV_CTRL_TARGET_TYPE_GPU,
44                                           sensor->nvidia_id,
45                                           0,
46                                           NV_CTRL_GPU_CORE_TEMPERATURE,
47                                           &temp);
48
49         if (res == True)
50                 return temp;
51
52         log_debug(_("NVIDIA proprietary driver not used or cannot "
53                     "retrieve NVIDIA GPU temperature."));
54         return 0;
55 }
56
57 static double get_usage_att(char *atts, char *att)
58 {
59         char *c, *key, *strv, *s;
60         size_t n;
61         double v;
62
63         c = atts;
64
65         v = UNKNOWN_DBL_VALUE;
66         while (*c) {
67                 s = c;
68                 n = 0;
69                 while (*c) {
70                         if (*c == '=')
71                                 break;
72                         c++;
73                         n++;
74                 }
75
76                 key = strndup(s, n);
77
78                 if (*c)
79                         c++;
80
81                 n = 0;
82                 s = c;
83                 while (*c) {
84                         if (*c == ',')
85                                 break;
86                         c++;
87                         n++;
88                 }
89
90                 strv = strndup(s, n);
91                 if (!strcmp(key, att))
92                         v = atoi(strv);
93
94                 free(key);
95                 free(strv);
96
97                 if (v != UNKNOWN_DBL_VALUE)
98                         break;
99
100                 while (*c && (*c == ' ' || *c == ','))
101                         c++;
102         }
103
104         return v;
105 }
106
107 static int get_usage(struct psensor *sensor)
108 {
109         char *temp;
110         Bool res;
111
112         res = XNVCTRLQueryTargetStringAttribute(display,
113                                           NV_CTRL_TARGET_TYPE_GPU,
114                                           sensor->nvidia_id,
115                                           0,
116                                           NV_CTRL_STRING_GPU_UTILIZATION,
117                                           &temp);
118
119         if (res == True)
120                 return get_usage_att(temp, "graphics");
121
122         log_debug(_("NVIDIA proprietary driver not used or cannot "
123                     "retrieve NVIDIA GPU usage."));
124         return 0;
125 }
126
127 static struct psensor *create_temp_sensor(int id, int values_len)
128 {
129         char name[200];
130         char *sid;
131         struct psensor *s;
132         int t;
133
134         sprintf(name, "GPU%d", id);
135
136         sid = malloc(strlen("NVIDIA") + 1 + strlen(name) + 1);
137         sprintf(sid, "NVIDIA %s", name);
138
139         t = SENSOR_TYPE_NVCTRL | SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP;
140
141         s = psensor_create(sid,
142                            strdup(name),
143                            strdup(_("NVIDIA GPU")),
144                            t,
145                            values_len);
146
147         s->nvidia_id = id;
148
149         return s;
150 }
151
152 static struct psensor *create_usage_sensor(int id, int values_len)
153 {
154         char name[200];
155         char *sid;
156         struct psensor *s;
157         int t;
158
159         sprintf(name, "GPU%d graphics", id);
160
161         sid = malloc(strlen("NVIDIA") + 1 + strlen(name) + 1);
162         sprintf(sid, "NVIDIA %s", name);
163
164         t = SENSOR_TYPE_NVCTRL | SENSOR_TYPE_GPU | SENSOR_TYPE_USAGE;
165
166         s = psensor_create(sid,
167                            strdup(name),
168                            strdup(_("NVIDIA GPU")),
169                            t,
170                            values_len);
171
172         s->nvidia_id = id;
173
174         return s;
175 }
176
177 /*
178   Opens connection to X server and returns the number
179   of NVIDIA GPUs.
180
181   Return 0 if no NVIDIA gpus or cannot get information.
182 */
183 static int init()
184 {
185         int evt, err, n;
186
187         display = XOpenDisplay(NULL);
188
189         if (!display) {
190                 log_err(_("Cannot open connection to X11 server."));
191                 return 0;
192         }
193
194         if (XNVCTRLQueryExtension(display, &evt, &err) &&
195             XNVCTRLQueryTargetCount(display, NV_CTRL_TARGET_TYPE_GPU, &n))
196                 return n;
197
198         log_err(_("Failed to retrieve NVIDIA information."));
199
200         return 0;
201 }
202
203 void nvidia_psensor_list_update(struct psensor **sensors)
204 {
205         struct psensor **ss, *s;
206
207         ss = sensors;
208         while (*ss) {
209                 s = *ss;
210
211                 if (s->type & SENSOR_TYPE_NVCTRL) {
212                         if (s->type & SENSOR_TYPE_TEMP)
213                                 psensor_set_current_value(s, get_temp(s));
214                         else if (s->type & SENSOR_TYPE_USAGE)
215                                 psensor_set_current_value(s, get_usage(s));
216                 }
217
218                 ss++;
219         }
220 }
221
222 struct psensor **nvidia_psensor_list_add(struct psensor **sensors,
223                                          int values_len)
224 {
225         int i, n;
226         struct psensor **tmp, **ss, *s;
227
228         n = init();
229
230         ss = sensors;
231         for (i = 0; i < n; i++) {
232                 s = create_temp_sensor(i, values_len);
233
234                 tmp = psensor_list_add(ss, s);
235
236                 if (ss != tmp)
237                         free(ss);
238
239                 ss = tmp;
240
241                 s = create_usage_sensor(i, values_len);
242
243                 tmp = psensor_list_add(ss, s);
244
245                 if (ss != tmp)
246                         free(ss);
247
248                 ss = tmp;
249         }
250
251         return ss;
252 }
253
254 void nvidia_cleanup()
255 {
256         if (display) {
257                 XCloseDisplay(display);
258                 display = NULL;
259         }
260 }