added experimental support for ati/amd gpu/fan
[psensor.git] / src / lib / amd.h
1 /*
2     Copyright (C) 2010-2011 thgreasi@gmail.com
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU 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 #ifndef LINUX
20 #define LINUX 1
21 #endif
22 #ifdef HAVE_LIBATIADL
23         /* AMD id for the aticonfig */
24         int amd_id;
25 #endif
26
27 #include <locale.h>
28 #include <libintl.h>
29 #define _(str) gettext(str)
30
31 #include <adl_sdk.h>
32 #include <dlfcn.h>      /* dyopen, dlsym, dlclose */
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>     /* memeset */
36
37 #include "psensor.h"
38
39 /* Definitions of the used function pointers.
40          Add more if you use other ADL APIs */
41 typedef int (*ADL_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int);
42 typedef int (*ADL_MAIN_CONTROL_DESTROY)();
43 typedef int (*ADL_ADAPTER_NUMBEROFADAPTERS_GET) (int *);
44 typedef int (*ADL_ADAPTER_ADAPTERINFO_GET) (LPAdapterInfo, int);
45 typedef int (*ADL_ADAPTER_ACTIVE_GET) (int, int*);
46 typedef int (*ADL_OVERDRIVE5_TEMPERATURE_GET) (int, int, ADLTemperature*);
47 typedef int (*ADL_OVERDRIVE5_FANSPEED_GET) (int, int, ADLFanSpeedValue*);
48
49 static ADL_MAIN_CONTROL_CREATE            adl_main_control_create;
50 static ADL_MAIN_CONTROL_DESTROY           adl_main_control_destroy;
51 static ADL_ADAPTER_NUMBEROFADAPTERS_GET   adl_adapter_numberofadapters_get;
52 static ADL_ADAPTER_ADAPTERINFO_GET        adl_adapter_adapterinfo_get;
53 static ADL_ADAPTER_ACTIVE_GET             adl_adapter_active_get;
54 static ADL_OVERDRIVE5_TEMPERATURE_GET     adl_overdrive5_temperature_get;
55 static ADL_OVERDRIVE5_FANSPEED_GET        adl_overdrive5_fanspeed_get;
56
57 static void *hdll;
58 static int adl_main_control_done;
59 static int *active_amd_adapters;
60
61 /* Memory allocation function */
62 static void __stdcall *adl_main_memory_alloc(int isize)
63 {
64     void *lpbuffer = malloc(isize);
65     return lpbuffer;
66 }
67
68 /* Optional Memory de-allocation function */
69 static void __stdcall adl_main_memory_free(void** lpbuffer)
70 {
71         if (*lpbuffer) {
72                 free(*lpbuffer);
73                 *lpbuffer = NULL;
74         }
75 }
76
77 static void *getprocaddress(void * plibrary, const char * name)
78 {
79     return dlsym(plibrary, name);
80 }
81
82 /*
83   Returns the temperature (Celcius) of an AMD/Ati GPU.
84 */
85 static int get_temp(struct psensor *sensor)
86 {
87         ADLTemperature temperature;
88
89         temperature.iSize = sizeof(ADLTemperature);
90         temperature.iTemperature = -273;
91         if (ADL_OK != adl_overdrive5_temperature_get(sensor->amd_id,
92                  0, &temperature))
93                 return -273;
94
95         return temperature.iTemperature/1000;
96 }
97
98 static int get_fanspeed(struct psensor *sensor)
99 {
100         ADLFanSpeedValue fanspeedvalue;
101
102         fanspeedvalue.iSize = sizeof(ADLFanSpeedValue);
103         fanspeedvalue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM;
104         fanspeedvalue.iFanSpeed = -1;
105         if (ADL_OK != adl_overdrive5_fanspeed_get(sensor->amd_id,
106                  0, &fanspeedvalue))
107                 return -1;
108
109         return fanspeedvalue.iFanSpeed;
110 }
111
112 static struct psensor *create_sensor(int id, int values_len)
113 {
114         char name[200];
115         char *sid;
116         int sensor_type;
117
118         struct psensor *s;
119
120         if (id & 1) {/* odd number ids represent fan sensors */
121                 id = id >> 1;
122                 sprintf(name, "GPU%dfan", id);
123                 sensor_type = SENSOR_TYPE_AMD_FAN;
124         } else {/* even number ids represent temperature sensors */
125                 id = id >> 1;
126                 sprintf(name, "GPU%dtemp", id);
127                 sensor_type = SENSOR_TYPE_AMD_TEMP;
128         }
129
130         sid = malloc(strlen("AMD") + 1 + strlen(name) + 1);
131         sprintf(sid, "AMD %s", name);
132
133         s = psensor_create(sid, strdup(name),
134                            sensor_type, values_len);
135
136         s->amd_id = active_amd_adapters[id];
137
138         return s;
139 }
140
141 /*
142   Returns the number of AMD/Ati GPU sensors (temperature and fan speed).
143
144   Return 0 if no AMD/Ati gpus or cannot get information.
145 */
146 static int init()
147 {
148         LPAdapterInfo lpadapterinfo = NULL;
149         int  i, inumberadapters, inumberadaptersactive = 0;
150         int lpstatus, iadapterindex;
151
152         hdll = NULL;
153         adl_main_control_done = 0;
154         active_amd_adapters = NULL;
155         hdll = dlopen("libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL);
156
157
158         if (!hdll) {
159                 fprintf(stderr,
160                         _("ERROR: ADL library not found!\n"));
161                 return 0;
162         }
163
164         adl_main_control_create = (ADL_MAIN_CONTROL_CREATE)
165                  getprocaddress(hdll, "ADL_Main_Control_Create");
166         adl_main_control_destroy = (ADL_MAIN_CONTROL_DESTROY)
167                  getprocaddress(hdll, "ADL_Main_Control_Destroy");
168         adl_adapter_numberofadapters_get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET)
169                  getprocaddress(hdll, "ADL_Adapter_NumberOfAdapters_Get");
170         adl_adapter_adapterinfo_get = (ADL_ADAPTER_ADAPTERINFO_GET)
171                  getprocaddress(hdll, "ADL_Adapter_AdapterInfo_Get");
172         adl_adapter_active_get = (ADL_ADAPTER_ACTIVE_GET)
173                  getprocaddress(hdll, "ADL_Adapter_Active_Get");
174         adl_overdrive5_temperature_get = (ADL_OVERDRIVE5_TEMPERATURE_GET)
175                  getprocaddress(hdll, "ADL_Overdrive5_Temperature_Get");
176         adl_overdrive5_fanspeed_get = (ADL_OVERDRIVE5_FANSPEED_GET)
177                  getprocaddress(hdll, "ADL_Overdrive5_FanSpeed_Get");
178         if (!adl_main_control_create ||
179                 !adl_main_control_destroy ||
180                 !adl_adapter_numberofadapters_get ||
181                 !adl_adapter_adapterinfo_get ||
182                 !adl_overdrive5_temperature_get ||
183                 !adl_overdrive5_fanspeed_get) {
184                 fprintf(stderr,
185                         _("ERROR: ADL's API is missing!\n"));
186                 return 0;
187         }
188
189         /* Initialize ADL. The second parameter is 1, which means:
190            retrieve adapter information only for adapters that
191            are physically present and enabled in the system */
192         if (ADL_OK != adl_main_control_create(adl_main_memory_alloc, 1)) {
193                 fprintf(stderr,
194                         _("ERROR: ADL Initialization Error!\n"));
195                 return 0;
196         }
197         adl_main_control_done = 1;
198
199         /* Obtain the number of adapters for the system */
200         if (ADL_OK != adl_adapter_numberofadapters_get(&inumberadapters)) {
201                 fprintf(stderr,
202                         _("ERROR: Cannot get the number of adapters!\n"));
203                 return 0;
204         }
205
206         if (!inumberadapters)
207                 return 0;
208
209     lpadapterinfo = malloc(sizeof(AdapterInfo) * inumberadapters);
210     memset(lpadapterinfo, '\0', sizeof(AdapterInfo) * inumberadapters);
211
212     /* Get the AdapterInfo structure for all adapters in the system */
213     adl_adapter_adapterinfo_get(lpadapterinfo,
214                  sizeof(AdapterInfo) * inumberadapters);
215
216     /* Repeat for all available adapters in the system */
217     for (i = 0; i < inumberadapters; i++) {
218                 iadapterindex = lpadapterinfo[i].iAdapterIndex;
219                 if (ADL_OK != adl_adapter_active_get(iadapterindex, &lpstatus))
220                         continue;
221                 if (lpstatus != ADL_TRUE)
222                         /* count only if the adapter is active */
223                         continue;
224
225                 if (!active_amd_adapters) {
226                         active_amd_adapters = (int *) malloc(sizeof(int));
227                         inumberadaptersactive = 1;
228                 } else {
229                         ++inumberadaptersactive;
230                         active_amd_adapters =
231                                  (int *) realloc(active_amd_adapters,
232                                  sizeof(int)*inumberadaptersactive);
233                 }
234                 active_amd_adapters[inumberadaptersactive-1] = iadapterindex;
235     }
236
237     adl_main_memory_free((void **) &lpadapterinfo);
238
239         /* Each Adapter has one GPU temperature
240          sensor and one fan control sensor */
241         return 2*inumberadaptersactive;
242 }
243
244 void amd_psensor_list_update(struct psensor **sensors)
245 {
246         struct psensor **ss, *s;
247
248         ss = sensors;
249         while (*ss) {
250                 s = *ss;
251
252                 if (s->type == SENSOR_TYPE_AMD_TEMP)
253                         psensor_set_current_value(s, get_temp(s));
254                 else if (s->type == SENSOR_TYPE_AMD_FAN)
255                         psensor_set_current_value(s, get_fanspeed(s));
256
257                 ss++;
258         }
259 }
260
261 struct psensor * *
262 amd_psensor_list_add(struct psensor **sensors, int values_len)
263 {
264         int i, n;
265         struct psensor **tmp, **ss, *s;
266
267         n = init();
268
269         ss = sensors;
270         for (i = 0; i < n; i++) {
271                 s = create_sensor(i, values_len);
272
273                 tmp = psensor_list_add(ss, s);
274
275                 if (ss != tmp)
276                         free(ss);
277
278                 ss = tmp;
279         }
280
281         if (active_amd_adapters) {
282                 free(active_amd_adapters);
283                 active_amd_adapters = NULL;
284         }
285
286         return ss;
287 }
288
289 void amd_cleanup()
290 {
291         if (hdll) {
292                 if (adl_main_control_done)
293                         adl_main_control_destroy();
294                 dlclose(hdll);
295         }
296         if (active_amd_adapters) {
297                 free(active_amd_adapters);
298                 active_amd_adapters = NULL;
299         }
300 }