normalize amd id
[psensor.git] / src / lib / amd.c
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 <dlfcn.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <adl_sdk.h>
37
38 #include "psensor.h"
39
40 typedef int (*ADL_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int);
41 typedef int (*ADL_MAIN_CONTROL_DESTROY)();
42 typedef int (*ADL_ADAPTER_NUMBEROFADAPTERS_GET) (int *);
43 typedef int (*ADL_ADAPTER_ADAPTERINFO_GET) (LPAdapterInfo, int);
44 typedef int (*ADL_ADAPTER_ACTIVE_GET) (int, int*);
45 typedef int (*ADL_OVERDRIVE5_TEMPERATURE_GET) (int, int, ADLTemperature*);
46 typedef int (*ADL_OVERDRIVE5_FANSPEED_GET) (int, int, ADLFanSpeedValue*);
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_OVERDRIVE5_TEMPERATURE_GET     adl_overdrive5_temperature_get;
54 static ADL_OVERDRIVE5_FANSPEED_GET        adl_overdrive5_fanspeed_get;
55
56 static void *hdll;
57 static int adl_main_control_done;
58 static int *active_amd_adapters;
59
60 /* Memory allocation function */
61 static void __stdcall *adl_main_memory_alloc(int isize)
62 {
63     void *lpbuffer = malloc(isize);
64     return lpbuffer;
65 }
66
67 /* Optional Memory de-allocation function */
68 static void __stdcall adl_main_memory_free(void **lpbuffer)
69 {
70         if (*lpbuffer) {
71                 free(*lpbuffer);
72                 *lpbuffer = NULL;
73         }
74 }
75
76 static void *getprocaddress(void *plibrary, const char * name)
77 {
78     return dlsym(plibrary, name);
79 }
80
81 /*
82   Returns the temperature (Celcius) of an AMD/Ati GPU.
83 */
84 static double get_temp(struct psensor *sensor)
85 {
86         ADLTemperature temperature;
87
88         temperature.iSize = sizeof(ADLTemperature);
89         temperature.iTemperature = -273;
90         if (ADL_OK != adl_overdrive5_temperature_get(sensor->amd_id,
91                  0, &temperature))
92                 return UNKNOWN_DBL_VALUE;
93
94         return temperature.iTemperature/1000;
95 }
96
97 static double get_fanspeed(struct psensor *sensor)
98 {
99         ADLFanSpeedValue fanspeedvalue;
100
101         fanspeedvalue.iSize = sizeof(ADLFanSpeedValue);
102         fanspeedvalue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM;
103         fanspeedvalue.iFanSpeed = -1;
104         if (ADL_OK != adl_overdrive5_fanspeed_get(sensor->amd_id,
105                  0, &fanspeedvalue))
106                 return UNKNOWN_DBL_VALUE;
107
108         return fanspeedvalue.iFanSpeed;
109 }
110
111 static struct psensor *create_sensor(int id, int values_len)
112 {
113         char name[200];
114         char *sid;
115         int sensor_type;
116
117         struct psensor *s;
118
119         if (id & 1) {/* odd number ids represent fan sensors */
120                 id = id >> 1;
121                 sprintf(name, "GPU%dfan", id);
122                 sensor_type = SENSOR_TYPE_AMD_FAN;
123         } else {/* even number ids represent temperature sensors */
124                 id = id >> 1;
125                 sprintf(name, "GPU%dtemp", id);
126                 sensor_type = SENSOR_TYPE_AMD_TEMP;
127         }
128
129         sid = malloc(strlen("amd") + 1 + strlen(name) + 1);
130         sprintf(sid, "amd %s", name);
131
132         s = psensor_create(sid, strdup(name),
133                            sensor_type, values_len);
134
135         s->amd_id = active_amd_adapters[id];
136
137         return s;
138 }
139
140 /*
141   Returns the number of AMD/Ati GPU sensors (temperature and fan
142   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         if (!hdll) {
158                 fprintf(stderr,
159                         _("ERROR: ADL library not found!\n"));
160                 return 0;
161         }
162
163         adl_main_control_create = (ADL_MAIN_CONTROL_CREATE)
164                  getprocaddress(hdll, "ADL_Main_Control_Create");
165         adl_main_control_destroy = (ADL_MAIN_CONTROL_DESTROY)
166                  getprocaddress(hdll, "ADL_Main_Control_Destroy");
167         adl_adapter_numberofadapters_get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET)
168                  getprocaddress(hdll, "ADL_Adapter_NumberOfAdapters_Get");
169         adl_adapter_adapterinfo_get = (ADL_ADAPTER_ADAPTERINFO_GET)
170                  getprocaddress(hdll, "ADL_Adapter_AdapterInfo_Get");
171         adl_adapter_active_get = (ADL_ADAPTER_ACTIVE_GET)
172                  getprocaddress(hdll, "ADL_Adapter_Active_Get");
173         adl_overdrive5_temperature_get = (ADL_OVERDRIVE5_TEMPERATURE_GET)
174                  getprocaddress(hdll, "ADL_Overdrive5_Temperature_Get");
175         adl_overdrive5_fanspeed_get = (ADL_OVERDRIVE5_FANSPEED_GET)
176                  getprocaddress(hdll, "ADL_Overdrive5_FanSpeed_Get");
177         if (!adl_main_control_create ||
178                 !adl_main_control_destroy ||
179                 !adl_adapter_numberofadapters_get ||
180                 !adl_adapter_adapterinfo_get ||
181                 !adl_overdrive5_temperature_get ||
182                 !adl_overdrive5_fanspeed_get) {
183                 fprintf(stderr,
184                         _("ERROR: ADL's API is missing!\n"));
185                 return 0;
186         }
187
188         /* Initialize ADL. The second parameter is 1, which means:
189            retrieve adapter information only for adapters that
190            are physically present and enabled in the system */
191         if (ADL_OK != adl_main_control_create(adl_main_memory_alloc, 1)) {
192                 fprintf(stderr,
193                         _("ERROR: ADL Initialization Error!\n"));
194                 return 0;
195         }
196         adl_main_control_done = 1;
197
198         /* Obtain the number of adapters for the system */
199         if (ADL_OK != adl_adapter_numberofadapters_get(&inumberadapters)) {
200                 fprintf(stderr,
201                         _("ERROR: Cannot get the number of adapters!\n"));
202                 return 0;
203         }
204
205         if (!inumberadapters)
206                 return 0;
207
208         lpadapterinfo = malloc(sizeof(AdapterInfo) * inumberadapters);
209         memset(lpadapterinfo, '\0', sizeof(AdapterInfo) * inumberadapters);
210
211         /* Get the AdapterInfo structure for all adapters in the system */
212         adl_adapter_adapterinfo_get(lpadapterinfo,
213                                     sizeof(AdapterInfo) * inumberadapters);
214
215         /* Repeat for all available adapters in the system */
216         for (i = 0; i < inumberadapters; i++) {
217
218                 iadapterindex = lpadapterinfo[i].iAdapterIndex;
219
220                 if (ADL_OK != adl_adapter_active_get(iadapterindex, &lpstatus))
221                         continue;
222                 if (lpstatus != ADL_TRUE)
223                         /* count only if the adapter is active */
224                         continue;
225
226                 if (!active_amd_adapters) {
227                         active_amd_adapters = (int *) malloc(sizeof(int));
228                         inumberadaptersactive = 1;
229                 } else {
230                         ++inumberadaptersactive;
231                         active_amd_adapters = (int *)realloc
232                                 (active_amd_adapters,
233                                  sizeof(int)*inumberadaptersactive);
234                 }
235                 active_amd_adapters[inumberadaptersactive-1] = iadapterindex;
236         }
237
238         adl_main_memory_free((void **) &lpadapterinfo);
239
240         /* Each Adapter has one GPU temperature sensor and one fan
241            control sensor */
242         return 2*inumberadaptersactive;
243 }
244
245 void amd_psensor_list_update(struct psensor **sensors)
246 {
247         struct psensor **ss, *s;
248
249         ss = sensors;
250         while (*ss) {
251                 s = *ss;
252
253                 if (s->type == SENSOR_TYPE_AMD_TEMP)
254                         psensor_set_current_value(s, get_temp(s));
255                 else if (s->type == SENSOR_TYPE_AMD_FAN)
256                         psensor_set_current_value(s, get_fanspeed(s));
257
258                 ss++;
259         }
260 }
261
262 struct psensor * *
263 amd_psensor_list_add(struct psensor **sensors, int values_len)
264 {
265         int i, n;
266         struct psensor **tmp, **ss, *s;
267
268         n = init();
269
270         ss = sensors;
271         for (i = 0; i < n; i++) {
272                 s = create_sensor(i, values_len);
273
274                 tmp = psensor_list_add(ss, s);
275
276                 if (ss != tmp)
277                         free(ss);
278
279                 ss = tmp;
280         }
281
282         return ss;
283 }
284
285 void amd_cleanup()
286 {
287         if (hdll) {
288                 if (adl_main_control_done)
289                         adl_main_control_destroy();
290                 dlclose(hdll);
291         }
292
293         if (active_amd_adapters) {
294                 free(active_amd_adapters);
295                 active_amd_adapters = NULL;
296         }
297 }