[12642] | 1 | /* $Id: DrvACPI.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
|
---|
[1] | 2 | /** @file
|
---|
[12642] | 3 | * DrvACPI - ACPI Host Driver.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[106061] | 7 | * Copyright (C) 2006-2024 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[1] | 26 | */
|
---|
| 27 |
|
---|
[57358] | 28 |
|
---|
| 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[1] | 32 | #define LOG_GROUP LOG_GROUP_DRV_ACPI
|
---|
| 33 |
|
---|
[3666] | 34 | #ifdef RT_OS_WINDOWS
|
---|
[62679] | 35 | # include <iprt/win/windows.h>
|
---|
[1] | 36 | #endif
|
---|
| 37 |
|
---|
[35346] | 38 | #include <VBox/vmm/pdmdrv.h>
|
---|
[1] | 39 | #include <VBox/log.h>
|
---|
[29250] | 40 | #include <iprt/asm.h>
|
---|
[1] | 41 | #include <iprt/assert.h>
|
---|
| 42 | #include <iprt/string.h>
|
---|
[25966] | 43 | #include <iprt/uuid.h>
|
---|
[1] | 44 |
|
---|
[3666] | 45 | #ifdef RT_OS_LINUX
|
---|
[27264] | 46 | # include <iprt/critsect.h>
|
---|
| 47 | # include <iprt/dir.h>
|
---|
| 48 | # include <iprt/semaphore.h>
|
---|
| 49 | # include <iprt/stream.h>
|
---|
[1] | 50 | #endif
|
---|
| 51 |
|
---|
[14710] | 52 | #ifdef RT_OS_DARWIN
|
---|
| 53 | # include <Carbon/Carbon.h>
|
---|
| 54 | # include <IOKit/ps/IOPowerSources.h>
|
---|
| 55 | # include <IOKit/ps/IOPSKeys.h>
|
---|
[60956] | 56 | # undef PVM /* This still messed up in the 10.9 SDK. Sigh. */
|
---|
[14710] | 57 | #endif
|
---|
| 58 |
|
---|
[19967] | 59 | #ifdef RT_OS_FREEBSD
|
---|
| 60 | # include <sys/ioctl.h>
|
---|
| 61 | # include <dev/acpica/acpiio.h>
|
---|
| 62 | # include <sys/types.h>
|
---|
| 63 | # include <sys/sysctl.h>
|
---|
| 64 | # include <stdio.h>
|
---|
| 65 | # include <errno.h>
|
---|
| 66 | # include <fcntl.h>
|
---|
| 67 | # include <unistd.h>
|
---|
| 68 | #endif
|
---|
| 69 |
|
---|
[35353] | 70 | #include "VBoxDD.h"
|
---|
[1] | 71 |
|
---|
| 72 |
|
---|
[57358] | 73 | /*********************************************************************************************************************************
|
---|
| 74 | * Structures and Typedefs *
|
---|
| 75 | *********************************************************************************************************************************/
|
---|
[1] | 76 | /**
|
---|
| 77 | * ACPI driver instance data.
|
---|
[25966] | 78 | *
|
---|
| 79 | * @implements PDMIACPICONNECTOR
|
---|
[1] | 80 | */
|
---|
| 81 | typedef struct DRVACPI
|
---|
| 82 | {
|
---|
| 83 | /** The ACPI interface. */
|
---|
| 84 | PDMIACPICONNECTOR IACPIConnector;
|
---|
| 85 | /** The ACPI port interface. */
|
---|
| 86 | PPDMIACPIPORT pPort;
|
---|
| 87 | /** Pointer to the driver instance. */
|
---|
| 88 | PPDMDRVINS pDrvIns;
|
---|
[27264] | 89 |
|
---|
| 90 | #ifdef RT_OS_LINUX
|
---|
| 91 | /** The current power source. */
|
---|
| 92 | PDMACPIPOWERSOURCE enmPowerSource;
|
---|
| 93 | /** true = one or more batteries preset, false = no battery present. */
|
---|
| 94 | bool fBatteryPresent;
|
---|
[27326] | 95 | /** No need to RTThreadPoke the poller when set. */
|
---|
| 96 | bool volatile fDontPokePoller;
|
---|
[27264] | 97 | /** Remaining battery capacity. */
|
---|
| 98 | PDMACPIBATCAPACITY enmBatteryRemainingCapacity;
|
---|
| 99 | /** Battery state. */
|
---|
| 100 | PDMACPIBATSTATE enmBatteryState;
|
---|
| 101 | /** Preset battery charging/discharging rate. */
|
---|
| 102 | uint32_t u32BatteryPresentRate;
|
---|
| 103 | /** The poller thread. */
|
---|
| 104 | PPDMTHREAD pPollerThread;
|
---|
| 105 | /** Synchronize access to the above fields.
|
---|
[33540] | 106 | * XXX A spinlock is probably cheaper ... */
|
---|
[27264] | 107 | RTCRITSECT CritSect;
|
---|
| 108 | /** Event semaphore the poller thread is sleeping on. */
|
---|
| 109 | RTSEMEVENT hPollerSleepEvent;
|
---|
| 110 | #endif
|
---|
| 111 |
|
---|
[1] | 112 | } DRVACPI, *PDRVACPI;
|
---|
| 113 |
|
---|
| 114 |
|
---|
| 115 | /**
|
---|
[25966] | 116 | * @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
---|
[1] | 117 | */
|
---|
[25966] | 118 | static DECLCALLBACK(void *) drvACPIQueryInterface(PPDMIBASE pInterface, const char *pszIID)
|
---|
[1] | 119 | {
|
---|
| 120 | PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
|
---|
[25985] | 121 | PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
|
---|
[25966] | 122 |
|
---|
[25985] | 123 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
|
---|
[25984] | 124 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIACPICONNECTOR, &pThis->IACPIConnector);
|
---|
[25966] | 125 | return NULL;
|
---|
[1] | 126 | }
|
---|
| 127 |
|
---|
| 128 | /**
|
---|
| 129 | * Get the current power source of the host system.
|
---|
| 130 | *
|
---|
| 131 | * @returns status code
|
---|
| 132 | * @param pInterface Pointer to the interface structure containing the called function pointer.
|
---|
| 133 | * @param pPowerSource Pointer to the power source result variable.
|
---|
| 134 | */
|
---|
| 135 | static DECLCALLBACK(int) drvACPIQueryPowerSource(PPDMIACPICONNECTOR pInterface,
|
---|
| 136 | PDMACPIPOWERSOURCE *pPowerSource)
|
---|
| 137 | {
|
---|
[3666] | 138 | #if defined(RT_OS_WINDOWS)
|
---|
[62908] | 139 | RT_NOREF(pInterface);
|
---|
[1] | 140 | SYSTEM_POWER_STATUS powerStatus;
|
---|
| 141 | if (GetSystemPowerStatus(&powerStatus))
|
---|
| 142 | {
|
---|
| 143 | /* running on battery? */
|
---|
[25966] | 144 | if ( powerStatus.ACLineStatus == 0 /* Offline */
|
---|
| 145 | || powerStatus.ACLineStatus == 255 /* Unknown */
|
---|
| 146 | && (powerStatus.BatteryFlag & 15) /* high | low | critical | charging */
|
---|
| 147 | ) /** @todo why is 'charging' included in the flag test? Add parenthesis around the right bits so the code is clearer. */
|
---|
[1] | 148 | {
|
---|
| 149 | *pPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
|
---|
| 150 | }
|
---|
| 151 | /* running on AC link? */
|
---|
| 152 | else if (powerStatus.ACLineStatus == 1)
|
---|
| 153 | {
|
---|
| 154 | *pPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
| 155 | }
|
---|
| 156 | else
|
---|
| 157 | /* what the hell we're running on? */
|
---|
| 158 | {
|
---|
| 159 | *pPowerSource = PDM_ACPI_POWER_SOURCE_UNKNOWN;
|
---|
| 160 | }
|
---|
| 161 | }
|
---|
| 162 | else
|
---|
| 163 | {
|
---|
| 164 | AssertMsgFailed(("Could not determine system power status, error: 0x%x\n",
|
---|
| 165 | GetLastError()));
|
---|
| 166 | *pPowerSource = PDM_ACPI_POWER_SOURCE_UNKNOWN;
|
---|
| 167 | }
|
---|
| 168 |
|
---|
[27264] | 169 | #elif defined (RT_OS_LINUX)
|
---|
| 170 | PDRVACPI pThis = RT_FROM_MEMBER(pInterface, DRVACPI, IACPIConnector);
|
---|
| 171 | RTCritSectEnter(&pThis->CritSect);
|
---|
[27316] | 172 | *pPowerSource = pThis->enmPowerSource;
|
---|
[27264] | 173 | RTCritSectLeave(&pThis->CritSect);
|
---|
[1] | 174 |
|
---|
[27264] | 175 | #elif defined (RT_OS_DARWIN)
|
---|
[62908] | 176 | RT_NOREF(pInterface);
|
---|
[14710] | 177 | *pPowerSource = PDM_ACPI_POWER_SOURCE_UNKNOWN;
|
---|
| 178 |
|
---|
| 179 | CFTypeRef pBlob = IOPSCopyPowerSourcesInfo();
|
---|
| 180 | CFArrayRef pSources = IOPSCopyPowerSourcesList(pBlob);
|
---|
| 181 |
|
---|
| 182 | CFDictionaryRef pSource = NULL;
|
---|
| 183 | const void *psValue;
|
---|
[27264] | 184 | bool fResult;
|
---|
[14710] | 185 |
|
---|
| 186 | if (CFArrayGetCount(pSources) > 0)
|
---|
| 187 | {
|
---|
[27264] | 188 | for (int i = 0; i < CFArrayGetCount(pSources); ++i)
|
---|
[14710] | 189 | {
|
---|
| 190 | pSource = IOPSGetPowerSourceDescription(pBlob, CFArrayGetValueAtIndex(pSources, i));
|
---|
| 191 | /* If the source is empty skip over to the next one. */
|
---|
| 192 | if(!pSource)
|
---|
| 193 | continue;
|
---|
| 194 | /* Skip all power sources which are currently not present like a
|
---|
| 195 | * second battery. */
|
---|
| 196 | if (CFDictionaryGetValue(pSource, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse)
|
---|
| 197 | continue;
|
---|
| 198 | /* Only internal power types are of interest. */
|
---|
[27264] | 199 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSTransportTypeKey), &psValue);
|
---|
| 200 | if ( fResult
|
---|
| 201 | && CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSInternalType), 0) == kCFCompareEqualTo)
|
---|
[14710] | 202 | {
|
---|
| 203 | /* Check which power source we are connect on. */
|
---|
[27264] | 204 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSPowerSourceStateKey), &psValue);
|
---|
| 205 | if ( fResult
|
---|
| 206 | && CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSACPowerValue), 0) == kCFCompareEqualTo)
|
---|
[14710] | 207 | *pPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
[27264] | 208 | else if ( fResult
|
---|
| 209 | && CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo)
|
---|
[14710] | 210 | *pPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
|
---|
| 211 | }
|
---|
| 212 | }
|
---|
| 213 | }
|
---|
| 214 | CFRelease(pBlob);
|
---|
| 215 | CFRelease(pSources);
|
---|
[27264] | 216 |
|
---|
| 217 | #elif defined(RT_OS_FREEBSD)
|
---|
[62908] | 218 | RT_NOREF(pInterface);
|
---|
[19967] | 219 | int fAcLine = 0;
|
---|
| 220 | size_t cbParameter = sizeof(fAcLine);
|
---|
| 221 |
|
---|
[54962] | 222 | int rc = sysctlbyname("hw.acpi.acline", &fAcLine, &cbParameter, NULL, 0);
|
---|
[19967] | 223 |
|
---|
| 224 | if (!rc)
|
---|
| 225 | {
|
---|
| 226 | if (fAcLine == 1)
|
---|
| 227 | *pPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
| 228 | else if (fAcLine == 0)
|
---|
| 229 | *pPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
|
---|
| 230 | else
|
---|
| 231 | *pPowerSource = PDM_ACPI_POWER_SOURCE_UNKNOWN;
|
---|
| 232 | }
|
---|
| 233 | else
|
---|
| 234 | {
|
---|
| 235 | AssertMsg(errno == ENOENT, ("rc=%d (%s)\n", rc, strerror(errno)));
|
---|
| 236 | *pPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
| 237 | }
|
---|
| 238 | #else /* !RT_OS_FREEBSD either - what could this be? */
|
---|
[62908] | 239 | RT_NOREF(pInterface);
|
---|
[1] | 240 | *pPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
[27264] | 241 |
|
---|
| 242 | #endif /* !RT_OS_FREEBSD */
|
---|
[1] | 243 | return VINF_SUCCESS;
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | /**
|
---|
[62956] | 247 | * @interface_method_impl{PDMIACPICONNECTOR,pfnQueryBatteryStatus}
|
---|
[1] | 248 | */
|
---|
| 249 | static DECLCALLBACK(int) drvACPIQueryBatteryStatus(PPDMIACPICONNECTOR pInterface, bool *pfPresent,
|
---|
[61675] | 250 | PPDMACPIBATCAPACITY penmRemainingCapacity,
|
---|
| 251 | PPDMACPIBATSTATE penmBatteryState,
|
---|
| 252 | uint32_t *pu32PresentRate)
|
---|
[1] | 253 | {
|
---|
| 254 | /* default return values for all architectures */
|
---|
[62908] | 255 | *pfPresent = false; /* no battery present */
|
---|
[1] | 256 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGED;
|
---|
| 257 | *penmRemainingCapacity = PDM_ACPI_BAT_CAPACITY_UNKNOWN;
|
---|
[62908] | 258 | *pu32PresentRate = UINT32_MAX; /* present rate is unknown */
|
---|
[1] | 259 |
|
---|
[3666] | 260 | #if defined(RT_OS_WINDOWS)
|
---|
[62908] | 261 | RT_NOREF(pInterface);
|
---|
[1] | 262 | SYSTEM_POWER_STATUS powerStatus;
|
---|
| 263 | if (GetSystemPowerStatus(&powerStatus))
|
---|
| 264 | {
|
---|
| 265 | /* 128 means no battery present */
|
---|
| 266 | *pfPresent = !(powerStatus.BatteryFlag & 128);
|
---|
| 267 | /* just forward the value directly */
|
---|
| 268 | *penmRemainingCapacity = (PDMACPIBATCAPACITY)powerStatus.BatteryLifePercent;
|
---|
| 269 | /* we assume that we are discharging the battery if we are not on-line and
|
---|
| 270 | * not charge the battery */
|
---|
| 271 | uint32_t uBs = PDM_ACPI_BAT_STATE_CHARGED;
|
---|
| 272 | if (powerStatus.BatteryFlag & 8)
|
---|
[14464] | 273 | uBs = PDM_ACPI_BAT_STATE_CHARGING;
|
---|
| 274 | else if (powerStatus.ACLineStatus == 0 || powerStatus.ACLineStatus == 255)
|
---|
[1] | 275 | uBs = PDM_ACPI_BAT_STATE_DISCHARGING;
|
---|
| 276 | if (powerStatus.BatteryFlag & 4)
|
---|
| 277 | uBs |= PDM_ACPI_BAT_STATE_CRITICAL;
|
---|
| 278 | *penmBatteryState = (PDMACPIBATSTATE)uBs;
|
---|
| 279 | /* on Windows it is difficult to request the present charging/discharging rate */
|
---|
| 280 | }
|
---|
| 281 | else
|
---|
| 282 | {
|
---|
| 283 | AssertMsgFailed(("Could not determine system power status, error: 0x%x\n",
|
---|
[27327] | 284 | GetLastError()));
|
---|
[1] | 285 | }
|
---|
[27264] | 286 |
|
---|
[3666] | 287 | #elif defined(RT_OS_LINUX)
|
---|
[27264] | 288 | PDRVACPI pThis = RT_FROM_MEMBER(pInterface, DRVACPI, IACPIConnector);
|
---|
| 289 | RTCritSectEnter(&pThis->CritSect);
|
---|
| 290 | *pfPresent = pThis->fBatteryPresent;
|
---|
| 291 | *penmRemainingCapacity = pThis->enmBatteryRemainingCapacity;
|
---|
[27316] | 292 | *penmBatteryState = pThis->enmBatteryState;
|
---|
[27264] | 293 | *pu32PresentRate = pThis->u32BatteryPresentRate;
|
---|
| 294 | RTCritSectLeave(&pThis->CritSect);
|
---|
[1] | 295 |
|
---|
[14710] | 296 | #elif defined(RT_OS_DARWIN)
|
---|
[62908] | 297 | RT_NOREF(pInterface);
|
---|
[14710] | 298 | CFTypeRef pBlob = IOPSCopyPowerSourcesInfo();
|
---|
| 299 | CFArrayRef pSources = IOPSCopyPowerSourcesList(pBlob);
|
---|
| 300 |
|
---|
| 301 | CFDictionaryRef pSource = NULL;
|
---|
| 302 | const void *psValue;
|
---|
[27264] | 303 | bool fResult;
|
---|
[14710] | 304 |
|
---|
| 305 | if (CFArrayGetCount(pSources) > 0)
|
---|
| 306 | {
|
---|
[27264] | 307 | for (int i = 0; i < CFArrayGetCount(pSources); ++i)
|
---|
[14710] | 308 | {
|
---|
| 309 | pSource = IOPSGetPowerSourceDescription(pBlob, CFArrayGetValueAtIndex(pSources, i));
|
---|
| 310 | /* If the source is empty skip over to the next one. */
|
---|
| 311 | if(!pSource)
|
---|
| 312 | continue;
|
---|
| 313 | /* Skip all power sources which are currently not present like a
|
---|
| 314 | * second battery. */
|
---|
| 315 | if (CFDictionaryGetValue(pSource, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse)
|
---|
| 316 | continue;
|
---|
| 317 | /* Only internal power types are of interest. */
|
---|
[27264] | 318 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSTransportTypeKey), &psValue);
|
---|
| 319 | if ( fResult
|
---|
[27327] | 320 | && CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSInternalType), 0) == kCFCompareEqualTo)
|
---|
[14710] | 321 | {
|
---|
| 322 | PDMACPIPOWERSOURCE powerSource = PDM_ACPI_POWER_SOURCE_UNKNOWN;
|
---|
| 323 | /* First check which power source we are connect on. */
|
---|
[27264] | 324 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSPowerSourceStateKey), &psValue);
|
---|
| 325 | if ( fResult
|
---|
[27327] | 326 | && CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSACPowerValue), 0) == kCFCompareEqualTo)
|
---|
[14710] | 327 | powerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
[27264] | 328 | else if ( fResult
|
---|
[27327] | 329 | && CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo)
|
---|
[14710] | 330 | powerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
|
---|
| 331 |
|
---|
| 332 | /* At this point the power source is present. */
|
---|
| 333 | *pfPresent = true;
|
---|
| 334 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGED;
|
---|
| 335 |
|
---|
| 336 | int curCapacity = 0;
|
---|
| 337 | int maxCapacity = 1;
|
---|
| 338 | float remCapacity = 0.0f;
|
---|
| 339 |
|
---|
| 340 | /* Fetch the current capacity value of the power source */
|
---|
[27264] | 341 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSCurrentCapacityKey), &psValue);
|
---|
| 342 | if (fResult)
|
---|
[14710] | 343 | CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &curCapacity);
|
---|
| 344 | /* Fetch the maximum capacity value of the power source */
|
---|
[27264] | 345 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSMaxCapacityKey), &psValue);
|
---|
| 346 | if (fResult)
|
---|
[14710] | 347 | CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &maxCapacity);
|
---|
| 348 |
|
---|
| 349 | /* Calculate the remaining capacity in percent */
|
---|
| 350 | remCapacity = ((float)curCapacity/(float)maxCapacity * PDM_ACPI_BAT_CAPACITY_MAX);
|
---|
| 351 | *penmRemainingCapacity = (PDMACPIBATCAPACITY)remCapacity;
|
---|
| 352 |
|
---|
| 353 | if (powerSource == PDM_ACPI_POWER_SOURCE_BATTERY)
|
---|
| 354 | {
|
---|
| 355 | /* If we are on battery power we are discharging in every
|
---|
| 356 | * case */
|
---|
| 357 | *penmBatteryState = PDM_ACPI_BAT_STATE_DISCHARGING;
|
---|
| 358 | int timeToEmpty = -1;
|
---|
| 359 | /* Get the time till the battery source will be empty */
|
---|
[27264] | 360 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSTimeToEmptyKey), &psValue);
|
---|
| 361 | if (fResult)
|
---|
[14710] | 362 | CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &timeToEmpty);
|
---|
| 363 | if (timeToEmpty != -1)
|
---|
| 364 | /* 0...1000 */
|
---|
| 365 | *pu32PresentRate = (uint32_t)roundf((remCapacity / ((float)timeToEmpty/60.0)) * 10.0);
|
---|
| 366 | }
|
---|
| 367 |
|
---|
[27264] | 368 | if ( powerSource == PDM_ACPI_POWER_SOURCE_OUTLET
|
---|
[27327] | 369 | && CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSIsChargingKey), &psValue))
|
---|
[14710] | 370 | {
|
---|
| 371 | /* We are running on an AC power source, but we also have a
|
---|
| 372 | * battery power source present. */
|
---|
| 373 | if (CFBooleanGetValue((CFBooleanRef)psValue) > 0)
|
---|
| 374 | {
|
---|
| 375 | /* This means charging. */
|
---|
| 376 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGING;
|
---|
| 377 | int timeToFull = -1;
|
---|
| 378 | /* Get the time till the battery source will be charged */
|
---|
[27264] | 379 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSTimeToFullChargeKey), &psValue);
|
---|
| 380 | if (fResult)
|
---|
[14710] | 381 | CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &timeToFull);
|
---|
| 382 | if (timeToFull != -1)
|
---|
| 383 | /* 0...1000 */
|
---|
| 384 | *pu32PresentRate = (uint32_t)roundf((100.0-(float)remCapacity) / ((float)timeToFull/60.0)) * 10.0;
|
---|
| 385 | }
|
---|
| 386 | }
|
---|
| 387 |
|
---|
| 388 | /* Check for critical */
|
---|
| 389 | int criticalValue = 20;
|
---|
[27264] | 390 | fResult = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSDeadWarnLevelKey), &psValue);
|
---|
| 391 | if (fResult)
|
---|
[14710] | 392 | CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &criticalValue);
|
---|
| 393 | if (remCapacity < criticalValue)
|
---|
| 394 | *penmBatteryState = (PDMACPIBATSTATE)(*penmBatteryState | PDM_ACPI_BAT_STATE_CRITICAL);
|
---|
| 395 | }
|
---|
| 396 | }
|
---|
| 397 | }
|
---|
| 398 | CFRelease(pBlob);
|
---|
| 399 | CFRelease(pSources);
|
---|
[27264] | 400 |
|
---|
[19967] | 401 | #elif defined(RT_OS_FREEBSD)
|
---|
[62908] | 402 | RT_NOREF(pInterface);
|
---|
[19967] | 403 | /* We try to use /dev/acpi first and if that fails use the sysctls. */
|
---|
| 404 | bool fSuccess = true;
|
---|
| 405 | int FileAcpi = 0;
|
---|
| 406 | int rc = 0;
|
---|
| 407 |
|
---|
| 408 | FileAcpi = open("/dev/acpi", O_RDONLY);
|
---|
| 409 | if (FileAcpi != -1)
|
---|
| 410 | {
|
---|
| 411 | bool fMilliWatt;
|
---|
| 412 | union acpi_battery_ioctl_arg BatteryIo;
|
---|
| 413 |
|
---|
| 414 | memset(&BatteryIo, 0, sizeof(BatteryIo));
|
---|
| 415 | BatteryIo.unit = 0; /* Always use the first battery. */
|
---|
| 416 |
|
---|
| 417 | /* Determine the power units first. */
|
---|
| 418 | if (ioctl(FileAcpi, ACPIIO_BATT_GET_BIF, &BatteryIo) == -1)
|
---|
| 419 | fSuccess = false;
|
---|
| 420 | else
|
---|
| 421 | {
|
---|
| 422 | if (BatteryIo.bif.units == ACPI_BIF_UNITS_MW)
|
---|
| 423 | fMilliWatt = true;
|
---|
| 424 | else
|
---|
| 425 | fMilliWatt = false; /* mA */
|
---|
| 426 |
|
---|
| 427 | BatteryIo.unit = 0;
|
---|
| 428 | if (ioctl(FileAcpi, ACPIIO_BATT_GET_BATTINFO, &BatteryIo) == -1)
|
---|
| 429 | fSuccess = false;
|
---|
| 430 | else
|
---|
| 431 | {
|
---|
| 432 | if ((BatteryIo.battinfo.state & ACPI_BATT_STAT_NOT_PRESENT) == ACPI_BATT_STAT_NOT_PRESENT)
|
---|
| 433 | *pfPresent = false;
|
---|
| 434 | else
|
---|
| 435 | {
|
---|
| 436 | *pfPresent = true;
|
---|
| 437 |
|
---|
| 438 | if (BatteryIo.battinfo.state & ACPI_BATT_STAT_DISCHARG)
|
---|
| 439 | *penmBatteryState = PDM_ACPI_BAT_STATE_DISCHARGING;
|
---|
| 440 | else if (BatteryIo.battinfo.state & ACPI_BATT_STAT_CHARGING)
|
---|
| 441 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGING;
|
---|
| 442 | else
|
---|
| 443 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGED;
|
---|
| 444 |
|
---|
| 445 | if (BatteryIo.battinfo.state & ACPI_BATT_STAT_CRITICAL)
|
---|
| 446 | *penmBatteryState = (PDMACPIBATSTATE)(*penmBatteryState | PDM_ACPI_BAT_STATE_CRITICAL);
|
---|
| 447 | }
|
---|
| 448 |
|
---|
| 449 | if (BatteryIo.battinfo.cap != -1)
|
---|
| 450 | *penmRemainingCapacity = (PDMACPIBATCAPACITY)BatteryIo.battinfo.cap;
|
---|
| 451 |
|
---|
| 452 | BatteryIo.unit = 0;
|
---|
| 453 | if (ioctl(FileAcpi, ACPIIO_BATT_GET_BST, &BatteryIo) == 0)
|
---|
| 454 | {
|
---|
| 455 | /* The rate can be either mW or mA but the ACPI device wants mW. */
|
---|
| 456 | if (BatteryIo.bst.rate != 0xffffffff)
|
---|
| 457 | {
|
---|
| 458 | if (fMilliWatt)
|
---|
| 459 | *pu32PresentRate = BatteryIo.bst.rate;
|
---|
| 460 | else if (BatteryIo.bst.volt != 0xffffffff)
|
---|
| 461 | {
|
---|
| 462 | /*
|
---|
| 463 | * The rate is in mA so we have to convert it.
|
---|
| 464 | * The current power rate can be calculated with P = U * I
|
---|
| 465 | */
|
---|
[27264] | 466 | *pu32PresentRate = (uint32_t)( ( ((float)BatteryIo.bst.volt/1000.0)
|
---|
[27327] | 467 | * ((float)BatteryIo.bst.rate/1000.0))
|
---|
| 468 | * 1000.0);
|
---|
[19967] | 469 | }
|
---|
| 470 | }
|
---|
| 471 | }
|
---|
| 472 | }
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | close(FileAcpi);
|
---|
| 476 | }
|
---|
| 477 | else
|
---|
| 478 | fSuccess = false;
|
---|
| 479 |
|
---|
| 480 | if (!fSuccess)
|
---|
| 481 | {
|
---|
| 482 | int fBatteryState = 0;
|
---|
| 483 | size_t cbParameter = sizeof(fBatteryState);
|
---|
| 484 |
|
---|
[54962] | 485 | rc = sysctlbyname("hw.acpi.battery.state", &fBatteryState, &cbParameter, NULL, 0);
|
---|
[19967] | 486 | if (!rc)
|
---|
| 487 | {
|
---|
| 488 | if ((fBatteryState & ACPI_BATT_STAT_NOT_PRESENT) == ACPI_BATT_STAT_NOT_PRESENT)
|
---|
| 489 | *pfPresent = false;
|
---|
| 490 | else
|
---|
| 491 | {
|
---|
| 492 | *pfPresent = true;
|
---|
| 493 |
|
---|
| 494 | if (fBatteryState & ACPI_BATT_STAT_DISCHARG)
|
---|
| 495 | *penmBatteryState = PDM_ACPI_BAT_STATE_DISCHARGING;
|
---|
| 496 | else if (fBatteryState & ACPI_BATT_STAT_CHARGING)
|
---|
| 497 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGING;
|
---|
| 498 | else
|
---|
| 499 | *penmBatteryState = PDM_ACPI_BAT_STATE_CHARGED;
|
---|
| 500 |
|
---|
| 501 | if (fBatteryState & ACPI_BATT_STAT_CRITICAL)
|
---|
| 502 | *penmBatteryState = (PDMACPIBATSTATE)(*penmBatteryState | PDM_ACPI_BAT_STATE_CRITICAL);
|
---|
| 503 |
|
---|
| 504 | /* Get battery level. */
|
---|
| 505 | int curCapacity = 0;
|
---|
| 506 | cbParameter = sizeof(curCapacity);
|
---|
[54962] | 507 | rc = sysctlbyname("hw.acpi.battery.life", &curCapacity, &cbParameter, NULL, 0);
|
---|
[27264] | 508 | if (!rc && curCapacity >= 0)
|
---|
[19967] | 509 | *penmRemainingCapacity = (PDMACPIBATCAPACITY)curCapacity;
|
---|
| 510 |
|
---|
| 511 | /* The rate can't be determined with sysctls. */
|
---|
| 512 | }
|
---|
| 513 | }
|
---|
| 514 | }
|
---|
[27264] | 515 |
|
---|
[19967] | 516 | #endif /* RT_OS_FREEBSD */
|
---|
[27264] | 517 |
|
---|
[1] | 518 | return VINF_SUCCESS;
|
---|
| 519 | }
|
---|
| 520 |
|
---|
[27264] | 521 | #ifdef RT_OS_LINUX
|
---|
[1] | 522 | /**
|
---|
[27264] | 523 | * Poller thread for /proc/acpi status files.
|
---|
| 524 | *
|
---|
| 525 | * Reading these files takes ages (several seconds) on some hosts, therefore
|
---|
| 526 | * start this thread. The termination of this thread may take some seconds
|
---|
| 527 | * on such a hosts!
|
---|
| 528 | *
|
---|
| 529 | * @param pDrvIns The driver instance data.
|
---|
| 530 | * @param pThread The thread.
|
---|
| 531 | */
|
---|
| 532 | static DECLCALLBACK(int) drvACPIPoller(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
|
---|
| 533 | {
|
---|
| 534 | PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
|
---|
| 535 |
|
---|
| 536 | if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
|
---|
| 537 | return VINF_SUCCESS;
|
---|
| 538 |
|
---|
| 539 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
| 540 | {
|
---|
[27326] | 541 | ASMAtomicWriteBool(&pThis->fDontPokePoller, false);
|
---|
| 542 |
|
---|
[27264] | 543 | PDMACPIPOWERSOURCE enmPowerSource = PDM_ACPI_POWER_SOURCE_UNKNOWN;
|
---|
[27327] | 544 | PRTSTREAM pStrmStatus;
|
---|
| 545 | PRTSTREAM pStrmType;
|
---|
[69753] | 546 | RTDIR hDir = NIL_RTDIR;
|
---|
[27264] | 547 | RTDIRENTRY DirEntry;
|
---|
[27327] | 548 | char szLine[1024];
|
---|
| 549 | bool fBatteryPresent = false; /* one or more batteries present */
|
---|
| 550 | bool fCharging = false; /* one or more batteries charging */
|
---|
| 551 | bool fDischarging = false; /* one or more batteries discharging */
|
---|
| 552 | bool fCritical = false; /* one or more batteries in critical state */
|
---|
[61675] | 553 | bool fDataChanged; /* if battery status data changed during last poll */
|
---|
[27327] | 554 | int32_t maxCapacityTotal = 0; /* total capacity of all batteries */
|
---|
| 555 | int32_t currentCapacityTotal = 0; /* total current capacity of all batteries */
|
---|
| 556 | int32_t presentRateTotal = 0; /* total present (dis)charging rate of all batts */
|
---|
[61675] | 557 | PDMACPIBATCAPACITY enmBatteryRemainingCapacity; /* total remaining capacity of vbox batt */
|
---|
| 558 | uint32_t u32BatteryPresentRate; /* total present (dis)charging rate of vbox batt */
|
---|
[27327] | 559 |
|
---|
[69753] | 560 | int rc = RTDirOpen(&hDir, "/sys/class/power_supply/");
|
---|
[27264] | 561 | if (RT_SUCCESS(rc))
|
---|
| 562 | {
|
---|
[27327] | 563 | /*
|
---|
| 564 | * The new /sys interface introduced with Linux 2.6.25.
|
---|
| 565 | */
|
---|
[27264] | 566 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
| 567 | {
|
---|
[69753] | 568 | rc = RTDirRead(hDir, &DirEntry, NULL);
|
---|
[27264] | 569 | if (RT_FAILURE(rc))
|
---|
| 570 | break;
|
---|
| 571 | if ( strcmp(DirEntry.szName, ".") == 0
|
---|
| 572 | || strcmp(DirEntry.szName, "..") == 0)
|
---|
| 573 | continue;
|
---|
[27327] | 574 | #define POWER_OPEN(s, n) RTStrmOpenF("r", s, "/sys/class/power_supply/%s/" n, DirEntry.szName)
|
---|
| 575 | rc = POWER_OPEN(&pStrmType, "type");
|
---|
[27264] | 576 | if (RT_FAILURE(rc))
|
---|
[27327] | 577 | continue;
|
---|
| 578 | rc = RTStrmGetLine(pStrmType, szLine, sizeof(szLine));
|
---|
[27264] | 579 | if (RT_SUCCESS(rc))
|
---|
| 580 | {
|
---|
[27327] | 581 | if (strcmp(szLine, "Mains") == 0)
|
---|
[27264] | 582 | {
|
---|
[27327] | 583 | /* AC adapter */
|
---|
| 584 | rc = POWER_OPEN(&pStrmStatus, "online");
|
---|
| 585 | if (RT_SUCCESS(rc))
|
---|
[27264] | 586 | {
|
---|
[27327] | 587 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 588 | if ( RT_SUCCESS(rc)
|
---|
| 589 | && strcmp(szLine, "1") == 0)
|
---|
[27264] | 590 | enmPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
| 591 | else
|
---|
| 592 | enmPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
|
---|
[27327] | 593 | RTStrmClose(pStrmStatus);
|
---|
[27264] | 594 | }
|
---|
| 595 | }
|
---|
[27327] | 596 | else if (strcmp(szLine, "Battery") == 0)
|
---|
| 597 | {
|
---|
| 598 | /* Battery */
|
---|
| 599 | rc = POWER_OPEN(&pStrmStatus, "present");
|
---|
| 600 | if (RT_SUCCESS(rc))
|
---|
| 601 | {
|
---|
| 602 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 603 | RTStrmClose(pStrmStatus);
|
---|
| 604 | if ( RT_SUCCESS(rc)
|
---|
| 605 | && strcmp(szLine, "1") == 0)
|
---|
| 606 | {
|
---|
| 607 | fBatteryPresent = true;
|
---|
| 608 | rc = RTStrmOpenF("r", &pStrmStatus,
|
---|
| 609 | "/sys/class/power_supply/%s/status", DirEntry.szName);
|
---|
| 610 | if (RT_SUCCESS(rc))
|
---|
| 611 | {
|
---|
| 612 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 613 | if (RT_SUCCESS(rc))
|
---|
| 614 | {
|
---|
| 615 | if (strcmp(szLine, "Discharging") == 0)
|
---|
| 616 | fDischarging = true;
|
---|
| 617 | else if (strcmp(szLine, "Charging") == 0)
|
---|
| 618 | fCharging = true;
|
---|
| 619 | }
|
---|
| 620 | RTStrmClose(pStrmStatus);
|
---|
| 621 | }
|
---|
| 622 | rc = POWER_OPEN(&pStrmStatus, "capacity_level");
|
---|
| 623 | if (RT_SUCCESS(rc))
|
---|
| 624 | {
|
---|
| 625 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 626 | if ( RT_SUCCESS(rc)
|
---|
| 627 | && strcmp(szLine, "Critical") == 0)
|
---|
| 628 | fCritical = true;
|
---|
| 629 | RTStrmClose(pStrmStatus);
|
---|
| 630 | }
|
---|
| 631 | rc = POWER_OPEN(&pStrmStatus, "energy_full");
|
---|
| 632 | if (RT_FAILURE(rc))
|
---|
| 633 | rc = POWER_OPEN(&pStrmStatus, "charge_full");
|
---|
| 634 | if (RT_SUCCESS(rc))
|
---|
| 635 | {
|
---|
| 636 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 637 | if (RT_SUCCESS(rc))
|
---|
| 638 | {
|
---|
| 639 | int32_t maxCapacity = 0;
|
---|
| 640 | rc = RTStrToInt32Full(szLine, 0, &maxCapacity);
|
---|
| 641 | if ( RT_SUCCESS(rc)
|
---|
| 642 | && maxCapacity > 0)
|
---|
| 643 | maxCapacityTotal += maxCapacity;
|
---|
| 644 | }
|
---|
| 645 | RTStrmClose(pStrmStatus);
|
---|
| 646 | }
|
---|
| 647 | rc = POWER_OPEN(&pStrmStatus, "energy_now");
|
---|
| 648 | if (RT_FAILURE(rc))
|
---|
| 649 | rc = POWER_OPEN(&pStrmStatus, "charge_now");
|
---|
| 650 | if (RT_SUCCESS(rc))
|
---|
| 651 | {
|
---|
| 652 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 653 | if (RT_SUCCESS(rc))
|
---|
| 654 | {
|
---|
| 655 | int32_t currentCapacity = 0;
|
---|
| 656 | rc = RTStrToInt32Full(szLine, 0, ¤tCapacity);
|
---|
| 657 | if ( RT_SUCCESS(rc)
|
---|
| 658 | && currentCapacity > 0)
|
---|
| 659 | currentCapacityTotal += currentCapacity;
|
---|
| 660 | }
|
---|
| 661 | RTStrmClose(pStrmStatus);
|
---|
| 662 | }
|
---|
[61676] | 663 | rc = POWER_OPEN(&pStrmStatus, "power_now");
|
---|
| 664 | if (RT_FAILURE(rc))
|
---|
| 665 | rc = POWER_OPEN(&pStrmStatus, "current_now");
|
---|
[27327] | 666 | if (RT_SUCCESS(rc))
|
---|
| 667 | {
|
---|
| 668 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 669 | if (RT_SUCCESS(rc))
|
---|
| 670 | {
|
---|
| 671 | int32_t presentRate = 0;
|
---|
| 672 | rc = RTStrToInt32Full(szLine, 0, &presentRate);
|
---|
| 673 | if ( RT_SUCCESS(rc)
|
---|
| 674 | && presentRate > 0)
|
---|
| 675 | {
|
---|
| 676 | if (fDischarging)
|
---|
| 677 | presentRateTotal -= presentRate;
|
---|
| 678 | else
|
---|
| 679 | presentRateTotal += presentRate;
|
---|
| 680 | }
|
---|
| 681 | }
|
---|
| 682 | RTStrmClose(pStrmStatus);
|
---|
| 683 | }
|
---|
| 684 | }
|
---|
| 685 | }
|
---|
| 686 | }
|
---|
[27264] | 687 | }
|
---|
[27327] | 688 | RTStrmClose(pStrmType);
|
---|
| 689 | #undef POWER_OPEN
|
---|
[27264] | 690 | }
|
---|
[69753] | 691 | RTDirClose(hDir);
|
---|
[27264] | 692 | }
|
---|
[27327] | 693 | else /* !/sys */
|
---|
[27264] | 694 | {
|
---|
[27327] | 695 | /*
|
---|
| 696 | * The old /proc/acpi interface
|
---|
| 697 | */
|
---|
| 698 | /*
|
---|
| 699 | * Read the status of the powerline-adapter.
|
---|
| 700 | */
|
---|
[69753] | 701 | rc = RTDirOpen(&hDir, "/proc/acpi/ac_adapter/");
|
---|
[27327] | 702 | if (RT_SUCCESS(rc))
|
---|
[27264] | 703 | {
|
---|
[27327] | 704 | #define POWER_OPEN(s, n) RTStrmOpenF("r", s, "/proc/acpi/ac_adapter/%s/" n, DirEntry.szName)
|
---|
| 705 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
[27264] | 706 | {
|
---|
[69753] | 707 | rc = RTDirRead(hDir, &DirEntry, NULL);
|
---|
[27327] | 708 | if (RT_FAILURE(rc))
|
---|
| 709 | break;
|
---|
| 710 | if ( strcmp(DirEntry.szName, ".") == 0
|
---|
| 711 | || strcmp(DirEntry.szName, "..") == 0)
|
---|
| 712 | continue;
|
---|
| 713 | rc = POWER_OPEN(&pStrmStatus, "status");
|
---|
| 714 | if (RT_FAILURE(rc))
|
---|
| 715 | rc = POWER_OPEN(&pStrmStatus, "state");
|
---|
| 716 | if (RT_SUCCESS(rc))
|
---|
| 717 | {
|
---|
| 718 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
| 719 | {
|
---|
| 720 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 721 | if (RT_FAILURE(rc))
|
---|
| 722 | break;
|
---|
| 723 | if ( strstr(szLine, "Status:") != NULL
|
---|
| 724 | || strstr(szLine, "state:") != NULL)
|
---|
| 725 | {
|
---|
| 726 | if (strstr(szLine, "on-line") != NULL)
|
---|
| 727 | enmPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
|
---|
| 728 | else
|
---|
| 729 | enmPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
|
---|
| 730 | break;
|
---|
| 731 | }
|
---|
| 732 | }
|
---|
| 733 | RTStrmClose(pStrmStatus);
|
---|
| 734 | break;
|
---|
| 735 | }
|
---|
[27264] | 736 | }
|
---|
[69753] | 737 | RTDirClose(hDir);
|
---|
[27327] | 738 | #undef POWER_OPEN
|
---|
| 739 | }
|
---|
[27264] | 740 |
|
---|
[27327] | 741 | /*
|
---|
| 742 | * Read the status of all batteries and collect it into one.
|
---|
| 743 | */
|
---|
[69753] | 744 | rc = RTDirOpen(&hDir, "/proc/acpi/battery/");
|
---|
[27327] | 745 | if (RT_SUCCESS(rc))
|
---|
| 746 | {
|
---|
| 747 | #define POWER_OPEN(s, n) RTStrmOpenF("r", s, "/proc/acpi/battery/%s/" n, DirEntry.szName)
|
---|
| 748 | bool fThisBatteryPresent = false;
|
---|
| 749 | bool fThisDischarging = false;
|
---|
[27264] | 750 |
|
---|
| 751 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
| 752 | {
|
---|
[69753] | 753 | rc = RTDirRead(hDir, &DirEntry, NULL);
|
---|
[27264] | 754 | if (RT_FAILURE(rc))
|
---|
| 755 | break;
|
---|
[27327] | 756 | if ( strcmp(DirEntry.szName, ".") == 0
|
---|
| 757 | || strcmp(DirEntry.szName, "..") == 0)
|
---|
| 758 | continue;
|
---|
| 759 |
|
---|
| 760 | rc = POWER_OPEN(&pStrmStatus, "status");
|
---|
| 761 | /* there is a 2nd variant of that file */
|
---|
| 762 | if (RT_FAILURE(rc))
|
---|
| 763 | rc = POWER_OPEN(&pStrmStatus, "state");
|
---|
| 764 | if (RT_FAILURE(rc))
|
---|
| 765 | continue;
|
---|
| 766 |
|
---|
| 767 | PRTSTREAM pStrmInfo;
|
---|
| 768 | rc = POWER_OPEN(&pStrmInfo, "info");
|
---|
| 769 | if (RT_FAILURE(rc))
|
---|
[27264] | 770 | {
|
---|
[27327] | 771 | RTStrmClose(pStrmStatus);
|
---|
| 772 | continue;
|
---|
[27264] | 773 | }
|
---|
| 774 |
|
---|
[27327] | 775 | /* get 'present' status from the info file */
|
---|
[27264] | 776 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
| 777 | {
|
---|
| 778 | rc = RTStrmGetLine(pStrmInfo, szLine, sizeof(szLine));
|
---|
| 779 | if (RT_FAILURE(rc))
|
---|
| 780 | break;
|
---|
[27327] | 781 | if (strstr(szLine, "present:") != NULL)
|
---|
[27264] | 782 | {
|
---|
[27327] | 783 | if (strstr(szLine, "yes") != NULL)
|
---|
[27264] | 784 | {
|
---|
[27327] | 785 | fThisBatteryPresent = true;
|
---|
| 786 | break;
|
---|
[27264] | 787 | }
|
---|
| 788 | }
|
---|
| 789 | }
|
---|
| 790 |
|
---|
[27327] | 791 | if (fThisBatteryPresent)
|
---|
[27264] | 792 | {
|
---|
[27327] | 793 | fBatteryPresent = true;
|
---|
| 794 | RTStrmRewind(pStrmInfo);
|
---|
| 795 |
|
---|
| 796 | /* get the maximum capacity from the info file */
|
---|
| 797 | while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
[27264] | 798 | {
|
---|
[27327] | 799 | rc = RTStrmGetLine(pStrmInfo, szLine, sizeof(szLine));
|
---|
[27264] | 800 | if (RT_FAILURE(rc))
|
---|
[27327] | 801 | break;
|
---|
| 802 | if (strstr(szLine, "last full capacity:") != NULL)
|
---|
[27264] | 803 | {
|
---|
[27327] | 804 | char *psz;
|
---|
| 805 | int32_t maxCapacity = 0;
|
---|
| 806 | rc = RTStrToInt32Ex(RTStrStripL(&szLine[19]), &psz, 0, &maxCapacity);
|
---|
| 807 | if (RT_FAILURE(rc))
|
---|
| 808 | maxCapacity = 0;
|
---|
| 809 | maxCapacityTotal += maxCapacity;
|
---|
| 810 | break;
|
---|
[27264] | 811 | }
|
---|
[27327] | 812 | }
|
---|
| 813 |
|
---|
| 814 | /* get the current capacity/state from the status file */
|
---|
| 815 | int32_t presentRate = 0;
|
---|
| 816 | bool fGotRemainingCapacity = false;
|
---|
| 817 | bool fGotBatteryState = false;
|
---|
| 818 | bool fGotCapacityState = false;
|
---|
| 819 | bool fGotPresentRate = false;
|
---|
| 820 | while ( ( !fGotRemainingCapacity
|
---|
| 821 | || !fGotBatteryState
|
---|
| 822 | || !fGotCapacityState
|
---|
| 823 | || !fGotPresentRate)
|
---|
| 824 | && pThread->enmState == PDMTHREADSTATE_RUNNING)
|
---|
| 825 | {
|
---|
| 826 | rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
|
---|
| 827 | if (RT_FAILURE(rc))
|
---|
| 828 | break;
|
---|
| 829 | if (strstr(szLine, "remaining capacity:") != NULL)
|
---|
[27264] | 830 | {
|
---|
[27327] | 831 | char *psz;
|
---|
| 832 | int32_t currentCapacity = 0;
|
---|
| 833 | rc = RTStrToInt32Ex(RTStrStripL(&szLine[19]), &psz, 0, ¤tCapacity);
|
---|
| 834 | if ( RT_SUCCESS(rc)
|
---|
| 835 | && currentCapacity > 0)
|
---|
[27264] | 836 | currentCapacityTotal += currentCapacity;
|
---|
[27327] | 837 | fGotRemainingCapacity = true;
|
---|
[27264] | 838 | }
|
---|
[27327] | 839 | else if (strstr(szLine, "charging state:") != NULL)
|
---|
| 840 | {
|
---|
| 841 | if (strstr(szLine + 15, "discharging") != NULL)
|
---|
| 842 | {
|
---|
| 843 | fDischarging = true;
|
---|
| 844 | fThisDischarging = true;
|
---|
| 845 | }
|
---|
| 846 | else if (strstr(szLine + 15, "charging") != NULL)
|
---|
| 847 | fCharging = true;
|
---|
| 848 | fGotBatteryState = true;
|
---|
| 849 | }
|
---|
| 850 | else if (strstr(szLine, "capacity state:") != NULL)
|
---|
| 851 | {
|
---|
| 852 | if (strstr(szLine + 15, "critical") != NULL)
|
---|
| 853 | fCritical = true;
|
---|
| 854 | fGotCapacityState = true;
|
---|
| 855 | }
|
---|
| 856 | if (strstr(szLine, "present rate:") != NULL)
|
---|
| 857 | {
|
---|
| 858 | char *psz;
|
---|
| 859 | rc = RTStrToInt32Ex(RTStrStripL(&szLine[13]), &psz, 0, &presentRate);
|
---|
| 860 | if (RT_FAILURE(rc))
|
---|
| 861 | presentRate = 0;
|
---|
| 862 | fGotPresentRate = true;
|
---|
| 863 | }
|
---|
[27264] | 864 | }
|
---|
[27327] | 865 | if (fThisDischarging)
|
---|
| 866 | presentRateTotal -= presentRate;
|
---|
| 867 | else
|
---|
| 868 | presentRateTotal += presentRate;
|
---|
[27264] | 869 | }
|
---|
[27327] | 870 | RTStrmClose(pStrmStatus);
|
---|
| 871 | RTStrmClose(pStrmInfo);
|
---|
[27264] | 872 | }
|
---|
[69753] | 873 | RTDirClose(hDir);
|
---|
[27327] | 874 | #undef POWER_OPEN
|
---|
[27264] | 875 | }
|
---|
[27327] | 876 | } /* /proc/acpi */
|
---|
[27264] | 877 |
|
---|
| 878 | /* atomic update of the state */
|
---|
| 879 | RTCritSectEnter(&pThis->CritSect);
|
---|
| 880 |
|
---|
| 881 | /* charging/discharging bits are mutual exclusive */
|
---|
| 882 | uint32_t uBs = PDM_ACPI_BAT_STATE_CHARGED;
|
---|
| 883 | if (fDischarging)
|
---|
| 884 | uBs = PDM_ACPI_BAT_STATE_DISCHARGING;
|
---|
| 885 | else if (fCharging)
|
---|
| 886 | uBs = PDM_ACPI_BAT_STATE_CHARGING;
|
---|
| 887 | if (fCritical)
|
---|
| 888 | uBs |= PDM_ACPI_BAT_STATE_CRITICAL;
|
---|
| 889 |
|
---|
| 890 | if (maxCapacityTotal > 0 && currentCapacityTotal > 0)
|
---|
| 891 | {
|
---|
| 892 | if (presentRateTotal < 0)
|
---|
| 893 | presentRateTotal = -presentRateTotal;
|
---|
| 894 |
|
---|
| 895 | /* calculate the percentage */
|
---|
[61675] | 896 |
|
---|
| 897 | enmBatteryRemainingCapacity =
|
---|
[27264] | 898 | (PDMACPIBATCAPACITY)( ( (float)currentCapacityTotal
|
---|
| 899 | / (float)maxCapacityTotal)
|
---|
| 900 | * PDM_ACPI_BAT_CAPACITY_MAX);
|
---|
[61675] | 901 | u32BatteryPresentRate =
|
---|
[27326] | 902 | (uint32_t)(( (float)presentRateTotal
|
---|
[27264] | 903 | / (float)maxCapacityTotal) * 1000);
|
---|
| 904 | }
|
---|
[27327] | 905 | else
|
---|
| 906 | {
|
---|
| 907 | /* unknown capacity / state */
|
---|
[61675] | 908 | enmBatteryRemainingCapacity = PDM_ACPI_BAT_CAPACITY_UNKNOWN;
|
---|
| 909 | u32BatteryPresentRate = ~0;
|
---|
[27327] | 910 | }
|
---|
[61675] | 911 |
|
---|
| 912 | if ( pThis->enmPowerSource == enmPowerSource
|
---|
| 913 | && pThis->fBatteryPresent == fBatteryPresent
|
---|
| 914 | && pThis->enmBatteryState == (PDMACPIBATSTATE) uBs
|
---|
| 915 | && pThis->enmBatteryRemainingCapacity == enmBatteryRemainingCapacity
|
---|
| 916 | && pThis->u32BatteryPresentRate == u32BatteryPresentRate)
|
---|
| 917 | {
|
---|
| 918 | fDataChanged = false;
|
---|
| 919 | }
|
---|
| 920 | else
|
---|
| 921 | {
|
---|
| 922 | fDataChanged = true;
|
---|
| 923 |
|
---|
| 924 | pThis->enmPowerSource = enmPowerSource;
|
---|
| 925 | pThis->fBatteryPresent = fBatteryPresent;
|
---|
| 926 | pThis->enmBatteryState = (PDMACPIBATSTATE)uBs;
|
---|
| 927 | pThis->enmBatteryRemainingCapacity = enmBatteryRemainingCapacity;
|
---|
| 928 | pThis->u32BatteryPresentRate = u32BatteryPresentRate;
|
---|
| 929 | }
|
---|
| 930 |
|
---|
[27264] | 931 | RTCritSectLeave(&pThis->CritSect);
|
---|
| 932 |
|
---|
[61675] | 933 | if (fDataChanged)
|
---|
| 934 | pThis->pPort->pfnBatteryStatusChangeEvent(pThis->pPort);
|
---|
| 935 |
|
---|
[27264] | 936 | /* wait a bit (e.g. Ubuntu/GNOME polls every 30 seconds) */
|
---|
[27326] | 937 | ASMAtomicWriteBool(&pThis->fDontPokePoller, true);
|
---|
[27264] | 938 | rc = RTSemEventWait(pThis->hPollerSleepEvent, 20000);
|
---|
| 939 | }
|
---|
| 940 |
|
---|
| 941 | return VINF_SUCCESS;
|
---|
| 942 | }
|
---|
| 943 |
|
---|
| 944 | static DECLCALLBACK(int) drvACPIPollerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
|
---|
| 945 | {
|
---|
| 946 | PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
|
---|
| 947 |
|
---|
| 948 | RTSemEventSignal(pThis->hPollerSleepEvent);
|
---|
[27326] | 949 | if (!ASMAtomicReadBool(&pThis->fDontPokePoller))
|
---|
| 950 | RTThreadPoke(pThread->Thread);
|
---|
[27264] | 951 | return VINF_SUCCESS;
|
---|
| 952 | }
|
---|
| 953 | #endif /* RT_OS_LINUX */
|
---|
| 954 |
|
---|
| 955 |
|
---|
| 956 | /**
|
---|
[1] | 957 | * Destruct a driver instance.
|
---|
| 958 | *
|
---|
| 959 | * Most VM resources are freed by the VM. This callback is provided so that any non-VM
|
---|
| 960 | * resources can be freed correctly.
|
---|
| 961 | *
|
---|
| 962 | * @param pDrvIns The driver instance data.
|
---|
| 963 | */
|
---|
| 964 | static DECLCALLBACK(void) drvACPIDestruct(PPDMDRVINS pDrvIns)
|
---|
| 965 | {
|
---|
| 966 | LogFlow(("drvACPIDestruct\n"));
|
---|
[26001] | 967 | PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
|
---|
[27264] | 968 |
|
---|
[27266] | 969 | #ifdef RT_OS_LINUX
|
---|
[62908] | 970 | PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
|
---|
[45061] | 971 | if (pThis->hPollerSleepEvent != NIL_RTSEMEVENT)
|
---|
| 972 | {
|
---|
| 973 | RTSemEventDestroy(pThis->hPollerSleepEvent);
|
---|
| 974 | pThis->hPollerSleepEvent = NIL_RTSEMEVENT;
|
---|
| 975 | }
|
---|
[27264] | 976 | RTCritSectDelete(&pThis->CritSect);
|
---|
[27266] | 977 | #endif
|
---|
[1] | 978 | }
|
---|
| 979 |
|
---|
| 980 | /**
|
---|
| 981 | * Construct an ACPI driver instance.
|
---|
| 982 | *
|
---|
[22277] | 983 | * @copydoc FNPDMDRVCONSTRUCT
|
---|
[1] | 984 | */
|
---|
[26173] | 985 | static DECLCALLBACK(int) drvACPIConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
|
---|
[1] | 986 | {
|
---|
[91864] | 987 | RT_NOREF(pCfg, fFlags);
|
---|
[62908] | 988 | PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
|
---|
[11269] | 989 | PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
|
---|
[27264] | 990 | int rc = VINF_SUCCESS;
|
---|
[1] | 991 |
|
---|
| 992 | /*
|
---|
| 993 | * Init the static parts.
|
---|
| 994 | */
|
---|
[11269] | 995 | pThis->pDrvIns = pDrvIns;
|
---|
[45061] | 996 | #ifdef RT_OS_LINUX
|
---|
| 997 | pThis->hPollerSleepEvent = NIL_RTSEMEVENT;
|
---|
| 998 | #endif
|
---|
[1] | 999 | /* IBase */
|
---|
| 1000 | pDrvIns->IBase.pfnQueryInterface = drvACPIQueryInterface;
|
---|
| 1001 | /* IACPIConnector */
|
---|
[11269] | 1002 | pThis->IACPIConnector.pfnQueryPowerSource = drvACPIQueryPowerSource;
|
---|
| 1003 | pThis->IACPIConnector.pfnQueryBatteryStatus = drvACPIQueryBatteryStatus;
|
---|
[1] | 1004 |
|
---|
| 1005 | /*
|
---|
| 1006 | * Validate the config.
|
---|
| 1007 | */
|
---|
[91864] | 1008 | PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", "");
|
---|
[1] | 1009 |
|
---|
| 1010 | /*
|
---|
| 1011 | * Check that no-one is attached to us.
|
---|
| 1012 | */
|
---|
[25893] | 1013 | AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
|
---|
[22277] | 1014 | ("Configuration error: Not possible to attach anything to this driver!\n"),
|
---|
| 1015 | VERR_PDM_DRVINS_NO_ATTACH);
|
---|
[1] | 1016 |
|
---|
| 1017 | /*
|
---|
| 1018 | * Query the ACPI port interface.
|
---|
| 1019 | */
|
---|
[25984] | 1020 | pThis->pPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIACPIPORT);
|
---|
[11269] | 1021 | if (!pThis->pPort)
|
---|
[1] | 1022 | {
|
---|
[22277] | 1023 | AssertMsgFailed(("Configuration error: the above device/driver didn't export the ACPI port interface!\n"));
|
---|
[1] | 1024 | return VERR_PDM_MISSING_INTERFACE_ABOVE;
|
---|
| 1025 | }
|
---|
| 1026 |
|
---|
[27264] | 1027 | #ifdef RT_OS_LINUX
|
---|
| 1028 | /*
|
---|
| 1029 | * Start the poller thread.
|
---|
| 1030 | */
|
---|
[28258] | 1031 | rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pPollerThread, pThis, drvACPIPoller,
|
---|
| 1032 | drvACPIPollerWakeup, 0, RTTHREADTYPE_INFREQUENT_POLLER, "ACPI Poller");
|
---|
[27264] | 1033 | if (RT_FAILURE(rc))
|
---|
| 1034 | return rc;
|
---|
| 1035 |
|
---|
| 1036 | rc = RTCritSectInit(&pThis->CritSect);
|
---|
| 1037 | if (RT_FAILURE(rc))
|
---|
| 1038 | return rc;
|
---|
| 1039 |
|
---|
| 1040 | rc = RTSemEventCreate(&pThis->hPollerSleepEvent);
|
---|
| 1041 | #endif
|
---|
| 1042 |
|
---|
| 1043 | return rc;
|
---|
[1] | 1044 | }
|
---|
| 1045 |
|
---|
| 1046 |
|
---|
| 1047 | /**
|
---|
| 1048 | * ACPI driver registration record.
|
---|
| 1049 | */
|
---|
| 1050 | const PDMDRVREG g_DrvACPI =
|
---|
| 1051 | {
|
---|
| 1052 | /* u32Version */
|
---|
| 1053 | PDM_DRVREG_VERSION,
|
---|
[26166] | 1054 | /* szName */
|
---|
[1] | 1055 | "ACPIHost",
|
---|
[25893] | 1056 | /* szRCMod */
|
---|
| 1057 | "",
|
---|
| 1058 | /* szR0Mod */
|
---|
| 1059 | "",
|
---|
[1] | 1060 | /* pszDescription */
|
---|
| 1061 | "ACPI Host Driver",
|
---|
| 1062 | /* fFlags */
|
---|
| 1063 | PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
---|
| 1064 | /* fClass. */
|
---|
| 1065 | PDM_DRVREG_CLASS_ACPI,
|
---|
| 1066 | /* cMaxInstances */
|
---|
[40282] | 1067 | ~0U,
|
---|
[1] | 1068 | /* cbInstance */
|
---|
| 1069 | sizeof(DRVACPI),
|
---|
| 1070 | /* pfnConstruct */
|
---|
| 1071 | drvACPIConstruct,
|
---|
| 1072 | /* pfnDestruct */
|
---|
| 1073 | drvACPIDestruct,
|
---|
[25893] | 1074 | /* pfnRelocate */
|
---|
| 1075 | NULL,
|
---|
[1] | 1076 | /* pfnIOCtl */
|
---|
| 1077 | NULL,
|
---|
| 1078 | /* pfnPowerOn */
|
---|
| 1079 | NULL,
|
---|
| 1080 | /* pfnReset */
|
---|
| 1081 | NULL,
|
---|
| 1082 | /* pfnSuspend */
|
---|
| 1083 | NULL,
|
---|
| 1084 | /* pfnResume */
|
---|
| 1085 | NULL,
|
---|
[22277] | 1086 | /* pfnAttach */
|
---|
| 1087 | NULL,
|
---|
[1] | 1088 | /* pfnDetach */
|
---|
[25893] | 1089 | NULL,
|
---|
[22277] | 1090 | /* pfnPowerOff */
|
---|
[25893] | 1091 | NULL,
|
---|
[22277] | 1092 | /* pfnSoftReset */
|
---|
[1] | 1093 | NULL,
|
---|
[22277] | 1094 | /* u32EndVersion */
|
---|
| 1095 | PDM_DRVREG_VERSION
|
---|
[1] | 1096 | };
|
---|