/* * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include "IOFireWireAVCLib.h" #include "IOFireWireAVCUserClientCommon.h" #include "IOFireWireAVCConsts.h" __BEGIN_DECLS #include __END_DECLS #define FWLOG printf struct _AVCProtocol; typedef struct _InterfaceMap { IUnknownVTbl *pseudoVTable; struct _AVCProtocol *obj; } InterfaceMap; typedef struct _AVCProtocol { ////////////////////////////////////// // cf plugin interfaces InterfaceMap fIOCFPlugInInterface; InterfaceMap fIOFireWireAVCLibProtocolInterface; ////////////////////////////////////// // cf plugin ref counting CFUUIDRef fFactoryId; UInt32 fRefCount; ////////////////////////////////////// // user client connection io_service_t fService; io_connect_t fConnection; ////////////////////////////////////// // async callbacks mach_port_t fAsyncPort; CFRunLoopRef fCFRunLoop; CFRunLoopSourceRef fCFRunLoopSource; CFMachPortRef fCFAsyncPort; IONotificationPortRef fNotifyPort; io_object_t fNotification; IOFWAVCMessageCallback fMessageCallbackRoutine; void * fMessageCallbackRefCon; ////////////////////////////////////// // Lump 'o stuff for handling AVC requests IOFWAVCRequestCallback fAVCRequestCallbackRoutine; void * fAVCRequestCallbackRefCon; UInt32 fCmdLen; UInt32 fCmdGeneration; UInt32 fCmdSource; UInt8 fCommand[512]; UInt8 fResponse[512]; } AVCProtocol; // utility function to get "this" pointer from interface #define getThis( self ) \ (((InterfaceMap *) self)->obj) static IOReturn stop( void * self ); static void removeIODispatcherFromRunLoop( void * self ); ////////////////////////////////////////////////////////////////// // callback static methods // // messageCallback // // static void messageCallback(void * refcon, io_service_t service, natural_t messageType, void *messageArgument) { AVCProtocol *me = (AVCProtocol *)refcon; if( me->fMessageCallbackRoutine != NULL ) (me->fMessageCallbackRoutine)( me->fMessageCallbackRefCon, messageType, messageArgument ); } static void avcRequestCallback( void *refcon, IOReturn result, void **args, int numArgs) { AVCProtocol *me = (AVCProtocol *)refcon; UInt32 pos; UInt32 len; const UInt8* src; pos = (UInt32)args[0]; len = (UInt32)args[1]; src = (const UInt8*)(args+2); if(pos == 0) { me->fCmdGeneration = (UInt32)args[2]; me->fCmdSource = (UInt32)args[3]; me->fCmdLen = (UInt32)args[4]; src = (const UInt8*)(args+5); } bcopy(src, me->fCommand+pos, len); if(pos+len == me->fCmdLen) { IOReturn status; UInt32 respLen = 512; status = me->fAVCRequestCallbackRoutine(me->fAVCRequestCallbackRefCon, me->fCmdGeneration, me->fCmdSource, me->fCommand, me->fCmdLen, me->fResponse, &respLen); if(status != kIOReturnSuccess) { // send back 'unsupported' response respLen = me->fCmdLen; bcopy(me->fCommand, me->fResponse, respLen); me->fResponse[0] = kAVCNotImplementedStatus; } status = IOConnectMethodScalarIStructureI(me->fConnection, kIOFWAVCProtocolUserClientSendAVCResponse, 2, respLen, me->fCmdGeneration, me->fCmdSource, me->fResponse); } } static void pcrWriteCallback( void *refcon, IOReturn result, void **args, int numArgs) { IOFWAVCPCRCallback func; func = (IOFWAVCPCRCallback)args[0]; func(refcon, (UInt32)args[1], (UInt16)args[2], (UInt32)args[3], (UInt32)args[4], (UInt32)args[5]); } static UInt32 addRef( void * self ) { AVCProtocol *me = getThis(self); me->fRefCount++; return me->fRefCount; } static UInt32 release( void * self ) { AVCProtocol *me = getThis(self); UInt32 retVal = me->fRefCount; if( 1 == me->fRefCount-- ) { removeIODispatcherFromRunLoop(self); stop(self); CFPlugInRemoveInstanceForFactory( me->fFactoryId ); CFRelease( me->fFactoryId ); free(me); } else if( me->fRefCount < 0 ) { me->fRefCount = 0; } return retVal; } static HRESULT queryInterface( void * self, REFIID iid, void **ppv ) { CFUUIDRef uuid = CFUUIDCreateFromUUIDBytes(NULL, iid); HRESULT result = S_OK; AVCProtocol *me = getThis(self); if( CFEqual(uuid, IUnknownUUID) || CFEqual(uuid, kIOCFPlugInInterfaceID) ) { *ppv = &me->fIOCFPlugInInterface; addRef(self); } else if( CFEqual(uuid, kIOFireWireAVCLibProtocolInterfaceID) ) { *ppv = &me->fIOFireWireAVCLibProtocolInterface; addRef(self); } else *ppv = 0; if( !*ppv ) result = E_NOINTERFACE; CFRelease( uuid ); return result; } ////////////////////////////////////////////////////////////////// // IOCFPlugInInterface methods // // probe // // static IOReturn probe( void * self, CFDictionaryRef propertyTable, io_service_t service, SInt32 *order ) { // only load against local FireWire node if( !service || !IOObjectConformsTo(service, "IOFireWireLocalNode") ) return kIOReturnBadArgument; return kIOReturnSuccess; } // start // // static IOReturn start( void * self, CFDictionaryRef propertyTable, io_service_t service ) { IOReturn status = kIOReturnSuccess; CFNumberRef guidDesc = 0; io_iterator_t enumerator = 0; io_object_t device = 0; mach_port_t masterDevicePort; AVCProtocol *me = getThis(self); CFMutableDictionaryRef dict; CFMutableDictionaryRef dict2; me->fService = service; // Conjure up our user client do { dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if(!dict) continue; dict2 = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if(!dict2) continue; CFDictionarySetValue( dict2, CFSTR("IODesiredChild"), CFSTR("IOFireWireAVCProtocolUserClient") ); CFDictionarySetValue( dict, CFSTR("SummonNub"), dict2 ); status = IORegistryEntrySetCFProperties(service, dict ); CFRelease( dict ); CFRelease( dict2 ); // Now find it - has same GUID as the IOFireWireLocalNode guidDesc = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("GUID"), kCFAllocatorDefault, 0); dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if(!dict) break; CFDictionarySetValue( dict, CFSTR(kIOProviderClassKey), CFSTR("IOFireWireAVCProtocolUserClient")); //CFDictionarySetValue( dict, CFSTR("GUID"), guidDesc); // get mach master port status = IOMasterPort(bootstrap_port, & masterDevicePort) ; if ( status != kIOReturnSuccess ) { break; } status = IOServiceGetMatchingServices( masterDevicePort, dict, & enumerator ); if( kIOReturnSuccess != status ) { break; } // Find an unused user client while(device = IOIteratorNext(enumerator)) { status = IOServiceOpen( device, mach_task_self(), kIOFireWireAVCLibConnection, &me->fConnection ); IOObjectRelease(device); if(kIOReturnSuccess == status) break; } } while(0); if(guidDesc) CFRelease(guidDesc); if (enumerator) IOObjectRelease(enumerator) ; if( !me->fConnection ) status = kIOReturnNoDevice; if( status == kIOReturnSuccess ) { status = IOCreateReceivePort( kOSAsyncCompleteMessageID, &me->fAsyncPort ); } return status; } // stop // // static IOReturn stop( void * self ) { AVCProtocol *me = getThis(self); if( me->fConnection ) { IOServiceClose( me->fConnection ); me->fConnection = MACH_PORT_NULL; } if( me->fAsyncPort != MACH_PORT_NULL ) { mach_port_destroy( mach_task_self(), me->fAsyncPort); me->fAsyncPort = MACH_PORT_NULL; } return kIOReturnSuccess; } ////////////////////////////////////////////////////////////////// // IOFireWireAVCLibProtocol methods // // addIODispatcherToRunLoop // // static IOReturn addIODispatcherToRunLoop( void *self, CFRunLoopRef cfRunLoopRef ) { AVCProtocol *me = getThis(self); IOReturn status = kIOReturnSuccess; mach_port_t masterDevicePort; IONotificationPortRef notifyPort; CFRunLoopSourceRef cfSource; if( !me->fConnection ) return kIOReturnNoDevice; // get mach master port status = IOMasterPort(bootstrap_port, &masterDevicePort) ; notifyPort = IONotificationPortCreate(masterDevicePort); cfSource = IONotificationPortGetRunLoopSource(notifyPort); CFRunLoopAddSource(cfRunLoopRef, cfSource, kCFRunLoopDefaultMode); // Get messages from device status = IOServiceAddInterestNotification(notifyPort, me->fService, kIOGeneralInterest, messageCallback, me, &me->fNotification); me->fCFRunLoop = cfRunLoopRef; me->fNotifyPort = notifyPort; if( status == kIOReturnSuccess ) { CFMachPortContext context; Boolean shouldFreeInfo; // zzz what's this for? I think it's set to true if the create failed. context.version = 1; context.info = me; context.retain = NULL; context.release = NULL; context.copyDescription = NULL; me->fCFAsyncPort = CFMachPortCreateWithPort( kCFAllocatorDefault, me->fAsyncPort, (CFMachPortCallBack) IODispatchCalloutFromMessage, &context, &shouldFreeInfo ); if( !me->fCFAsyncPort ) status = kIOReturnNoMemory; } if( status == kIOReturnSuccess ) { me->fCFRunLoopSource = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, me->fCFAsyncPort, 0 ); if( !me->fCFRunLoopSource ) status = kIOReturnNoMemory; } if( status == kIOReturnSuccess ) { CFRunLoopAddSource(cfRunLoopRef, me->fCFRunLoopSource, kCFRunLoopDefaultMode ); } return status; } // removeIODispatcherFromRunLoop // // static void removeIODispatcherFromRunLoop( void * self ) { AVCProtocol *me = getThis(self); if( me->fNotification ) { IOObjectRelease(me->fNotification); me->fNotification = NULL; } if( me->fNotifyPort ) { CFRunLoopRemoveSource( me->fCFRunLoop, IONotificationPortGetRunLoopSource(me->fNotifyPort), kCFRunLoopDefaultMode ); IONotificationPortDestroy(me->fNotifyPort); me->fNotifyPort = NULL; } if(me->fCFRunLoopSource) { CFRunLoopRemoveSource( me->fCFRunLoop, me->fCFRunLoopSource, kCFRunLoopDefaultMode ); CFRelease(me->fCFRunLoopSource); me->fCFRunLoopSource = NULL; } if( me->fCFAsyncPort != NULL ) { CFMachPortInvalidate(me->fCFAsyncPort); CFRelease( me->fCFAsyncPort ); me->fCFAsyncPort = NULL; } } // setMessageCallback // // static void setMessageCallback( void * self, void * refCon, IOFWAVCMessageCallback callback ) { AVCProtocol *me = getThis(self); me->fMessageCallbackRoutine = callback; me->fMessageCallbackRefCon = refCon; } // setAVCRequestCallback // // static IOReturn setAVCRequestCallback( void *self, UInt32 subUnitType, UInt32 subUnitID, void *refCon, IOFWAVCRequestCallback callback) { AVCProtocol *me = getThis(self); io_async_ref_t asyncRef; io_scalar_inband_t params; mach_msg_type_number_t size = 0; IOReturn status; asyncRef[0] = 0x1234; asyncRef[kIOAsyncCalloutFuncIndex] = (UInt32)(IOAsyncCallback)&avcRequestCallback; asyncRef[kIOAsyncCalloutRefconIndex] = (UInt32)me; asyncRef[3] = 0x3456; params[0] = subUnitType; params[1] = subUnitID; status = io_async_method_scalarI_scalarO( me->fConnection, me->fAsyncPort, asyncRef, 3, kIOFWAVCProtocolUserClientSetAVCRequestCallback, params, 2, NULL, &size ); if(status == kIOReturnSuccess) { me->fAVCRequestCallbackRoutine = callback; me->fAVCRequestCallbackRefCon = refCon; } return status; } static IOReturn allocateInputPlug( void *self, void *refcon, IOFWAVCPCRCallback func, UInt32 *plug) { AVCProtocol *me = getThis(self); io_async_ref_t asyncRef; io_scalar_inband_t params; mach_msg_type_number_t size = 1; IOReturn status; asyncRef[kIOAsyncCalloutFuncIndex] = (UInt32)(IOAsyncCallback)&pcrWriteCallback; asyncRef[kIOAsyncCalloutRefconIndex] = (UInt32)refcon; params[0] = (int)func; status = io_async_method_scalarI_scalarO( me->fConnection, me->fAsyncPort, asyncRef, 3, kIOFWAVCProtocolUserClientAllocateInputPlug, params, 1, (int *)plug, &size ); return status; } static void freeInputPlug( void *self, UInt32 plug) { AVCProtocol *me = getThis(self); IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientFreeInputPlug, 1, 0, plug); } static UInt32 readInputPlug( void *self, UInt32 plug) { AVCProtocol *me = getThis(self); UInt32 val; IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientReadInputPlug, 1, 1, plug, &val); return val; } static IOReturn updateInputPlug( void *self, UInt32 plug, UInt32 oldVal, UInt32 newVal) { AVCProtocol *me = getThis(self); return IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientUpdateInputPlug, 3, 0, plug, oldVal, newVal); } static IOReturn allocateOutputPlug( void *self, void *refcon, IOFWAVCPCRCallback func, UInt32 *plug) { AVCProtocol *me = getThis(self); io_async_ref_t asyncRef; io_scalar_inband_t params; mach_msg_type_number_t size = 1; IOReturn status; asyncRef[kIOAsyncCalloutFuncIndex] = (UInt32)(IOAsyncCallback)&pcrWriteCallback; asyncRef[kIOAsyncCalloutRefconIndex] = (UInt32)refcon; params[0] = (int)func; status = io_async_method_scalarI_scalarO( me->fConnection, me->fAsyncPort, asyncRef, 3, kIOFWAVCProtocolUserClientAllocateOutputPlug, params, 1, (int *)plug, &size ); return status; } static void freeOutputPlug( void *self, UInt32 plug) { AVCProtocol *me = getThis(self); IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientFreeOutputPlug, 1, 0, plug); } static UInt32 readOutputPlug( void *self, UInt32 plug) { AVCProtocol *me = getThis(self); UInt32 val; IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientReadOutputPlug, 1, 1, plug, &val); return val; } static IOReturn updateOutputPlug( void *self, UInt32 plug, UInt32 oldVal, UInt32 newVal) { AVCProtocol *me = getThis(self); return IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientUpdateOutputPlug, 3, 0, plug, oldVal, newVal); } static UInt32 readOutputMasterPlug( void *self) { AVCProtocol *me = getThis(self); UInt32 val; IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientReadOutputMasterPlug, 0, 1, &val); return val; } static IOReturn updateOutputMasterPlug( void *self, UInt32 oldVal, UInt32 newVal) { AVCProtocol *me = getThis(self); return IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientUpdateOutputMasterPlug, 2, 0, oldVal, newVal); } static UInt32 readInputMasterPlug( void *self) { AVCProtocol *me = getThis(self); UInt32 val; IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientReadInputMasterPlug, 0, 1, &val); return val; } static IOReturn updateInputMasterPlug( void *self, UInt32 oldVal, UInt32 newVal) { AVCProtocol *me = getThis(self); return IOConnectMethodScalarIScalarO(me->fConnection, kIOFWAVCProtocolUserClientUpdateInputMasterPlug, 2, 0, oldVal, newVal); } // static interface table for IOCFPlugInInterface // static IOCFPlugInInterface sIOCFPlugInInterface = { 0, &queryInterface, &addRef, &release, 1, 0, // version/revision &probe, &start, &stop }; // // static interface table for IOFireWireAVCLibProtocolInterface // static IOFireWireAVCLibProtocolInterface sProtocolInterface = { 0, &queryInterface, &addRef, &release, 1, 0, // version/revision &addIODispatcherToRunLoop, &removeIODispatcherFromRunLoop, &setMessageCallback, &setAVCRequestCallback, &allocateInputPlug, &freeInputPlug, &readInputPlug, &updateInputPlug, &allocateOutputPlug, &freeOutputPlug, &readOutputPlug, &updateOutputPlug, &readOutputMasterPlug, &updateOutputMasterPlug, &readInputMasterPlug, &updateInputMasterPlug, }; // IOFireWireAVCLibProtocolFactory // alloc // // static allocator, called by factory method static IOCFPlugInInterface ** alloc() { IOCFPlugInInterface ** interface = NULL; AVCProtocol * me; me = (AVCProtocol *)malloc(sizeof(AVCProtocol)); if( me ) { bzero(me, sizeof(AVCProtocol)); // we return an interface here. queryInterface will not be called. set refs to 1 // init cf plugin ref counting me->fRefCount = 1; // init user client connection me->fConnection = MACH_PORT_NULL; me->fService = MACH_PORT_NULL; // create plugin interface map me->fIOCFPlugInInterface.pseudoVTable = (IUnknownVTbl *) &sIOCFPlugInInterface; me->fIOCFPlugInInterface.obj = me; // create test driver interface map me->fIOFireWireAVCLibProtocolInterface.pseudoVTable = (IUnknownVTbl *) &sProtocolInterface; me->fIOFireWireAVCLibProtocolInterface.obj = me; me->fFactoryId = kIOFireWireAVCLibProtocolFactoryID; CFRetain( me->fFactoryId ); CFPlugInAddInstanceForFactory( me->fFactoryId ); interface = (IOCFPlugInInterface **) &me->fIOCFPlugInInterface.pseudoVTable; } return interface; } // // factory method (only exported symbol) void *IOFireWireAVCLibProtocolFactory( CFAllocatorRef allocator, CFUUIDRef typeID ) { if( CFEqual(typeID, kIOFireWireAVCLibProtocolTypeID) ) return (void *) alloc(); else return NULL; }