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