1/*
2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1999-2002 Apple Computer, Inc.  All rights reserved.
24 *
25 * HISTORY
26 * 13 February 2001 wgulland created.
27 *
28 */
29
30#define DEBUGGING_LEVEL 0	// 1 = low; 2 = high; 3 = extreme
31#define DEBUGLOG IOLog
32
33// protected
34#import <IOKit/firewire/IOFWWorkLoop.h>
35
36// system
37#import <IOKit/IOWorkLoop.h>
38#import <IOKit/IOLocksPrivate.h>
39
40SInt32 IOFWWorkLoop::sLockGroupCount = 0;
41
42OSDefineMetaClassAndStructors( IOFWWorkLoop, IOWorkLoop )
43
44// workLoop
45//
46// factory method
47
48IOFWWorkLoop * IOFWWorkLoop::workLoop()
49{
50    IOFWWorkLoop *loop;
51
52    loop = OSTypeAlloc( IOFWWorkLoop );
53    if( !loop )
54        return loop;
55
56    if( !loop->init() )
57	{
58        loop->release();
59        loop = NULL;
60    }
61
62    return loop;
63}
64
65// init
66//
67//
68
69bool IOFWWorkLoop::init( void )
70{
71	bool success = true;
72
73	if( success )
74	{
75		// create a unique lock group for this instance of the FireWire workloop
76		// this helps elucidate lock statistics
77
78		SInt32	count = OSIncrementAtomic( &sLockGroupCount );
79		char	name[64];
80
81		snprintf( name, sizeof(name), "FireWire %d", (int)count );
82		fLockGroup = lck_grp_alloc_init( name, LCK_GRP_ATTR_NULL );
83		if( !fLockGroup )
84		{
85			success = false;
86		}
87	}
88
89	if( success )
90	{
91		gateLock = IORecursiveLockAllocWithLockGroup( fLockGroup );
92	}
93
94	if( success )
95	{
96		fRemoveSourceDeferredSet = OSSet::withCapacity( 1 );
97		if( fRemoveSourceDeferredSet == NULL  )
98		{
99			success = false;
100		}
101	}
102
103	if( success )
104	{
105		success = IOWorkLoop::init();
106	}
107
108	return success;
109}
110
111// free
112//
113//
114
115void IOFWWorkLoop::free( void )
116{
117	if( fLockGroup )
118	{
119		lck_grp_free( fLockGroup );
120		fLockGroup = NULL;
121	}
122
123	if( fRemoveSourceDeferredSet )
124	{
125		fRemoveSourceDeferredSet->release();
126		fRemoveSourceDeferredSet = NULL;
127	}
128
129	IOWorkLoop::free();
130}
131
132// removeEventSource
133//
134//
135
136IOReturn IOFWWorkLoop::removeEventSource(IOEventSource *toRemove)
137{
138	IOReturn status = kIOReturnSuccess;
139
140	// the PM thread retains ioservices while fiddling with them
141	// that means the PM thread may be the thread that releases the final retain on an ioservices
142	// ioservices may remove and free event sources in their free routines
143	// removing and freeing event sources grabs the workloop lock
144	// if the PM has already put FireWire to sleep we would sleep any thread who grabs the workloop lock
145	// if we sleep the PM thread then sleep hangs. that's bad.
146
147	// if we could have a do over we should not sleep the entire workloop, but only those that belong
148	// to the core FireWire services. at this point though there are likely too many drivers relying
149	// on a full workloop sleep to change things safely.
150
151	// so for now we do these slightly crazy machinations to allow event source removal without sleeping the
152	// calling thread
153
154	// we only need to do this if the calling thread is the PM workloop, but I don't want to make
155	// assumptions about PM internals that may change so I'll do this for all threads
156
157	IOWorkLoop::closeGate();
158
159	if( fRemoveSourceThread != NULL )
160	{
161		IOLog( "IOFWWorkLoop::removeEventSource - fRemoveSourceThread = (%p) != NULL\n", fRemoveSourceThread );
162	}
163
164	// remember who's removing the event source
165	fRemoveSourceThread = IOThreadSelf();
166
167	// if we're asleep
168	if( fSleepToken )
169	{
170		// we can't let this object be freed after we return since freeing a command gate grabs the workloop lock
171		// we will flush this set on wake
172		fRemoveSourceDeferredSet->setObject( toRemove );
173	}
174
175	// do the actual removal, this will succeed since fRemoveSourceThread will be allowed to grab the lock
176	status = IOWorkLoop::removeEventSource( toRemove );
177
178	// forget the thread
179	fRemoveSourceThread = NULL;
180
181	IOWorkLoop::openGate();
182
183	return status;
184}
185
186// closeGate
187//
188//
189
190void IOFWWorkLoop::closeGate()
191{
192    IOWorkLoop::closeGate();
193    if( fSleepToken &&
194	    (fRemoveSourceThread != IOThreadSelf()) )
195	{
196        IOReturn res;
197        do
198		{
199            res = sleepGate( fSleepToken, THREAD_ABORTSAFE );
200            if( res == kIOReturnSuccess )
201                break;
202            IOLog("sleepGate returned 0x%x\n", res);
203        }
204		while( true );
205    }
206}
207
208// tryCloseGate
209//
210//
211
212bool IOFWWorkLoop::tryCloseGate()
213{
214    bool ret;
215    ret = IOWorkLoop::tryCloseGate();
216    if( ret &&
217	    fSleepToken &&
218	    (fRemoveSourceThread != IOThreadSelf()) )
219	{
220        openGate();
221        ret = false;
222    }
223
224    return ret;
225}
226
227// sleep
228//
229//
230
231IOReturn IOFWWorkLoop::sleep(void *token)
232{
233    if( fSleepToken )
234	{
235        DEBUGLOG( "IOFWWorkLoop::sleep: Already asleep: %p\n", token );
236        return kIOReturnError;
237    }
238
239    fSleepToken = token;
240    openGate();
241
242	return kIOReturnSuccess;
243}
244
245// wake
246//
247//
248
249IOReturn IOFWWorkLoop::wake(void *token)
250{
251#if 0
252    if( fSleepToken != token )
253	{
254        DEBUGLOG( "IOFWWorkLoop::wake: wrong token: %p<->%p\n", token, fSleepToken );
255        return kIOReturnError;
256    }
257#endif
258
259	if( fSleepToken )
260	{
261		IORecursiveLockLock( gateLock );
262
263		void * the_token = fSleepToken;
264		fSleepToken = NULL;
265
266		// delete any event sources that were removed during sleep
267		fRemoveSourceDeferredSet->flushCollection();
268
269		// wake up waiting threads
270		wakeupGate( the_token, false );
271    }
272	else
273	{
274		closeGate();
275	}
276
277	return kIOReturnSuccess;
278}
279