1/*
2 * Copyright (c) 1998-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#include <IOKit/avc/IOFireWireAVCUnit.h>
23#include <IOKit/avc/IOFireWireAVCCommand.h>
24#include <IOKit/avc/IOFireWireAVCConsts.h>
25#include <IOKit/IOMessage.h>
26#include <IOKit/IOKitKeys.h>
27#include <IOKit/firewire/IOFireWireUnit.h>
28#include <IOKit/firewire/IOFireWireBus.h>
29#include <IOKit/firewire/IOFWAddressSpace.h>
30#include <IOKit/firewire/IOFireWireController.h>
31#include <IOKit/avc/IOFireWirePCRSpace.h>
32
33#if FIRELOG
34#import <IOKit/firewire/FireLog.h>
35#define FIRELOG_MSG(x) FireLog x
36#else
37#define FIRELOG_MSG(x) do {} while (0)
38#endif
39
40const OSSymbol *gIOFireWireAVCUnitType;
41const OSSymbol *gIOFireWireAVCSubUnitType;
42const OSSymbol *gIOFireWireAVCSubUnitCount[kAVCNumSubUnitTypes];
43
44OSDefineMetaClassAndStructors(IOFireWireAVCAsynchronousCommand, IOCommand)
45OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 0);
46OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 1);
47OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 2);
48OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 3);
49
50OSDefineMetaClass(IOFireWireAVCNub, IOService)
51OSDefineAbstractStructors(IOFireWireAVCNub, IOService)
52//OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 0);
53OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 1);
54OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 2);
55OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 3);
56
57OSDefineMetaClassAndStructors(IOFireWireAVCUnit, IOFireWireAVCNub)
58OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 0);
59OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 1);
60OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 2);
61OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 3);
62
63//////////////////////////////////////////////////////
64// IOFireWireAVCAsynchronousCommand::isPending
65//////////////////////////////////////////////////////
66bool IOFireWireAVCAsynchronousCommand::isPending(void)
67{
68	bool res;
69
70	switch (cmdState)
71	{
72		case kAVCAsyncCommandStateRequestSent:				// Command has been submitted, but no write done yet
73		case kAVCAsyncCommandStateWaitingForResponse:		// Received write done, but no first response
74		case kAVCAsyncCommandStateReceivedInterimResponse:  // Received interim response, but no final response
75			res = true;
76			break;
77
78		case kAVCAsyncCommandStatePendingRequest:			// Command created, but not yet submitted
79		case kAVCAsyncCommandStateRequestFailed:			// Submitting the request failed
80		case kAVCAsyncCommandStateReceivedFinalResponse:	// Received a final response
81		case kAVCAsyncCommandStateTimeOutBeforeResponse:	// Timeout before first response
82		case kAVCAsyncCommandStateBusReset:					// Bus reset before first response
83		case kAVCAsyncCommandStateOutOfMemory:				// Ran out of memory
84		case kAVCAsyncCommandStateCanceled:					// Command cancled
85		default:
86			res = false;
87			break;
88	};
89
90	return res;
91}
92
93//////////////////////////////////////////////////////
94// IOFireWireAVCAsynchronousCommand::free
95//////////////////////////////////////////////////////
96void IOFireWireAVCAsynchronousCommand::free()
97{
98    FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::free (this=0x%08X)\n",this));
99
100	// Only allow a free if we're not pending.
101	if ( isPending() )
102	{
103		cancel();
104	}
105
106	fWriteNodeID = kIOFWAVCAsyncCmdFreed;	// Special flag to indicate this command was cancled in this unit's free routine.
107	// The command is now canceled
108	cmdState	 = kAVCAsyncCommandStateCanceled;
109
110	if ( fWriteCmd->Busy() )
111	{
112		fWriteCmd->cancel(kIOReturnOffline);
113	}
114
115	if ( fWriteCmd )
116	{
117		fWriteCmd->release();
118		fWriteCmd = NULL;
119	}
120
121	if( fDelayCmd->Busy() )
122	{
123		fDelayCmd->cancel(kIOReturnOffline);
124	}
125
126	if (fDelayCmd)
127	{
128		fDelayCmd->release();
129		fDelayCmd = NULL;
130	}
131
132	if (fMem)
133	{
134		fMem->release();
135		fMem = NULL;
136	}
137
138	if (pCommandBuf)
139	{
140		delete[] pCommandBuf;
141		pCommandBuf = NULL;
142	}
143
144	if (pInterimResponseBuf)
145	{
146		delete[] pInterimResponseBuf;
147		pInterimResponseBuf = NULL;
148	}
149
150	if (pFinalResponseBuf)
151	{
152		delete[] pFinalResponseBuf;
153		pFinalResponseBuf = NULL;
154	}
155
156	OSObject::free();
157}
158
159//////////////////////////////////////////////////////
160// IOFireWireAVCAsynchronousCommand::init
161//////////////////////////////////////////////////////
162IOReturn IOFireWireAVCAsynchronousCommand::init(const UInt8 * command,
163												UInt32 len,
164												IOFireWireAVCAsynchronousCommandCallback completionCallback,
165												void *pClientRefCon)
166{
167	FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::init (this=0x%08X, opCode=0x%02X)\n",this,command[kAVCOpcode]));
168
169	// Validate the length of the command buffer
170    if(len == 0 || len > 512)
171        return kIOReturnBadArgument;
172
173	// Initialize async command object
174	pCommandBuf = new UInt8[len];
175	if (!pCommandBuf)
176		return kIOReturnNoMemory;
177	bcopy(command, pCommandBuf, len);
178	cmdLen = len;
179	cmdState = kAVCAsyncCommandStatePendingRequest;
180	fCallback = completionCallback;
181	pRefCon = pClientRefCon;
182	pInterimResponseBuf = NULL;
183	interimResponseLen = 0;
184	pFinalResponseBuf = NULL;
185	finalResponseLen = 0;
186	fAVCUnit = NULL;
187	fMem = NULL;
188	fWriteCmd = NULL;
189	fDelayCmd = NULL;
190	fWriteNodeID = kFWBadNodeID;
191	fWriteGen = 0xFFFFFFFF;
192
193	return kIOReturnSuccess;
194}
195
196//////////////////////////////////////////////////////
197// IOFireWireAVCAsynchronousCommand::submit
198//////////////////////////////////////////////////////
199IOReturn IOFireWireAVCAsynchronousCommand::submit(IOFireWireAVCNub *pAVCNub)
200{
201	IOReturn res;
202	FWAddress addr;
203	IOFireWireAVCUnit *pAVCUnit;
204	IOFireWireAVCSubUnit *pAVCSubunit;
205
206    FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::submit (this=0x%08X, opCode=0x%02X)\n",this,pCommandBuf[kAVCOpcode]));
207
208	retain();
209
210	// If fWriteNodeID is kIOFWAVCAsyncCmdFreed, this command was cancled in the AVCUnit's free routine,
211	// so, we will not let this command be reinited or submitted again!
212	if (fWriteNodeID == kIOFWAVCAsyncCmdFreed)
213	{
214		release();
215		return kIOReturnNotPermitted;
216	}
217
218	// Figure out if the nub is a unit or subunit
219	if (OSDynamicCast(IOFireWireAVCUnit, pAVCNub) != NULL)
220	{
221		pAVCUnit = (IOFireWireAVCUnit*) pAVCNub;
222	}
223	else if (OSDynamicCast(IOFireWireAVCSubUnit, pAVCNub) != NULL)
224	{
225		pAVCSubunit = (IOFireWireAVCSubUnit*) pAVCNub;
226		pAVCUnit = pAVCSubunit->fAVCUnit;
227	}
228	else
229	{
230		release();
231		return kIOReturnBadArgument;
232	}
233
234    // setup AVC Request address
235    addr.addressHi   = kCSRRegisterSpaceBaseAddressHi;
236    addr.addressLo   = kFCPCommandAddress;
237
238	// Only submit the write command, if we are pending request
239	if (cmdState != kAVCAsyncCommandStatePendingRequest)
240	{
241		release();
242		return kIOReturnNotPermitted;
243	}
244
245	// Save a pointer to the unit
246	fAVCUnit = pAVCUnit;
247
248	if ( not fAVCUnit->available() )
249	{
250		release();
251		return kIOReturnNotPermitted;
252	}
253
254	// Create a memory descriptor for the request bytes
255	fMem = IOMemoryDescriptor::withAddress((void *)pCommandBuf,
256										   cmdLen,
257										   kIODirectionOutIn);
258	if(!fMem)
259	{
260		release();
261		return kIOReturnNoMemory;
262	}
263
264	// Prepare the memory descriptor
265	IOReturn err = fMem->prepare();
266	if( err != kIOReturnSuccess )
267	{
268		release();
269		return kIOReturnNoMemory;
270	}
271
272	// Create a write command
273	fWriteCmd = fAVCUnit->fDevice->createWriteCommand(addr,
274											fMem,
275											IOFireWireAVCUnit::AVCAsynchRequestWriteDone,
276											this);
277	if(!fWriteCmd)
278	{
279		release();
280		return kIOReturnNoMemory;
281	}
282
283	// Create a delay command for providing a timeout when waiting for AVC response
284	fDelayCmd = fAVCUnit->fIOFireWireAVCUnitExpansion->fControl->createDelayedCmd(250000,
285																					IOFireWireAVCUnit::AVCAsynchDelayDone,
286																					this);
287	if (!fDelayCmd)
288	{
289		release();
290		return kIOReturnNoMemory;
291	}
292
293	// Get the async command lock
294	fAVCUnit->lockAVCAsynchronousCommandLock();
295
296	// Add this command to the unit's array of pending async commands
297	// Try to add the new command to our array of outstanding commands
298	if(!fAVCUnit->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->setObject(this))
299	{
300		res = kIOReturnNoMemory;
301	}
302	else
303	{
304		// Submit the write command
305		res = fWriteCmd->submit();
306		// Note that the write done routine may have already been called during the submit
307		// Only change the command state here if it hasn't already changed
308		if (cmdState == kAVCAsyncCommandStatePendingRequest)
309		{
310			if (res == kIOReturnSuccess)
311				cmdState = kAVCAsyncCommandStateRequestSent;
312			else
313				cmdState = kAVCAsyncCommandStateRequestFailed;
314		}
315	}
316
317	// Free the async command lock
318	fAVCUnit->unlockAVCAsynchronousCommandLock();
319
320	release();
321
322	return res;
323}
324
325//////////////////////////////////////////////////////
326// IOFireWireAVCAsynchronousCommand::reinit
327//////////////////////////////////////////////////////
328IOReturn IOFireWireAVCAsynchronousCommand::reinit(const UInt8 * command, UInt32 len)
329{
330    FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::reinit (this=0x%08X)\n",this));
331
332	// If fWriteNodeID is kIOFWAVCAsyncCmdFreed, this command was cancled in the AVCUnit's free routine,
333	// so, we will not let this command be reinited or submitted again!
334	if (fWriteNodeID == kIOFWAVCAsyncCmdFreed)
335		return kIOReturnNotPermitted;
336
337	// Only allow a reinit if we're not pending.
338	if (isPending())
339		return kIOReturnNotPermitted;
340
341	// Validate the length of the command buffer
342    if(len == 0 || len > 512)
343        return kIOReturnBadArgument;
344
345	if (fWriteCmd)
346		fWriteCmd->release();
347
348	if (fDelayCmd)
349		fDelayCmd->release();
350
351	if (fMem)
352		fMem->release();
353
354	if (pCommandBuf)
355		delete pCommandBuf;
356
357	if (pInterimResponseBuf)
358		delete pInterimResponseBuf;
359
360	if (pFinalResponseBuf)
361		delete pFinalResponseBuf;
362
363	// Initialize async command object
364	pCommandBuf = new UInt8[len];
365	if (!pCommandBuf)
366		return kIOReturnNoMemory;
367	bcopy(command, pCommandBuf, len);
368
369	cmdLen = len;
370	cmdState = kAVCAsyncCommandStatePendingRequest;
371	pInterimResponseBuf = NULL;
372	interimResponseLen = 0;
373	pFinalResponseBuf = NULL;
374	finalResponseLen = 0;
375	fAVCUnit = NULL;
376	fMem = NULL;
377	fWriteCmd = NULL;
378	fDelayCmd = NULL;
379	fWriteNodeID = kFWBadNodeID;
380	fWriteGen = 0xFFFFFFFF;
381
382	return kIOReturnSuccess;
383}
384
385//////////////////////////////////////////////////////
386// IOFireWireAVCAsynchronousCommand::cancel
387//////////////////////////////////////////////////////
388IOReturn IOFireWireAVCAsynchronousCommand::cancel(void)
389{
390	// Local Vars
391	UInt32 cmdIndex;
392
393    FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::cancel (this=0x%08X)\n",this));
394
395	// TODO: What if the AVCUnit is already been freed?
396
397	// TODO: Do some state checking before continuing
398
399	// Get the async command lock
400	fAVCUnit->lockAVCAsynchronousCommandLock();
401
402	// Cancel the delay command, and write command (if needed?)
403	if ((cmdState == kAVCAsyncCommandStateRequestSent) && (fWriteCmd))
404		fWriteCmd->cancel(kIOReturnAborted);
405	else if ((cmdState == kAVCAsyncCommandStateWaitingForResponse) && (fDelayCmd))
406			fDelayCmd->cancel(kIOReturnAborted);
407
408	// The command is now canceled
409	cmdState = kAVCAsyncCommandStateCanceled;
410
411	// Remove this object from the unit's array
412	cmdIndex = fAVCUnit->indexOfAVCAsynchronousCommandObject(this);
413	if (cmdIndex != 0xFFFFFFFF)
414	{
415		fAVCUnit->removeAVCAsynchronousCommandObjectAtIndex(cmdIndex);
416	}
417
418	// Free the async command lock
419	fAVCUnit->unlockAVCAsynchronousCommandLock();
420
421	// We do a client callback here
422	if (fCallback != NULL)
423		fCallback(pRefCon,this);
424
425	return kIOReturnSuccess;
426}
427
428//////////////////////////////////////////////////////
429// IOFireWireAVCUnit::setProperties
430//////////////////////////////////////////////////////
431IOReturn IOFireWireAVCUnit::setProperties (OSObject * properties )
432{
433	IOReturn result = kIOReturnSuccess ;
434
435	//IOLog(IOFireWireAVCUnit::setProperties\n");
436
437	OSDictionary*	dict = OSDynamicCast( OSDictionary, properties ) ;
438
439	if ( dict )
440	{
441		OSObject*	value = dict->getObject( "RobustAVCResponseMatching" ) ;
442
443		if ( value )
444		{
445			// Disable robust AV/C command/response matching
446			//IOLog("Disabling RobustAVCResponseMatching for AV/C device 0x%08X\n",(unsigned int) this);
447			fIOFireWireAVCUnitExpansion->enableRobustAVCCommandResponseMatching = false;
448		}
449		else
450		{
451			result = IOFireWireAVCNub::setProperties ( properties ) ;
452		}
453	}
454	else
455		result = IOFireWireAVCNub::setProperties ( properties ) ;
456
457	return result ;
458}
459
460//////////////////////////////////////////////////////
461// IOFireWireAVCUnit::AVCResponse
462//////////////////////////////////////////////////////
463UInt32 IOFireWireAVCUnit::AVCResponse(void *refcon, UInt16 nodeID, IOFWSpeed &speed,
464                    FWAddress addr, UInt32 len, const void *buf, IOFWRequestRefCon requestRefcon)
465{
466	// Local Vars
467    IOFireWireAVCUnit *me = (IOFireWireAVCUnit *)refcon;
468	UInt8 *pResponseBytes = (UInt8*) buf;
469	UInt32 res = kFWResponseAddressError;
470	UInt32 i;
471	IOFireWireAVCAsynchronousCommand *pCmd;
472	bool foundOutstandingAVCAsynchCommandForNode = false;
473	bool matchFound = false;
474	UInt32 matchedCommandIndex;
475	bool doCallback = false;
476
477    FIRELOG_MSG(("IOFireWireAVCUnit::AVCResponse (this=0x%08X)\n",me));
478    FIRELOG_MSG(("AVCResponse Info: nodeID=0x%04X, opCode=0x%02X, avcAddress=0x%02X respLen=0x%08X\n",nodeID, pResponseBytes[kAVCOpcode], pResponseBytes[kAVCAddress],len));
479
480	// Check this packet for validity
481	if ((addr.addressLo != kFCPResponseAddress) || (len < 3))
482		return res;
483
484	// Get the async command lock
485	me->lockAVCAsynchronousCommandLock();
486
487	// Look through all the pending AVCAsynch commands to find a match
488	for (i = 0; i < me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(); i++)
489	{
490		pCmd = (IOFireWireAVCAsynchronousCommand*) me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
491		FIRELOG_MSG(("Evaluating outstanding AVC async cmd %d (%d total): cmd = 0x%08X, nodeID = 0x%04X, opCode=0x%02X, avcAddress=0x%02X pending=%s\n",
492					 i,
493					 me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(),
494					 pCmd,
495					 pCmd->fWriteNodeID,
496					 pCmd->pCommandBuf[kAVCOpcode] ,
497					 pCmd->pCommandBuf[kAVCAddress],
498					 (pCmd->isPending() ? "YES" : "NO")
499					 ));
500
501		// Does the nodeID match?
502		if (pCmd->fWriteNodeID == nodeID)
503		{
504			// Mark that we found at least one pending AVCAsync command for this node
505			foundOutstandingAVCAsynchCommandForNode = true;
506
507			// Evaluate the AVCAddress, and Opcode, looking for a match
508			if ((pCmd->pCommandBuf[kAVCAddress] == pResponseBytes[kAVCAddress]) && (pCmd->pCommandBuf[kAVCOpcode] == pResponseBytes[kAVCOpcode]))
509			{
510				if ((pCmd->pCommandBuf[kAVCCommandResponse] == kAVCNotifyCommand) &&
511						((pResponseBytes[kAVCCommandResponse] == kAVCAcceptedStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCInTransitionStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCImplementedStatus)))
512				{
513					// This is not a match because notify commands cannot have this type of response!
514				}
515				else
516				{
517					// This is a match
518					matchFound = true;
519					matchedCommandIndex = i;
520					break;
521				}
522			}
523		}
524	}
525
526	// If we didn't match, yet we have an oustanding command for this node, and the response is from a tape-subunit,
527	// see if this is the special-case of the tape-subunit transport-state command, which overwrites the opcode
528	// in the response packet.
529	if ((!matchFound) && (foundOutstandingAVCAsynchCommandForNode) && ((pResponseBytes[kAVCAddress] & 0xF8) == 0x20))
530	{
531		// Look again through all the pending AVCAsynch commands to find a match
532		for (i = 0; i < me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(); i++)
533		{
534			pCmd = (IOFireWireAVCAsynchronousCommand*) me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
535
536			// Does the nodeID match?
537			if (pCmd->fWriteNodeID == nodeID)
538			{
539				// Evaluate the AVCAddress for a match, and see if this command is the tape subunit transport-state opcode
540				if ((pCmd->pCommandBuf[kAVCAddress] == pResponseBytes[kAVCAddress]) && (pCmd->pCommandBuf[kAVCOpcode] == 0xD0))
541				{
542					// It is a tape-subunit transport state command. See if the response packet look like it could indeed
543					// be the one we're looking for
544					if (((pResponseBytes[kAVCOpcode] == 0xC1) ||
545						 (pResponseBytes[kAVCOpcode] == 0xC2) ||
546						 (pResponseBytes[kAVCOpcode] == 0xC3) ||
547						 (pResponseBytes[kAVCOpcode] == 0xC4)) && (len == 4))
548					{
549						if ((pCmd->pCommandBuf[kAVCCommandResponse] == kAVCNotifyCommand) &&
550							((pResponseBytes[kAVCCommandResponse] == kAVCAcceptedStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCInTransitionStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCImplementedStatus)))
551						{
552							// This is not a match because notify commands cannot have this type of response!
553						}
554						else
555						{
556							// This is a match
557							matchFound = true;
558							matchedCommandIndex = i;
559							break;
560						}
561					}
562				}
563			}
564		}
565	}
566
567	// We found a match, so deal with it
568	if (matchFound)
569	{
570		FIRELOG_MSG(("AVC Async Request/Response Match Found: %d\n",matchedCommandIndex));
571
572		// At this point, if the interim response buffer is NULL, then this is the
573		// first response for this command, so now is the time to cancle the timer
574		if (pCmd->pInterimResponseBuf == NULL)
575		{
576			// Abort the timeOut delay command
577			if (pCmd->fDelayCmd)
578				pCmd->fDelayCmd->cancel(kIOReturnAborted);
579		}
580
581		// Is this an Interim, or Final Response
582		if (pResponseBytes[kAVCCommandResponse] == 0x0F)
583		{
584			// Interim Response
585
586			// Allocate the command's interim response buffer, and copy response bytes
587			pCmd->pInterimResponseBuf = new UInt8[len];
588			if (pCmd->pInterimResponseBuf)
589			{
590				pCmd->interimResponseLen = len;
591				bcopy(pResponseBytes, pCmd->pInterimResponseBuf, len);
592
593				// Set the command' state
594				pCmd->cmdState = kAVCAsyncCommandStateReceivedInterimResponse;
595			}
596			else
597			{
598				pCmd->cmdState = kAVCAsyncCommandStateOutOfMemory;
599
600				// Remove this command from the unit's pending async command list
601				me->removeAVCAsynchronousCommandObjectAtIndex(matchedCommandIndex);
602			}
603		}
604		else
605		{
606			// Final Response
607
608			// Allocate the command's final response buffer, and copy response bytes
609			pCmd->pFinalResponseBuf = new UInt8[len];
610			if (pCmd->pFinalResponseBuf)
611			{
612				pCmd->finalResponseLen = len;
613				bcopy(pResponseBytes, pCmd->pFinalResponseBuf, len);
614
615				// Set the command' state
616				pCmd->cmdState = kAVCAsyncCommandStateReceivedFinalResponse;
617			}
618			else
619				pCmd->cmdState = kAVCAsyncCommandStateOutOfMemory;
620
621			// Remove this command from the unit's pending async command list
622			me->removeAVCAsynchronousCommandObjectAtIndex(matchedCommandIndex);
623		}
624
625		// We need to do a callback after we release the lock
626		doCallback = true;
627
628		res = kFWResponseComplete;
629	}
630
631	// Free the async command lock
632	me->unlockAVCAsynchronousCommandLock();
633
634	// Se if we need to do a callback to the client
635	if (doCallback == true)
636	{
637		// Notify the client
638		if (pCmd->fCallback != NULL)
639			pCmd->fCallback(pCmd->pRefCon,pCmd);
640	}
641
642	// If we don't have a match, see if there is a pending blocking-AVC command for this node
643	if (!matchFound)
644	{
645		// if this is for us, copy the status bytes from fPseudoSpace
646		if(me->fCommand)
647		{
648			if (me->fIOFireWireAVCUnitExpansion->enableRobustAVCCommandResponseMatching)
649				res = me->fCommand->handleResponse(nodeID, len, buf);
650			else
651				res = me->fCommand->handleResponseWithSimpleMatching(nodeID, len, buf);
652		}
653	}
654
655    return res;
656}
657
658//////////////////////////////////////////////////////
659// IOFireWireAVCUnit::rescanSubUnits
660//////////////////////////////////////////////////////
661void IOFireWireAVCUnit::rescanSubUnits(void *arg)
662{
663
664    IOFireWireAVCUnit *me = (IOFireWireAVCUnit *)arg;
665
666	FIRELOG_MSG(("IOFireWireAVCUnit::rescanSubUnits (this=0x%08X)\n",me));
667
668    me->updateSubUnits(false);
669}
670
671//////////////////////////////////////////////////////
672// IOFireWireAVCUnit::updateSubUnits
673//////////////////////////////////////////////////////
674void IOFireWireAVCUnit::updateSubUnits(bool firstTime)
675{
676	FIRELOG_MSG(("IOFireWireAVCUnit::updateSubUnits (this=0x%08X)\n",this));
677
678    IOReturn res;
679    UInt32 size;
680    UInt8 cmd[8],response[8];
681    OSObject *prop;
682    bool hasFCP = true;
683// Get SubUnit info
684    cmd[kAVCCommandResponse] = kAVCStatusInquiryCommand;
685    cmd[kAVCAddress] = kAVCUnitAddress;
686    cmd[kAVCOpcode] = kAVCSubunitInfoOpcode;
687    cmd[kAVCOperand0] = 7;
688    cmd[4] = cmd[5] = cmd[6] = cmd[7] = 0xff;
689    size = 8;
690
691    for(int i = 0; i<10; i++) {
692        res = AVCCommand(cmd, 8, response, &size);
693        if(res == (kIOFireWireResponseBase + kFWResponseConflictError)) {
694            IOSleep(10);
695            continue;	// Try again
696        }
697        else if(res == kIOReturnSuccess && response[kAVCOperand1] == 0xff) {
698            // Some devices initially say they have no subunits.
699            IOSleep(10);
700            continue;	// Try again
701        }
702		else if(res == kIOReturnOffline){
703			// Bus-reset occurred.
704			FIRELOG_MSG(("IOFireWireAVCUnit %p, bus-reset during subunit scan! firstTime=%s\n",this,firstTime == true ? "true" : "false"));
705			IOSleep(10);
706			continue;	// Try again
707		}
708        else
709            break;		// Got a final result code
710    }
711    if(res != kIOReturnSuccess || response[kAVCCommandResponse] != kAVCImplementedStatus) {
712        if(firstTime) {
713            // Sony convertor box doesn't do AVC, make it look like a camcorder.
714            // Panasonic NV-C5 doesn't support SubunitInfo query but does support VCR commands
715            if(res != kIOReturnSuccess)
716                hasFCP = false;
717
718            response[kAVCOperand1] = 0x20;	// One VCR
719            response[kAVCOperand2] = 0xff;
720            response[kAVCOperand3] = 0xff;
721            response[kAVCOperand4] = 0xff;
722        }
723        else
724		{
725			this->release();	// If this is not the first-time we need to release before returning
726			return;	// No update necessary
727		}
728    }
729    else if(size == 5) {
730        // some mLAN devices don't report their subunit info correctly,
731        // set it up here
732        size = 8;
733        response[kAVCOperand1] = 0x08;	// One Audio subunit
734        response[kAVCOperand2] = 0xff;
735        response[kAVCOperand3] = 0xff;
736        response[kAVCOperand4] = 0xff;
737    }
738    if(firstTime)
739        setProperty("supportsFCP", hasFCP);
740
741    // Zero count of subunits before updating with new counts
742    bzero(fSubUnitCount, sizeof(fSubUnitCount));
743    for(int i=0; i<kAVCNumSubUnitTypes; i++) {
744        removeProperty(gIOFireWireAVCSubUnitCount[i]);
745    }
746
747    for(int i=0; i<4; i++) {
748        UInt8 val = response[kAVCOperand1+i];
749        if(val != 0xff) {
750            UInt8 type, num;
751            type = val >> 3;
752            num = (val & 0x7)+1;
753            fSubUnitCount[type] = num;
754            //IOLog("Subunit type %x, num %d\n", type, num);
755            setProperty(gIOFireWireAVCSubUnitCount[type]->getCStringNoCopy(), num, 8);
756
757            // Create sub unit nub if it doesn't exist
758            IOFireWireAVCSubUnit *sub = NULL;
759            OSDictionary * propTable = 0;
760            do {
761                propTable = OSDictionary::withCapacity(6);
762                if(!propTable)
763                    break;
764                prop = OSNumber::withNumber(type, 32);
765                propTable->setObject(gIOFireWireAVCSubUnitType, prop);
766                prop->release();
767                if(!firstTime) {
768                    OSIterator *childIterator;
769                    IOFireWireAVCSubUnit * found = NULL;
770                    childIterator = getClientIterator();
771                    if(childIterator) {
772                        OSObject *child;
773                        while( (child = childIterator->getNextObject())) {
774                            found = OSDynamicCast(IOFireWireAVCSubUnit, child);
775                            if(found && found->matchPropertyTable(propTable)) {
776                                break;
777                            }
778                            else
779                                found = NULL;
780                        }
781                        childIterator->release();
782                        if(found) {
783                            break;
784                        }
785                    }
786                }
787                sub = new IOFireWireAVCSubUnit;
788                if(!sub)
789                    break;
790
791                if (!sub->init(propTable, this))
792                    break;
793                if (!sub->attach(this))
794                    break;
795                sub->setProperty("supportsFCP", hasFCP);
796
797                sub->registerService();
798
799				// Special handling for Sony TVs - make them root!
800				if (type == 0)
801				{
802					OSObject *prop;
803					OSNumber *deviceGUID;
804					unsigned long long guidVal;
805
806					prop = getProperty(gFireWire_GUID);
807					deviceGUID = OSDynamicCast( OSNumber, prop );
808					guidVal = deviceGUID->unsigned64BitValue();
809
810					if ((guidVal & 0xFFFFFF0000000000LL) == 0x0800460000000000LL) // Sony
811					{
812						fDevice->setNodeFlags(kIOFWMustBeRoot);
813					}
814				}
815
816            } while (0);
817            if(sub)
818                sub->release();
819            if(propTable)
820                propTable->release();
821        }
822    }
823
824    // Prune sub units that have gone away.
825    if(!firstTime) {
826        OSIterator *childIterator;
827        IOFireWireAVCSubUnit * sub = NULL;
828        childIterator = getClientIterator();
829        if(childIterator) {
830            OSObject *child;
831            while( (child = childIterator->getNextObject())) {
832                sub = OSDynamicCast(IOFireWireAVCSubUnit, child);
833                if(sub) {
834                    OSNumber *type;
835                    type = OSDynamicCast(OSNumber, sub->getProperty(gIOFireWireAVCSubUnitType));
836                    if(type && !fSubUnitCount[type->unsigned32BitValue()])
837                        sub->terminate();
838                }
839            }
840            childIterator->release();
841        }
842    }
843
844	if (!firstTime)
845		this->release(); // If this is not the first-time we need to release before returning
846}
847
848//////////////////////////////////////////////////////
849// IOFireWireAVCUnit::start
850//////////////////////////////////////////////////////
851bool IOFireWireAVCUnit::start(IOService *provider)
852{
853	FIRELOG_MSG(("IOFireWireAVCUnit::start (this=0x%08X)\n",this));
854
855    OSObject *prop;
856    UInt32 type;
857	OSNumber *deviceGUID;
858	unsigned long long guidVal;
859	UInt8 series;
860
861    fDevice = OSDynamicCast(IOFireWireNub, provider);
862    if(!fDevice)
863        return false;
864
865	// Retain our provider, the IOFireWireUnit object
866	fDevice->retain();
867
868	// create/clear expansion data
869	fIOFireWireAVCUnitExpansion = (ExpansionData*) IOMalloc( sizeof(ExpansionData) );
870	if( fIOFireWireAVCUnitExpansion == NULL )
871		return false;
872	else
873		bzero( fIOFireWireAVCUnitExpansion, sizeof(ExpansionData) );
874
875	// Get the controller
876	fIOFireWireAVCUnitExpansion->fControl = fDevice->getController();
877    if(!fIOFireWireAVCUnitExpansion->fControl)
878		return false;
879
880	// Enable robust AV/C Command/Response Matching
881	fIOFireWireAVCUnitExpansion->enableRobustAVCCommandResponseMatching = true;
882
883	// Create array to hold outstanding async AVC commands
884	fIOFireWireAVCUnitExpansion->fAVCAsyncCommands = OSArray::withCapacity(1);
885
886    if(!gIOFireWireAVCUnitType)
887        gIOFireWireAVCUnitType = OSSymbol::withCString("Unit_Type");
888    if(!gIOFireWireAVCUnitType)
889		return false;
890
891    if(!gIOFireWireAVCSubUnitType)
892        gIOFireWireAVCSubUnitType = OSSymbol::withCString("SubUnit_Type");
893    if(!gIOFireWireAVCSubUnitType)
894		return false;
895
896    for(int i=0; i<kAVCNumSubUnitTypes; i++) {
897        char buff[16];
898        if(!gIOFireWireAVCSubUnitCount[i]) {
899            snprintf(buff, sizeof(buff), "AVCSubUnit_%x", i);
900            gIOFireWireAVCSubUnitCount[i] = OSSymbol::withCString(buff);
901            if(!gIOFireWireAVCSubUnitCount[i])
902				return false;
903        }
904    }
905
906    if( !IOService::start(provider))
907		return false;
908
909    fFCPResponseSpace = fDevice->getBus()->createInitialAddressSpace(kFCPResponseAddress, 512,
910                                                                        NULL, AVCResponse, this);
911    if(!fFCPResponseSpace)
912		return false;
913
914    fFCPResponseSpace->activate();
915
916    avcLock = IOLockAlloc();
917    if (avcLock == NULL) {
918        IOLog("IOAVCUnit::start avcLock failed\n");
919		return false;
920    }
921
922    cmdLock = IOLockAlloc();
923    if (cmdLock == NULL) {
924        IOLog("IOAVCUnit::start cmdLock failed\n");
925        return false;
926    }
927
928// Get Unit type
929    IOReturn res;
930    UInt32 size;
931    UInt8 cmd[8],response[8];
932	UInt32 unitInfoRetryCount = 0;
933
934    cmd[kAVCCommandResponse] = kAVCStatusInquiryCommand;
935    cmd[kAVCAddress] = kAVCUnitAddress;
936    cmd[kAVCOpcode] = kAVCUnitInfoOpcode;
937    cmd[3] = cmd[4] = cmd[5] = cmd[6] = cmd[7] = 0xff;
938    size = 8;
939    res = AVCCommand(cmd, 8, response, &size);
940	if(kIOReturnSuccess != res)
941	{
942		do
943		{
944			unitInfoRetryCount++;
945			IOSleep(2000);	// two seconds, give device time to get it's act together
946			size = 8;
947			res = AVCCommand(cmd, 8, response, &size);
948		}while((kIOReturnSuccess != res) && (unitInfoRetryCount <= 4));
949    }
950
951	if(kIOReturnSuccess != res || response[kAVCCommandResponse] != kAVCImplementedStatus)
952        type = kAVCVideoCamera;	// Anything that doesn't implement AVC properly is probably a camcorder!
953    else
954        type = IOAVCType(response[kAVCOperand1]);
955
956    // Copy over matching properties from FireWire Unit
957    prop = provider->getProperty(gFireWireVendor_ID);
958    if(prop)
959        setProperty(gFireWireVendor_ID, prop);
960
961
962	prop = provider->getProperty(gFireWire_GUID);
963    if(prop)
964	{
965        setProperty(gFireWire_GUID, prop);
966
967		// Check the guid to see if this device requires special asynch throttling
968		deviceGUID = OSDynamicCast( OSNumber, prop );
969		guidVal = deviceGUID->unsigned64BitValue();
970		if ((guidVal & 0xFFFFFFFFFF000000LL) == 0x0000850000000000LL)
971		{
972			series = (UInt8) ((guidVal & 0x0000000000FF0000LL) >> 16);
973			if ((series <= 0x13) || ((series >= 0x18) && (series <= 0x23)))
974				fDevice->setNodeFlags( kIOFWLimitAsyncPacketSize );
975
976			series = (UInt8) (((guidVal & 0x00000000FFFFFFFFLL) >> 18) & 0x3f); // GL-2
977			if(series == 0x19) // GL-2
978				fDevice->setNodeFlags(kIOFWMustNotBeRoot);
979		}
980
981		if ((guidVal & 0xFFFFFF0000000000LL) == 0x0080450000000000LL) // panasonic
982		{
983			series = (UInt8) ((guidVal & 0x0000000000FF0000LL) >> 16);
984
985			prop = provider->getProperty(gFireWireProduct_Name);
986			if(prop)
987			{
988				OSString * string = OSDynamicCast ( OSString, prop ) ;
989				if (string->isEqualTo("PV-GS15"))
990				{
991					fDevice->setNodeFlags(kIOFWMustNotBeRoot);
992					fDevice->setNodeFlags(kIOFWMustHaveGap63);
993					IOLog("Panasonic guid=%lld series=%x model=%s\n", guidVal, series, string->getCStringNoCopy()); // node flags happens here
994				}
995				else if (string->isEqualTo("PV-GS120 "))
996				{
997					fDevice->setNodeFlags(kIOFWMustBeRoot);
998					IOLog("Panasonic guid=%lld series=%x model=%s\n", guidVal, series, string->getCStringNoCopy()); // node flags happens here
999				}
1000				else
1001				{
1002					FIRELOG_MSG(( "Unknown Panasonic series\n" ));
1003				}
1004			}
1005		}
1006	}
1007
1008	prop = provider->getProperty(gFireWireProduct_Name);
1009    if(prop)
1010        setProperty(gFireWireProduct_Name, prop);
1011
1012    setProperty("Unit_Type", type, 32);
1013
1014	// mark ourselves as started, this allows us to service resumed messages
1015	// resumed messages after this point should be safe.
1016	fStarted = true;
1017
1018    updateSubUnits(true);
1019
1020    // Finally enable matching on this object.
1021    registerService();
1022
1023    return true;
1024}
1025
1026bool IOFireWireAVCUnit::available()
1027{
1028	return fStarted;
1029}
1030
1031//////////////////////////////////////////////////////
1032// IOFireWireAVCUnit::free
1033//////////////////////////////////////////////////////
1034void IOFireWireAVCUnit::free(void)
1035{
1036	// Local Vars
1037	IOFireWireAVCAsynchronousCommand *pCmd;
1038
1039	FIRELOG_MSG(("IOFireWireAVCUnit::free (this=0x%08X)\n",this));
1040
1041	if ((fIOFireWireAVCUnitExpansion) && (fIOFireWireAVCUnitExpansion->fControl))
1042	{
1043		lockAVCAsynchronousCommandLock();
1044
1045		fStarted = false;
1046
1047		unlockAVCAsynchronousCommandLock();
1048	}
1049
1050    if (fFCPResponseSpace) {
1051        fFCPResponseSpace->deactivate();
1052        fFCPResponseSpace->release();
1053		fFCPResponseSpace = NULL;
1054    }
1055    if (avcLock) {
1056        IOLockFree(avcLock);
1057		avcLock = NULL;
1058    }
1059
1060	if ((fIOFireWireAVCUnitExpansion) && (fIOFireWireAVCUnitExpansion->fControl) && (fIOFireWireAVCUnitExpansion->fAVCAsyncCommands))
1061	{
1062		// Get the unit's async command lock
1063		lockAVCAsynchronousCommandLock();
1064
1065		// Cancel any remaining pending AVC async commands in the AVC command array
1066		while (fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount())
1067		{
1068			pCmd = (IOFireWireAVCAsynchronousCommand*) fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(0);
1069			pCmd->fWriteNodeID = kIOFWAVCAsyncCmdFreed;	// Special flag to indicate this command was cancled in this unit's free routine.
1070			pCmd->cancel();
1071		}
1072
1073		// Free the async command lock
1074		unlockAVCAsynchronousCommandLock();
1075
1076		// Release the async AVC command array
1077		fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->release();
1078	}
1079
1080    if (cmdLock)
1081	{
1082        IOLockFree(cmdLock);
1083		cmdLock = NULL;
1084    }
1085
1086	// Release our provider, the IOFireWireUnit object
1087	if( fDevice )
1088	{
1089		fDevice->release();
1090		fDevice = NULL;
1091	}
1092
1093	// free expansion data
1094	if (fIOFireWireAVCUnitExpansion)
1095	{
1096		IOFree ( fIOFireWireAVCUnitExpansion, sizeof(ExpansionData) );
1097		fIOFireWireAVCUnitExpansion = NULL;
1098	}
1099
1100    IOService::free();
1101}
1102
1103//////////////////////////////////////////////////////
1104// IOFireWireAVCUnit::matchPropertyTable
1105//////////////////////////////////////////////////////
1106bool IOFireWireAVCUnit::matchPropertyTable(OSDictionary * table)
1107{
1108	//FIRELOG_MSG(("IOFireWireAVCUnit::matchPropertyTable (this=0x%08X)\n",this));
1109
1110    //
1111    // If the service object wishes to compare some of its properties in its
1112    // property table against the supplied matching dictionary,
1113    // it should do so in this method and return truth on success.
1114    //
1115    if (!IOService::matchPropertyTable(table))  return false;
1116
1117    // We return success if the following expression is true -- individual
1118    // comparisions evaluate to truth if the named property is not present
1119    // in the supplied matching dictionary.
1120
1121
1122    bool res = compareProperty(table, gIOFireWireAVCUnitType) &&
1123        compareProperty(table, gFireWireVendor_ID) &&
1124        compareProperty(table, gFireWire_GUID);
1125
1126    if(res) {
1127        // Also see if requested subunits are available.
1128        int i;
1129        //OLog("Checking subunit foo\n");
1130        for(i=0; i<kAVCNumSubUnitTypes; i++) {
1131            OSNumber *	value;
1132            value = OSDynamicCast(OSNumber, table->getObject( gIOFireWireAVCSubUnitCount[i] ));
1133            if(value) {
1134                // make sure we have at least the requested number of subunits of the requested type
1135                //IOLog("Want %d AVCSubUnit_%x, got %d\n", value->unsigned8BitValue(), i, fSubUnitCount[i]);
1136                res = value->unsigned8BitValue() <= fSubUnitCount[i];
1137                if(!res)
1138                    break;
1139            }
1140        }
1141        //IOLog("After Checking subunit foo, match is %d\n", res);
1142    }
1143    return res;
1144}
1145
1146//////////////////////////////////////////////////////
1147// IOFireWireAVCUnit::AVCCommand
1148//////////////////////////////////////////////////////
1149IOReturn IOFireWireAVCUnit::AVCCommand(const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
1150{
1151	FIRELOG_MSG(("IOFireWireAVCUnit::AVCCommand (this=0x%08X, opCode=0x%02X)\n",this,in[2]));
1152
1153    IOReturn res;
1154    IOFireWireAVCCommand *cmd;
1155    if(len == 0 || len > 512) {
1156        IOLog("Loopy AVCCmd, len %d, respLen %d\n", (uint32_t)len, (uint32_t)*size);
1157        return kIOReturnBadArgument;
1158    }
1159
1160	// Retain the AVCUnit object while processing the command
1161	this->retain();
1162
1163    cmd = IOFireWireAVCCommand::withNub(fDevice, in, len, out, size);
1164    if(!cmd)
1165	{
1166		// Remove the extra retain we made above.
1167		this->release();
1168
1169		return kIOReturnNoMemory;
1170	}
1171
1172    // lock avc space
1173    IOTakeLock(avcLock);
1174
1175    fCommand = cmd;
1176
1177    res = fCommand->submit();
1178    if(res != kIOReturnSuccess) {
1179        //IOLog("AVCCommand returning 0x%x\n", res);
1180        //IOLog("command %x\n", *(UInt32 *)in);
1181    }
1182    IOTakeLock(cmdLock);
1183    fCommand = NULL;
1184    IOUnlock(cmdLock);
1185    cmd->release();
1186    IOUnlock(avcLock);
1187
1188	// Remove the extra retain we made above.
1189	this->release();
1190
1191    return res;
1192}
1193
1194//////////////////////////////////////////////////////
1195// IOFireWireAVCUnit::AVCCommandInGeneration
1196//////////////////////////////////////////////////////
1197IOReturn IOFireWireAVCUnit::AVCCommandInGeneration(UInt32 generation, const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
1198{
1199	FIRELOG_MSG(("IOFireWireAVCUnit::AVCCommandInGeneration (this=0x%08X)\n",this));
1200
1201    IOReturn res;
1202    IOFireWireAVCCommand *cmd;
1203    if(len == 0 || len > 512) {
1204        IOLog("Loopy AVCCmd, len %d, respLen %d\n", (uint32_t)len, (uint32_t)*size);
1205        return kIOReturnBadArgument;
1206    }
1207
1208    cmd = IOFireWireAVCCommand::withNub(fDevice, generation, in, len, out, size);
1209    if(!cmd)
1210        return kIOReturnNoMemory;
1211
1212    // lock avc space
1213    IOTakeLock(avcLock);
1214    fCommand = cmd;
1215
1216    res = fCommand->submit();
1217    if(res != kIOReturnSuccess) {
1218        //IOLog("AVCCommand returning 0x%x\n", res);
1219        //IOLog("command %x\n", *(UInt32 *)in);
1220    }
1221    IOTakeLock(cmdLock);
1222    fCommand = NULL;
1223    IOUnlock(cmdLock);
1224    cmd->release();
1225    IOUnlock(avcLock);
1226
1227    return res;
1228}
1229
1230//////////////////////////////////////////////////////
1231// IOFireWireAVCUnit::AVCAsynchRequestWriteDone
1232//////////////////////////////////////////////////////
1233void IOFireWireAVCUnit::AVCAsynchRequestWriteDone(void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd)
1234{
1235	IOFireWireAVCAsynchronousCommand *pCmdObject = OSDynamicCast(IOFireWireAVCAsynchronousCommand, (IOFireWireAVCAsynchronousCommand*)refcon);
1236
1237	if(!pCmdObject)
1238		return;
1239
1240	IOFireWireAVCUnit *pAVCUnit = OSDynamicCast(IOFireWireAVCUnit, pCmdObject->fAVCUnit);
1241
1242	if(!pAVCUnit)
1243		return;
1244
1245	UInt32 cmdIndex;
1246	bool doCallback = false;
1247
1248	FIRELOG_MSG(("IOFireWireAVCUnit::AVCAsynchRequestWriteDone (cmd=0x%08X, status=0x%08X)\n",pCmdObject,status));
1249
1250	// Get the async command lock
1251	pAVCUnit->lockAVCAsynchronousCommandLock();
1252
1253	// If this is due to a cancel, don't process further
1254	if(status == kIOReturnAborted)
1255	{
1256		pAVCUnit->unlockAVCAsynchronousCommandLock();
1257		return;
1258	}
1259
1260	// Verify the async command object is still on our list of pending commands
1261	cmdIndex = pAVCUnit->indexOfAVCAsynchronousCommandObject(pCmdObject);
1262	if (cmdIndex == 0xFFFFFFFF)
1263	{
1264		// The AVC async command must have already been terminated. Free the lock, and return.
1265		pAVCUnit->unlockAVCAsynchronousCommandLock();
1266		return;
1267	}
1268
1269	if(status == kIOReturnSuccess)
1270	{
1271        // Store current node and generation
1272        if(device)
1273            device->getNodeIDGeneration(pCmdObject->fWriteGen, pCmdObject->fWriteNodeID);
1274
1275		// Start the delay
1276		pCmdObject->fDelayCmd->submit();
1277
1278		// Change the state of this command
1279		pCmdObject->cmdState = kAVCAsyncCommandStateWaitingForResponse;
1280    }
1281    else
1282	{
1283		// Change the state of this command
1284		pCmdObject->cmdState = kAVCAsyncCommandStateRequestFailed;
1285
1286		// We need to do a callback after we release the lock
1287		doCallback = true;
1288
1289		// Remove this command from the unit's pending async command list
1290		pAVCUnit->removeAVCAsynchronousCommandObjectAtIndex(cmdIndex);
1291	}
1292
1293	// Free the async command lock
1294	pAVCUnit->unlockAVCAsynchronousCommandLock();
1295
1296	// Se if we need to do a callback to the client
1297	if (doCallback == true)
1298	{
1299		// Notify the client
1300		if (pCmdObject->fCallback != NULL)
1301			pCmdObject->fCallback(pCmdObject->pRefCon,pCmdObject);
1302	}
1303}
1304
1305//////////////////////////////////////////////////////
1306// IOFireWireAVCUnit::AVCAsynchDelayDone
1307//////////////////////////////////////////////////////
1308void IOFireWireAVCUnit::AVCAsynchDelayDone(void *refcon, IOReturn status, IOFireWireBus *bus, IOFWBusCommand *fwCmd)
1309{
1310	IOFireWireAVCAsynchronousCommand *pCmdObject = OSDynamicCast( IOFireWireAVCAsynchronousCommand, (IOFireWireAVCAsynchronousCommand*)refcon );
1311
1312	if(!pCmdObject)
1313		return;
1314
1315	IOFireWireAVCUnit *pAVCUnit = OSDynamicCast(IOFireWireAVCUnit, (IOFireWireAVCUnit*)pCmdObject->fAVCUnit);
1316
1317	if(!pAVCUnit)
1318		return;
1319
1320	UInt32 cmdIndex;
1321
1322	FIRELOG_MSG(("IOFireWireAVCUnit::AVCAsynchDelayDone, cmd=0x%08X, status = 0x%08X\n",pCmdObject,status));
1323
1324	// only proceed if status is time-out!
1325	if (status != kIOReturnTimeout)
1326		return;
1327
1328	// Get the async command lock
1329	pAVCUnit->lockAVCAsynchronousCommandLock();
1330
1331	// Verify the async command object is still on our list of pending commands
1332	cmdIndex = pAVCUnit->indexOfAVCAsynchronousCommandObject(pCmdObject);
1333	if (cmdIndex == 0xFFFFFFFF)
1334	{
1335		// The AVC async command must have already been terminated. Free the lock, and return.
1336		pAVCUnit->unlockAVCAsynchronousCommandLock();
1337		return;
1338	}
1339
1340	// Change the state of this command
1341	pCmdObject->cmdState = kAVCAsyncCommandStateTimeOutBeforeResponse;
1342
1343	// Remove this command from the unit's pending async command list
1344	pAVCUnit->removeAVCAsynchronousCommandObjectAtIndex(cmdIndex);
1345
1346	// Free the async command lock
1347	pAVCUnit->unlockAVCAsynchronousCommandLock();
1348
1349	// Notify the client
1350	if (pCmdObject->fCallback != NULL)
1351		pCmdObject->fCallback(pCmdObject->pRefCon,pCmdObject);
1352}
1353
1354//////////////////////////////////////////////////////
1355// IOFireWireAVCUnit::indexOfAVCAsynchronousCommandObject
1356//////////////////////////////////////////////////////
1357UInt32 IOFireWireAVCUnit::indexOfAVCAsynchronousCommandObject(IOFireWireAVCAsynchronousCommand *pCommandObject)
1358{
1359	UInt32 res = 0xFFFFFFFF;
1360	int i;
1361
1362	// NOTE: Assume that the AVCAsynchronousCommandLock has already
1363	// been taken before this function was called!
1364
1365	for (i=(fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount()-1);i>=0;i--)
1366	{
1367		IOFireWireAVCAsynchronousCommand *pCmd;
1368		pCmd = (IOFireWireAVCAsynchronousCommand*) fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
1369        if(pCommandObject == pCmd)
1370		{
1371			res = i;
1372			break;
1373		}
1374	}
1375
1376	return res;
1377}
1378
1379//////////////////////////////////////////////////////
1380// IOFireWireAVCUnit::removeAVCAsynchronousCommandObjectAtIndex
1381//////////////////////////////////////////////////////
1382void IOFireWireAVCUnit::removeAVCAsynchronousCommandObjectAtIndex(UInt32 index)
1383{
1384	// NOTE: Assume that the AVCAsynchronousCommandLock has already
1385	// been taken before this function was called!
1386
1387	fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->removeObject(index);
1388}
1389
1390//////////////////////////////////////////////////////
1391// IOFireWireAVCUnit::lockAVCAsynchronousCommandLock
1392//////////////////////////////////////////////////////
1393void IOFireWireAVCUnit::lockAVCAsynchronousCommandLock()
1394{
1395	FIRELOG_MSG(("IOFireWireAVCUnit::lockAVCAsynchronousCommandLock (this=0x%08X)\n",this));
1396	fIOFireWireAVCUnitExpansion->fControl->closeGate();
1397}
1398
1399//////////////////////////////////////////////////////
1400// IOFireWireAVCUnit::unlockAVCAsynchronousCommandLock
1401//////////////////////////////////////////////////////
1402void IOFireWireAVCUnit::unlockAVCAsynchronousCommandLock()
1403{
1404	FIRELOG_MSG(("IOFireWireAVCUnit::unlockAVCAsynchronousCommandLock (this=0x%08X)\n",this));
1405	fIOFireWireAVCUnitExpansion->fControl->openGate();
1406}
1407
1408//////////////////////////////////////////////////////
1409// IOFireWireAVCUnit::handleOpen
1410//////////////////////////////////////////////////////
1411bool IOFireWireAVCUnit::handleOpen( IOService * forClient, IOOptionBits options, void * arg )
1412{
1413	FIRELOG_MSG(("IOFireWireAVCUnit::handleOpen (this=0x%08X)\n",this));
1414
1415	bool ok = false;
1416
1417	if( !isOpen() )
1418	{
1419		ok = fDevice->open(this, options, arg);
1420		if(ok)
1421			ok = IOService::handleOpen(forClient, options, arg);
1422	}
1423
1424	return ok;
1425}
1426
1427//////////////////////////////////////////////////////
1428// IOFireWireAVCUnit::handleClose
1429//////////////////////////////////////////////////////
1430void IOFireWireAVCUnit::handleClose( IOService * forClient, IOOptionBits options )
1431{
1432	FIRELOG_MSG(("IOFireWireAVCUnit::handleClose (this=0x%08X)\n",this));
1433
1434	if( isOpen( forClient ) )
1435	{
1436		IOService::handleClose(forClient, options);
1437		fDevice->close(this, options);
1438	}
1439}
1440
1441//////////////////////////////////////////////////////
1442// IOFireWireAVCUnit::message
1443//////////////////////////////////////////////////////
1444IOReturn IOFireWireAVCUnit::message(UInt32 type, IOService *provider, void *argument)
1445{
1446	// Local Vars
1447	UInt32 i;
1448	IOFireWireAVCAsynchronousCommand *pCmd;
1449	OSArray *pTerminatedCommandsArray = NULL;
1450
1451	FIRELOG_MSG(("IOFireWireAVCUnit::message (type = 0x%08X, this=0x%08X)\n",type,this));
1452
1453	// If we have outstanding Async AVC commands, process them here for bus-reset command termination.
1454	if( fStarted == true &&
1455		type == kIOMessageServiceIsSuspended &&
1456		(fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount() > 0))
1457	{
1458		// Get the unit's async command lock
1459		lockAVCAsynchronousCommandLock();
1460
1461		for (i = 0; i < fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(); i++)
1462		{
1463			pCmd = (IOFireWireAVCAsynchronousCommand*) fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
1464
1465			// If the write command has been submitted, but no write done yet, cancel it now
1466			if (pCmd->cmdState == kAVCAsyncCommandStateRequestSent)
1467				pCmd->fWriteCmd->cancel(kIOReturnAborted);
1468
1469			// If the delay command has been submitted, but not completed, cancel it now
1470			if (pCmd->cmdState == kAVCAsyncCommandStateWaitingForResponse)
1471				pCmd->fDelayCmd->cancel(kIOReturnAborted);
1472
1473			FIRELOG_MSG(("IOFireWireAVCUnit::message setting pending async AVC command (0x%08X) to bus-reset state\n",pCmd));
1474			pCmd->cmdState = kAVCAsyncCommandStateBusReset;
1475
1476			// Remove this command from the unit's pending async command list
1477			removeAVCAsynchronousCommandObjectAtIndex(i);
1478
1479			// Add this command to the array of commands which we need to do client callbacks for
1480			// Note - this will add an extra retain to the command, which will be released when the array is released
1481			if (pTerminatedCommandsArray == NULL)
1482				pTerminatedCommandsArray = OSArray::withCapacity(1);
1483			if (pTerminatedCommandsArray != NULL)
1484				pTerminatedCommandsArray->setObject(pCmd);
1485		}
1486
1487		// Free the async command lock
1488		unlockAVCAsynchronousCommandLock();
1489
1490		// Do we have any terminated commands which we should do client callbacks for?
1491		if (pTerminatedCommandsArray != NULL)
1492		{
1493			for (i = 0; i < pTerminatedCommandsArray->getCount(); i++)
1494			{
1495				pCmd = (IOFireWireAVCAsynchronousCommand*) pTerminatedCommandsArray->getObject(i);
1496
1497				// Notify the client
1498				if (pCmd->fCallback != NULL)
1499					pCmd->fCallback(pCmd->pRefCon,pCmd);
1500			}
1501
1502			// Release the array - note that this will release all the objects from the array
1503			// to remove the extra retain that was done when the command was added to the array
1504			pTerminatedCommandsArray->release();
1505		}
1506	}
1507
1508	// If this is a bus-reset complete, then rescan subunits on the device
1509	// on another thread
1510	if( fStarted == true && type == kIOMessageServiceIsResumed )
1511	{
1512		this->retain(); // Retain this object before starting the rescan thread!
1513		thread_t		thread;
1514		if( kernel_thread_start((thread_continue_t)rescanSubUnits, this, &thread ) == KERN_SUCCESS )
1515		{
1516			thread_deallocate(thread);
1517		}
1518    }
1519    messageClients(type);
1520
1521    return kIOReturnSuccess;
1522}
1523
1524//////////////////////////////////////////////////////
1525// IOFireWireAVCUnit::updateAVCCommandTimeout
1526//////////////////////////////////////////////////////
1527IOReturn IOFireWireAVCUnit::updateAVCCommandTimeout()
1528{
1529	FIRELOG_MSG(("IOFireWireAVCUnit::updateAVCCommandTimeout (this=0x%08X)\n",this));
1530
1531    IOTakeLock(cmdLock);
1532    if(fCommand != NULL)
1533        fCommand->resetInterimTimeout();
1534    IOUnlock(cmdLock);
1535
1536    return kIOReturnSuccess;
1537}
1538
1539/* -------------------------------------------- AVC SubUnit -------------------------------------------- */
1540
1541OSDefineMetaClassAndStructors(IOFireWireAVCSubUnit, IOFireWireAVCNub)
1542OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 0);
1543OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 1);
1544OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 2);
1545OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 3);
1546
1547//////////////////////////////////////////////////////
1548// IOFireWireAVCSubUnit::init
1549//////////////////////////////////////////////////////
1550bool IOFireWireAVCSubUnit::init(OSDictionary *propTable, IOFireWireAVCUnit *provider)
1551{
1552	FIRELOG_MSG(("IOFireWireAVCSubUnit::init (this=0x%08X)\n",this));
1553
1554    OSObject *prop;
1555
1556    if(!IOFireWireAVCNub::init(propTable))
1557        return false;
1558    fAVCUnit = provider;
1559    if(!fAVCUnit)
1560        return false;
1561    fDevice = fAVCUnit->getDevice();
1562    if(!fDevice)
1563        return false;
1564
1565    // Copy over matching properties from AVC Unit
1566    prop = provider->getProperty(gFireWireVendor_ID);
1567    if(prop)
1568        setProperty(gFireWireVendor_ID, prop);
1569    prop = provider->getProperty(gFireWire_GUID);
1570    if(prop)
1571        setProperty(gFireWire_GUID, prop);
1572    prop = provider->getProperty(gFireWireProduct_Name);
1573    if(prop)
1574        setProperty(gFireWireProduct_Name, prop);
1575
1576    // Copy over user client properties
1577    prop = provider->getProperty(gIOUserClientClassKey);
1578    if(prop)
1579        setProperty(gIOUserClientClassKey, prop);
1580    prop = provider->getProperty(kIOCFPlugInTypesKey);
1581    if(prop)
1582        setProperty(kIOCFPlugInTypesKey, prop);
1583
1584    return true;
1585}
1586
1587/**
1588 ** Matching methods
1589 **/
1590//////////////////////////////////////////////////////
1591// IOFireWireAVCSubUnit::matchPropertyTable
1592//////////////////////////////////////////////////////
1593bool IOFireWireAVCSubUnit::matchPropertyTable(OSDictionary * table)
1594{
1595	//FIRELOG_MSG(("IOFireWireAVCSubUnit::matchPropertyTable (this=0x%08X)\n",this));
1596
1597    //
1598    // If the service object wishes to compare some of its properties in its
1599    // property table against the supplied matching dictionary,
1600    // it should do so in this method and return truth on success.
1601    //
1602    if (!IOService::matchPropertyTable(table))  return false;
1603
1604    // We return success if the following expression is true -- individual
1605    // comparisions evaluate to truth if the named property is not present
1606    // in the supplied matching dictionary.
1607
1608
1609    return compareProperty(table, gIOFireWireAVCSubUnitType) &&
1610        compareProperty(table, gFireWireVendor_ID) &&
1611        compareProperty(table, gFireWire_GUID);
1612}
1613
1614//////////////////////////////////////////////////////
1615// IOFireWireAVCSubUnit::AVCCommand
1616//////////////////////////////////////////////////////
1617IOReturn IOFireWireAVCSubUnit::AVCCommand(const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
1618{
1619	FIRELOG_MSG(("IOFireWireAVCSubUnit::AVCCommand (this=0x%08X)\n",this));
1620
1621    return fAVCUnit->AVCCommand(in, len, out, size);
1622}
1623
1624//////////////////////////////////////////////////////
1625// IOFireWireAVCSubUnit::AVCCommandInGeneration
1626//////////////////////////////////////////////////////
1627IOReturn IOFireWireAVCSubUnit::AVCCommandInGeneration(UInt32 generation, const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
1628{
1629	FIRELOG_MSG(("IOFireWireAVCSubUnit::AVCCommandInGeneration (this=0x%08X)\n",this));
1630
1631    return fAVCUnit->AVCCommandInGeneration(generation, in, len, out, size);
1632}
1633
1634//////////////////////////////////////////////////////
1635// IOFireWireAVCSubUnit::updateAVCCommandTimeout
1636//////////////////////////////////////////////////////
1637IOReturn IOFireWireAVCSubUnit::updateAVCCommandTimeout()
1638{
1639	FIRELOG_MSG(("IOFireWireAVCSubUnit::updateAVCCommandTimeout (this=0x%08X)\n",this));
1640
1641    return fAVCUnit->updateAVCCommandTimeout();
1642}
1643
1644//////////////////////////////////////////////////////
1645// IOFireWireAVCSubUnit::handleOpen
1646//////////////////////////////////////////////////////
1647bool IOFireWireAVCSubUnit::handleOpen( IOService * forClient, IOOptionBits options, void * arg )
1648{
1649	FIRELOG_MSG(("IOFireWireAVCSubUnit::handleOpen (this=0x%08X)\n",this));
1650
1651	bool ok = false;
1652
1653	if( !isOpen() )
1654	{
1655		ok = fAVCUnit->open(this, options, arg);
1656		if(ok)
1657			ok = IOService::handleOpen(forClient, options, arg);
1658	}
1659
1660	return ok;
1661}
1662
1663//////////////////////////////////////////////////////
1664// IOFireWireAVCSubUnit::handleClose
1665//////////////////////////////////////////////////////
1666void IOFireWireAVCSubUnit::handleClose( IOService * forClient, IOOptionBits options )
1667{
1668	FIRELOG_MSG(("IOFireWireAVCSubUnit::handleClose (this=0x%08X)\n",this));
1669
1670	if( isOpen( forClient ) )
1671	{
1672		IOService::handleClose(forClient, options);
1673		fAVCUnit->close(this, options);
1674	}
1675}
1676
1677//////////////////////////////////////////////////////
1678// IOFireWireAVCSubUnit::message
1679//////////////////////////////////////////////////////
1680IOReturn IOFireWireAVCSubUnit::message(UInt32 type, IOService *provider, void *argument)
1681{
1682	//FIRELOG_MSG(("IOFireWireAVCSubUnit::message (this=0x%08X)\n",this));
1683
1684    messageClients(type);
1685
1686    return kIOReturnSuccess;
1687}
1688
1689