VirtualBox

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

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

*: scm cleanup run.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use