VirtualBox

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

Last change on this file since 62506 was 61676, checked in by vboxsync, 8 years ago

Devices/ACPI: when determining the current power consumption, use power_now and only fall back to current_now if the former isn't available. Thanks to Dennis Wassenberg / secunet!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 42.9 KB
Line 
1/* $Id: DrvACPI.cpp 61676 2016-06-13 12:40:45Z 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.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 <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, "power_now");
647 if (RT_FAILURE(rc))
648 rc = POWER_OPEN(&pStrmStatus, "current_now");
649 if (RT_SUCCESS(rc))
650 {
651 rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
652 if (RT_SUCCESS(rc))
653 {
654 int32_t presentRate = 0;
655 rc = RTStrToInt32Full(szLine, 0, &presentRate);
656 if ( RT_SUCCESS(rc)
657 && presentRate > 0)
658 {
659 if (fDischarging)
660 presentRateTotal -= presentRate;
661 else
662 presentRateTotal += presentRate;
663 }
664 }
665 RTStrmClose(pStrmStatus);
666 }
667 }
668 }
669 }
670 }
671 RTStrmClose(pStrmType);
672#undef POWER_OPEN
673 }
674 RTDirClose(pDir);
675 }
676 else /* !/sys */
677 {
678 /*
679 * The old /proc/acpi interface
680 */
681 /*
682 * Read the status of the powerline-adapter.
683 */
684 rc = RTDirOpen(&pDir, "/proc/acpi/ac_adapter/");
685 if (RT_SUCCESS(rc))
686 {
687#define POWER_OPEN(s, n) RTStrmOpenF("r", s, "/proc/acpi/ac_adapter/%s/" n, DirEntry.szName)
688 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
689 {
690 rc = RTDirRead(pDir, &DirEntry, NULL);
691 if (RT_FAILURE(rc))
692 break;
693 if ( strcmp(DirEntry.szName, ".") == 0
694 || strcmp(DirEntry.szName, "..") == 0)
695 continue;
696 rc = POWER_OPEN(&pStrmStatus, "status");
697 if (RT_FAILURE(rc))
698 rc = POWER_OPEN(&pStrmStatus, "state");
699 if (RT_SUCCESS(rc))
700 {
701 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
702 {
703 rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
704 if (RT_FAILURE(rc))
705 break;
706 if ( strstr(szLine, "Status:") != NULL
707 || strstr(szLine, "state:") != NULL)
708 {
709 if (strstr(szLine, "on-line") != NULL)
710 enmPowerSource = PDM_ACPI_POWER_SOURCE_OUTLET;
711 else
712 enmPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY;
713 break;
714 }
715 }
716 RTStrmClose(pStrmStatus);
717 break;
718 }
719 }
720 RTDirClose(pDir);
721#undef POWER_OPEN
722 }
723
724 /*
725 * Read the status of all batteries and collect it into one.
726 */
727 rc = RTDirOpen(&pDir, "/proc/acpi/battery/");
728 if (RT_SUCCESS(rc))
729 {
730#define POWER_OPEN(s, n) RTStrmOpenF("r", s, "/proc/acpi/battery/%s/" n, DirEntry.szName)
731 bool fThisBatteryPresent = false;
732 bool fThisDischarging = false;
733
734 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
735 {
736 rc = RTDirRead(pDir, &DirEntry, NULL);
737 if (RT_FAILURE(rc))
738 break;
739 if ( strcmp(DirEntry.szName, ".") == 0
740 || strcmp(DirEntry.szName, "..") == 0)
741 continue;
742
743 rc = POWER_OPEN(&pStrmStatus, "status");
744 /* there is a 2nd variant of that file */
745 if (RT_FAILURE(rc))
746 rc = POWER_OPEN(&pStrmStatus, "state");
747 if (RT_FAILURE(rc))
748 continue;
749
750 PRTSTREAM pStrmInfo;
751 rc = POWER_OPEN(&pStrmInfo, "info");
752 if (RT_FAILURE(rc))
753 {
754 RTStrmClose(pStrmStatus);
755 continue;
756 }
757
758 /* get 'present' status from the info file */
759 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
760 {
761 rc = RTStrmGetLine(pStrmInfo, szLine, sizeof(szLine));
762 if (RT_FAILURE(rc))
763 break;
764 if (strstr(szLine, "present:") != NULL)
765 {
766 if (strstr(szLine, "yes") != NULL)
767 {
768 fThisBatteryPresent = true;
769 break;
770 }
771 }
772 }
773
774 if (fThisBatteryPresent)
775 {
776 fBatteryPresent = true;
777 RTStrmRewind(pStrmInfo);
778
779 /* get the maximum capacity from the info file */
780 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
781 {
782 rc = RTStrmGetLine(pStrmInfo, szLine, sizeof(szLine));
783 if (RT_FAILURE(rc))
784 break;
785 if (strstr(szLine, "last full capacity:") != NULL)
786 {
787 char *psz;
788 int32_t maxCapacity = 0;
789 rc = RTStrToInt32Ex(RTStrStripL(&szLine[19]), &psz, 0, &maxCapacity);
790 if (RT_FAILURE(rc))
791 maxCapacity = 0;
792 maxCapacityTotal += maxCapacity;
793 break;
794 }
795 }
796
797 /* get the current capacity/state from the status file */
798 int32_t presentRate = 0;
799 bool fGotRemainingCapacity = false;
800 bool fGotBatteryState = false;
801 bool fGotCapacityState = false;
802 bool fGotPresentRate = false;
803 while ( ( !fGotRemainingCapacity
804 || !fGotBatteryState
805 || !fGotCapacityState
806 || !fGotPresentRate)
807 && pThread->enmState == PDMTHREADSTATE_RUNNING)
808 {
809 rc = RTStrmGetLine(pStrmStatus, szLine, sizeof(szLine));
810 if (RT_FAILURE(rc))
811 break;
812 if (strstr(szLine, "remaining capacity:") != NULL)
813 {
814 char *psz;
815 int32_t currentCapacity = 0;
816 rc = RTStrToInt32Ex(RTStrStripL(&szLine[19]), &psz, 0, &currentCapacity);
817 if ( RT_SUCCESS(rc)
818 && currentCapacity > 0)
819 currentCapacityTotal += currentCapacity;
820 fGotRemainingCapacity = true;
821 }
822 else if (strstr(szLine, "charging state:") != NULL)
823 {
824 if (strstr(szLine + 15, "discharging") != NULL)
825 {
826 fDischarging = true;
827 fThisDischarging = true;
828 }
829 else if (strstr(szLine + 15, "charging") != NULL)
830 fCharging = true;
831 fGotBatteryState = true;
832 }
833 else if (strstr(szLine, "capacity state:") != NULL)
834 {
835 if (strstr(szLine + 15, "critical") != NULL)
836 fCritical = true;
837 fGotCapacityState = true;
838 }
839 if (strstr(szLine, "present rate:") != NULL)
840 {
841 char *psz;
842 rc = RTStrToInt32Ex(RTStrStripL(&szLine[13]), &psz, 0, &presentRate);
843 if (RT_FAILURE(rc))
844 presentRate = 0;
845 fGotPresentRate = true;
846 }
847 }
848 if (fThisDischarging)
849 presentRateTotal -= presentRate;
850 else
851 presentRateTotal += presentRate;
852 }
853 RTStrmClose(pStrmStatus);
854 RTStrmClose(pStrmInfo);
855 }
856 RTDirClose(pDir);
857#undef POWER_OPEN
858 }
859 } /* /proc/acpi */
860
861 /* atomic update of the state */
862 RTCritSectEnter(&pThis->CritSect);
863
864 /* charging/discharging bits are mutual exclusive */
865 uint32_t uBs = PDM_ACPI_BAT_STATE_CHARGED;
866 if (fDischarging)
867 uBs = PDM_ACPI_BAT_STATE_DISCHARGING;
868 else if (fCharging)
869 uBs = PDM_ACPI_BAT_STATE_CHARGING;
870 if (fCritical)
871 uBs |= PDM_ACPI_BAT_STATE_CRITICAL;
872
873 if (maxCapacityTotal > 0 && currentCapacityTotal > 0)
874 {
875 if (presentRateTotal < 0)
876 presentRateTotal = -presentRateTotal;
877
878 /* calculate the percentage */
879
880 enmBatteryRemainingCapacity =
881 (PDMACPIBATCAPACITY)( ( (float)currentCapacityTotal
882 / (float)maxCapacityTotal)
883 * PDM_ACPI_BAT_CAPACITY_MAX);
884 u32BatteryPresentRate =
885 (uint32_t)(( (float)presentRateTotal
886 / (float)maxCapacityTotal) * 1000);
887 }
888 else
889 {
890 /* unknown capacity / state */
891 enmBatteryRemainingCapacity = PDM_ACPI_BAT_CAPACITY_UNKNOWN;
892 u32BatteryPresentRate = ~0;
893 }
894
895 if ( pThis->enmPowerSource == enmPowerSource
896 && pThis->fBatteryPresent == fBatteryPresent
897 && pThis->enmBatteryState == (PDMACPIBATSTATE) uBs
898 && pThis->enmBatteryRemainingCapacity == enmBatteryRemainingCapacity
899 && pThis->u32BatteryPresentRate == u32BatteryPresentRate)
900 {
901 fDataChanged = false;
902 }
903 else
904 {
905 fDataChanged = true;
906
907 pThis->enmPowerSource = enmPowerSource;
908 pThis->fBatteryPresent = fBatteryPresent;
909 pThis->enmBatteryState = (PDMACPIBATSTATE)uBs;
910 pThis->enmBatteryRemainingCapacity = enmBatteryRemainingCapacity;
911 pThis->u32BatteryPresentRate = u32BatteryPresentRate;
912 }
913
914 RTCritSectLeave(&pThis->CritSect);
915
916 if (fDataChanged)
917 pThis->pPort->pfnBatteryStatusChangeEvent(pThis->pPort);
918
919 /* wait a bit (e.g. Ubuntu/GNOME polls every 30 seconds) */
920 ASMAtomicWriteBool(&pThis->fDontPokePoller, true);
921 rc = RTSemEventWait(pThis->hPollerSleepEvent, 20000);
922 }
923
924 return VINF_SUCCESS;
925}
926
927static DECLCALLBACK(int) drvACPIPollerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
928{
929 PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
930
931 RTSemEventSignal(pThis->hPollerSleepEvent);
932 if (!ASMAtomicReadBool(&pThis->fDontPokePoller))
933 RTThreadPoke(pThread->Thread);
934 return VINF_SUCCESS;
935}
936#endif /* RT_OS_LINUX */
937
938
939/**
940 * Destruct a driver instance.
941 *
942 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
943 * resources can be freed correctly.
944 *
945 * @param pDrvIns The driver instance data.
946 */
947static DECLCALLBACK(void) drvACPIDestruct(PPDMDRVINS pDrvIns)
948{
949 PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
950
951 LogFlow(("drvACPIDestruct\n"));
952 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
953
954#ifdef RT_OS_LINUX
955 if (pThis->hPollerSleepEvent != NIL_RTSEMEVENT)
956 {
957 RTSemEventDestroy(pThis->hPollerSleepEvent);
958 pThis->hPollerSleepEvent = NIL_RTSEMEVENT;
959 }
960 RTCritSectDelete(&pThis->CritSect);
961#endif
962}
963
964/**
965 * Construct an ACPI driver instance.
966 *
967 * @copydoc FNPDMDRVCONSTRUCT
968 */
969static DECLCALLBACK(int) drvACPIConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
970{
971 PDRVACPI pThis = PDMINS_2_DATA(pDrvIns, PDRVACPI);
972 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
973 int rc = VINF_SUCCESS;
974
975 /*
976 * Init the static parts.
977 */
978 pThis->pDrvIns = pDrvIns;
979#ifdef RT_OS_LINUX
980 pThis->hPollerSleepEvent = NIL_RTSEMEVENT;
981#endif
982 /* IBase */
983 pDrvIns->IBase.pfnQueryInterface = drvACPIQueryInterface;
984 /* IACPIConnector */
985 pThis->IACPIConnector.pfnQueryPowerSource = drvACPIQueryPowerSource;
986 pThis->IACPIConnector.pfnQueryBatteryStatus = drvACPIQueryBatteryStatus;
987
988 /*
989 * Validate the config.
990 */
991 if (!CFGMR3AreValuesValid(pCfg, "\0"))
992 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
993
994 /*
995 * Check that no-one is attached to us.
996 */
997 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
998 ("Configuration error: Not possible to attach anything to this driver!\n"),
999 VERR_PDM_DRVINS_NO_ATTACH);
1000
1001 /*
1002 * Query the ACPI port interface.
1003 */
1004 pThis->pPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIACPIPORT);
1005 if (!pThis->pPort)
1006 {
1007 AssertMsgFailed(("Configuration error: the above device/driver didn't export the ACPI port interface!\n"));
1008 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1009 }
1010
1011#ifdef RT_OS_LINUX
1012 /*
1013 * Start the poller thread.
1014 */
1015 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pPollerThread, pThis, drvACPIPoller,
1016 drvACPIPollerWakeup, 0, RTTHREADTYPE_INFREQUENT_POLLER, "ACPI Poller");
1017 if (RT_FAILURE(rc))
1018 return rc;
1019
1020 rc = RTCritSectInit(&pThis->CritSect);
1021 if (RT_FAILURE(rc))
1022 return rc;
1023
1024 rc = RTSemEventCreate(&pThis->hPollerSleepEvent);
1025#endif
1026
1027 return rc;
1028}
1029
1030
1031/**
1032 * ACPI driver registration record.
1033 */
1034const PDMDRVREG g_DrvACPI =
1035{
1036 /* u32Version */
1037 PDM_DRVREG_VERSION,
1038 /* szName */
1039 "ACPIHost",
1040 /* szRCMod */
1041 "",
1042 /* szR0Mod */
1043 "",
1044 /* pszDescription */
1045 "ACPI Host Driver",
1046 /* fFlags */
1047 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1048 /* fClass. */
1049 PDM_DRVREG_CLASS_ACPI,
1050 /* cMaxInstances */
1051 ~0U,
1052 /* cbInstance */
1053 sizeof(DRVACPI),
1054 /* pfnConstruct */
1055 drvACPIConstruct,
1056 /* pfnDestruct */
1057 drvACPIDestruct,
1058 /* pfnRelocate */
1059 NULL,
1060 /* pfnIOCtl */
1061 NULL,
1062 /* pfnPowerOn */
1063 NULL,
1064 /* pfnReset */
1065 NULL,
1066 /* pfnSuspend */
1067 NULL,
1068 /* pfnResume */
1069 NULL,
1070 /* pfnAttach */
1071 NULL,
1072 /* pfnDetach */
1073 NULL,
1074 /* pfnPowerOff */
1075 NULL,
1076 /* pfnSoftReset */
1077 NULL,
1078 /* u32EndVersion */
1079 PDM_DRVREG_VERSION
1080};
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