VirtualBox

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

Last change on this file since 45761 was 45061, checked in by vboxsync, 12 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 41.3 KB
Line 
1/* $Id: DrvACPI.cpp 45061 2013-03-18 14:09:03Z vboxsync $ */
2/** @file
3 * DrvACPI - ACPI Host Driver.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 if (pThis->hPollerSleepEvent != NIL_RTSEMEVENT)
928 {
929 RTSemEventDestroy(pThis->hPollerSleepEvent);
930 pThis->hPollerSleepEvent = NIL_RTSEMEVENT;
931 }
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#ifdef RT_OS_LINUX
952 pThis->hPollerSleepEvent = NIL_RTSEMEVENT;
953#endif
954 /* IBase */
955 pDrvIns->IBase.pfnQueryInterface = drvACPIQueryInterface;
956 /* IACPIConnector */
957 pThis->IACPIConnector.pfnQueryPowerSource = drvACPIQueryPowerSource;
958 pThis->IACPIConnector.pfnQueryBatteryStatus = drvACPIQueryBatteryStatus;
959
960 /*
961 * Validate the config.
962 */
963 if (!CFGMR3AreValuesValid(pCfg, "\0"))
964 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
965
966 /*
967 * Check that no-one is attached to us.
968 */
969 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
970 ("Configuration error: Not possible to attach anything to this driver!\n"),
971 VERR_PDM_DRVINS_NO_ATTACH);
972
973 /*
974 * Query the ACPI port interface.
975 */
976 pThis->pPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIACPIPORT);
977 if (!pThis->pPort)
978 {
979 AssertMsgFailed(("Configuration error: the above device/driver didn't export the ACPI port interface!\n"));
980 return VERR_PDM_MISSING_INTERFACE_ABOVE;
981 }
982
983#ifdef RT_OS_LINUX
984 /*
985 * Start the poller thread.
986 */
987 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pPollerThread, pThis, drvACPIPoller,
988 drvACPIPollerWakeup, 0, RTTHREADTYPE_INFREQUENT_POLLER, "ACPI Poller");
989 if (RT_FAILURE(rc))
990 return rc;
991
992 rc = RTCritSectInit(&pThis->CritSect);
993 if (RT_FAILURE(rc))
994 return rc;
995
996 rc = RTSemEventCreate(&pThis->hPollerSleepEvent);
997#endif
998
999 return rc;
1000}
1001
1002
1003/**
1004 * ACPI driver registration record.
1005 */
1006const PDMDRVREG g_DrvACPI =
1007{
1008 /* u32Version */
1009 PDM_DRVREG_VERSION,
1010 /* szName */
1011 "ACPIHost",
1012 /* szRCMod */
1013 "",
1014 /* szR0Mod */
1015 "",
1016 /* pszDescription */
1017 "ACPI Host Driver",
1018 /* fFlags */
1019 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1020 /* fClass. */
1021 PDM_DRVREG_CLASS_ACPI,
1022 /* cMaxInstances */
1023 ~0U,
1024 /* cbInstance */
1025 sizeof(DRVACPI),
1026 /* pfnConstruct */
1027 drvACPIConstruct,
1028 /* pfnDestruct */
1029 drvACPIDestruct,
1030 /* pfnRelocate */
1031 NULL,
1032 /* pfnIOCtl */
1033 NULL,
1034 /* pfnPowerOn */
1035 NULL,
1036 /* pfnReset */
1037 NULL,
1038 /* pfnSuspend */
1039 NULL,
1040 /* pfnResume */
1041 NULL,
1042 /* pfnAttach */
1043 NULL,
1044 /* pfnDetach */
1045 NULL,
1046 /* pfnPowerOff */
1047 NULL,
1048 /* pfnSoftReset */
1049 NULL,
1050 /* u32EndVersion */
1051 PDM_DRVREG_VERSION
1052};
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