source: branches/branch-1-0/src/helpers/acpih.c@ 472

Last change on this file since 472 was 443, checked in by rlwalsh, 6 years ago

acpih: add CPU temperature support; update battery support
patch from David Azarewicz

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 15.0 KB
Line 
1
2/*
3 *@@sourcefile apcih.c:
4 * contains helpers for accessing ACPI.
5 *
6 * Usage: All OS/2 programs.
7 *
8 * Function prefixes:
9 * -- acpih* ACPI helper functions
10 *
11 * Note: Version numbering in this file relates to XWorkplace version
12 * numbering.
13 *
14 *@@header "helpers\acpih.h"
15 *@@added V1.0.5 (2006-06-26) [pr]
16 */
17
18/*
19 * Copyright (C) 2006-2014 Paul Ratcliffe.
20 * This file is part of the "XWorkplace helpers" source package.
21 * This is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published
23 * by the Free Software Foundation, in version 2 as it comes in the
24 * "COPYING" file of the XWorkplace main distribution.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 */
30
31#define OS2EMX_PLAIN_CHAR
32 // this is needed for "os2emx.h"; if this is defined,
33 // emx will define PSZ as _signed_ char, otherwise
34 // as unsigned char
35
36#define INCL_DOSMODULEMGR
37#define INCL_DOSERRORS
38#include <os2.h>
39
40#include "setup.h" // code generation and debugging options
41
42#include "helpers\apmh.h" // @@added V1.0.9 (2012-02-20) [slevine]
43#include "helpers\acpih.h"
44#include "helpers\standards.h"
45
46/* ******************************************************************
47 *
48 * Globals
49 *
50 ********************************************************************/
51
52HMODULE G_hmodACPI = NULLHANDLE;
53ULONG G_ulCount = 0;
54// @@added V1.0.9 (2012-02-20) [slevine]: additional ACPI support, code from David Azarewicz
55ACPI_HANDLE G_ahAC = 0;
56ACPI_HANDLE G_ahTemp = 0;
57ULONG G_uiTempWalked = 0;
58#define MAX_BATTERY_COUNT 4
59struct _gahbat_ {
60 ACPI_HANDLE ahBat;
61 ULONG ulLastFull;
62} G_Bat[MAX_BATTERY_COUNT];
63ULONG G_uiBatteryCount = 0;
64ULONG G_uiAlreadyWalked = 0;
65
66ACPITKSTARTAPI *pAcpiStartApi = NULL;
67ACPITKENDAPI *pAcpiEndApi = NULL;
68ACPITKGOTOSLEEP *pAcpiGoToSleep = NULL;
69
70ACPITKGETOBJECTINFOALLOC *pAcpiTkGetObjectInfoAlloc = NULL;
71ACPITKGETHANDLE *pAcpiTkGetHandle = NULL;
72ACPITKOSFREE *pAcpiTkOsFree = NULL;
73ACPITKWALKNAMESPACE *pAcpiTkWalkNamespace = NULL;
74ACPITKEVALUATEOBJECT *pAcpiTkEvaluateObject = NULL;
75// @@added V1.0.9 (2012-12-10) [slevine]: additional ACPI support
76ACPITKPREPARETOSLEEP *pAcpiTkPrepareToSleep = NULL;
77
78/*
79 *@@category: Helpers\Control program helpers\ACPI
80 * See acpih.c.
81 */
82
83/*
84 *@@ acpihOpen:
85 * resolves the ACPI entrypoints and loads the ACPI DLL.
86 *
87 *@@changed V1.0.10 (2014-08-30) [dazarewicz]: Call ACPI start API func.
88 */
89
90APIRET acpihOpen(ACPI_API_HANDLE *phACPI)
91{
92 APIRET arc = NO_ERROR;
93
94 if (!G_hmodACPI)
95 {
96 if (!(arc = DosLoadModule(NULL,
97 0,
98 "ACPI32",
99 &G_hmodACPI)))
100 {
101 arc = DosQueryProcAddr(G_hmodACPI,
102 ORD_ACPITKSTARTAPI,
103 NULL,
104 (PFN *) &pAcpiStartApi);
105 if (!arc)
106 arc = DosQueryProcAddr(G_hmodACPI,
107 ORD_ACPITKENDAPI,
108 NULL,
109 (PFN *) &pAcpiEndApi);
110
111 if (!arc)
112 arc = DosQueryProcAddr(G_hmodACPI,
113 ORD_ACPITKGOTOSLEEP,
114 NULL,
115 (PFN *) &pAcpiGoToSleep);
116 if (arc)
117 {
118 DosFreeModule(G_hmodACPI);
119 G_hmodACPI = NULLHANDLE;
120 pAcpiStartApi = NULL;
121 pAcpiEndApi = NULL;
122 pAcpiGoToSleep = NULL;
123 return(arc);
124 }
125
126 // @@added V1.0.9 (2012-02-20) [slevine]: additional ACPI support, code from David Azarewicz
127 DosQueryProcAddr(G_hmodACPI, ORD_ACPITKGETOBJECTINFOALLOC3,
128 NULL, (PFN *) &pAcpiTkGetObjectInfoAlloc);
129 DosQueryProcAddr(G_hmodACPI, ORD_ACPITKGETHANDLE,
130 NULL, (PFN *) &pAcpiTkGetHandle);
131 DosQueryProcAddr(G_hmodACPI, ORD_ACPITKOSFREE,
132 NULL, (PFN *) &pAcpiTkOsFree);
133 DosQueryProcAddr(G_hmodACPI, ORD_ACPITKWALKNAMESPACE,
134 NULL, (PFN *) &pAcpiTkWalkNamespace);
135 DosQueryProcAddr(G_hmodACPI, ORD_ACPITKEVALUATEOBJECT,
136 NULL, (PFN *) &pAcpiTkEvaluateObject);
137 // @@added V1.0.9 (2012-12-10) [slevine]: additional ACPI support
138 DosQueryProcAddr(G_hmodACPI, ORD_ACPITKPREPARETOSLEEP,
139 NULL, (PFN *) &pAcpiTkPrepareToSleep);
140 }
141 }
142
143 if (arc)
144 return(arc);
145 else
146 {
147 G_ulCount++;
148
149 return(pAcpiStartApi(phACPI));
150 }
151}
152
153/*
154 *@@ acpihPrepareToSleep:
155 * Prepares the system to sleep or power off
156 *
157 *@@added V1.0.10 (2014-08-30) [dazarewicz]
158 */
159
160VOID acpihPrepareToSleep(UCHAR ucState)
161{
162 // @@added V1.0.9 (2012-12-10) [slevine]: use AcpiTkPrepareToSleep rather than workaround
163 /* This function does not exist in older versions of acpi
164 * As a result the shutdown attempt will usually hang because
165 * the required code has not been committed into memory.
166 */
167 if (pAcpiTkPrepareToSleep)
168 pAcpiTkPrepareToSleep(ucState);
169}
170
171/*
172 *@@ acpihClose:
173 * unloads the ACPI DLL.
174 */
175
176VOID acpihClose(ACPI_API_HANDLE *phACPI)
177{
178 if (pAcpiEndApi)
179 {
180 pAcpiEndApi(phACPI);
181 G_ulCount--;
182 }
183
184 if (!G_ulCount)
185 {
186 DosFreeModule(G_hmodACPI);
187 G_hmodACPI = NULLHANDLE;
188 pAcpiStartApi = NULL;
189 pAcpiEndApi = NULL;
190 pAcpiGoToSleep = NULL;
191 // @@added V1.0.9 (2012-12-10) [slevine]: additional ACPI support
192 pAcpiTkPrepareToSleep = NULL;
193 }
194}
195
196/*
197 *@@ acpihGoToSleep:
198 * changes the Power State.
199 */
200
201APIRET acpihGoToSleep(ACPI_API_HANDLE *phACPI, UCHAR ucState)
202{
203 if (pAcpiGoToSleep)
204 return(pAcpiGoToSleep(phACPI, ucState));
205 else
206 return(ERROR_PROTECTION_VIOLATION);
207}
208
209 // VAC 3.08 long long compatibility support
210#ifdef INCL_LONGLONG // VAC 3.6.5 - compiler supports long long
211#define OBJECT_VALUE(index) (Object[index].Integer.Value)
212#define OBJ_VALUE(index) (pObj[index].Integer.Value)
213#else // VAC 3.08 - compiler does not support long long
214#define OBJECT_VALUE(index) (Object[index].Integer.Value.ulLo)
215#define OBJ_VALUE(index) (pObj[index].Integer.Value.ulLo)
216#endif
217
218/**
219 *@@ AcpiCallbackWidget:
220 * ACPI callback helper for battery and power status queries.
221 * Code provided by David Azarewicz
222 *@@added V1.0.9 (2012-02-20) [slevine]: code from David Azarewicz
223 *@@changed V1.0.10 (2014-08-30) [dazarewicz]: release resources correctly
224 */
225
226ACPI_STATUS APIENTRY AcpiCallbackWidget( ACPI_HANDLE ObjHandle, UINT32 NestingLevel, void *Context, void **ReturnValue )
227{
228 ACPI_DEVICE_INFO *pDevInfo = NULL;
229 ACPI_BUFFER Buffer;
230 ACPI_OBJECT *pObj, Object[25];
231
232 if (pAcpiTkGetObjectInfoAlloc( ObjHandle, &pDevInfo ) != AE_OK)
233 return AE_OK;
234
235 do
236 {
237 if (pDevInfo->Type != ACPI_TYPE_DEVICE)
238 break;
239
240 if (!(pDevInfo->Valid & ACPI_VALID_HID))
241 break;
242
243 if (!pDevInfo->HardwareId.String)
244 break;
245
246 if (strncmp(pDevInfo->HardwareId.String, "ACPI0003", 8) == 0)
247 { /* AC Power */
248 if (pAcpiTkGetHandle(ObjHandle, "_PSR", &G_ahAC))
249 G_ahAC = 0;
250
251 break;
252 }
253
254 if (strncmp(pDevInfo->HardwareId.String, "PNP0C0A", 7) == 0)
255 { /* Smart battery */
256 if (G_uiBatteryCount < MAX_BATTERY_COUNT)
257 {
258 G_Bat[G_uiBatteryCount].ahBat = ObjHandle;
259 G_Bat[G_uiBatteryCount].ulLastFull = 0xffffffff;
260
261 /* Get static battery inFormation */
262 Buffer.Length = sizeof(Object);
263 Buffer.Pointer = Object;
264 if (pAcpiTkEvaluateObject(ObjHandle, "_BIX", NULL, &Buffer) == AE_OK)
265 {
266 pObj = Buffer.Pointer;
267 pObj = (ACPI_OBJECT *)pObj[0].Package.Elements;
268 G_Bat[G_uiBatteryCount].ulLastFull = (UINT32)OBJ_VALUE(3);
269 }
270 else
271 {
272 Buffer.Length = sizeof(Object);
273 Buffer.Pointer = Object;
274 if (pAcpiTkEvaluateObject(ObjHandle, "_BIF", NULL, &Buffer) == AE_OK)
275 {
276 pObj = Buffer.Pointer;
277 pObj = (ACPI_OBJECT *)pObj[0].Package.Elements;
278 G_Bat[G_uiBatteryCount].ulLastFull = (UINT32)OBJ_VALUE(2);
279 }
280 }
281
282 G_uiBatteryCount++;
283 }
284
285 break;
286 }
287 } while (0);
288
289 if (pDevInfo)
290 pAcpiTkOsFree(pDevInfo);
291
292 return AE_OK;
293}
294
295/**
296 *@@ acpihGetPowerStatus:
297 * Returns power and battery status in caller provided buffers.
298 * Returns zero if success, non-zero if fail.
299 * Code provided by David Azarewicz
300 *@@added V1.0.9 (2012-02-20) [slevine]: code from David Azarewicz
301 *@@changed V1.0.10 (2014-08-30) [dazarewicz]: tidies
302 */
303
304APIRET acpihGetPowerStatus(PAPM pApm, PBOOL pfChanged)
305{
306 ACPI_STATUS Status;
307 ACPI_BUFFER Result;
308 ACPI_OBJECT *pObj, Object[20];
309 UINT32 uiI;
310 ULONG ulTmp, BRemaining;
311 BOOL fChanged;
312
313 /* Make sure all the functions we need have valid pointers.
314 * @@added V1.0.9 (2012-02-25) [dazarewicz]: additional ACPI support
315 */
316 if ( (pAcpiTkWalkNamespace == NULL)
317 || (pAcpiTkGetObjectInfoAlloc == NULL)
318 || (pAcpiTkGetHandle == NULL)
319 || (pAcpiTkOsFree == NULL)
320 || (pAcpiTkEvaluateObject == NULL)
321 || (pApm == NULL)
322 )
323 return 1;
324
325 if (!G_uiAlreadyWalked)
326 {
327 Status = pAcpiTkWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
328 ACPI_UINT32_MAX, AcpiCallbackWidget,
329 pApm, NULL);
330 G_uiAlreadyWalked = 1;
331 }
332
333 fChanged = FALSE;
334
335 if (G_ahAC)
336 {
337 // Have _PSR
338 Result.Length = sizeof(Object);
339 Result.Pointer = Object;
340 Status = pAcpiTkEvaluateObject(G_ahAC, NULL, NULL, &Result);
341 if (Status != AE_OK)
342 ulTmp = 2; // assume on backup power
343 else if (Object[0].Type != ACPI_TYPE_INTEGER)
344 ulTmp = 2; // assume on backup power
345 else
346 ulTmp = (UINT32)OBJECT_VALUE(0);
347
348 if (pApm->fUsingAC != (BYTE) ulTmp)
349 {
350 pApm->fUsingAC = (BYTE) ulTmp;
351 fChanged = TRUE;
352 }
353 }
354
355 for (uiI=0; uiI < G_uiBatteryCount; uiI++)
356 {
357 if (G_Bat[uiI].ahBat == 0)
358 continue;
359 if (G_Bat[uiI].ulLastFull == 0xffffffff)
360 continue;
361
362 Result.Length = sizeof(Object);
363 Result.Pointer = Object;
364 // Get battery status
365 Status = pAcpiTkEvaluateObject(G_Bat[uiI].ahBat, "_BST", NULL, &Result);
366 if (Status != AE_OK)
367 {
368 G_Bat[uiI].ahBat = 0;
369 continue;
370 }
371
372 pObj = Result.Pointer;
373 pObj = (ACPI_OBJECT *)pObj[0].Package.Elements; // Battery status package
374 BRemaining = (UINT32)OBJ_VALUE(2);
375
376 // If battery units are mWh or mAh
377 // If not, it is a percentage
378 if (BRemaining == 0xffffffff)
379 continue;
380
381 if (BRemaining > (G_Bat[uiI].ulLastFull >> 1)) // > 50% is high. < 50% is low
382 ulTmp = 1; // High
383 else
384 ulTmp = 2; // Low
385
386 if (OBJ_VALUE(0) & 4)
387 ulTmp = 2; // Critical
388
389 // If battery charging - it can't be critical
390 if (OBJ_VALUE(0) & 2)
391 ulTmp = 3; // Charging
392
393 if (pApm->bBatteryStatus != ulTmp)
394 {
395 pApm->bBatteryStatus = (BYTE)ulTmp;
396 fChanged = TRUE;
397 }
398
399 ulTmp = 0;
400 if (G_Bat[uiI].ulLastFull)
401 ulTmp = (BRemaining*100) / G_Bat[uiI].ulLastFull;
402 if (ulTmp > 100)
403 ulTmp = 100;
404
405 if (pApm->bBatteryLife != ulTmp)
406 {
407 pApm->bBatteryLife = (BYTE) ulTmp;
408 fChanged = TRUE;
409 }
410 }
411
412 if (pfChanged)
413 *pfChanged = fChanged;
414
415 pApm->fAlreadyRead = FALSE;
416 return 0;
417}
418
419#undef OBJECT_VALUE
420#undef OBJ_VALUE
421
422/*
423 *@@ acpihHasBattery:
424 * quick'n'dirty helper which returns TRUE only
425 * if ACPI is supported on the system and the
426 * system actually has a battery (i.e. is a laptop).
427 * Code provided by David Azarewicz.
428 * @@added V1.0.9 (2012-02-20) [slevine]: code from David Azarewicz
429 */
430BOOL acpihHasBattery(VOID)
431{
432 BOOL brc = FALSE;
433 ACPI_API_HANDLE hACPI;
434 APM Apm;
435
436 if (!acpihOpen(&hACPI))
437 {
438 Apm.bBatteryStatus = 0xff;
439 if (!acpihGetPowerStatus(&Apm, NULL))
440 brc = (Apm.bBatteryStatus != 0xFF);
441
442 acpihClose(&hACPI);
443 }
444
445 return brc;
446}
447
448/**
449 *@@ AcpiCallbackTemperature:
450 * ACPI callback helper for CPU Temperature
451 * Code provided by David Azarewicz
452 */
453
454ACPI_STATUS APIENTRY AcpiCallbackTemperature( ACPI_HANDLE ObjHandle, UINT32 NestingLevel, void *Context, void **ReturnValue )
455{
456 if (G_ahTemp)
457 return AE_OK;
458
459 if (pAcpiTkGetHandle(ObjHandle, "_TMP", &G_ahTemp))
460 G_ahTemp = 0;
461
462 return AE_OK;
463}
464
465/*
466 *@@ acpihGetCpuTemp:
467 * Code provided by David Azarewicz.
468 */
469ULONG acpihGetCpuTemp(ACPI_API_HANDLE *phACPI)
470{
471 ACPI_STATUS Status;
472 ACPI_BUFFER Result;
473 ACPI_OBJECT Object;
474 ULONG ulTemp = 0;
475
476 /* Make sure all the functions we need have valid pointers. */
477 if ( (pAcpiTkWalkNamespace == NULL)
478 || (pAcpiTkGetHandle == NULL)
479 || (pAcpiTkEvaluateObject == NULL)
480 )
481 return 1;
482
483 if (!G_uiTempWalked)
484 {
485 Status = pAcpiTkWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
486 ACPI_UINT32_MAX, AcpiCallbackTemperature,
487 NULL, NULL);
488 G_uiTempWalked = 1;
489 }
490
491 if (G_ahTemp)
492 {
493 Result.Length = sizeof(Object);
494 Result.Pointer = &Object;
495 Status = pAcpiTkEvaluateObject(G_ahTemp, NULL, NULL, &Result);
496 if (Status == AE_OK)
497 ulTemp = (Object.Integer.Value - 2732) / 10;
498 }
499
500 return ulTemp;
501}
502
Note: See TracBrowser for help on using the repository browser.