VirtualBox

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

Last change on this file since 42202 was 40282, checked in by vboxsync, 13 years ago

*: gcc-4.7: ~0 => ~0U in initializers (warning: narrowing conversion of -1' from int' to `unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing])

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