VirtualBox

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

最後變更 在這個檔案從28258是 28258,由 vboxsync 提交於 15 年 前

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

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

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