1/*
2 * Copyright (c) 1998-2000 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 *	IOFWUserAsyncStreamListener.cpp
24 *	IOFireWireFamily
25 *
26 *	Created by Arul on Wed Nov 08 2006.
27 *	Copyright (c) 2006 Apple, Inc. All rights reserved.
28 *
29 */
30/*
31	$Log: IOFWUserAsyncStreamListener.cpp,v $
32	Revision 1.6  2007/10/27 01:12:34  arulchan
33	fix for rdar://5558059
34
35	Revision 1.5  2007/02/16 19:03:43  arulchan
36	*** empty log message ***
37
38	Revision 1.4  2007/02/07 06:35:20  collin
39	*** empty log message ***
40
41	Revision 1.3  2007/01/15 23:29:04  arulchan
42	Fixed Skipped Packet Handler Notifications
43
44	Revision 1.2  2006/12/21 21:17:44  ayanowit
45	More changes necessary to eventually get support for 64-bit apps working (4222965).
46
47	Revision 1.1  2006/12/06 00:03:28  arulchan
48	Isoch Channel 31 Receiver
49
50*/
51
52#import <IOKit/firewire/IOFireWireNub.h>
53#import <IOKit/firewire/IOFireWireController.h>
54
55
56// protected
57#import <IOKit/firewire/IOFireWireLink.h>
58
59// private
60#import "IOFireWireUserClient.h"
61#import "IOFireWireLib.h"
62#import "IOFWUserPseudoAddressSpace.h"
63#import "IOFWUserAsyncStreamListener.h"
64
65// system
66#import <IOKit/assert.h>
67#import <IOKit/IOLib.h>
68#import <IOKit/IOWorkLoop.h>
69#import <IOKit/IOTypes.h>
70#import <IOKit/IOMessage.h>
71#import <IOKit/IOUserClient.h>
72
73extern void InitSkippedPacketHeader(
74	IOFWPacketHeader*				header,
75	IOFWPacketHeader*				next,
76	const IOByteCount				offset,
77	OSAsyncReference64*				ref);
78
79Boolean IsAsyncStreamSkippedPacketHeader(
80	IOFWPacketHeader*				header)
81{
82	return header->CommonHeader.type == IOFWPacketHeader::kSkippedPacket ;
83}
84
85Boolean IsAsyncStreamFreePacketHeader(
86	IOFWPacketHeader*				header)
87{
88	return header->CommonHeader.type == IOFWPacketHeader::kFree ;
89}
90
91// ============================================================
92//	IOFWUserAsyncStreamListener methods
93// ============================================================
94
95OSDefineMetaClassAndStructors( IOFWUserAsyncStreamListener, IOFWAsyncStreamListener ) ;
96
97#if IOFIREWIREUSERCLIENTDEBUG > 0
98
99bool
100IOFWUserAsyncStreamListener::serialize(OSSerialize *s) const
101{
102	if (s->previouslySerialized(this))
103		return true ;
104
105	char temp[256] ;
106
107	if ( fFlags )
108	{
109		snprintf(temp+strlen(temp), sizeof(temp), ", flags:") ;
110	}
111	else
112	{
113		snprintf(temp+strlen(temp), sizeof(temp), ", no flags") ;
114	}
115
116	OSString*	string = OSString::withCString(temp) ;
117	if (!string)
118		return false ;
119
120	bool result =  string->serialize(s) ;
121	string->release() ;
122
123	return result ;
124}
125
126#endif
127
128void
129IOFWUserAsyncStreamListener::free()
130{
131	if ( fPacketQueuePrepared )
132		fPacketQueueBuffer->complete() ;
133
134	if ( fPacketQueueBuffer )
135		fPacketQueueBuffer->release() ;
136
137	delete fLastWrittenHeader ;
138
139	if( fLock )
140	{
141		IOLockFree( fLock );
142		fLock = NULL;
143	}
144
145	IOFWAsyncStreamListener::free() ;
146}
147
148// exporterCleanup
149//
150//
151
152void
153IOFWUserAsyncStreamListener::exporterCleanup( OSObject * self, IOFWUserObjectExporter * exporter )
154{
155	IOFWUserAsyncStreamListener * me = (IOFWUserAsyncStreamListener*)self;
156
157	me->deactivate();
158
159	((IOFireWireUserClient*)exporter->getOwner())->getOwner()->getController()->removeAsyncStreamListener(me);
160}
161
162void
163IOFWUserAsyncStreamListener::deactivate()
164{
165	IOFWAsyncStreamListener::TurnOffNotification() ;
166
167	IOLockLock(fLock) ;
168
169	fBufferAvailable = 0 ;	// zzz do we need locking here to protect our data?
170	fLastReadHeader = NULL ;
171
172	IOFWPacketHeader*	firstHeader = fLastWrittenHeader ;
173	IOFWPacketHeader*	tempHeader ;
174
175	if (fLastWrittenHeader)
176	{
177		while (fLastWrittenHeader->CommonHeader.next != firstHeader)
178		{
179			tempHeader = fLastWrittenHeader->CommonHeader.next ;
180			delete fLastWrittenHeader ;
181			fLastWrittenHeader = tempHeader ;
182		}
183
184	}
185
186	fWaitingForUserCompletion = false ;
187
188	IOLockUnlock(fLock) ;
189}
190
191bool
192IOFWUserAsyncStreamListener::completeInit( IOFireWireUserClient* userclient, FWUserAsyncStreamListenerCreateParams* params )
193{
194	Boolean	status = true ;
195
196
197	fUserRefCon					= params->refCon ;
198	fFlags						= params->flags ;
199	fWaitingForUserCompletion	= false ;
200
201	// set user client
202	fUserClient = userclient ;
203
204	// see if user specified a packet queue and queue size
205	if ( !params->queueBuffer )
206	{
207		DebugLog("IOFWUserAsyncStreamListener::initAll: async stream listener without queue buffer\n") ;
208		status = false ;
209	}
210
211	// make memory descriptor around queue
212	if ( status )
213	{
214		if ( params->queueBuffer )
215		{
216			fPacketQueueBuffer = IOMemoryDescriptor::withAddressRange(	params->queueBuffer,
217																		params->queueSize,
218																		kIODirectionOutIn,
219																		fUserClient->getOwningTask() ) ;
220			if ( !fPacketQueueBuffer )
221			{
222				DebugLog("%s %u: couldn't make fPacketQueueBuffer memory descriptor\n", __FILE__, __LINE__) ;
223				status = false ;
224			}
225
226			if ( status )
227			{
228				status =  ( kIOReturnSuccess == fPacketQueueBuffer->prepare() ) ;
229
230				fPacketQueuePrepared = status ;
231			}
232
233			if ( status )
234				fBufferAvailable = fPacketQueueBuffer->getLength() ;
235		}
236	}
237
238	if ( status )
239	{
240		// init the easy vars
241		fLastReadHeader 			= new IOFWPacketHeader ;
242		fLastWrittenHeader			= fLastReadHeader ;
243
244		fLastWrittenHeader->CommonHeader.whichAsyncRef = NULL;
245
246		// get a lock for the packet queue
247		fLock = IOLockAlloc() ;
248
249		if ( !fLock )
250		{
251			DebugLog("%s %u: couldn't allocate lock\n", __FILE__, __LINE__) ;
252			status = false ;
253		}
254	}
255
256
257	if (status)
258	{
259		fUserLocks = true ;
260	}
261
262	return status ;
263}
264
265bool
266IOFWUserAsyncStreamListener::initAsyncStreamListener(
267			IOFireWireUserClient									*userclient,
268			IOFireWireLib::FWUserAsyncStreamListenerCreateParams	*params)
269{
270	if ( !IOFWUserAsyncStreamListener::initAll( userclient->getOwner()->getController(),
271												params->channel,
272												IOFWUserAsyncStreamListener::asyncStreamListenerHandler,
273												this ))
274	{
275		DebugLog("IOFWUserAsyncStreamListener::initAsyncStreamListener failed\n") ;
276		return false ;
277	}
278
279	bool result = completeInit( userclient, params ) ;
280
281	return result ;
282}
283
284
285void
286IOFWUserAsyncStreamListener::doPacket(
287	UInt32							len,
288	const void*						buf,
289	IOFWPacketHeader::QueueTag		tag,
290	UInt32*							oldVal)	// oldVal only used in lock case
291{
292	IOByteCount		destOffset	= 0 ;
293	bool			wontFit		= false ;
294
295	IOLockLock(fLock) ;
296
297	IOFWPacketHeader*	currentHeader = fLastWrittenHeader ;
298
299	if ( tag == IOFWPacketHeader::kIncomingPacket )
300	{
301		IOByteCount		spaceAtEnd	= fPacketQueueBuffer->getLength() ;
302
303		spaceAtEnd -= (IOFWPacketHeaderGetOffset(currentHeader) + IOFWPacketHeaderGetSize(currentHeader)) ;
304
305		if ( fBufferAvailable < len )
306        {
307			wontFit = true ;
308        }
309		else
310		{
311			if (len <= spaceAtEnd)
312				destOffset = IOFWPacketHeaderGetOffset(currentHeader) + IOFWPacketHeaderGetSize(currentHeader) ;
313			else
314			{
315				if ( (len + spaceAtEnd) <= fBufferAvailable )
316					destOffset = 0 ;
317				else
318				{
319					destOffset = IOFWPacketHeaderGetOffset(currentHeader) ;
320					wontFit = true ;
321				}
322			}
323		}
324	}
325
326	if (wontFit)
327	{
328		if (IsAsyncStreamSkippedPacketHeader(currentHeader))
329		{
330			++(currentHeader->SkippedPacket.skippedPacketCount) ;
331		}
332		else
333		{
334			if (!IsAsyncStreamFreePacketHeader(currentHeader))
335			{
336				if ( !IsAsyncStreamFreePacketHeader(currentHeader->CommonHeader.next) )
337				{
338					IOFWPacketHeader*	newHeader = new IOFWPacketHeader ;
339					newHeader->CommonHeader.next = currentHeader->CommonHeader.next ;
340					currentHeader->CommonHeader.next = newHeader ;
341				}
342
343				currentHeader = currentHeader->CommonHeader.next ;
344
345			}
346
347			InitSkippedPacketHeader(
348					currentHeader,
349					currentHeader->CommonHeader.next,
350					destOffset,
351					& fSkippedPacketAsyncNotificationRef ) ;
352
353			fLastWrittenHeader = currentHeader ;
354		}
355	}
356	else
357	{
358		if (!IsAsyncStreamFreePacketHeader(currentHeader))
359		{
360			if ( !IsAsyncStreamFreePacketHeader(currentHeader->CommonHeader.next) )
361			{
362				IOFWPacketHeader*	newHeader		= new IOFWPacketHeader ;
363				newHeader->CommonHeader.next		= currentHeader->CommonHeader.next ;
364				currentHeader->CommonHeader.next	= newHeader ;
365			}
366
367		}
368
369		currentHeader = currentHeader->CommonHeader.next ;
370
371		FWAddress addr;
372		addr.addressHi = 0; addr.addressLo = 0;
373
374		IOFWSpeed speed = kFWSpeedInvalid;
375
376		InitIncomingPacketHeader(
377				currentHeader,
378				currentHeader->CommonHeader.next,
379				len,
380				destOffset,
381				& fPacketAsyncNotificationRef,
382				0,
383				speed,
384				addr,
385				false) ;
386
387		fPacketQueueBuffer->writeBytes(destOffset, buf, len) ;
388
389		fBufferAvailable -= len ;
390
391		fLastWrittenHeader = currentHeader ;
392	}
393
394	if( currentHeader->CommonHeader.type != IOFWPacketHeader::kFree )
395		sendPacketNotification(currentHeader) ;
396
397	IOLockUnlock(fLock) ;
398}
399
400void
401IOFWUserAsyncStreamListener::asyncStreamListenerHandler(
402                                            void*					refCon,
403                                            const void*				buf)
404{
405	IOFWUserAsyncStreamListener*	me = (IOFWUserAsyncStreamListener*)refCon ;
406	ISOC_DATA_PKT *pkt = (ISOC_DATA_PKT*)buf;
407
408	me->doPacket( pkt->size+sizeof(ISOC_DATA_PKT), buf, IOFWPacketHeader::kIncomingPacket ) ;
409}
410
411void
412IOFWUserAsyncStreamListener::setAsyncStreamRef_Packet(
413	OSAsyncReference64	asyncRef)
414{
415	bcopy(asyncRef, fPacketAsyncNotificationRef, sizeof(OSAsyncReference64)) ;
416}
417
418void
419IOFWUserAsyncStreamListener::setAsyncStreamRef_SkippedPacket(
420	OSAsyncReference64	asyncRef)
421{
422	bcopy(asyncRef, fSkippedPacketAsyncNotificationRef, sizeof(OSAsyncReference64)) ;
423}
424
425void
426IOFWUserAsyncStreamListener::clientCommandIsComplete(
427	FWClientCommandID 	inCommandID)
428{
429	IOLockLock(fLock) ;
430
431	if ( fWaitingForUserCompletion )
432	{
433		IOFWPacketHeader*			oldHeader 	= fLastReadHeader ;
434		fLastReadHeader							= fLastReadHeader->CommonHeader.next ;
435
436		fBufferAvailable += oldHeader->IncomingPacket.packetSize ;
437
438		oldHeader->CommonHeader.type = IOFWPacketHeader::kFree ;
439		oldHeader->CommonHeader.whichAsyncRef = 0;
440
441		fWaitingForUserCompletion = false ;
442	}
443	if ( fLastReadHeader->CommonHeader.type != IOFWPacketHeader::kFree )
444		sendPacketNotification(fLastReadHeader) ;
445
446	IOLockUnlock(fLock) ;
447}
448
449void
450IOFWUserAsyncStreamListener::sendPacketNotification(
451	IOFWPacketHeader*	inPacketHeader)
452{
453	if (!fWaitingForUserCompletion)
454	{
455		if (inPacketHeader->CommonHeader.whichAsyncRef[0])
456		{
457			IOReturn ret = IOFireWireUserClient::sendAsyncResult64(*(inPacketHeader->CommonHeader.whichAsyncRef),
458																kIOReturnSuccess,
459																(io_user_reference_t *)inPacketHeader->CommonHeader.args,
460																inPacketHeader->CommonHeader.argCount) ;
461
462			if(ret == kIOReturnSuccess)
463				fWaitingForUserCompletion = true ;
464		}
465	}
466}