Imported Upstream version 1.1.2
[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
80         if (ADL_OK == adl_od5_temperature_get(sensor->amd_id, 0, &v))
81                 return v.iTemperature/1000;
82
83         return UNKNOWN_DBL_VALUE;
84 }
85
86 static double get_fanspeed(struct psensor *sensor)
87 {
88         ADLFanSpeedValue v;
89
90         v.iSize = sizeof(ADLFanSpeedValue);
91         v.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM;
92         v.iFanSpeed = -1;
93
94         if (ADL_OK == adl_od5_fanspeed_get(sensor->amd_id, 0, &v))
95                 return v.iFanSpeed;
96
97         return UNKNOWN_DBL_VALUE;
98 }
99
100 static double get_usage(struct psensor *sensor)
101 {
102         ADLPMActivity v;
103
104         v.iSize = sizeof(ADLPMActivity);
105
106         if (ADL_OK == adl_od5_currentactivity_get(sensor->amd_id, &v))
107                 return v.iActivityPercent;
108
109         return UNKNOWN_DBL_VALUE;
110 }
111
112 static struct psensor *create_sensor(int id, int type, int values_len)
113 {
114         char name[200];
115         char *sid;
116         int sensor_type;
117         struct psensor *s;
118
119         sensor_type = SENSOR_TYPE_ATIADL;
120         switch (type) {
121         /* Fan rotation speed */
122         case 0:
123                 sprintf(name, "AMD GPU%d Fan", id);
124                 sensor_type |= SENSOR_TYPE_FAN | SENSOR_TYPE_RPM;
125                 break;
126
127         /* Temperature */
128         case 1:
129                 sprintf(name, "AMD GPU%d Temperature", id);
130                 sensor_type |= SENSOR_TYPE_GPU | SENSOR_TYPE_TEMP;
131                 break;
132
133         /* GPU Usage (Activity/Load %) */
134         case 2:
135                 sprintf(name, "AMD GPU%d Usage", id);
136                 sensor_type |= SENSOR_TYPE_GPU | SENSOR_TYPE_PERCENT;
137                 break;
138         }
139
140         sid = malloc(strlen("amd") + 1 + strlen(name) + 1);
141         sprintf(sid, "amd %s", name);
142
143         s = psensor_create(sid,
144                            strdup(name),
145                            strdup("AMD/ATI GPU"),
146                            sensor_type,
147                            values_len);
148
149         s->amd_id = active_adapters[id];
150
151         return s;
152 }
153
154 /*
155   Returns the number of active AMD/ATI GPU adapters
156
157   Return 0 if no AMD/ATI GPUs or cannot get information.
158 */
159 static int init()
160 {
161         LPAdapterInfo lpadapterinfo;
162         int i, inumberadapters, inumberadaptersactive, lpstatus, iadapterindex;
163
164         adl_main_control_done = 0;
165         inumberadaptersactive = 0;
166         active_adapters = NULL;
167         lpadapterinfo = NULL;
168
169         hdll = dlopen("libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL);
170         if (!hdll) {
171                 log_debug(_("AMD: cannot found ADL library."));
172                 return 0;
173         }
174
175         adl_main_control_create = (ADL_MAIN_CONTROL_CREATE)
176                 getprocaddress(hdll, "ADL_Main_Control_Create");
177         adl_main_control_destroy = (ADL_MAIN_CONTROL_DESTROY)
178                 getprocaddress(hdll, "ADL_Main_Control_Destroy");
179         adl_adapter_numberofadapters_get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET)
180                 getprocaddress(hdll, "ADL_Adapter_NumberOfAdapters_Get");
181         adl_adapter_adapterinfo_get = (ADL_ADAPTER_ADAPTERINFO_GET)
182                 getprocaddress(hdll, "ADL_Adapter_AdapterInfo_Get");
183         adl_adapter_active_get = (ADL_ADAPTER_ACTIVE_GET)
184                 getprocaddress(hdll, "ADL_Adapter_Active_Get");
185         adl_od5_temperature_get = (ADL_OD5_TEMPERATURE_GET)
186                 getprocaddress(hdll, "ADL_Overdrive5_Temperature_Get");
187         adl_od5_fanspeed_get = (ADL_OD5_FANSPEED_GET)
188                 getprocaddress(hdll, "ADL_Overdrive5_FanSpeed_Get");
189         adl_od5_currentactivity_get = (ADL_OD5_CURRENTACTIVITY_GET)
190                 getprocaddress(hdll, "ADL_Overdrive5_CurrentActivity_Get");
191         if (!adl_main_control_create
192             || !adl_main_control_destroy
193             || !adl_adapter_numberofadapters_get
194             || !adl_adapter_adapterinfo_get
195             || !adl_od5_temperature_get
196             || !adl_od5_fanspeed_get
197             || !adl_od5_currentactivity_get) {
198                 log_err(_("AMD: missing ADL's API."));
199                 return 0;
200         }
201
202         /*
203            1 in 2nd parameter means retrieve adapter information only
204            for adapters that are physically present and enabled in the
205            system
206          */
207         if (ADL_OK != adl_main_control_create(adl_main_memory_alloc, 1)) {
208                 log_err(_("AMD: failed to initialize ADL."));
209                 return 0;
210         }
211         adl_main_control_done = 1;
212
213         if (ADL_OK != adl_adapter_numberofadapters_get(&inumberadapters)) {
214                 log_err(_("AMD: cannot get the number of adapters."));
215                 return 0;
216         }
217
218         if (!inumberadapters)
219                 return 0;
220
221         lpadapterinfo = malloc(sizeof(AdapterInfo) * inumberadapters);
222         memset(lpadapterinfo, '\0', sizeof(AdapterInfo) * inumberadapters);
223
224         adl_adapter_adapterinfo_get(lpadapterinfo,
225                                     sizeof(AdapterInfo) * inumberadapters);
226
227         for (i = 0; i < inumberadapters; i++) {
228
229                 iadapterindex = lpadapterinfo[i].iAdapterIndex;
230
231                 if (ADL_OK != adl_adapter_active_get(iadapterindex, &lpstatus))
232                         continue;
233                 if (lpstatus != ADL_TRUE)
234                         continue;
235
236                 if (!active_adapters) {
237                         active_adapters = (int *) malloc(sizeof(int));
238                         inumberadaptersactive = 1;
239                 } else {
240                         ++inumberadaptersactive;
241                         active_adapters = (int *)realloc
242                                 (active_adapters,
243                                  sizeof(int)*inumberadaptersactive);
244
245                         if (!active_adapters)
246                                 exit(EXIT_FAILURE);
247                 }
248                 active_adapters[inumberadaptersactive-1] = iadapterindex;
249         }
250
251         free(lpadapterinfo);
252
253         log_debug(_("Number of AMD/ATI adapters: %d"), inumberadapters);
254         log_debug(_("Number of active AMD/ATI adapters: %d"),
255                   inumberadaptersactive);
256
257         return inumberadaptersactive;
258 }
259
260 /* Called regularly to update sensors values */
261 void amd_psensor_list_update(struct psensor **sensors)
262 {
263         struct psensor **ss, *s;
264
265         ss = sensors;
266         while (*ss) {
267                 s = *ss;
268
269                 if (s->type & SENSOR_TYPE_ATIADL) {
270                         if (s->type & SENSOR_TYPE_TEMP)
271                                 psensor_set_current_value(s, get_temp(s));
272                         else if (s->type & SENSOR_TYPE_RPM)
273                                 psensor_set_current_value(s, get_fanspeed(s));
274                         else if (s->type & SENSOR_TYPE_PERCENT)
275                                 psensor_set_current_value(s, get_usage(s));
276                 }
277
278                 ss++;
279         }
280 }
281
282 /* Entry point for AMD sensors */
283 void amd_psensor_list_append(struct psensor ***sensors, int values_len)
284 {
285         int i, j, n;
286         struct psensor *s;
287
288         n = init();
289
290         for (i = 0; i < n; i++)
291                 /* Each GPU Adapter has 3 sensors: temp, fan speed and usage */
292                 for (j = 0; j < 3; j++) {
293                         s = create_sensor(i, j, values_len);
294                         psensor_list_append(sensors, s);
295                 }
296 }
297
298 void amd_cleanup()
299 {
300         if (hdll) {
301                 if (adl_main_control_done)
302                         adl_main_control_destroy();
303                 dlclose(hdll);
304         }
305
306         if (active_adapters) {
307                 free(active_adapters);
308                 active_adapters = NULL;
309         }
310 }