VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/powernotification-r0drv.c@ 19948

Last change on this file since 19948 was 13908, checked in by vboxsync, 16 years ago

Fixed include order, a bunch of GCC 3.3 warnings, OS/2 build.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: powernotification-r0drv.c 13908 2008-11-06 11:53:47Z vboxsync $ */
2/** @file
3 * IPRT - Power Management, Ring-0 Driver, Event Notifications.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include <iprt/power.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/spinlock.h>
40#include <iprt/string.h>
41#include "r0drv/mp-r0drv.h"
42#include "r0drv/power-r0drv.h"
43
44
45/*******************************************************************************
46* Structures and Typedefs *
47*******************************************************************************/
48/**
49 * Notification registration record tracking
50 * RTPowerRegisterNotification() calls.
51 */
52typedef struct RTPOWERNOTIFYREG
53{
54 /** Pointer to the next record. */
55 struct RTPOWERNOTIFYREG * volatile pNext;
56 /** The callback. */
57 PFNRTPOWERNOTIFICATION pfnCallback;
58 /** The user argument. */
59 void *pvUser;
60 /** Bit mask indicating whether we've done this callback or not. */
61 uint8_t bmDone[sizeof(void *)];
62} RTPOWERNOTIFYREG;
63/** Pointer to a registration record. */
64typedef RTPOWERNOTIFYREG *PRTPOWERNOTIFYREG;
65
66
67/*******************************************************************************
68* Global Variables *
69*******************************************************************************/
70/** The spinlock protecting the list. */
71static RTSPINLOCK volatile g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
72/** List of callbacks, in registration order. */
73static PRTPOWERNOTIFYREG volatile g_pRTPowerCallbackHead = NULL;
74/** The current done bit. */
75static uint32_t volatile g_iRTPowerDoneBit;
76/** The list generation.
77 * This is increased whenever the list has been modified. The callback routine
78 * make use of this to avoid having restart at the list head after each callback. */
79static uint32_t volatile g_iRTPowerGeneration;
80/** The number of RTPowerNotification users.
81 * This is incremented on init and decremented on termination. */
82static uint32_t volatile g_cRTPowerUsers = 0;
83
84
85
86
87RTDECL(int) RTPowerSignalEvent(RTPOWEREVENT enmEvent)
88{
89 PRTPOWERNOTIFYREG pCur;
90 RTSPINLOCKTMP Tmp;
91 RTSPINLOCK hSpinlock;
92
93 /*
94 * This is a little bit tricky as we cannot be holding the spinlock
95 * while calling the callback. This means that the list might change
96 * while we're walking it, and that multiple events might be running
97 * concurrently (depending on the OS).
98 *
99 * So, the first measure is to employ a 32-bitmask for each
100 * record where we'll use a bit that rotates for each call to
101 * this function to indicate which records that has been
102 * processed. This will take care of both changes to the list
103 * and a reasonable amount of concurrent events.
104 *
105 * In order to avoid having to restart the list walks for every
106 * callback we make, we'll make use a list generation number that is
107 * incremented everytime the list is changed. So, if it remains
108 * unchanged over a callback we can safely continue the iteration.
109 */
110 uint32_t iDone = ASMAtomicIncU32(&g_iRTPowerDoneBit);
111 iDone %= RT_SIZEOFMEMB(RTPOWERNOTIFYREG, bmDone) * 8;
112
113 hSpinlock = g_hRTPowerNotifySpinLock;
114 if (hSpinlock == NIL_RTSPINLOCK)
115 return VERR_ACCESS_DENIED;
116 RTSpinlockAcquire(hSpinlock, &Tmp);
117
118 /* Clear the bit. */
119 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
120 ASMAtomicBitClear(&pCur->bmDone[0], iDone);
121
122 /* Iterate the records and perform the callbacks. */
123 do
124 {
125 uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTPowerGeneration);
126
127 pCur = g_pRTPowerCallbackHead;
128 while (pCur)
129 {
130 if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
131 {
132 PFNRTPOWERNOTIFICATION pfnCallback = pCur->pfnCallback;
133 void *pvUser = pCur->pvUser;
134 pCur = pCur->pNext;
135 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
136
137 pfnCallback(enmEvent, pvUser);
138
139 /* carefully require the lock here, see RTR0MpNotificationTerm(). */
140 hSpinlock = g_hRTPowerNotifySpinLock;
141 if (hSpinlock == NIL_RTSPINLOCK)
142 return VERR_ACCESS_DENIED;
143 RTSpinlockAcquire(hSpinlock, &Tmp);
144 if (ASMAtomicUoReadU32(&g_iRTPowerGeneration) != iGeneration)
145 break;
146 }
147 else
148 pCur = pCur->pNext;
149 }
150 } while (pCur);
151
152 RTSpinlockRelease(hSpinlock, &Tmp);
153 return VINF_SUCCESS;
154}
155
156
157
158RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
159{
160 PRTPOWERNOTIFYREG pCur;
161 PRTPOWERNOTIFYREG pNew;
162 RTSPINLOCKTMP Tmp;
163
164 /*
165 * Validation.
166 */
167 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
168 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
169
170 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
171 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
172 if ( pCur->pvUser == pvUser
173 && pCur->pfnCallback == pfnCallback)
174 break;
175 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
176 AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
177
178 /*
179 * Allocate a new record and attempt to insert it.
180 */
181 pNew = (PRTPOWERNOTIFYREG)RTMemAlloc(sizeof(*pNew));
182 if (!pNew)
183 return VERR_NO_MEMORY;
184
185 pNew->pNext = NULL;
186 pNew->pfnCallback = pfnCallback;
187 pNew->pvUser = pvUser;
188 memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
189
190 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
191
192 pCur = g_pRTPowerCallbackHead;
193 if (!pCur)
194 g_pRTPowerCallbackHead = pNew;
195 else
196 {
197 for (pCur = g_pRTPowerCallbackHead; ; pCur = pCur->pNext)
198 if ( pCur->pvUser == pvUser
199 && pCur->pfnCallback == pfnCallback)
200 break;
201 else if (!pCur->pNext)
202 {
203 pCur->pNext = pNew;
204 pCur = NULL;
205 break;
206 }
207 }
208
209 ASMAtomicIncU32(&g_iRTPowerGeneration);
210
211 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
212
213 /* duplicate? */
214 if (pCur)
215 {
216 RTMemFree(pCur);
217 AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
218 }
219
220 return VINF_SUCCESS;
221}
222
223
224
225RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
226{
227 PRTPOWERNOTIFYREG pPrev;
228 PRTPOWERNOTIFYREG pCur;
229 RTSPINLOCKTMP Tmp;
230
231 /*
232 * Validation.
233 */
234 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
235 AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
236
237 /*
238 * Find and unlink the record from the list.
239 */
240 RTSpinlockAcquire(g_hRTPowerNotifySpinLock, &Tmp);
241 pPrev = NULL;
242 for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
243 {
244 if ( pCur->pvUser == pvUser
245 && pCur->pfnCallback == pfnCallback)
246 break;
247 pPrev = pCur;
248 }
249 if (pCur)
250 {
251 if (pPrev)
252 pPrev->pNext = pCur->pNext;
253 else
254 g_pRTPowerCallbackHead = pCur->pNext;
255 ASMAtomicIncU32(&g_iRTPowerGeneration);
256 }
257 RTSpinlockRelease(g_hRTPowerNotifySpinLock, &Tmp);
258
259 if (!pCur)
260 return VERR_NOT_FOUND;
261
262 /*
263 * Invalidate and free the record.
264 */
265 pCur->pNext = NULL;
266 pCur->pfnCallback = NULL;
267 RTMemFree(pCur);
268
269 return VINF_SUCCESS;
270}
271
272
273int rtR0PowerNotificationInit(void)
274{
275 int rc = VINF_SUCCESS;
276
277 if (ASMAtomicIncU32(&g_cRTPowerUsers) == 1)
278 {
279 rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTPowerNotifySpinLock);
280 if (RT_SUCCESS(rc))
281 {
282 /** @todo OS specific init here */
283 return rc;
284#if 0
285 RTSpinlockDestroy(g_hRTPowerNotifySpinLock);
286 g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
287#endif
288 }
289 ASMAtomicDecU32(&g_cRTPowerUsers);
290 }
291 return rc;
292}
293
294
295void rtR0PowerNotificationTerm(void)
296{
297 RTSPINLOCK hSpinlock = g_hRTPowerNotifySpinLock;
298 if (hSpinlock != NIL_RTSPINLOCK)
299 {
300 AssertMsg(g_cRTPowerUsers > 0, ("%d\n", g_cRTPowerUsers));
301 if (ASMAtomicDecU32(&g_cRTPowerUsers) == 0)
302 {
303 PRTPOWERNOTIFYREG pHead;
304 RTSPINLOCKTMP Tmp;
305
306 /** @todo OS specific term here */
307
308 /* pick up the list and the spinlock. */
309 RTSpinlockAcquire(hSpinlock, &Tmp);
310 ASMAtomicWriteSize(&g_hRTPowerNotifySpinLock, NIL_RTSPINLOCK);
311 pHead = g_pRTPowerCallbackHead;
312 g_pRTPowerCallbackHead = NULL;
313 ASMAtomicIncU32(&g_iRTPowerGeneration);
314 RTSpinlockRelease(hSpinlock, &Tmp);
315
316 /* free the list. */
317 while (pHead)
318 {
319 PRTPOWERNOTIFYREG pFree = pHead;
320 pHead = pHead->pNext;
321
322 pFree->pNext = NULL;
323 pFree->pfnCallback = NULL;
324 RTMemFree(pFree);
325 }
326
327 RTSpinlockDestroy(hSpinlock);
328 }
329 }
330}
331
332
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