VirtualBox

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

Last change on this file since 29085 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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