VirtualBox

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

Last change on this file since 93435 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

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