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// This is for testing of the new async AVC command kernel stuff
24//#define kUseAsyncAVCCommandForBlockingAVCCommand 1
25
26#include "IOFireWireAVCUserClient.h"
27#include <IOKit/IOLib.h>
28#include <IOKit/IOMessage.h>
29#include <IOKit/IOSyncer.h>
30#include <IOKit/firewire/IOFireWireUnit.h>
31#include <IOKit/firewire/IOFireWireController.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
40OSDefineMetaClassAndStructors(IOFireWireAVCConnection, OSObject)
41OSDefineMetaClassAndStructors(IOFireWireAVCUserClientAsyncCommand, OSObject)
42OSDefineMetaClassAndStructors(IOFireWireAVCUserClient, IOUserClient)
43
44void AVCUserClientAsyncCommandCallback(void *pRefCon, IOFireWireAVCAsynchronousCommand *pCommandObject);
45
46//////////////////////////////////////////////////////
47// IOFireWireAVCUserClient::externalMethod
48//////////////////////////////////////////////////////
49IOReturn IOFireWireAVCUserClient::externalMethod( uint32_t selector,
50						IOExternalMethodArguments * arguments,
51						IOExternalMethodDispatch * dispatch,
52						OSObject * target,
53						void * reference)
54{
55	IOReturn result = kIOReturnBadArgument;
56
57	FIRELOG_MSG(("IOFireWireAVCUserClient::externalMethod (this=0x%08X), selector=0x%08X\n",this,selector));
58
59	// Dispatch the method call
60	switch (selector)
61	{
62	    case kIOFWAVCUserClientOpen:
63			result = open(NULL,NULL,NULL,NULL,NULL,NULL);
64			break;
65
66		case kIOFWAVCUserClientClose:
67			result = close(NULL,NULL,NULL,NULL,NULL,NULL);
68			break;
69
70		case kIOFWAVCUserClientGetSessionRef:
71			result = getSessionRef( arguments->scalarOutput,NULL,NULL,NULL,NULL,NULL);
72			break;
73
74		case kIOFWAVCUserClientAVCCommand:
75			result = AVCCommand((UInt8*)arguments->structureInput,
76								(UInt8*)arguments->structureOutput,
77								arguments->structureInputSize,
78								(UInt32*)&arguments->structureOutputSize);
79			break;
80
81		case kIOFWAVCUserClientOpenWithSessionRef:
82			result = openWithSessionRef((IOFireWireLib::UserObjectHandle) arguments->scalarInput[0],NULL,NULL,NULL,NULL,NULL);
83			break;
84
85		case kIOFWAVCUserClientAVCCommandInGen:
86			result = AVCCommandInGen((UInt8*) arguments->structureInput,
87								(UInt8*)arguments->structureOutput,
88								arguments->structureInputSize,
89								(UInt32*)&arguments->structureOutputSize);
90			break;
91
92		case kIOFWAVCUserClientUpdateAVCCommandTimeout:
93			result = updateAVCCommandTimeout(NULL,NULL,NULL,NULL,NULL,NULL);
94			break;
95
96		case kIOFWAVCUserClientMakeP2PInputConnection:
97			result = makeP2PInputConnection(arguments->scalarInput[0],arguments->scalarInput[1],NULL,NULL,NULL,NULL);
98			break;
99
100		case kIOFWAVCUserClientBreakP2PInputConnection:
101			result = breakP2PInputConnection(arguments->scalarInput[0],NULL,NULL,NULL,NULL,NULL);
102			break;
103
104		case kIOFWAVCUserClientMakeP2POutputConnection:
105			result = makeP2POutputConnection(arguments->scalarInput[0],arguments->scalarInput[1],(IOFWSpeed)arguments->scalarInput[2],NULL,NULL,NULL);
106			break;
107
108		case kIOFWAVCUserClientBreakP2POutputConnection:
109			result = breakP2POutputConnection(arguments->scalarInput[0],NULL,NULL,NULL,NULL,NULL);
110			break;
111
112		case kIOFWAVCUserClientCreateAsyncAVCCommand:
113			result = CreateAVCAsyncCommand((UInt8*)arguments->structureInput,
114								(UInt8*)arguments->structureOutput,
115								arguments->structureInputSize,
116								(UInt32*)&arguments->structureOutputSize);
117			break;
118
119		case kIOFWAVCUserClientSubmitAsyncAVCCommand:
120			result = SubmitAVCAsyncCommand(arguments->scalarInput[0]);
121			break;
122
123		case kIOFWAVCUserClientCancelAsyncAVCCommand:
124			result = CancelAVCAsyncCommand(arguments->scalarInput[0]);
125			break;
126
127		case kIOFWAVCUserClientReleaseAsyncAVCCommand:
128			result = ReleaseAVCAsyncCommand(arguments->scalarInput[0]);
129			break;
130
131		case kIOFWAVCUserClientReinitAsyncAVCCommand:
132			result = ReinitAVCAsyncCommand(arguments->scalarInput[0], (const UInt8*) arguments->structureInput, arguments->structureInputSize);
133			break;
134
135		case kIOFWAVCUserClientInstallAsyncAVCCommandCallback:
136			result = installUserLibAsyncAVCCommandCallback(arguments->asyncReference,arguments->scalarInput[0], arguments->scalarOutput);
137			break;
138
139		default:
140			// None of the above!
141			break;
142	};
143
144	return result;
145}
146
147//////////////////////////////////////////////////////
148// IOFireWireAVCUserClient::initWithTask
149//////////////////////////////////////////////////////
150bool IOFireWireAVCUserClient::initWithTask(
151				  task_t owningTask, void * securityToken, UInt32 type,
152				  OSDictionary * properties)
153{
154	FIRELOG_MSG(("IOFireWireAVCUserClient::initWithTask (this=0x%08X)\n",this));
155
156	fTask = owningTask;
157
158	// Allow Rosetta based apps access to this user-client
159	if (properties)
160		properties->setObject("IOUserClientCrossEndianCompatible", kOSBooleanTrue);
161
162	return IOUserClient::initWithTask(owningTask, securityToken, type,properties);
163}
164
165//////////////////////////////////////////////////////
166// IOFireWireAVCUserClient::free
167//////////////////////////////////////////////////////
168void IOFireWireAVCUserClient::free()
169{
170    FIRELOG_MSG(("IOFireWireAVCUserClient::free (this=0x%08X)\n",this));
171
172    if(fConnections) {
173        UInt32 i;
174
175        if(fUnit) {
176            for(i=0; i<fConnections->getCount(); i++) {
177                IOFireWireAVCConnection *connection;
178                connection = (IOFireWireAVCConnection *)fConnections->getObject(i);
179                //IOLog("Cleaning up connection %d %p\n", i, connection);
180                updateP2PCount(connection->fPlugAddr, -1, false, 0xFFFFFFFF, kFWSpeedInvalid);
181            }
182		}
183        fConnections->release();
184	}
185
186	// Free the async cmd lock
187	if (fAsyncAVCCmdLock)
188		IOLockFree(fAsyncAVCCmdLock);
189
190#ifdef kUseAsyncAVCCommandForBlockingAVCCommand
191    if (avcCmdLock)
192	{
193        if (IOLockTryLock(avcCmdLock) == false)
194		{
195			// The avcCmdLock is currently locked, meaning we are in the process of
196			// running IOFireWireAVCUserClient::AVCCommand on another thread. Cancel
197			// that command now.
198			if (pCommandObject)
199				pCommandObject->cancel();
200			IOTakeLock(avcCmdLock);
201		}
202		IOLockFree(avcCmdLock);
203    }
204#endif
205
206	// Release our retain on the IOFireWireAVCUnit!
207	if (fUnit)
208		fUnit->release();
209
210	IOService::free();
211}
212
213//////////////////////////////////////////////////////
214// IOFireWireAVCUserClient::stop
215//////////////////////////////////////////////////////
216void IOFireWireAVCUserClient::stop( IOService * provider )
217{
218	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
219
220    FIRELOG_MSG(("IOFireWireAVCUserClient::stop (this=0x%08X)\n",this));
221
222    fStarted = false;
223
224	// Deal with the fUCAsyncCommands array, deal with any outstanding commands and release it
225	IOTakeLock(fAsyncAVCCmdLock);
226	while (fUCAsyncCommands->getCount())
227	{
228		pUCAsyncCommand = (IOFireWireAVCUserClientAsyncCommand *)fUCAsyncCommands->getObject(0);
229		if (pUCAsyncCommand)
230		{
231			pUCAsyncCommand->pAsyncCommand->cancel();	// Cancel, just in case it's still pending!
232			pUCAsyncCommand->pAsyncCommand->release();
233
234			// Get rid of the memory descriptor for the shared buf
235			pUCAsyncCommand->fMem->complete();
236			pUCAsyncCommand->fMem->release() ;
237
238			// Remove this object from our array. This will release it.
239			fUCAsyncCommands->removeObject(0);
240		}
241	}
242
243	IOUnlock(fAsyncAVCCmdLock);
244
245    IOService::stop(provider);
246}
247
248
249//////////////////////////////////////////////////////
250// IOFireWireAVCUserClient::start
251//////////////////////////////////////////////////////
252bool IOFireWireAVCUserClient::start( IOService * provider )
253{
254    FIRELOG_MSG(("IOFireWireAVCUserClient::start (this=0x%08X)\n",this));
255
256	if( fStarted )
257		return false;
258
259    fUnit = OSDynamicCast(IOFireWireAVCNub, provider);
260    if (fUnit == NULL)
261        return false;
262
263	fConnections = OSArray::withCapacity(1);
264
265	// Create array to hold outstanding async AVC commands for this UC, and a lock for protecting it
266	fUCAsyncCommands = OSArray::withCapacity(1);
267	fAsyncAVCCmdLock = IOLockAlloc();
268    if (fAsyncAVCCmdLock == NULL)
269        return false;
270
271#ifdef kUseAsyncAVCCommandForBlockingAVCCommand
272	pCommandObject = NULL;
273	avcCmdLock = IOLockAlloc();
274    if (avcCmdLock == NULL) {
275        return false;
276    }
277#endif
278
279	if( !IOUserClient::start(provider) )
280        return false;
281
282    fStarted = true;
283
284	// Retain the IOFireWireAVCUnit to prevent it from being terminated
285	fUnit->retain();
286
287     return true;
288}
289
290//////////////////////////////////////////////////////
291// IOFireWireAVCUserClient::clientClose
292//////////////////////////////////////////////////////
293IOReturn IOFireWireAVCUserClient::clientClose( void )
294{
295    FIRELOG_MSG(("IOFireWireAVCUserClient::clientClose (this=0x%08X)\n",this));
296
297    if( fOpened )
298    {
299        fOpened = false;
300		fUnit->close(this);
301    }
302
303	fStarted = false;
304
305	terminate( kIOServiceRequired );
306
307	return kIOReturnSuccess;
308}
309
310//////////////////////////////////////////////////////
311// IOFireWireAVCUserClient::clientDied
312//////////////////////////////////////////////////////
313IOReturn IOFireWireAVCUserClient::clientDied( void )
314{
315    FIRELOG_MSG(("IOFireWireAVCUserClient::clientDied (this=0x%08X)\n",this));
316
317    return clientClose();
318}
319
320//////////////////////////////////////////////////////
321// IOFireWireAVCUserClient::open
322//////////////////////////////////////////////////////
323IOReturn IOFireWireAVCUserClient::open
324	( void *, void *, void *, void *, void *, void * )
325{
326	FIRELOG_MSG(("IOFireWireAVCUserClient::open (this=0x%08X)\n",this));
327
328	IOReturn status = kIOReturnSuccess;
329
330    if( fOpened )
331        status = kIOReturnError;
332
333    if( status == kIOReturnSuccess )
334    {
335        if( fUnit->open(this) )
336		{
337			IOFWUserObjectExporter * exporter = fUnit->getDevice()->getBus()->getSessionRefExporter();
338			status = exporter->addObject( this, NULL, &fSessionRef );
339			if( status == kIOReturnSuccess )
340			{
341				fOpened = true;
342			}
343			else
344			{
345				fUnit->close(this);
346			}
347		}
348		else
349            status = kIOReturnExclusiveAccess;
350    }
351
352     return status;
353}
354
355//////////////////////////////////////////////////////
356// IOFireWireAVCUserClient::openWithSessionRef
357//////////////////////////////////////////////////////
358IOReturn IOFireWireAVCUserClient::openWithSessionRef( IOFireWireLib::UserObjectHandle sessionRef, void *, void *, void *, void *, void * )
359{
360	FIRELOG_MSG(("IOFireWireAVCUserClient::openWithSessionRef (this=0x%08X)\n",this));
361
362    IOReturn status = kIOReturnSuccess;
363	IOService * service = NULL;
364	IOService * original_service = NULL;
365
366    if( fOpened || !fUnit->isOpen() )
367        status = kIOReturnError;
368
369	if( status == kIOReturnSuccess )
370	{
371		IOFWUserObjectExporter * exporter = fUnit->getDevice()->getBus()->getSessionRefExporter();
372		original_service = (IOService*) exporter->lookupObjectForType( sessionRef, OSTypeID(IOService) );
373		if( original_service == NULL )
374			status = kIOReturnBadArgument;
375	}
376
377	if( status == kIOReturnSuccess )
378	{
379		// look for us in provider chain
380		service = original_service;
381		while( fUnit != service && service != NULL )
382			service = service->getProvider();
383
384		// were we found
385		if( service == NULL )
386			status = kIOReturnBadArgument;
387	}
388
389	if( original_service )
390	{
391		original_service->release();
392		original_service = NULL;
393	}
394
395	return status;
396}
397
398//////////////////////////////////////////////////////
399// IOFireWireAVCUserClient::getSessionRef
400//////////////////////////////////////////////////////
401IOReturn IOFireWireAVCUserClient::getSessionRef( uint64_t * sessionRef, void *, void *, void *, void *, void * )
402{
403	FIRELOG_MSG(("IOFireWireAVCUserClient::getSessionRef (this=0x%08X)\n",this));
404
405    IOReturn status = kIOReturnSuccess;
406
407    if( !fOpened )
408        status = kIOReturnError;
409
410    if( status == kIOReturnSuccess )
411    {
412		*sessionRef = (uint64_t) fSessionRef;
413	}
414
415	return status;
416}
417
418//////////////////////////////////////////////////////
419// IOFireWireAVCUserClient::close
420//////////////////////////////////////////////////////
421IOReturn IOFireWireAVCUserClient::close
422	( void *, void *, void *, void *, void *, void * )
423{
424	FIRELOG_MSG(("IOFireWireAVCUserClient::close (this=0x%08X)\n",this));
425
426    if( fOpened )
427    {
428		IOFWUserObjectExporter * exporter = fUnit->getDevice()->getBus()->getSessionRefExporter();
429		exporter->removeObject( fSessionRef );
430		fSessionRef = 0;
431
432		fUnit->close(this);
433        fOpened = false;
434	}
435
436    return kIOReturnSuccess;
437}
438
439#ifdef kUseAsyncAVCCommandForBlockingAVCCommand
440//////////////////////////////////////////////////////
441// AVCAsyncCommandCallback
442//////////////////////////////////////////////////////
443void AVCAsyncCommandCallback(void *pRefCon, IOFireWireAVCAsynchronousCommand *pCommandObject)
444{
445	IOSyncer *fSyncWakeup = (IOSyncer*) pRefCon;
446
447	FIRELOG_MSG(("IOFireWireAVCUserClient::AVCAsyncCommandCallback (cmd=0x%08X, state=%d)\n",pCommandObject,pCommandObject->cmdState));
448
449	// If this command is no longer pending, release the blocking lock
450	if (!pCommandObject->isPending())
451        fSyncWakeup->signal(pCommandObject->cmdState);
452}
453#endif
454
455//////////////////////////////////////////////////////
456// IOFireWireAVCUserClient::AVCCommand
457//////////////////////////////////////////////////////
458IOReturn IOFireWireAVCUserClient::AVCCommand(UInt8 * cmd, UInt8 * response,
459    UInt32 len, UInt32 *size)
460{
461	FIRELOG_MSG(("IOFireWireAVCUserClient::AVCCommand (this=0x%08X)\n",this));
462
463    IOReturn res;
464
465    if(!fStarted )
466	return kIOReturnNoDevice;
467
468#ifndef kUseAsyncAVCCommandForBlockingAVCCommand
469
470    res = fUnit->AVCCommand(cmd,len,response,size);
471    return res;
472
473#else
474
475	// Local Vars
476    IOSyncer *fSyncWakeup;
477	UInt32 responseLen;
478
479	IOTakeLock(avcCmdLock);
480
481	// TODO: Remove (Just a sanity check. This should never happen!)
482	if (pCommandObject)
483	{
484		FIRELOG_MSG(("IOFireWireAVCUserClient::AVCCommand ERROR: pCommandObject is not NULL!\n"));
485		return kIOReturnError;
486	}
487
488	// Allocate a syncer
489	fSyncWakeup = IOSyncer::create();
490	if(!fSyncWakeup)
491	{
492		IOUnlock(avcCmdLock);
493        return kIOReturnNoMemory;
494	}
495
496	pCommandObject = new IOFireWireAVCAsynchronousCommand;
497	if (pCommandObject)
498	{
499		res = pCommandObject->init(cmd,len,AVCAsyncCommandCallback,fSyncWakeup);
500		if (res == kIOReturnSuccess)
501		{
502			FIRELOG_MSG(("IOFireWireAVCUserClient::AVCCommand createAVCAsynchronousCommand successful, cmd=0x%08X\n",pCommandObject));
503
504			// Submit it
505			res = pCommandObject->submit(fUnit);
506			if (res == kIOReturnSuccess)
507			{
508				// Wait for the async command callback to signal us.
509				res = fSyncWakeup->wait();
510				FIRELOG_MSG(("IOFireWireAVCUserClient::AVCCommand continuing after receiving async command complete notification(this=0x%08X)\n",this));
511
512				// Copy the async command final response, if it exists
513				if ((pCommandObject->cmdState == kAVCAsyncCommandStateReceivedFinalResponse) &&
514					(pCommandObject->pFinalResponseBuf != NULL))
515				{
516					// Copy as much of the response as will fit into the caller's response buf
517					if (pCommandObject->finalResponseLen > *size)
518						responseLen = *size;
519					else
520						responseLen = pCommandObject->finalResponseLen;
521					bcopy(pCommandObject->pFinalResponseBuf, response, responseLen);
522					*size = responseLen;
523
524					// Set the return value to success
525					res = kIOReturnSuccess;
526				}
527				else
528				{
529					// This is a failure, set the return value correctly
530					res = kIOReturnError;   // TODO: further refine error codes based on command state
531				}
532			}
533		}
534		else
535		{
536			// TODO: Since the init failed, we need to manually release the syncer
537		}
538
539		// Release the command object
540		pCommandObject->release();
541		pCommandObject = NULL;
542	}
543
544	// Release the syncer
545	//fSyncWakeup->release();
546
547	IOUnlock(avcCmdLock);
548
549	return res;
550#endif
551}
552
553//////////////////////////////////////////////////////
554// IOFireWireAVCUserClient::AVCCommandInGen
555//////////////////////////////////////////////////////
556IOReturn IOFireWireAVCUserClient::AVCCommandInGen(UInt8 * cmd, UInt8 * response,
557    UInt32 len, UInt32 *size)
558{
559	FIRELOG_MSG(("IOFireWireAVCUserClient::AVCCommandInGen (this=0x%08X)\n",this));
560
561    IOReturn res;
562    UInt32 generation;
563    generation = *(UInt32 *)cmd;
564    cmd += sizeof(UInt32);
565    len -= sizeof(UInt32);
566
567    if(!fStarted )
568	return kIOReturnNoDevice;
569
570    res = fUnit->AVCCommandInGeneration(generation,cmd,len,response,size);
571
572	return res;
573}
574
575//////////////////////////////////////////////////////
576// IOFireWireAVCUserClient::updateAVCCommandTimeout
577//////////////////////////////////////////////////////
578IOReturn IOFireWireAVCUserClient::updateAVCCommandTimeout
579	( void *, void *, void *, void *, void *, void * )
580{
581	FIRELOG_MSG(("IOFireWireAVCUserClient::updateAVCCommandTimeout (this=0x%08X)\n",this));
582
583    if(!fStarted )
584	return kIOReturnNoDevice;
585
586    fUnit->updateAVCCommandTimeout();
587
588    return kIOReturnSuccess;
589}
590
591//////////////////////////////////////////////////////
592// IOFireWireAVCUserClient::updateP2PCount
593//////////////////////////////////////////////////////
594IOReturn IOFireWireAVCUserClient::updateP2PCount(UInt32 addr, SInt32 inc, bool failOnBusReset, UInt32 chan, IOFWSpeed speed)
595{
596	FIRELOG_MSG(("IOFireWireAVCUserClient::updateP2PCount (this=0x%08X)\n",this));
597
598    if(!fStarted )
599	return kIOReturnNoDevice;
600
601    IOFireWireNub *device = fUnit->getDevice();
602    FWAddress plugAddr(kCSRRegisterSpaceBaseAddressHi, addr);
603    IOFWReadQuadCommand *readCmd;
604    IOFWCompareAndSwapCommand *lockCmd;
605    UInt32 plugVal, newVal;
606	UInt32 plugValHost, newValHost;
607	UInt32 curCount;
608	UInt32 curChan;
609	IOFWSpeed curSpeed;
610    IOReturn res;
611
612    readCmd = device->createReadQuadCommand(plugAddr, &plugVal, 1, NULL, NULL, failOnBusReset);
613    res = readCmd->submit();
614    readCmd->release();
615    if(res != kIOReturnSuccess)
616        return res;
617
618	plugValHost = OSSwapBigToHostInt32( plugVal );
619
620    for(int i=0; i<4; i++) {
621        bool success;
622
623		// Parse current plug value
624		curCount = ((plugValHost & kIOFWPCRP2PCount) >> 24);
625		curChan = ((plugValHost & kIOFWPCRChannel) >> 16);
626		curSpeed = (IOFWSpeed)((plugValHost & kIOFWPCROutputDataRate) >> 14);
627		newValHost = plugValHost;
628
629		// If requested, modify channel
630		if (chan != 0xFFFFFFFF)
631		{
632			if ((curCount != 0) && (chan != curChan))
633				return kIOReturnError;
634
635			newValHost &= ~kIOFWPCRChannel;
636			newValHost |= ((chan & 0x3F) << 16);
637		}
638
639		// If requested, modify speed
640		if (speed != kFWSpeedInvalid)
641		{
642			if ((curCount != 0) && (speed != curSpeed))
643				return kIOReturnError;
644
645			newValHost &= ~kIOFWPCROutputDataRate;
646			newValHost |= ((speed & 0x03) << 14);
647		}
648
649		// Modify P2P count
650		newValHost &= ~kIOFWPCRP2PCount;
651		if (inc > 0)
652		{
653			if (curCount == 0x3F)
654				return kIOReturnError;
655			newValHost |= ((curCount+1) << 24);
656		}
657		else
658		{
659			if (curCount == 0)
660				return kIOReturnError;
661			newValHost |= ((curCount-1) << 24);
662		}
663
664		newVal = OSSwapHostToBigInt32( newValHost );
665        lockCmd = device->createCompareAndSwapCommand(plugAddr, &plugVal, &newVal, 1);
666        res = lockCmd->submit();
667        success = lockCmd->locked(&plugVal);
668		plugValHost = OSSwapBigToHostInt32( plugVal );
669        lockCmd->release();
670        if(res != kIOReturnSuccess)
671            break;
672        if(success)
673            break;
674    }
675    return res;
676}
677
678//////////////////////////////////////////////////////
679// IOFireWireAVCUserClient::makeConnection
680//////////////////////////////////////////////////////
681IOReturn IOFireWireAVCUserClient::makeConnection(UInt32 addr, UInt32 chan, IOFWSpeed speed)
682{
683	FIRELOG_MSG(("IOFireWireAVCUserClient::makeConnection (this=0x%08X)\n",this));
684
685    IOReturn err;
686    IOFireWireAVCConnection *connection;
687    connection = new IOFireWireAVCConnection;
688    if(!connection)
689        return kIOReturnNoMemory;
690
691    err = updateP2PCount(addr, 1, false, chan, speed);
692    if(kIOReturnSuccess == err) {
693        connection->fPlugAddr = addr;
694        connection->fChannel = chan;
695        fConnections->setObject(connection);
696    }
697    connection->release();
698    return err;
699}
700
701//////////////////////////////////////////////////////
702// IOFireWireAVCUserClient::breakConnection
703//////////////////////////////////////////////////////
704void IOFireWireAVCUserClient::breakConnection(UInt32 addr)
705{
706	FIRELOG_MSG(("IOFireWireAVCUserClient::breakConnection (this=0x%08X)\n",this));
707
708    UInt32 i;
709
710    for(i=0; i<fConnections->getCount(); i++) {
711        IOFireWireAVCConnection *connection;
712        connection = (IOFireWireAVCConnection *)fConnections->getObject(i);
713         if(connection->fPlugAddr == addr) {
714            updateP2PCount(addr, -1, false, 0xFFFFFFFF, kFWSpeedInvalid);
715            fConnections->removeObject(i);
716            break;
717        }
718    }
719}
720
721//////////////////////////////////////////////////////
722// IOFireWireAVCUserClient::makeP2PInputConnection
723//////////////////////////////////////////////////////
724IOReturn IOFireWireAVCUserClient::makeP2PInputConnection( UInt32 plugNo, UInt32 chan, void *, void *, void *, void *)
725{
726	FIRELOG_MSG(("IOFireWireAVCUserClient::makeP2PInputConnection (this=0x%08X)\n",this));
727
728    return makeConnection(kPCRBaseAddress+0x84+4*plugNo, chan, kFWSpeedInvalid);
729}
730
731//////////////////////////////////////////////////////
732// IOFireWireAVCUserClient::breakP2PInputConnection
733//////////////////////////////////////////////////////
734IOReturn IOFireWireAVCUserClient::breakP2PInputConnection( UInt32 plugNo, void *, void *, void *, void *, void *)
735{
736	FIRELOG_MSG(("IOFireWireAVCUserClient::breakP2PInputConnection (this=0x%08X)\n",this));
737
738    breakConnection(kPCRBaseAddress+0x84+4*plugNo);
739	return kIOReturnSuccess;
740}
741
742//////////////////////////////////////////////////////
743// IOFireWireAVCUserClient::makeP2POutputConnection
744//////////////////////////////////////////////////////
745IOReturn IOFireWireAVCUserClient::makeP2POutputConnection( UInt32 plugNo, UInt32 chan, IOFWSpeed speed, void *, void *, void *)
746{
747	FIRELOG_MSG(("IOFireWireAVCUserClient::makeP2POutputConnection (this=0x%08X)\n",this));
748
749    return makeConnection(kPCRBaseAddress+4+4*plugNo, chan, speed);
750}
751
752//////////////////////////////////////////////////////
753// IOFireWireAVCUserClient::breakP2POutputConnection
754//////////////////////////////////////////////////////
755IOReturn IOFireWireAVCUserClient::breakP2POutputConnection( UInt32 plugNo, void *, void *, void *, void *, void *)
756{
757	FIRELOG_MSG(("IOFireWireAVCUserClient::breakP2POutputConnection (this=0x%08X)\n",this));
758
759    breakConnection(kPCRBaseAddress+4+4*plugNo);
760	return kIOReturnSuccess;
761}
762
763//////////////////////////////////////////////////////
764// IOFireWireAVCUserClient::message
765//////////////////////////////////////////////////////
766IOReturn IOFireWireAVCUserClient::message(UInt32 type, IOService *provider, void *argument)
767{
768	//FIRELOG_MSG(("IOFireWireAVCUserClient::message (this=0x%08X)\n",this));
769
770	if( fStarted == true && type == kIOMessageServiceIsResumed ) {
771        retain();	// Make sure we don't get deleted with the thread running
772		thread_t		thread;
773		if( kernel_thread_start((thread_continue_t)remakeConnections, this, &thread ) == KERN_SUCCESS )
774		{
775			thread_deallocate(thread);
776		}
777    }
778
779    return kIOReturnSuccess;
780}
781
782//////////////////////////////////////////////////////
783// IOFireWireAVCUserClient::remakeConnections
784//////////////////////////////////////////////////////
785void IOFireWireAVCUserClient::remakeConnections(void *arg)
786{
787    IOFireWireAVCUserClient *me = (IOFireWireAVCUserClient *)arg;
788
789	FIRELOG_MSG(("IOFireWireAVCUserClient::remakeConnections (this=0x%08X)\n",me));
790
791    UInt32 i;
792    IOReturn res;
793    for(i=0; i<me->fConnections->getCount(); i++) {
794        IOFireWireAVCConnection *connection;
795        connection = (IOFireWireAVCConnection *)me->fConnections->getObject(i);
796        //IOLog("Remaking connection %d %p\n", i, connection);
797        res = me->updateP2PCount(connection->fPlugAddr, 1, true, connection->fChannel, kFWSpeedInvalid);
798        if(res == kIOFireWireBusReset)
799            break;
800    }
801
802    me->release();
803}
804
805//////////////////////////////////////////////////////
806// IOFireWireAVCUserClient::installUserLibAsyncAVCCommandCallback
807//////////////////////////////////////////////////////
808IOReturn IOFireWireAVCUserClient::installUserLibAsyncAVCCommandCallback(io_user_reference_t *asyncRef, uint64_t userRefcon, uint64_t *returnParam)
809{
810	FIRELOG_MSG(("IOFireWireAVCUserClient::installUserLibAsyncAVCCommandCallback (this=0x%08X, userRefcon=0x%08X)\n",this,userRefcon));
811
812	bcopy(asyncRef,fAsyncAVCCmdCallbackInfo,sizeof(OSAsyncReference64));
813	*returnParam = 0x12345678;
814
815	return kIOReturnSuccess;
816}
817
818//////////////////////////////////////////////////////
819// IOFireWireAVCUserClient::CreateAVCAsyncCommand
820//////////////////////////////////////////////////////
821IOReturn IOFireWireAVCUserClient::CreateAVCAsyncCommand(UInt8 * cmd, UInt8 * asyncAVCCommandHandle, UInt32 len, UInt32 *refSize)
822{
823	IOReturn res = kIOReturnNoMemory;
824	UInt32 *pReturnedCommandHandle = (UInt32*) asyncAVCCommandHandle;
825	UInt32 cmdLen = len - sizeof(mach_vm_address_t);
826	mach_vm_address_t *ppSharedBufAddress = (mach_vm_address_t*) &cmd[cmdLen];
827	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
828	bool memDescPrepared = false;
829
830	FIRELOG_MSG(("IOFireWireAVCUserClient::CreateAVCAsyncCommand (this=0x%08X)\n",this));
831
832	if(!fStarted )
833		return kIOReturnNoDevice;
834
835	do
836	{
837		// Create the wrapper object for the async command
838		pUCAsyncCommand = new IOFireWireAVCUserClientAsyncCommand;
839		if (!pUCAsyncCommand)
840			break;
841
842		// Initialize the wrapper object
843		pUCAsyncCommand->pUserClient = this;
844
845		// Create the memory descriptor for the user/kernel shared response buffer
846		pUCAsyncCommand->fMem = IOMemoryDescriptor::withAddressRange( *ppSharedBufAddress, 1024, kIODirectionInOut, fTask ) ;
847		if (!pUCAsyncCommand->fMem)
848			break;
849
850		// Prepare the memory descriptor
851		res = pUCAsyncCommand->fMem->prepare() ;
852		if (res != kIOReturnSuccess)
853			break;
854		else
855			memDescPrepared = true;
856
857		// Create the Async command object
858		pUCAsyncCommand->pAsyncCommand = new IOFireWireAVCAsynchronousCommand;
859		if (!pUCAsyncCommand->pAsyncCommand)
860		{
861			res = kIOReturnNoMemory;
862			break;
863		}
864
865		// Init the async command object
866		res = pUCAsyncCommand->pAsyncCommand->init(cmd,cmdLen,AVCUserClientAsyncCommandCallback,pUCAsyncCommand);
867		if (res != kIOReturnSuccess)
868			break;
869
870	}while(0);
871
872	if (res == kIOReturnSuccess)
873	{
874		// Everything created successfully. Add this to the array of created async commands
875		IOTakeLock(fAsyncAVCCmdLock);
876		pUCAsyncCommand->commandIdentifierHandle = fNextAVCAsyncCommandHandle++;
877		fUCAsyncCommands->setObject(pUCAsyncCommand);
878		IOUnlock(fAsyncAVCCmdLock);
879
880		// Now that it's retained by the array, remove the extra retain count
881		pUCAsyncCommand->release();
882
883		// Set the return handle for this new command to the user-side lib
884		*pReturnedCommandHandle = pUCAsyncCommand->commandIdentifierHandle;
885	}
886	else
887	{
888		// Something went wrong. Cleanup the mess.
889		*pReturnedCommandHandle = 0xFFFFFFFF;
890		if (pUCAsyncCommand)
891		{
892			if (pUCAsyncCommand->fMem)
893			{
894				if (memDescPrepared == true)
895					pUCAsyncCommand->fMem->complete();
896				pUCAsyncCommand->fMem->release() ;
897			}
898
899			if (pUCAsyncCommand->pAsyncCommand)
900				pUCAsyncCommand->pAsyncCommand->release();
901
902			pUCAsyncCommand->release();
903		}
904	}
905
906	return res;
907}
908
909//////////////////////////////////////////////////////
910// IOFireWireAVCUserClient::SubmitAVCAsyncCommand
911//////////////////////////////////////////////////////
912IOReturn IOFireWireAVCUserClient::SubmitAVCAsyncCommand(UInt32 commandHandle)
913{
914	IOReturn res = kIOReturnBadArgument;
915	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
916
917	FIRELOG_MSG(("IOFireWireAVCUserClient::SubmitAVCAsyncCommand (this=0x%08X)\n",this));
918
919	pUCAsyncCommand = FindUCAsyncCommandWithHandle(commandHandle);
920
921	if (pUCAsyncCommand)
922	{
923		// Submit it
924		res = pUCAsyncCommand->pAsyncCommand->submit(fUnit);
925	}
926	return res;
927}
928
929//////////////////////////////////////////////////////
930// IOFireWireAVCUserClient::CancelAVCAsyncCommand
931//////////////////////////////////////////////////////
932IOReturn IOFireWireAVCUserClient::CancelAVCAsyncCommand(UInt32 commandHandle)
933{
934	IOReturn res = kIOReturnBadArgument;
935	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
936
937	FIRELOG_MSG(("IOFireWireAVCUserClient::CancelAVCAsyncCommand (this=0x%08X)\n",this));
938
939	pUCAsyncCommand = FindUCAsyncCommandWithHandle(commandHandle);
940
941	if (pUCAsyncCommand)
942			res = pUCAsyncCommand->pAsyncCommand->cancel();
943
944	return res;
945}
946
947//////////////////////////////////////////////////////
948// IOFireWireAVCUserClient::ReleaseAVCAsyncCommand
949//////////////////////////////////////////////////////
950IOReturn IOFireWireAVCUserClient::ReleaseAVCAsyncCommand(UInt32 commandHandle)
951{
952	IOReturn res = kIOReturnBadArgument;
953	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
954	bool found = false;
955	UInt32 i;
956
957	FIRELOG_MSG(("IOFireWireAVCUserClient::ReleaseAVCAsyncCommand (this=0x%08X)\n",this));
958
959	// Look for an command in our array with the specified command handle
960	IOTakeLock(fAsyncAVCCmdLock);
961	for(i=0; i<fUCAsyncCommands->getCount(); i++)
962	{
963		pUCAsyncCommand = (IOFireWireAVCUserClientAsyncCommand *)fUCAsyncCommands->getObject(i);
964		if (pUCAsyncCommand->commandIdentifierHandle == commandHandle)
965		{
966			found = true;
967			break;
968		}
969	}
970
971	if (found == true)
972	{
973		pUCAsyncCommand->pAsyncCommand->cancel();	// Cancel, just in case it's still pending!
974		pUCAsyncCommand->pAsyncCommand->release();
975
976		// Get rid of the memory descriptor for the shared buf
977		pUCAsyncCommand->fMem->complete();
978		pUCAsyncCommand->fMem->release() ;
979
980		// Remove this object from our array. This will release it.
981		fUCAsyncCommands->removeObject(i);
982
983		res = kIOReturnSuccess;
984	}
985
986	IOUnlock(fAsyncAVCCmdLock);
987	return res;
988}
989
990//////////////////////////////////////////////////////
991// IOFireWireAVCUserClient::ReinitAVCAsyncCommand
992//////////////////////////////////////////////////////
993IOReturn IOFireWireAVCUserClient::ReinitAVCAsyncCommand(UInt32 commandHandle, const UInt8 *pCommandBytes, UInt32 len)
994{
995	IOReturn res = kIOReturnBadArgument;
996	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
997
998	FIRELOG_MSG(("IOFireWireAVCUserClient::CancelAVCAsyncCommand (this=0x%08X)\n",this));
999
1000	pUCAsyncCommand = FindUCAsyncCommandWithHandle(commandHandle);
1001
1002	if (pUCAsyncCommand)
1003		res = pUCAsyncCommand->pAsyncCommand->reinit(pCommandBytes, len);
1004
1005	return res;
1006}
1007
1008//////////////////////////////////////////////////////
1009// IOFireWireAVCUserClient::FindUCAsyncCommandWithHandle
1010//////////////////////////////////////////////////////
1011IOFireWireAVCUserClientAsyncCommand *IOFireWireAVCUserClient::FindUCAsyncCommandWithHandle(UInt32 commandHandle)
1012{
1013	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand;
1014	bool found = false;
1015	UInt32 i;
1016
1017	FIRELOG_MSG(("IOFireWireAVCUserClient::FindUCAsyncCommandWithHandle (this=0x%08X)\n",this));
1018
1019	// Look for an command in our array with the specified command handle
1020	IOTakeLock(fAsyncAVCCmdLock);
1021	for(i=0; i<fUCAsyncCommands->getCount(); i++)
1022	{
1023		pUCAsyncCommand = (IOFireWireAVCUserClientAsyncCommand *)fUCAsyncCommands->getObject(i);
1024		if (pUCAsyncCommand->commandIdentifierHandle == commandHandle)
1025		{
1026			found = true;
1027			break;
1028		}
1029	}
1030	IOUnlock(fAsyncAVCCmdLock);
1031
1032	if (found == true)
1033		return pUCAsyncCommand;
1034	else
1035		return NULL;
1036}
1037
1038//////////////////////////////////////////////////////
1039// IOFireWireAVCUserClient::HandleUCAsyncCommandCallback
1040//////////////////////////////////////////////////////
1041void IOFireWireAVCUserClient::HandleUCAsyncCommandCallback(IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand)
1042{
1043	UInt32 respLen;
1044	//void * args[kMaxAsyncArgs];
1045	io_user_reference_t args[kMaxAsyncArgs];
1046	OSAsyncReference64 asyncRef;
1047
1048	FIRELOG_MSG(("IOFireWireAVCUserClient::HandleUCAsyncCommandCallback (this=0x%08X)\n",this));
1049
1050	bcopy(fAsyncAVCCmdCallbackInfo, asyncRef, kOSAsyncRef64Size);
1051
1052	// If we just got a response, copy it into the shared user/kernel response memory buffer for this command
1053	switch(pUCAsyncCommand->pAsyncCommand->cmdState)
1054	{
1055		case kAVCAsyncCommandStateReceivedInterimResponse:
1056			pUCAsyncCommand->fMem->writeBytes(kAsyncCmdSharedBufInterimRespOffset,
1057											  pUCAsyncCommand->pAsyncCommand->pInterimResponseBuf,
1058											  pUCAsyncCommand->pAsyncCommand->interimResponseLen);
1059			respLen = pUCAsyncCommand->pAsyncCommand->interimResponseLen;
1060			break;
1061
1062		case kAVCAsyncCommandStateReceivedFinalResponse:
1063			pUCAsyncCommand->fMem->writeBytes(kAsyncCmdSharedBufFinalRespOffset,
1064											  pUCAsyncCommand->pAsyncCommand->pFinalResponseBuf,
1065											  pUCAsyncCommand->pAsyncCommand->finalResponseLen);
1066			respLen = pUCAsyncCommand->pAsyncCommand->finalResponseLen;
1067			break;
1068
1069		case kAVCAsyncCommandStatePendingRequest:
1070		case kAVCAsyncCommandStateRequestSent:
1071		case kAVCAsyncCommandStateRequestFailed:
1072		case kAVCAsyncCommandStateWaitingForResponse:
1073		case kAVCAsyncCommandStateTimeOutBeforeResponse:
1074		case kAVCAsyncCommandStateBusReset:
1075		case kAVCAsyncCommandStateOutOfMemory:
1076		case kAVCAsyncCommandStateCanceled:
1077		default:
1078			respLen = 0;
1079			break;
1080	}
1081
1082	// Send the results to user space
1083	args[0] = (io_user_reference_t) pUCAsyncCommand->commandIdentifierHandle;
1084	args[1] = (io_user_reference_t) pUCAsyncCommand->pAsyncCommand->cmdState;
1085	args[2] = (io_user_reference_t) respLen;
1086	sendAsyncResult64(asyncRef, kIOReturnSuccess, args, 3);
1087}
1088
1089//////////////////////////////////////////////////////
1090// AVCUserClientAsyncCommandCallback
1091//////////////////////////////////////////////////////
1092void AVCUserClientAsyncCommandCallback(void *pRefCon, IOFireWireAVCAsynchronousCommand *pCommandObject)
1093{
1094	IOFireWireAVCUserClientAsyncCommand *pUCAsyncCommand = (IOFireWireAVCUserClientAsyncCommand*) pRefCon;
1095
1096	FIRELOG_MSG(("AVCUserClientAsyncCommandCallback (pCommandObject=0x%08X)\n",pCommandObject));
1097
1098	pUCAsyncCommand->pUserClient->HandleUCAsyncCommandCallback(pUCAsyncCommand);
1099}
1100
1101// requestTerminate
1102//
1103//
1104
1105bool IOFireWireAVCUserClient::requestTerminate( IOService * provider, IOOptionBits options )
1106{
1107	// don't let this go inactive while its open, else the close can't be sent from the user app
1108
1109//	kprintf( "IOFireWireAVCUserClient::requestTerminate\n" );
1110	if( fOpened )
1111	{
1112		return false;
1113	}
1114	else
1115	{
1116		return IOService::requestTerminate(provider, options);
1117	}
1118}
1119