VirtualBox

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

Last change on this file since 28374 was 28258, checked in by vboxsync, 15 years ago

PDM critsects for drivers. Fixed critsect cleanup in failure path. Started on new transmit locking scheme (required for intnet buffer serialization).

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

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