VirtualBox

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

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

DrvACPI.cpp: Another 'ing PVM undef.

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