VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DrvACPI.cpp@ 61675

Last change on this file since 61675 was 61675, checked in by vboxsync, 9 years ago

Devices/ACPI: Notify the guest by sending a battery status change event by calling the GPE bit 0 handler. Also tell the guest to re-evaluate _BST (battery dynamic state) and _PSR (AC adapter status). I don't think that the guest needs to re-evaluate _BIF (battery info) at this time. Thanks Dennis Wassenberg / secunet!

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette