Imported Upstream version 1.0.1
[psensor-pkg-debian.git] / src / lib / amd.c
1 /*
2  * Copyright (C) 2010-2011 thgreasi@gmail.com, jeanfi@gmail.com
3  * Copyright (C) 2012-2014 jeanfi@gmail.com
4  *
5  * GPU usage is a contribution of MestreLion
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22 #ifndef LINUX
23 #define LINUX 1
24 #endif
25
26 #include <locale.h>
27 #include <libintl.h>
28 #define _(str) gettext(str)
29
30 #include <dlfcn.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <adl_sdk.h>
36
37 #include "psensor.h"
38
39 typedef int (*ADL_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int);
40 typedef int (*ADL_MAIN_CONTROL_DESTROY)();
41 typedef int (*ADL_ADAPTER_NUMBEROFADAPTERS_GET) (int *);
42 typedef int (*ADL_ADAPTER_ADAPTERINFO_GET) (LPAdapterInfo, int);
43 typedef int (*ADL_ADAPTER_ACTIVE_GET) (int, int*);
44 typedef int (*ADL_OD5_TEMPERATURE_GET) (int, int, ADLTemperature*);
45 typedef int (*ADL_OD5_FANSPEED_GET) (int, int, ADLFanSpeedValue*);
46 typedef int (*ADL_OD5_CURRENTACTIVITY_GET) (int, ADLPMActivity*);
47
48 static ADL_MAIN_CONTROL_CREATE adl_main_control_create;
49 static ADL_MAIN_CONTROL_DESTROY adl_main_control_destroy;
50 static ADL_ADAPTER_NUMBEROFADAPTERS_GET adl_adapter_numberofadapters_get;
51 static ADL_ADAPTER_ADAPTERINFO_GET adl_adapter_adapterinfo_get;
52 static ADL_ADAPTER_ACTIVE_GET adl_adapter_active_get;
53 static ADL_OD5_TEMPERATURE_GET adl_od5_temperature_get;
54 static ADL_OD5_FANSPEED_GET adl_od5_fanspeed_get;
55 static ADL_OD5_CURRENTACTIVITY_GET adl_od5_currentactivity_get;
56
57 static void *hdll;
58 static int adl_main_control_done;
59 static int *active_adapters;
60
61 static void __stdcall *adl_main_memory_alloc(int isize)
62 {
63         void *lpbuffer = malloc(isize);
64         return lpbuffer;
65 }
66
67 static void *getprocaddress(void *plibrary, const char *name)
68 {
69         return dlsym(plibrary, name);
70 }
71
72 /* Returns the temperature (Celsius) of an AMD/ATI GPU. */
73 static double get_temp(struct psensor *sensor)
74 {
75         ADLTemperature v;
76
77         v.iSize = sizeof(ADLTemperature);
78         v.iTemperature = -273;
79         if (ADL_OK == adl_od5_temperature_get(sensor->amd_id, 0, &v))
80                 return v.iTemperature/1000;
81         else
82                 return UNKNOWN_DBL_VALUE;
83 }
84
85 static double get_fanspeed(struct psensor *sensor)
86 {
87         ADLFanSpeedValue v;
88
89         v.iSize = sizeof(ADLFanSpeedValue);
90         v.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM;
91         v.iFanSpeed = -1;
92         if (ADL_OK == adl_od5_fanspeed_get(sensor->amd_id, 0, &v))
93                 return v.iFanSpeed;
94         else
95                 return UNKNOWN_DBL_VALUE;
96 }
97
98 static double get_usage(struct psensor *sensor)
99 {
100         ADLPMActivity v;
101
102         v.iSize = sizeof(ADLPMActivity);
103         if (ADL_OK == adl_od5_currentactivity_get(sensor->amd_id, &v))
104                 return v.iActivityPercent;
105         else
106                 return UNKNOWN_DBL_VALUE;
107 }
108
109 static struct psensor *create_sensor(int id, int type, int values_len)
110 {
111         char name[200];
112         char *sid;
113         int sensor_type;
114         struct psensor *s;
115
116         sensor_type = SENSOR_TYPE_ATIADL;
117         switch (type) {
118         /* Fan rotation speed */
119         case 0:
120                 sprintf(name, "AMD GPU%d Fan", id);
121                 sensor_type |= SENSOR_TYPE_FAN | SENSOR_TYPE_RPM;
122                 break;
123
124         /* Temperature */
125         case 1:
126                 sprintf(name, "AMD GPU%d Temperature", id);
127                 sensor_type |= SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP;
128                 break;
129
130         /* GPU Usage (Activity/Load %) */
131         case 2:
132                 sprintf(name, "AMD GPU%d Usage", id);
133                 sensor_type |= SENSOR_TYPE_GPU | SENSOR_TYPE_USAGE;
134                 break;
135         }
136
137         sid = malloc(strlen("amd") + 1 + strlen(name) + 1);
138         sprintf(sid, "amd %s", name);
139
140         s = psensor_create(sid,
141                            strdup(name),
142                            strdup("AMD/ATI GPU"),
143                            sensor_type,
144                            values_len);
145
146         s->amd_id = active_adapters[id];
147
148         return s;
149 }
150
151 /*
152   Returns the number of active AMD/ATI GPU adapters
153
154   Return 0 if no AMD/ATI GPUs or cannot get information.
155 */
156 static int init()
157 {
158         LPAdapterInfo lpadapterinfo;
159         int i, inumberadapters, inumberadaptersactive, lpstatus, iadapterindex;
160
161         adl_main_control_done = 0;
162         inumberadaptersactive = 0;
163         active_adapters = NULL;
164         lpadapterinfo = NULL;
165
166         hdll = dlopen("libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL);
167         if (!hdll) {
168                 log_debug(_("AMD: cannot found ADL library."));
169                 return 0;
170         }
171
172         adl_main_control_create = (ADL_MAIN_CONTROL_CREATE)
173                 getprocaddress(hdll, "ADL_Main_Control_Create");
174         adl_main_control_destroy = (ADL_MAIN_CONTROL_DESTROY)
175                 getprocaddress(hdll, "ADL_Main_Control_Destroy");
176         adl_adapter_numberofadapters_get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET)
177                 getprocaddress(hdll, "ADL_Adapter_NumberOfAdapters_Get");
178         adl_adapter_adapterinfo_get = (ADL_ADAPTER_ADAPTERINFO_GET)
179                 getprocaddress(hdll, "ADL_Adapter_AdapterInfo_Get");
180         adl_adapter_active_get = (ADL_ADAPTER_ACTIVE_GET)
181                 getprocaddress(hdll, "ADL_Adapter_Active_Get");
182         adl_od5_temperature_get = (ADL_OD5_TEMPERATURE_GET)
183                 getprocaddress(hdll, "ADL_Overdrive5_Temperature_Get");
184         adl_od5_fanspeed_get = (ADL_OD5_FANSPEED_GET)
185                 getprocaddress(hdll, "ADL_Overdrive5_FanSpeed_Get");
186         adl_od5_currentactivity_get = (ADL_OD5_CURRENTACTIVITY_GET)
187                 getprocaddress(hdll, "ADL_Overdrive5_CurrentActivity_Get");
188         if (!adl_main_control_create
189             || !adl_main_control_destroy
190             || !adl_adapter_numberofadapters_get
191             || !adl_adapter_adapterinfo_get
192             || !adl_od5_temperature_get
193             || !adl_od5_fanspeed_get
194             || !adl_od5_currentactivity_get) {
195                 log_err(_("AMD: missing ADL's API."));
196                 return 0;
197         }
198
199         /*
200            1 in 2nd parameter means retrieve adapter information only
201            for adapters that are physically present and enabled in the
202            system
203          */
204         if (ADL_OK != adl_main_control_create(adl_main_memory_alloc, 1)) {
205                 log_err(_("AMD: failed to initialize ADL."));
206                 return 0;
207         }
208         adl_main_control_done = 1;
209
210         if (ADL_OK != adl_adapter_numberofadapters_get(&inumberadapters)) {
211                 log_err(_("AMD: cannot get the number of adapters."));
212                 return 0;
213         }
214
215         if (!inumberadapters)
216                 return 0;
217
218         lpadapterinfo = malloc(sizeof(AdapterInfo) * inumberadapters);
219         memset(lpadapterinfo, '\0', sizeof(AdapterInfo) * inumberadapters);
220
221         adl_adapter_adapterinfo_get(lpadapterinfo,
222                                     sizeof(AdapterInfo) * inumberadapters);
223
224         for (i = 0; i < inumberadapters; i++) {
225
226                 iadapterindex = lpadapterinfo[i].iAdapterIndex;
227
228                 if (ADL_OK != adl_adapter_active_get(iadapterindex, &lpstatus))
229                         continue;
230                 if (lpstatus != ADL_TRUE)
231                         continue;
232
233                 if (!active_adapters) {
234                         active_adapters = (int *) malloc(sizeof(int));
235                         inumberadaptersactive = 1;
236                 } else {
237                         ++inumberadaptersactive;
238                         active_adapters = (int *)realloc
239                                 (active_adapters,
240                                  sizeof(int)*inumberadaptersactive);
241
242                         if (!active_adapters)
243                                 exit(EXIT_FAILURE);
244                 }
245                 active_adapters[inumberadaptersactive-1] = iadapterindex;
246         }
247
248         free(lpadapterinfo);
249
250         log_debug(_("Number of AMD/ATI adapters: %d"), inumberadapters);
251         log_debug(_("Number of active AMD/ATI adapters: %d"),
252                   inumberadaptersactive);
253
254         return inumberadaptersactive;
255 }
256
257 /* Called regularly to update sensors values */
258 void amd_psensor_list_update(struct psensor **sensors)
259 {
260         struct psensor **ss, *s;
261
262         ss = sensors;
263         while (*ss) {
264                 s = *ss;
265
266                 if (s->type & SENSOR_TYPE_ATIADL) {
267                         if (s->type & SENSOR_TYPE_TEMP)
268                                 psensor_set_current_value(s, get_temp(s));
269                         else if (s->type & SENSOR_TYPE_RPM)
270                                 psensor_set_current_value(s, get_fanspeed(s));
271                         else if (s->type & SENSOR_TYPE_USAGE)
272                                 psensor_set_current_value(s, get_usage(s));
273                 }
274
275                 ss++;
276         }
277 }
278
279 /* Entry point for AMD sensors */
280 struct psensor * *
281 amd_psensor_list_add(struct psensor **sensors, int values_len)
282 {
283         int i, j, n;
284         struct psensor **tmp, **ss, *s;
285
286         n = init();
287
288         ss = sensors;
289         for (i = 0; i < n; i++)
290                 /* Each GPU Adapter has 3 sensors: temp, fan speed and usage */
291                 for (j = 0; j < 3; j++) {
292                         s = create_sensor(i, j, values_len);
293                         tmp = psensor_list_add(ss, s);
294
295                         if (ss != tmp)
296                                 free(ss);
297
298                         ss = tmp;
299                 }
300
301         return ss;
302 }
303
304 void amd_cleanup()
305 {
306         if (hdll) {
307                 if (adl_main_control_done)
308                         adl_main_control_destroy();
309                 dlclose(hdll);
310         }
311
312         if (active_adapters) {
313                 free(active_adapters);
314                 active_adapters = NULL;
315         }
316 }