/* *@@sourcefile apcih.c: * contains helpers for accessing ACPI. * * Usage: All OS/2 programs. * * Function prefixes: * -- acpih* ACPI helper functions * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@header "helpers\acpih.h" *@@added V1.0.5 (2006-06-26) [pr] */ /* * Copyright (C) 2006-2014 Paul Ratcliffe. * This file is part of the "XWorkplace helpers" source package. * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, in version 2 as it comes in the * "COPYING" file of the XWorkplace main distribution. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSMODULEMGR #define INCL_DOSERRORS #include #include "setup.h" // code generation and debugging options #include "helpers\apmh.h" // @@added V1.0.9 (2012-02-20) [slevine] #include "helpers\acpih.h" #include "helpers\standards.h" /* ****************************************************************** * * Globals * ********************************************************************/ HMODULE G_hmodACPI = NULLHANDLE; ULONG G_ulCount = 0; // @@added V1.0.9 (2012-02-20) [slevine]: additional ACPI support, code from David Azarewicz ACPI_HANDLE G_ahAC = 0; #define MAX_BATTERY_COUNT 4 ACPI_HANDLE G_ahBat[MAX_BATTERY_COUNT]; ULONG G_uiBatteryCount = 0; ULONG G_uiAlreadyWalked = 0; ACPITKSTARTAPI *pAcpiStartApi = NULL; ACPITKENDAPI *pAcpiEndApi = NULL; ACPITKGOTOSLEEP *pAcpiGoToSleep = NULL; ACPITKGETOBJECTINFOALLOC *pAcpiTkGetObjectInfoAlloc = NULL; ACPITKGETHANDLE *pAcpiTkGetHandle = NULL; ACPITKOSFREE *pAcpiTkOsFree = NULL; ACPITKWALKNAMESPACE *pAcpiTkWalkNamespace = NULL; ACPITKEVALUATEOBJECT *pAcpiTkEvaluateObject = NULL; // @@added V1.0.9 (2012-12-10) [slevine]: additional ACPI support ACPITKPREPARETOSLEEP *pAcpiTkPrepareToSleep = NULL; /* *@@category: Helpers\Control program helpers\ACPI * See acpih.c. */ /* *@@ acpihOpen: * resolves the ACPI entrypoints and loads the ACPI DLL. * *@@changed V1.0.10 (2014-08-30) [dazarewicz]: Call ACPI start API func. */ APIRET acpihOpen(ACPI_API_HANDLE *phACPI) { APIRET arc = NO_ERROR; if (!G_hmodACPI) { if (!(arc = DosLoadModule(NULL, 0, "ACPI32", &G_hmodACPI))) { arc = DosQueryProcAddr(G_hmodACPI, ORD_ACPITKSTARTAPI, NULL, (PFN *) &pAcpiStartApi); if (!arc) arc = DosQueryProcAddr(G_hmodACPI, ORD_ACPITKENDAPI, NULL, (PFN *) &pAcpiEndApi); if (!arc) arc = DosQueryProcAddr(G_hmodACPI, ORD_ACPITKGOTOSLEEP, NULL, (PFN *) &pAcpiGoToSleep); if (arc) { DosFreeModule(G_hmodACPI); G_hmodACPI = NULLHANDLE; pAcpiStartApi = NULL; pAcpiEndApi = NULL; pAcpiGoToSleep = NULL; return(arc); } // @@added V1.0.9 (2012-02-20) [slevine]: additional ACPI support, code from David Azarewicz DosQueryProcAddr(G_hmodACPI, ORD_ACPITKGETOBJECTINFOALLOC3, NULL, (PFN *) &pAcpiTkGetObjectInfoAlloc); DosQueryProcAddr(G_hmodACPI, ORD_ACPITKGETHANDLE, NULL, (PFN *) &pAcpiTkGetHandle); DosQueryProcAddr(G_hmodACPI, ORD_ACPITKOSFREE, NULL, (PFN *) &pAcpiTkOsFree); DosQueryProcAddr(G_hmodACPI, ORD_ACPITKWALKNAMESPACE, NULL, (PFN *) &pAcpiTkWalkNamespace); DosQueryProcAddr(G_hmodACPI, ORD_ACPITKEVALUATEOBJECT, NULL, (PFN *) &pAcpiTkEvaluateObject); // @@added V1.0.9 (2012-12-10) [slevine]: additional ACPI support DosQueryProcAddr(G_hmodACPI, ORD_ACPITKPREPARETOSLEEP, NULL, (PFN *) &pAcpiTkPrepareToSleep); } } if (arc) return(arc); else { G_ulCount++; return(pAcpiStartApi(phACPI)); } } /* *@@ acpihPrepareToSleep: * Prepares the system to sleep or power off * *@@added V1.0.10 (2014-08-30) [dazarewicz] */ VOID acpihPrepareToSleep(UCHAR ucState) { // @@added V1.0.9 (2012-12-10) [slevine]: use AcpiTkPrepareToSleep rather than workaround /* This function does not exist in older versions of acpi * As a result the shutdown attempt will usually hang because * the required code has not been committed into memory. */ if (pAcpiTkPrepareToSleep) pAcpiTkPrepareToSleep(ucState); } /* *@@ acpihClose: * unloads the ACPI DLL. */ VOID acpihClose(ACPI_API_HANDLE *phACPI) { if (pAcpiEndApi) { pAcpiEndApi(phACPI); G_ulCount--; } if (!G_ulCount) { DosFreeModule(G_hmodACPI); G_hmodACPI = NULLHANDLE; pAcpiStartApi = NULL; pAcpiEndApi = NULL; pAcpiGoToSleep = NULL; // @@added V1.0.9 (2012-12-10) [slevine]: additional ACPI support pAcpiTkPrepareToSleep = NULL; } } /* *@@ acpihGoToSleep: * changes the Power State. */ APIRET acpihGoToSleep(ACPI_API_HANDLE *phACPI, UCHAR ucState) { if (pAcpiGoToSleep) return(pAcpiGoToSleep(phACPI, ucState)); else return(ERROR_PROTECTION_VIOLATION); } /** *@@ AcpiCallbackWidget: * ACPI callback helper for battery and power status queries. * Code provided by David Azarewicz *@@added V1.0.9 (2012-02-20) [slevine]: code from David Azarewicz *@@changed V1.0.10 (2014-08-30) [dazarewicz]: release resources correctly */ ACPI_STATUS APIENTRY AcpiCallbackWidget( ACPI_HANDLE ObjHandle, UINT32 NestingLevel, void *Context, void **ReturnValue ) { ACPI_DEVICE_INFO *pDevInfo = NULL; if (pAcpiTkGetObjectInfoAlloc( ObjHandle, &pDevInfo ) != AE_OK) return AE_OK; do { if (pDevInfo->Type != ACPI_TYPE_DEVICE) break; if (!(pDevInfo->Valid & ACPI_VALID_HID)) break; if (!pDevInfo->HardwareId.String) break; if (strncmp(pDevInfo->HardwareId.String, "ACPI0003", 8) == 0) { /* AC Power */ if (pAcpiTkGetHandle(ObjHandle, "_PSR", &G_ahAC)) G_ahAC = 0; break; } if (strncmp(pDevInfo->HardwareId.String, "PNP0C0A", 7) == 0) { /* Smart battery */ if (G_uiBatteryCount < MAX_BATTERY_COUNT) G_ahBat[G_uiBatteryCount++] = ObjHandle; break; } } while (0); if (pDevInfo) pAcpiTkOsFree(pDevInfo); return AE_OK; } /** *@@ acpihGetPowerStatus: * Returns power and battery status in caller provided buffers. * Returns zero if success, non-zero if fail. * Code provided by David Azarewicz *@@added V1.0.9 (2012-02-20) [slevine]: code from David Azarewicz *@@changed V1.0.10 (2014-08-30) [dazarewicz]: tidies */ APIRET acpihGetPowerStatus(PAPM pApm, PBOOL pfChanged) { ACPI_STATUS Status; ACPI_BUFFER Result; ACPI_OBJECT *Obj, Object[20]; UINT32 uiI; ULONG ulTmp, BRemaining, LastFull; BOOL fChanged; /* Make sure all the functions we need have valid pointers. * @@added V1.0.9 (2012-02-25) [dazarewicz]: additional ACPI support */ if ( (pAcpiTkWalkNamespace == NULL) || (pAcpiTkGetObjectInfoAlloc == NULL) || (pAcpiTkGetHandle == NULL) || (pAcpiTkOsFree == NULL) || (pAcpiTkEvaluateObject == NULL) || (pApm == NULL) ) return 1; if (!G_uiAlreadyWalked) { Status = pAcpiTkWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, AcpiCallbackWidget, pApm, NULL); G_uiAlreadyWalked = 1; } fChanged = FALSE; // VAC 3.08 long long compatibility support #ifdef INCL_LONGLONG // VAC 3.6.5 - compiler supports long long #define OBJECT_VALUE(index) (Object[index].Integer.Value) #define OBJ_VALUE(index) (Obj[index].Integer.Value) #else // VAC 3.08 - compiler does not support long long #define OBJECT_VALUE(index) (Object[index].Integer.Value.ulLo) #define OBJ_VALUE(index) (Obj[index].Integer.Value.ulLo) #endif if (G_ahAC) { // Have _PSR Result.Length = sizeof(Object); Result.Pointer = Object; Status = pAcpiTkEvaluateObject(G_ahAC, NULL, NULL, &Result); if (Status != AE_OK) ulTmp = 2; // assume on backup power else if (Object[0].Type != ACPI_TYPE_INTEGER) ulTmp = 2; // assume on backup power else ulTmp = (UINT32)OBJECT_VALUE(0); if (pApm->fUsingAC != (BYTE) ulTmp) { pApm->fUsingAC = (BYTE) ulTmp; fChanged = TRUE; } } for (uiI=0; uiI < G_uiBatteryCount; uiI++) { if (G_ahBat[uiI] == 0) continue; Result.Length = sizeof(Object); Result.Pointer = Object; // Get battery info Status = pAcpiTkEvaluateObject(G_ahBat[uiI], "_BIF", NULL, &Result); if (Status != AE_OK) { G_ahBat[uiI] = 0; continue; } Obj = Result.Pointer; Obj = (ACPI_OBJECT *)Obj[0].Package.Elements; // Battery info package LastFull = (UINT32)OBJ_VALUE(2); Result.Length = sizeof(Object); Result.Pointer = Object; // Get battery status Status = pAcpiTkEvaluateObject(G_ahBat[uiI], "_BST", NULL, &Result); if (Status != AE_OK) { G_ahBat[uiI] = 0; continue; } Obj = Result.Pointer; Obj = (ACPI_OBJECT *)Obj[0].Package.Elements; // Battery status package BRemaining = (UINT32)OBJ_VALUE(2); // If battery units are mWh or mAh // If not, it is a percentage if ( (LastFull != 0xffffffff) && (BRemaining != 0xffffffff) ) { if (BRemaining > (LastFull >> 1)) // > 50% is high. < 50% is low ulTmp = 1; // High else ulTmp = 2; // Low if (OBJ_VALUE(0) & 4) ulTmp = 2; // Critical // If battery charging - it can't be critical if (OBJ_VALUE(0) & 2) ulTmp = 3; // Charging if (pApm->bBatteryStatus != ulTmp) { pApm->bBatteryStatus = (BYTE)ulTmp; fChanged = TRUE; } ulTmp = 0; if (LastFull) ulTmp = (BRemaining*100) / LastFull; if (ulTmp > 100) ulTmp = 100; if (pApm->bBatteryLife != ulTmp) { pApm->bBatteryLife = (BYTE) ulTmp; fChanged = TRUE; } } } if (pfChanged) *pfChanged = fChanged; pApm->fAlreadyRead = FALSE; return 0; #undef OBJECT_VALUE #undef OBJ_VALUE } /* *@@ acpihHasBattery: * quick'n'dirty helper which returns TRUE only * if ACPI is supported on the system and the * system actually has a battery (i.e. is a laptop). * Code provided by David Azarewicz. * @@added V1.0.9 (2012-02-20) [slevine]: code from David Azarewicz */ BOOL acpihHasBattery(VOID) { BOOL brc = FALSE; ACPI_API_HANDLE hACPI; APM Apm; if (!acpihOpen(&hACPI)) { Apm.bBatteryStatus = 0xff; if (!acpihGetPowerStatus(&Apm, NULL)) brc = (Apm.bBatteryStatus != 0xFF); acpihClose(&hACPI); } return brc; }