1/*
2 * Copyright (c) 1998-2007 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#include "IOFWUserPHYPacketListener.h"
24
25#import <IOKit/firewire/IOFireWireController.h>
26#import <IOKit/firewire/IOFireWireNub.h>
27#import <IOKit/firewire/IOFWPHYPacketListener.h>
28
29//#import <IOKit/firewire/FireLog.h>
30
31// ============================================================
32//	IOFWUserPHYPacketListener methods
33// ============================================================
34
35OSDefineMetaClassAndStructors( IOFWUserPHYPacketListener, IOFWPHYPacketListener );
36
37// withUserClient
38//
39//
40
41IOFWUserPHYPacketListener *
42IOFWUserPHYPacketListener::withUserClient( IOFireWireUserClient * inUserClient, UInt32 queue_count )
43{
44	IOFWUserPHYPacketListener*	result	= NULL;
45
46	result = OSTypeAlloc( IOFWUserPHYPacketListener );
47	if( result && !result->initWithUserClient( inUserClient, queue_count ) )
48	{
49		result->release();
50		result = NULL;
51	}
52
53	return result;
54}
55
56// init
57//
58//
59
60bool
61IOFWUserPHYPacketListener::initWithUserClient( IOFireWireUserClient * inUserClient, UInt32 queue_count )
62{
63	bool success = true;
64
65	IOFireWireController * control = inUserClient->getOwner()->getController();
66	if( !IOFWPHYPacketListener::initWithController( control ) )
67		success = false;
68
69	if( success )
70	{
71		fUserClient = inUserClient;
72
73		fMaxQueueCount = queue_count;
74
75		// enforce a minimum queue size
76		if( fMaxQueueCount < 2 )
77		{
78			fMaxQueueCount = 2;
79		}
80
81		fAllocatedQueueCount = 0;
82
83		fElementWaitingCompletion = NULL;
84
85		// get a lock for the packet queue
86		fLock = IOLockAlloc();
87		if( fLock == NULL )
88		{
89			success = false;
90		}
91	}
92
93	return success;
94}
95
96// free
97//
98//
99
100void
101IOFWUserPHYPacketListener::free( void )
102{
103	destroyAllElements();
104
105	if( fLock )
106	{
107		IOLockFree( fLock );
108		fLock = NULL;
109	}
110
111	IOFWPHYPacketListener::free();
112}
113
114// exporterCleanup
115//
116//
117
118void
119IOFWUserPHYPacketListener::exporterCleanup( const OSObject * self )
120{
121	IOFWUserPHYPacketListener * me = (IOFWUserPHYPacketListener*)self;
122
123	DebugLog("IOFWUserPHYPacketListener::exporterCleanup\n");
124
125	me->deactivate();
126}
127
128#pragma mark -
129/////////////////////////////////////////////////////////////////////////////////
130
131// setPacketCallback
132//
133//
134
135IOReturn
136IOFWUserPHYPacketListener::setPacketCallback(	OSAsyncReference64		async_ref,
137												mach_vm_address_t		callback,
138												io_user_reference_t		refCon )
139{
140//	FireLog( "IOFWUserPHYPacketListener::setPacketCallback - asyncref = 0x%016llx callback = 0x%016llx, refcon = 0x%016llx\n", async_ref[0], callback, refCon );
141
142	if( callback )
143	{
144		IOFireWireUserClient::setAsyncReference64( fCallbackAsyncRef, (mach_port_t)async_ref[0], callback, refCon );
145	}
146	else
147	{
148		fCallbackAsyncRef[0] = 0;
149	}
150
151	return kIOReturnSuccess;
152}
153
154// setSkippedCallback
155//
156//
157
158IOReturn
159IOFWUserPHYPacketListener::setSkippedCallback(	OSAsyncReference64		async_ref,
160												mach_vm_address_t		callback,
161												io_user_reference_t		refCon )
162{
163//	FireLog( "IOFWUserPHYPacketListener::setSkippedCallback - asyncref = 0x%016llx callback = 0x%016llx, refcon = 0x%016llx\n", async_ref[0], callback, refCon );
164
165	if( callback )
166	{
167		IOFireWireUserClient::setAsyncReference64( fSkippedAsyncRef, (mach_port_t)async_ref[0], callback, refCon );
168	}
169	else
170	{
171		fSkippedAsyncRef[0] = 0;
172	}
173
174	return kIOReturnSuccess;
175}
176
177// processPHYPacket
178//
179// on workloop
180
181void IOFWUserPHYPacketListener::processPHYPacket( UInt32 data1, UInt32 data2 )
182{
183	IOLockLock( fLock );
184
185	PHYRxElement * element = NULL;
186
187//	FireLog( "IOFWUserPHYPacketListener::processPHYPacket - 0x%08lx %08lx\n", data1, data2 );
188
189	// try to get a data element
190
191	// allocate an element, but tell the allocator to reserve an element for skipped packets
192	element = allocateDataElement();
193	if( element )
194	{
195		// we've got a data element
196		element->type = kTypeData;
197		element->data1 = data1;
198		element->data2 = data2;
199
200		// tell user space if it's not already busy
201		if( fElementWaitingCompletion == NULL )
202		{
203		//	FireLog( "IOFWUserPHYPacketListener::processPHYPacket - data element - send notification\n" );
204
205			sendPacketNotification( element );
206		}
207		else
208		{
209		//	FireLog( "IOFWUserPHYPacketListener::processPHYPacket - data element - queue - fElementWaitingCompletion = 0x%08lx\n", fElementWaitingCompletion );
210
211			// else queue it up
212			addElementToPendingList( element );
213		}
214	}
215	else
216	{
217		// if we couldn't get a data element we're skipping
218
219		// if we're skipping, but user space isn't working on anything
220		// then we're in trouble
221		FWKLOGASSERT( fElementWaitingCompletion != NULL );
222
223		// reuse the tail if it's a skip packet
224		if( fPendingListTail && fPendingListTail->type == kTypeSkipped )
225		{
226			element = fPendingListTail;
227
228			// and it hasn't been sent to the user yet
229			// this should be true because the queue size is always at least 2 and the
230			// commands must be processed by user space sequentially
231			FWKLOGASSERT( fPendingListTail != fElementWaitingCompletion );
232
233			// bump the count
234			UInt32 count = element->getSkippedCount();
235			count++;
236			element->setSkippedCount( count );
237		}
238		else
239		{
240			// else get an element
241			element = allocateElement();
242			if( element )
243			{
244				element->type = kTypeSkipped;
245
246				// set our skipped count
247				element->setSkippedCount( 1 );
248
249		//		FireLog( "IOFWUserPHYPacketListener::processPHYPacket - skip element - queue\n" );
250
251				// queue it up
252				addElementToPendingList( element );
253			}
254			else
255			{
256				// just drop it I guess
257				IOLog( "FireWire - UserPHYPacketListener out of elements\n" );
258			}
259		}
260	}
261
262	IOLockUnlock( fLock );
263}
264
265// clientCommandIsComplete
266//
267// on user thread
268
269void
270IOFWUserPHYPacketListener::clientCommandIsComplete( FWClientCommandID commandID )
271{
272	IOLockLock( fLock );
273
274//	FireLog( "IOFWUserPHYPacketListener::clientCommandIsComplete - commandID = 0x%08lx\n", commandID );
275
276	// verify we're completing the right command
277	if( fElementWaitingCompletion == commandID )
278	{
279		// we're done with the outstanding element
280		deallocateElement( fElementWaitingCompletion );
281
282		fElementWaitingCompletion = NULL;
283
284	//	FireLog( "IOFWUserPHYPacketListener::clientCommandIsComplete - element = 0x%08lx fElementWaitingCompletion = 0x%08lx\n", commandID, fElementWaitingCompletion );
285
286		// if we've got another one on the pending list, send it off
287		PHYRxElement * element = fPendingListHead;
288		if( element )
289		{
290			removeElementFromPendingList( element );
291			sendPacketNotification( element );
292		}
293	}
294
295	IOLockUnlock( fLock );
296}
297
298// sendPacketNotification
299//
300// lock is held
301
302void
303IOFWUserPHYPacketListener::sendPacketNotification( IOFWUserPHYPacketListener::PHYRxElement * element )
304{
305	if( fElementWaitingCompletion == NULL )
306	{
307		if( element->type == kTypeData )
308		{
309			fElementWaitingCompletion = element;
310
311			io_user_reference_t args[3];
312			args[0] = (io_user_reference_t)element;		// commandID
313			args[1] = element->data1;					// data1
314			args[2] = element->data2;					// data2
315
316		//	FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - kTypeData fElementWaitingCompletion = 0x%08lx\n", fElementWaitingCompletion );
317
318		//	FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - fCallbackAsyncRef[0] = 0x%016llx\n", fCallbackAsyncRef[0] );
319
320			IOFireWireUserClient::sendAsyncResult64( fCallbackAsyncRef, kIOReturnSuccess, args, 3 );
321		}
322		else if( element->type == kTypeSkipped )
323		{
324			fElementWaitingCompletion = element;
325
326			io_user_reference_t args[3];
327			args[0] = (io_user_reference_t)element;		// commandID
328			args[1] = element->getSkippedCount();		// count
329			args[2] = 0;								//zzz for some reason I need to send an arg count of 3 for my data to make it to user space
330														//zzz sounds like a kernel bug. pad it for now
331
332		//	FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - kTypeSkipped count = %d, fElementWaitingCompletion = 0x%08lx\n", element->getSkippedCount(), (UInt32)fElementWaitingCompletion );
333
334		//	FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - fSkippedAsyncRef[0] = 0x%016llx\n", fSkippedAsyncRef[0] );
335
336			IOFireWireUserClient::sendAsyncResult64( fSkippedAsyncRef, kIOReturnSuccess, args, 3 );
337		}
338	}
339}
340
341#pragma mark -
342
343// allocateElement
344//
345//
346
347IOFWUserPHYPacketListener::PHYRxElement * IOFWUserPHYPacketListener::allocateElement( void )
348{
349	//
350	// allocate
351	//
352
353	PHYRxElement * element = fFreeListHead;
354
355	if( element == NULL )
356	{
357		// create elements on demand
358		// make skipped elements up to the threshold
359		if(fAllocatedQueueCount < fMaxQueueCount )
360		{
361			element = new PHYRxElement;
362			if( element != NULL )
363			{
364				element->next = NULL;
365				element->prev = NULL;
366				element->type = kTypeNone;
367				element->state = kFreeState;
368				element->data1 = 0;
369				element->data2 = 0;
370
371				//
372				// link it in
373				//
374
375				fFreeListHead = element;
376				fFreeListTail = element;
377
378				fAllocatedQueueCount++;
379			}
380		}
381	}
382
383	if( element != NULL )
384	{
385		fFreeListHead = element->next;
386		if( fFreeListHead )
387		{
388			fFreeListHead->prev = NULL;
389		}
390		else
391		{
392			FWKLOGASSERT( fFreeListTail == element );
393
394			fFreeListTail = NULL;
395		}
396
397		FWKLOGASSERT( element->prev == NULL );
398		FWKLOGASSERT( element->state == kFreeState );
399
400		element->next = NULL;
401		element->prev = NULL;
402		element->state = kFreeState;
403
404		DebugLog( "IOFWUserPHYPacketListener::allocateElement - element = %p\n", element );
405	}
406
407	return element;
408}
409
410// allocateElement
411//
412//
413
414IOFWUserPHYPacketListener::PHYRxElement * IOFWUserPHYPacketListener::allocateDataElement( void )
415{
416	//
417	// allocate
418	//
419
420	PHYRxElement * element = fFreeListHead;
421
422	if( element == NULL )
423	{
424		// create elements on demand
425		// make data elements only if we can make one more for skipped packets
426
427		if( fAllocatedQueueCount < (fMaxQueueCount - 1) )
428		{
429			element = new PHYRxElement;
430			if( element != NULL )
431			{
432				element->next = NULL;
433				element->prev = NULL;
434				element->type = kTypeNone;
435				element->state = kFreeState;
436				element->data1 = 0;
437				element->data2 = 0;
438
439				//
440				// link it in
441				//
442
443				fFreeListHead = element;
444				fFreeListTail = element;
445
446				fAllocatedQueueCount++;
447			}
448		}
449	}
450
451	if( element != NULL )
452	{
453		// we cannot allocate the last element if it is a data element
454		if( (element != fFreeListTail) ||							// if it's not the tail
455			(fAllocatedQueueCount < (fMaxQueueCount - 1))  )		// or we can allocate more
456		{
457			fFreeListHead = element->next;
458			if( fFreeListHead )
459			{
460				fFreeListHead->prev = NULL;
461			}
462			else
463			{
464				FWKLOGASSERT( fFreeListTail == element );
465
466				fFreeListTail = NULL;
467			}
468
469			FWKLOGASSERT( element->prev == NULL );
470			FWKLOGASSERT( element->state == kFreeState );
471
472			element->next = NULL;
473			element->prev = NULL;
474			element->state = kFreeState;
475
476			DebugLog( "IOFWUserPHYPacketListener::allocateDataElement - element = %p\n", element );
477		}
478		else
479		{
480			element = NULL;
481		}
482	}
483
484	return element;
485}
486
487
488// deallocateElement
489//
490//
491
492void IOFWUserPHYPacketListener::deallocateElement( PHYRxElement * element )
493{
494	DebugLog( "IOFWUserPHYPacketListener::deallocateElement - element = %p\n", element );
495
496	element->next = NULL;
497	element->prev = fFreeListTail;
498	element->state = kFreeState;
499
500	if( fFreeListTail )
501	{
502		fFreeListTail->next = element;
503	}
504	else
505	{
506		FWKLOGASSERT( fFreeListHead == NULL )
507
508		fFreeListHead = element;
509	}
510
511	fFreeListTail = element;
512
513	FWKLOGASSERT( fFreeListHead != NULL );
514	FWKLOGASSERT( fFreeListTail != NULL );
515}
516
517#pragma mark -
518
519// destroyAllElements
520//
521//
522
523void IOFWUserPHYPacketListener::destroyAllElements( void )
524{
525	DebugLog(( "IOFWUserPHYPacketListener::destroyAllElements\n" ));
526
527	//
528	// return all elements to the free pool
529	//
530
531	{
532		PHYRxElement * element = fPendingListHead;
533
534		while( element )
535		{
536			PHYRxElement * next_element = element->next;
537
538			removeElementFromPendingList( element );
539
540			deallocateElement( element );
541
542			element = next_element;
543		}
544
545		FWKLOGASSERT( fPendingListHead == NULL );
546		fPendingListHead = NULL;		// should already be NULL
547
548		FWKLOGASSERT( fPendingListTail == NULL );
549		fPendingListTail = NULL;		// should already be NULL
550	}
551
552	//
553	// delete all elements in free pool
554	//
555
556	{
557		PHYRxElement * element = fFreeListHead;
558		while( element )
559		{
560			PHYRxElement * next_element = element->next;
561
562			delete element;
563
564			element = next_element;
565		}
566
567		fFreeListHead = 0;
568		fFreeListTail = 0;
569	}
570}
571
572#pragma mark -
573
574// addElementToPendingList
575//
576//
577
578void IOFWUserPHYPacketListener::addElementToPendingList( PHYRxElement * element )
579{
580	DebugLog( "IOFWUserPHYPacketListener::addElementToPendingList - element = %p\n", element );
581
582	// pending should only be entered from the free state
583
584	FWKLOGASSERT( element != NULL );
585	FWKLOGASSERT( element->next == NULL );
586	FWKLOGASSERT( element->state == kFreeState );
587
588	element->next = NULL;
589	element->prev = fPendingListTail;
590	element->state = kPendingState;
591
592	if( fPendingListTail )
593	{
594		fPendingListTail->next = element;
595	}
596	else
597	{
598		FWKLOGASSERT( fPendingListHead == NULL );
599
600		fPendingListHead = element;
601	}
602
603	fPendingListTail = element;
604
605	FWKLOGASSERT( fPendingListHead != NULL );
606	FWKLOGASSERT( fPendingListTail != NULL );
607
608}
609
610// removeElementFromPendingList
611//
612//
613
614void IOFWUserPHYPacketListener::removeElementFromPendingList( PHYRxElement * element )
615{
616	DebugLog( "IOFWUserPHYPacketListener::removeElementFromPendingList - element = %p\n", element );
617
618	// element on the pending list should not be in the free state
619
620	FWKLOGASSERT( element->state != kFreeState );
621
622	// remove from pending list
623
624	//
625	// handle head / next ptr
626	//
627
628	if( fPendingListHead == element )
629	{
630		FWKLOGASSERT( element->prev == NULL );
631
632		fPendingListHead = element->next;
633		if( fPendingListHead != NULL )
634		{
635			fPendingListHead->prev = NULL;
636		}
637	}
638	else
639	{
640		FWPANICASSERT( element->prev != NULL );
641
642		element->prev->next = element->next;
643	}
644
645	//
646	// handle tail / previous ptr
647	//
648
649	if( fPendingListTail == element )
650	{
651		FWKLOGASSERT( element->next == NULL );
652
653		fPendingListTail = element->prev;
654		if( fPendingListTail != NULL )
655		{
656			fPendingListTail->prev = NULL;
657		}
658	}
659	else
660	{
661		FWPANICASSERT( element->next != NULL );
662
663		element->next->prev = element->prev;
664	}
665
666	//
667	// reset link ptrs
668	//
669
670	element->next = NULL;
671	element->prev = NULL;
672}
673