IOFireWireAVCLibUnit.cpp [plain text]
#include <stdlib.h>
#include <stdio.h>
#include <IOKit/avc/IOFireWireAVCLib.h>
#include <IOKit/avc/IOFireWireAVCConsts.h>
#include "IOFireWireAVCLibConsumer.h"
#include "IOFireWireAVCUserClientCommon.h"
#include "IOFireWireAVCLibUnit.h"
#include <CoreFoundation/CFMachPort.h>
#include <IOKit/IOMessage.h>
#include <syslog.h> // Debug messages
#include <pthread.h> // for mutexes
#include <unistd.h>
#import <sys/mman.h>
#include <mach/mach_port.h>
#include <mach/vm_map.h>
#import <System/libkern/OSCrossEndian.h>
#if 0
#define FWLOG(x) printf x
#else
#define FWLOG(x) do {} while (0)
#endif
__BEGIN_DECLS
#include <IOKit/iokitmig.h>
void *IOFireWireAVCLibUnitFactory( CFAllocatorRef allocator, CFUUIDRef typeID );
__END_DECLS
struct _AVCUnit;
typedef struct _InterfaceMap
{
IUnknownVTbl *pseudoVTable;
struct _AVCUnit *obj;
} InterfaceMap;
typedef struct _AVCUnit
{
InterfaceMap fIOCFPlugInInterface;
InterfaceMap fIOFireWireAVCLibUnitInterface;
CFUUIDRef fFactoryId;
UInt32 fRefCount;
io_service_t fService;
io_connect_t fConnection;
CFRunLoopRef fCFRunLoop;
CFRunLoopSourceRef fCFRunLoopSource;
IONotificationPortRef fNotifyPort;
io_object_t fNotification;
IOFWAVCMessageCallback fMessageCallbackRoutine;
void * fMessageCallbackRefCon;
CFMachPortRef fCFAsyncPort;
mach_port_t fAsyncPort;
CFMutableArrayRef fACObjectArray;
pthread_mutex_t fACObjectArrayLock;
CFMutableArrayRef fAVCAsyncCommandArray;
pthread_mutex_t fAVCAsyncCommandArrayLock;
Boolean fSuspended;
Boolean fHighPerfAVCCommands;
} AVCUnit;
typedef struct _AVCLibAsynchronousCommandPriv
{
IOFireWireAVCLibAsynchronousCommand *pCmd;
IOFireWireAVCLibAsynchronousCommandCallback clientCallback;
UInt32 kernelAsyncAVCCommandHandle;
UInt8 *pResponseBuf;
}AVCLibAsynchronousCommandPriv;
#define AVCUnit_getThis( self ) \
(((InterfaceMap *) self)->obj)
AVCLibAsynchronousCommandPriv *FindPrivAVCAsyncCommand(AVCUnit *me, IOFireWireAVCLibAsynchronousCommand *pCommandObject);
static IOReturn stop( void * self );
static void removeIODispatcherFromRunLoop( void * self );
static void AVCAsyncCommandCallback( void *refcon, IOReturn result, io_user_reference_t *args, int numArgs)
{
AVCUnit *me = (AVCUnit*) refcon;
UInt32 commandIdentifierHandle = (args[0] & 0xFFFFFFFF);
UInt32 cmdState = (args[1] & 0xFFFFFFFF);
UInt32 respLen = (args[2] & 0xFFFFFFFF);
CFIndex count = 0;
CFIndex i = 0;
AVCLibAsynchronousCommandPriv *pPrivCmd = NULL;
bool found = false;
pthread_mutex_lock( &me->fAVCAsyncCommandArrayLock );
count = CFArrayGetCount( me->fAVCAsyncCommandArray );
for( i = 0; i < count; i++ )
{
pPrivCmd = (AVCLibAsynchronousCommandPriv*) CFArrayGetValueAtIndex( me->fAVCAsyncCommandArray, i);
if (pPrivCmd->kernelAsyncAVCCommandHandle == commandIdentifierHandle)
{
found = true;
break;
}
}
pthread_mutex_unlock( &me->fAVCAsyncCommandArrayLock );
if (found == true)
{
pPrivCmd->pCmd->cmdState = (IOFWAVCAsyncCommandState) cmdState;
switch (cmdState)
{
case kAVCAsyncCommandStateReceivedInterimResponse:
pPrivCmd->pCmd->pInterimResponseBuf = &pPrivCmd->pResponseBuf[kAsyncCmdSharedBufInterimRespOffset];
pPrivCmd->pCmd->interimResponseLen = respLen;
break;
case kAVCAsyncCommandStateReceivedFinalResponse:
pPrivCmd->pCmd->pFinalResponseBuf = &pPrivCmd->pResponseBuf[kAsyncCmdSharedBufFinalRespOffset];
pPrivCmd->pCmd->finalResponseLen = respLen;
break;
case kAVCAsyncCommandStatePendingRequest:
case kAVCAsyncCommandStateRequestSent:
case kAVCAsyncCommandStateRequestFailed:
case kAVCAsyncCommandStateWaitingForResponse:
case kAVCAsyncCommandStateTimeOutBeforeResponse:
case kAVCAsyncCommandStateBusReset:
case kAVCAsyncCommandStateOutOfMemory:
case kAVCAsyncCommandStateCanceled:
default:
break;
};
if (pPrivCmd->clientCallback)
pPrivCmd->clientCallback(pPrivCmd->pCmd->pRefCon,pPrivCmd->pCmd);
}
}
static UInt32 addRef( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
me->fRefCount++;
return me->fRefCount;
}
static UInt32 release( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
UInt32 retVal = me->fRefCount;
AVCLibAsynchronousCommandPriv *pPrivCmd;
uint32_t outputCnt = 0;
if( 1 == me->fRefCount-- )
{
removeIODispatcherFromRunLoop(self);
stop(self);
pthread_mutex_lock( &me->fACObjectArrayLock );
if( me->fACObjectArray )
{
CFRelease( me->fACObjectArray ); me->fACObjectArray = NULL;
}
pthread_mutex_unlock( &me->fACObjectArrayLock );
pthread_mutex_destroy( &me->fACObjectArrayLock );
pthread_mutex_lock( &me->fAVCAsyncCommandArrayLock );
if( me->fAVCAsyncCommandArray )
{
while(CFArrayGetCount( me->fAVCAsyncCommandArray ))
{
pPrivCmd = (AVCLibAsynchronousCommandPriv*) CFArrayGetValueAtIndex( me->fAVCAsyncCommandArray, 0);
if (pPrivCmd)
{
const uint64_t inArg = pPrivCmd->kernelAsyncAVCCommandHandle;
IOConnectCallScalarMethod(me->fConnection,
kIOFWAVCUserClientReleaseAsyncAVCCommand,
&inArg,
1,NULL,&outputCnt);
if (pPrivCmd->pResponseBuf)
vm_deallocate(mach_task_self(), (vm_address_t) pPrivCmd->pResponseBuf,1024);
if (pPrivCmd->pCmd)
{
if (pPrivCmd->pCmd->pCommandBuf)
delete pPrivCmd->pCmd->pCommandBuf;
delete pPrivCmd->pCmd;
}
CFArrayRemoveValueAtIndex(me->fAVCAsyncCommandArray, 0);
delete pPrivCmd;
}
}
CFRelease( me->fAVCAsyncCommandArray );
me->fACObjectArray = NULL;
}
pthread_mutex_unlock( &me->fAVCAsyncCommandArrayLock );
pthread_mutex_destroy( &me->fAVCAsyncCommandArrayLock );
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;
AVCUnit *me = AVCUnit_getThis(self);
if( CFEqual(uuid, IUnknownUUID) || CFEqual(uuid, kIOCFPlugInInterfaceID) )
{
*ppv = &me->fIOCFPlugInInterface;
addRef(self);
}
else if( CFEqual(uuid, kIOFireWireAVCLibUnitInterfaceID) )
{
me->fHighPerfAVCCommands = false;
*ppv = &me->fIOFireWireAVCLibUnitInterface;
addRef(self);
}
else if( CFEqual(uuid, kIOFireWireAVCLibUnitInterfaceID_v2) )
{
me->fHighPerfAVCCommands = true;
*ppv = &me->fIOFireWireAVCLibUnitInterface;
addRef(self);
}
else
*ppv = 0;
if( !*ppv )
result = E_NOINTERFACE;
CFRelease( uuid );
return result;
}
static void messageCallback(void * refcon, io_service_t service,
natural_t messageType, void *messageArgument)
{
AVCUnit *me = (AVCUnit *)refcon;
IOReturn status = kIOReturnSuccess;
CFIndex count = 0;
CFIndex i = 0;
switch( messageType )
{
case kIOMessageServiceIsSuspended:
me->fSuspended = true;
break;
case kIOMessageServiceIsResumed:
me->fSuspended = false;
break;
default:
break;
}
if (me->fACObjectArray)
{
CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault,
2, IOFireWireAVCLibConsumer::getCFArrayCallbacks() );
if( array == NULL )
status = kIOReturnNoMemory;
if( status == kIOReturnSuccess )
{
pthread_mutex_lock( &me->fACObjectArrayLock );
count = CFArrayGetCount( me->fACObjectArray );
for( i = 0; i < count; i++ )
{
CFArrayAppendValue( array, CFArrayGetValueAtIndex( me->fACObjectArray, i ) );
}
pthread_mutex_unlock( &me->fACObjectArrayLock );
for( i = 0; i < count; i++ )
{
IOFireWireAVCLibConsumer * consumer =
( IOFireWireAVCLibConsumer*)CFArrayGetValueAtIndex( array, i );
consumer->deviceInterestCallback( messageType, messageArgument );
}
CFRelease( array );
}
}
if( me->fMessageCallbackRoutine != NULL )
(me->fMessageCallbackRoutine)( me->fMessageCallbackRefCon, messageType, messageArgument );
}
Boolean isDeviceSuspended( void * self )
{
AVCUnit * me = AVCUnit_getThis(self);
return me->fSuspended;
}
static IOReturn probe( void * self, CFDictionaryRef propertyTable,
io_service_t service, SInt32 *order )
{
if( !service || !IOObjectConformsTo(service, "IOFireWireAVCNub") )
return kIOReturnBadArgument;
return kIOReturnSuccess;
}
static IOReturn start( void * self, CFDictionaryRef propertyTable,
io_service_t service )
{
IOReturn status = kIOReturnSuccess;
AVCUnit *me = AVCUnit_getThis(self);
uint64_t returnVal;
uint64_t refrncData[kOSAsyncRef64Count];
uint32_t outputCnt = 1;
me->fService = service;
status = IOServiceOpen( me->fService, mach_task_self(),
kIOFireWireAVCLibConnection, &me->fConnection );
if( !me->fConnection )
status = kIOReturnNoDevice;
if( status == kIOReturnSuccess )
{
status = IOCreateReceivePort( kOSAsyncCompleteMessageID, &me->fAsyncPort );
}
if( status == kIOReturnSuccess )
{
refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t)AVCAsyncCommandCallback;
refrncData[kIOAsyncCalloutRefconIndex] = (uint64_t)me;
status = IOConnectCallAsyncScalarMethod(me->fConnection,
kIOFWAVCUserClientInstallAsyncAVCCommandCallback,
me->fAsyncPort,
refrncData,kOSAsyncRef64Count,
NULL,0,
&returnVal,&outputCnt);
}
return status;
}
static IOReturn stop( void * self )
{
AVCUnit *me = AVCUnit_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;
}
static IOReturn open( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
uint32_t outputCnt = 0;
if( !me->fConnection )
return kIOReturnNoDevice;
status = IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientOpen,NULL,0,NULL,&outputCnt);
return status;
}
static IOReturn openWithSessionRef( void * self, IOFireWireSessionRef sessionRef )
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
uint32_t outputCnt = 0;
if( !me->fConnection )
return kIOReturnNoDevice;
const uint64_t inputs[1]={(const uint64_t)sessionRef};
status = IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientOpenWithSessionRef,inputs,1,NULL,&outputCnt);
return status;
}
static IOFireWireSessionRef getSessionRef(void * self)
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
IOFireWireSessionRef sessionRef = 0;
uint32_t outputCnt = 1;
uint64_t outputVal;
if( !me->fConnection )
return sessionRef;
status = IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientGetSessionRef,NULL,0,&outputVal,&outputCnt);
if( status != kIOReturnSuccess )
sessionRef = 0; else
sessionRef = (IOFireWireSessionRef) outputVal;
return sessionRef;
}
static void close( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
uint32_t outputCnt = 0;
if( !me->fConnection )
return;
IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientClose,NULL,0,NULL,&outputCnt);
}
static IOReturn addIODispatcherToRunLoop( void *self, CFRunLoopRef cfRunLoopRef )
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
mach_port_t masterDevicePort;
IONotificationPortRef notifyPort;
CFRunLoopSourceRef cfSource;
if( !me->fConnection )
return kIOReturnNoDevice;
status = IOMasterPort(bootstrap_port, &masterDevicePort) ;
notifyPort = IONotificationPortCreate(masterDevicePort);
cfSource = IONotificationPortGetRunLoopSource(notifyPort);
CFRunLoopAddSource(cfRunLoopRef, cfSource, kCFRunLoopDefaultMode);
status = IOServiceAddInterestNotification(notifyPort, me->fService,
kIOGeneralInterest, messageCallback, me,
&me->fNotification);
me->fCFRunLoop = cfRunLoopRef;
me->fNotifyPort = notifyPort;
if( status == kIOReturnSuccess )
{
CFMachPortContext context;
Boolean shouldFreeInfo;
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;
}
static void removeIODispatcherFromRunLoop( void * self )
{
AVCUnit *me = AVCUnit_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;
}
}
static void setMessageCallback( void * self, void * refCon,
IOFWAVCMessageCallback callback )
{
AVCUnit *me = AVCUnit_getThis(self);
me->fMessageCallbackRoutine = callback;
me->fMessageCallbackRefCon = refCon;
}
static IOReturn AVCCommand(void *self, const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 *responseLen)
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status;
size_t outputCnt = *responseLen;
if( !me->fConnection )
return kIOReturnNotOpen;
status = IOConnectCallStructMethod(me->fConnection, kIOFWAVCUserClientAVCCommand, command, cmdLen, response,&outputCnt);
if(status == kIOReturnSuccess)
*responseLen = outputCnt;
if (me->fHighPerfAVCCommands == false)
{
usleep( 8 * 1000 );
}
return status;
}
static IOReturn AVCCommandInGeneration(void *self, UInt32 generation,
const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 *responseLen)
{
UInt8 annoying[sizeof(UInt32) + 512];
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status;
size_t outputCnt = *responseLen;
if( !me->fConnection )
return kIOReturnNotOpen;
ROSETTA_ONLY(
{
generation = OSSwapInt32(generation);
}
);
*(UInt32 *)annoying = generation;
bcopy(command, annoying+sizeof(UInt32), cmdLen);
status = IOConnectCallStructMethod(me->fConnection, kIOFWAVCUserClientAVCCommandInGen, annoying, cmdLen+sizeof(UInt32), response,&outputCnt);
if(status == kIOReturnSuccess)
*responseLen = outputCnt;
if (me->fHighPerfAVCCommands == false)
{
usleep( 8 * 1000 );
}
return status;
}
static void *GetAncestorInterface( void * self, char * object_class, REFIID pluginType, REFIID iid)
{
io_registry_entry_t parent = NULL;
io_registry_entry_t notTheDesiredParent = NULL;
IOCFPlugInInterface** theCFPlugInInterface = 0;
void * resultInterface = 0 ;
SInt32 theScore ;
IOReturn err;
HRESULT comErr;
AVCUnit * me = AVCUnit_getThis(self);
CFUUIDRef type_id = CFUUIDCreateFromUUIDBytes(NULL, pluginType);
do {
err = IORegistryEntryGetParentEntry(me->fService, kIOServicePlane, &parent);
while(!err && !IOObjectConformsTo(parent, object_class) )
{
notTheDesiredParent = parent;
err = IORegistryEntryGetParentEntry(notTheDesiredParent, kIOServicePlane, &parent);
IOObjectRelease(notTheDesiredParent);
}
if(err)
{
parent = NULL;
break;
}
err = IOCreatePlugInInterfaceForService(
parent,
type_id,
kIOCFPlugInInterfaceID, & theCFPlugInInterface,
& theScore);
if(err)
break;
comErr = (*theCFPlugInInterface)->QueryInterface(
theCFPlugInInterface,
iid,
(void**) & resultInterface);
if (comErr != S_OK) {
err = comErr;
break;
}
} while (false);
if(theCFPlugInInterface) {
UInt32 ref;
ref = (*theCFPlugInInterface)->Release(theCFPlugInInterface); }
CFRelease( type_id );
if ((!resultInterface) && (parent))
IOObjectRelease(parent);
return resultInterface;
}
static void *GetProtocolInterface( void * self, REFIID pluginType, REFIID iid)
{
io_registry_entry_t parent = NULL;
io_registry_entry_t notTheDesiredParent = NULL;
io_registry_entry_t child = NULL;
io_iterator_t iterator = NULL;
IOCFPlugInInterface** theCFPlugInInterface = 0;
void * resultInterface = 0 ;
SInt32 theScore ;
IOReturn err;
HRESULT comErr;
AVCUnit * me = AVCUnit_getThis(self);
CFUUIDRef type_id = CFUUIDCreateFromUUIDBytes(NULL, pluginType);
do {
err = IORegistryEntryGetParentEntry(me->fService, kIOServicePlane, &parent);
while(!err && !IOObjectConformsTo(parent, "IOFireWireController") )
{
notTheDesiredParent = parent;
err = IORegistryEntryGetParentEntry(notTheDesiredParent, kIOServicePlane, &parent);
IOObjectRelease(notTheDesiredParent);
}
if(err)
{
parent = NULL;
break;
}
err = IORegistryEntryGetChildIterator(parent, kIOServicePlane, &iterator );
if(err)
break;
while(child = IOIteratorNext(iterator)) {
if(IOObjectConformsTo(child, "IOFireWireLocalNode"))
break;
IOObjectRelease(child);
child = NULL;
}
if(!child)
break;
err = IOCreatePlugInInterfaceForService(
child,
type_id,
kIOCFPlugInInterfaceID, & theCFPlugInInterface,
& theScore);
if(err)
break;
comErr = (*theCFPlugInInterface)->QueryInterface(
theCFPlugInInterface,
iid,
(void**) & resultInterface);
if (comErr != S_OK) {
err = comErr;
break;
}
} while (false);
if(theCFPlugInInterface) {
UInt32 ref;
ref = (*theCFPlugInInterface)->Release(theCFPlugInInterface); }
if(iterator)
IOObjectRelease(iterator);
CFRelease( type_id );
if (parent)
IOObjectRelease(parent);
if ((!resultInterface) && (child))
IOObjectRelease(child);
return resultInterface;
}
static IOReturn getAsyncConnectionPlugCounts( void *self, UInt8 * inputPlugCount, UInt8 * outputPlugCount )
{
IOReturn status;
UInt8 command[8];
UInt8 response[8];
UInt32 responseLength = 0;
command[0] = 0x01;
command[1] = kAVCUnitAddress;
command[2] = 0x02;
command[3] = 0x01;
command[4] = 0xFF;
command[5] = 0xFF;
command[6] = 0XFF;
command[7] = 0XFF;
status = AVCCommand( self, command, 8, response, &responseLength );
if( status == kIOReturnSuccess && responseLength == 8 )
{
*inputPlugCount = response[4];
*outputPlugCount = response[5];
return kIOReturnSuccess;
}
else
return kIOReturnError;
}
static IUnknownVTbl ** createConsumerPlug( void *self, UInt8 plugNumber, REFIID iid )
{
IOReturn status = kIOReturnSuccess;
IUnknownVTbl ** iunknown = NULL;
IUnknownVTbl ** consumer = NULL;
AVCUnit * me = AVCUnit_getThis(self);
pthread_mutex_lock( &me->fACObjectArrayLock );
if( status == kIOReturnSuccess )
{
iunknown = IOFireWireAVCLibConsumer::alloc( (IOFireWireAVCLibUnitInterface **)self, me->fCFRunLoop, plugNumber);
if( iunknown == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
HRESULT res;
res = (*iunknown)->QueryInterface( iunknown, iid,
(void **) &consumer );
if( res != S_OK )
status = kIOReturnError;
}
FWLOG(( "IOFireWireAVCLibUnit : about to CFArrayAppendValue\n" ));
if( status == kIOReturnSuccess )
{
IOFireWireAVCLibConsumer * consumerObject;
consumerObject = IOFireWireAVCLibConsumer::getThis( consumer );
CFArrayAppendValue( me->fACObjectArray, (void*)consumerObject );
}
if( iunknown != NULL )
{
(*iunknown)->Release(iunknown);
}
FWLOG(( "IOFireWireAVCLibUnit : just CFArrayAppendValue\n" ));
pthread_mutex_unlock( &me->fACObjectArrayLock );
if( status == kIOReturnSuccess )
return consumer;
else
return NULL;
}
void consumerPlugDestroyed( void * self, IOFireWireAVCLibConsumer * consumer )
{
CFIndex count = 0;
CFIndex index = 0;
AVCUnit * me = AVCUnit_getThis(self);
FWLOG(( "IOFireWireAVCLibUnit : consumerPlugDestroyed\n" ));
pthread_mutex_lock( &me->fACObjectArrayLock );
count = CFArrayGetCount( me->fACObjectArray );
index = CFArrayGetFirstIndexOfValue( me->fACObjectArray,
CFRangeMake(0, count),
(void *)consumer );
if( index != -1 )
{
CFArrayRemoveValueAtIndex( me->fACObjectArray, index );
}
pthread_mutex_unlock( &me->fACObjectArrayLock );
}
static IOReturn updateAVCCommandTimeout( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
uint32_t outputCnt = 0;
if( !me->fConnection )
return kIOReturnNotOpen;
return IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientUpdateAVCCommandTimeout,NULL,0,NULL,&outputCnt);
}
static IOReturn makeP2PInputConnection(void * self, UInt32 inputPlug, UInt32 chan)
{
AVCUnit *me = AVCUnit_getThis(self);
uint32_t outputCnt = 0;
const uint64_t inputs[2] = {inputPlug,chan};
return IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientMakeP2PInputConnection,inputs,2,NULL,&outputCnt);
}
static IOReturn breakP2PInputConnection(void * self, UInt32 inputPlug)
{
AVCUnit *me = AVCUnit_getThis(self);
uint32_t outputCnt = 0;
const uint64_t inputs[1]={(const uint64_t)inputPlug};
return IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientBreakP2PInputConnection,inputs,1,NULL,&outputCnt);
}
static IOReturn makeP2POutputConnection(void * self, UInt32 outputPlug, UInt32 chan, IOFWSpeed speed)
{
AVCUnit *me = AVCUnit_getThis(self);
uint32_t outputCnt = 0;
const uint64_t inputs[3] = {outputPlug,chan,speed};
return IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientMakeP2POutputConnection,inputs,3,NULL,&outputCnt);
}
static IOReturn breakP2POutputConnection(void * self, UInt32 outputPlug)
{
AVCUnit *me = AVCUnit_getThis(self);
uint32_t outputCnt = 0;
const uint64_t inputs[1]={(const uint64_t)outputPlug};
return IOConnectCallScalarMethod(me->fConnection,kIOFWAVCUserClientBreakP2POutputConnection,inputs,1,NULL,&outputCnt);
}
static IOReturn createAVCAsynchronousCommand(void * self,
const UInt8 * command,
UInt32 cmdLen,
IOFireWireAVCLibAsynchronousCommandCallback completionCallback,
void *pRefCon,
IOFireWireAVCLibAsynchronousCommand **ppCommandObject)
{
AVCUnit *me = AVCUnit_getThis(self);
AVCLibAsynchronousCommandPriv *pPrivCmd;
IOReturn status = kIOReturnNoMemory;
size_t outputCnt = sizeof(UInt32);
mach_vm_address_t *pSharedBufAddress;
mach_vm_address_t sharedBuf;
if(cmdLen == 0 || cmdLen > 512)
return kIOReturnBadArgument;
do
{
pPrivCmd = new AVCLibAsynchronousCommandPriv;
if (!pPrivCmd)
break;
pPrivCmd->pCmd = new IOFireWireAVCLibAsynchronousCommand;
if (!pPrivCmd->pCmd)
break;
pPrivCmd->pCmd->pCommandBuf = (UInt8*) malloc(cmdLen+sizeof(mach_vm_address_t));
if (!pPrivCmd->pCmd->pCommandBuf)
break;
bcopy(command, pPrivCmd->pCmd->pCommandBuf, cmdLen);
vm_allocate(mach_task_self(), (vm_address_t *)&pPrivCmd->pResponseBuf,1024, VM_FLAGS_ANYWHERE);
if (!pPrivCmd->pResponseBuf)
break;
pSharedBufAddress = (mach_vm_address_t *) &(pPrivCmd->pCmd->pCommandBuf[cmdLen]);
sharedBuf = (mach_vm_address_t) pPrivCmd->pResponseBuf;
*pSharedBufAddress = sharedBuf;
ROSETTA_ONLY(
{
*pSharedBufAddress = (mach_vm_address_t) OSSwapInt64(sharedBuf);
}
);
pPrivCmd->pCmd->cmdLen = cmdLen;
pPrivCmd->pCmd->cmdState = kAVCAsyncCommandStatePendingRequest;
pPrivCmd->pCmd->pRefCon = pRefCon;
pPrivCmd->pCmd->pInterimResponseBuf = nil;
pPrivCmd->pCmd->interimResponseLen = 0;
pPrivCmd->pCmd->pFinalResponseBuf = nil;
pPrivCmd->pCmd->finalResponseLen = 0;
pPrivCmd->clientCallback = completionCallback;
status = IOConnectCallStructMethod(me->fConnection,
kIOFWAVCUserClientCreateAsyncAVCCommand,
pPrivCmd->pCmd->pCommandBuf,
cmdLen+sizeof(mach_vm_address_t),
&(pPrivCmd->kernelAsyncAVCCommandHandle),
&outputCnt);
ROSETTA_ONLY(
{
pPrivCmd->kernelAsyncAVCCommandHandle = OSSwapInt32(pPrivCmd->kernelAsyncAVCCommandHandle);
}
);
if (status != kIOReturnSuccess)
*ppCommandObject = nil;
else
*ppCommandObject = pPrivCmd->pCmd;
}while(0);
if (status == kIOReturnSuccess)
{
pthread_mutex_lock( &me->fAVCAsyncCommandArrayLock );
CFArrayAppendValue(me->fAVCAsyncCommandArray, pPrivCmd);
pthread_mutex_unlock( &me->fAVCAsyncCommandArrayLock );
}
else
{
if (pPrivCmd)
{
if (pPrivCmd->pResponseBuf)
vm_deallocate(mach_task_self(), (vm_address_t) pPrivCmd->pResponseBuf,1024);
if (pPrivCmd->pCmd)
{
if (pPrivCmd->pCmd->pCommandBuf)
delete pPrivCmd->pCmd->pCommandBuf;
delete pPrivCmd->pCmd;
}
delete pPrivCmd;
}
}
return status;
}
static IOReturn AVCAsynchronousCommandSubmit(void * self, IOFireWireAVCLibAsynchronousCommand *pCommandObject)
{
AVCUnit *me = AVCUnit_getThis(self);
AVCLibAsynchronousCommandPriv *pPrivCmd;
IOReturn res = kIOReturnBadArgument;
uint32_t outputCnt = 0;
pPrivCmd = FindPrivAVCAsyncCommand(me,pCommandObject);
if (pPrivCmd)
{
if (pPrivCmd->pCmd->cmdState != kAVCAsyncCommandStatePendingRequest)
{
res = kIOReturnNotPermitted;
}
else
{
const uint64_t inArg = pPrivCmd->kernelAsyncAVCCommandHandle;
res = IOConnectCallScalarMethod(me->fConnection,
kIOFWAVCUserClientSubmitAsyncAVCCommand,
&inArg,
1,NULL,&outputCnt);
if (res == kIOReturnSuccess)
{
if (pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStatePendingRequest)
pPrivCmd->pCmd->cmdState = kAVCAsyncCommandStateRequestSent;
}
else
{
pPrivCmd->pCmd->cmdState = kAVCAsyncCommandStateRequestFailed;
}
}
}
return res;
}
static IOReturn AVCAsynchronousCommandReinit(void * self, IOFireWireAVCLibAsynchronousCommand *pCommandObject)
{
AVCUnit *me = AVCUnit_getThis(self);
AVCLibAsynchronousCommandPriv *pPrivCmd;
IOReturn res = kIOReturnBadArgument;
uint32_t outputCnt = 0;
size_t outputStructCnt = 0;
pPrivCmd = FindPrivAVCAsyncCommand(me,pCommandObject);
if (pPrivCmd)
{
if ( (pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStateRequestSent) ||
(pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStateWaitingForResponse) ||
(pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStateReceivedInterimResponse) )
{
res = kIOReturnNotPermitted;
}
else
{
const uint64_t inArg = pPrivCmd->kernelAsyncAVCCommandHandle;
res = IOConnectCallMethod(me->fConnection,
kIOFWAVCUserClientReinitAsyncAVCCommand,
&inArg,
1,
pPrivCmd->pCmd->pCommandBuf,
pPrivCmd->pCmd->cmdLen,
NULL,
&outputCnt,
NULL,
&outputStructCnt);
if (res == kIOReturnSuccess)
{
pPrivCmd->pCmd->cmdState = kAVCAsyncCommandStatePendingRequest;
pPrivCmd->pCmd->pInterimResponseBuf = nil;
pPrivCmd->pCmd->interimResponseLen = 0;
pPrivCmd->pCmd->pFinalResponseBuf = nil;
pPrivCmd->pCmd->finalResponseLen = 0;
}
}
}
return res;
}
static IOReturn AVCAsynchronousCommandReinitWithCommandBytes(void * self,
IOFireWireAVCLibAsynchronousCommand *pCommandObject,
const UInt8 * command,
UInt32 cmdLen)
{
AVCUnit *me = AVCUnit_getThis(self);
AVCLibAsynchronousCommandPriv *pPrivCmd;
IOReturn res = kIOReturnBadArgument;
UInt8 *pNewCommandBuf;
uint32_t outputCnt = 0;
size_t outputStructCnt = 0;
if(cmdLen == 0 || cmdLen > 512)
return kIOReturnBadArgument;
pPrivCmd = FindPrivAVCAsyncCommand(me,pCommandObject);
if (pPrivCmd)
{
pNewCommandBuf = (UInt8*) malloc(cmdLen);
if (pNewCommandBuf)
{
res = kIOReturnBadArgument;
}
else
{
if ( (pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStateRequestSent) ||
(pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStateWaitingForResponse) ||
(pPrivCmd->pCmd->cmdState == kAVCAsyncCommandStateReceivedInterimResponse) )
{
delete pNewCommandBuf; res = kIOReturnNotPermitted;
}
else
{
delete pPrivCmd->pCmd->pCommandBuf;
pPrivCmd->pCmd->pCommandBuf = pNewCommandBuf;
bcopy(command, pPrivCmd->pCmd->pCommandBuf, cmdLen);
const uint64_t inArg = pPrivCmd->kernelAsyncAVCCommandHandle;
res = IOConnectCallMethod(me->fConnection,
kIOFWAVCUserClientReinitAsyncAVCCommand,
&inArg,
1,
pPrivCmd->pCmd->pCommandBuf,
pPrivCmd->pCmd->cmdLen,
NULL,
&outputCnt,
NULL,
&outputStructCnt);
if (res == kIOReturnSuccess)
{
pPrivCmd->pCmd->cmdState = kAVCAsyncCommandStatePendingRequest;
pPrivCmd->pCmd->pInterimResponseBuf = nil;
pPrivCmd->pCmd->interimResponseLen = 0;
pPrivCmd->pCmd->pFinalResponseBuf = nil;
pPrivCmd->pCmd->finalResponseLen = 0;
}
}
}
}
return res;
}
static IOReturn AVCAsynchronousCommandCancel(void * self, IOFireWireAVCLibAsynchronousCommand *pCommandObject)
{
AVCUnit *me = AVCUnit_getThis(self);
AVCLibAsynchronousCommandPriv *pPrivCmd;
IOReturn res = kIOReturnBadArgument;
uint32_t outputCnt = 0;
pPrivCmd = FindPrivAVCAsyncCommand(me,pCommandObject);
if (pPrivCmd)
{
const uint64_t inArg = pPrivCmd->kernelAsyncAVCCommandHandle;
res = IOConnectCallScalarMethod(me->fConnection,
kIOFWAVCUserClientCancelAsyncAVCCommand,
&inArg,
1,NULL,&outputCnt);
pPrivCmd->pCmd->cmdState = kAVCAsyncCommandStateCanceled;
}
return res;
}
static IOReturn AVCAsynchronousCommandRelease(void * self, IOFireWireAVCLibAsynchronousCommand *pCommandObject)
{
AVCUnit *me = AVCUnit_getThis(self);
AVCLibAsynchronousCommandPriv *pPrivCmd;
IOReturn res = kIOReturnBadArgument;
CFIndex count = 0;
CFIndex i = 0;
bool found = false;
uint32_t outputCnt = 0;
pthread_mutex_lock( &me->fAVCAsyncCommandArrayLock );
count = CFArrayGetCount( me->fAVCAsyncCommandArray );
for( i = 0; i < count; i++ )
{
pPrivCmd = (AVCLibAsynchronousCommandPriv*) CFArrayGetValueAtIndex( me->fAVCAsyncCommandArray, i);
if (pCommandObject == pPrivCmd->pCmd)
{
found = true;
break;
}
}
if (found == true)
{
const uint64_t inArg = pPrivCmd->kernelAsyncAVCCommandHandle;
IOConnectCallScalarMethod(me->fConnection,
kIOFWAVCUserClientReleaseAsyncAVCCommand,
&inArg,
1,NULL,&outputCnt);
if (pPrivCmd->pResponseBuf)
vm_deallocate(mach_task_self(), (vm_address_t) pPrivCmd->pResponseBuf,1024);
if (pPrivCmd->pCmd)
{
if (pPrivCmd->pCmd->pCommandBuf)
delete pPrivCmd->pCmd->pCommandBuf;
delete pPrivCmd->pCmd;
}
CFArrayRemoveValueAtIndex(me->fAVCAsyncCommandArray, i);
delete pPrivCmd;
res = kIOReturnSuccess;
}
pthread_mutex_unlock( &me->fAVCAsyncCommandArrayLock );
return res;
}
AVCLibAsynchronousCommandPriv *FindPrivAVCAsyncCommand(AVCUnit *me, IOFireWireAVCLibAsynchronousCommand *pCommandObject)
{
CFIndex count = 0;
CFIndex i = 0;
AVCLibAsynchronousCommandPriv *pPrivCmd = NULL;
bool found = false;
pthread_mutex_lock( &me->fAVCAsyncCommandArrayLock );
count = CFArrayGetCount( me->fAVCAsyncCommandArray );
for( i = 0; i < count; i++ )
{
pPrivCmd = (AVCLibAsynchronousCommandPriv*) CFArrayGetValueAtIndex( me->fAVCAsyncCommandArray, i);
if (pCommandObject == pPrivCmd->pCmd)
{
found = true;
break;
}
}
pthread_mutex_unlock( &me->fAVCAsyncCommandArrayLock );
if (found == true)
return pPrivCmd;
else
return nil;
}
static IOCFPlugInInterface sIOCFPlugInInterface =
{
0,
&queryInterface,
&addRef,
&release,
1, 0, &probe,
&start,
&stop
};
static IOFireWireAVCLibUnitInterface sUnitInterface =
{
0,
&queryInterface,
&addRef,
&release,
4, 0, &open,
&openWithSessionRef,
&getSessionRef,
&close,
&addIODispatcherToRunLoop,
&removeIODispatcherFromRunLoop,
&setMessageCallback,
&AVCCommand,
&AVCCommandInGeneration,
&GetAncestorInterface,
&GetProtocolInterface,
&getAsyncConnectionPlugCounts,
&createConsumerPlug,
&updateAVCCommandTimeout,
&makeP2PInputConnection,
&breakP2PInputConnection,
&makeP2POutputConnection,
&breakP2POutputConnection,
&createAVCAsynchronousCommand,
&AVCAsynchronousCommandSubmit,
&AVCAsynchronousCommandReinit,
&AVCAsynchronousCommandCancel,
&AVCAsynchronousCommandRelease,
&AVCAsynchronousCommandReinitWithCommandBytes
};
static IOCFPlugInInterface ** alloc()
{
AVCUnit * me;
IOCFPlugInInterface ** interface = NULL;
me = (AVCUnit *)malloc(sizeof(AVCUnit));
if( me )
{
bzero(me, sizeof(AVCUnit));
me->fRefCount = 1;
me->fConnection = MACH_PORT_NULL;
me->fService = MACH_PORT_NULL;
me->fIOCFPlugInInterface.pseudoVTable = (IUnknownVTbl *) &sIOCFPlugInInterface;
me->fIOCFPlugInInterface.obj = me;
me->fIOFireWireAVCLibUnitInterface.pseudoVTable
= (IUnknownVTbl *) &sUnitInterface;
me->fIOFireWireAVCLibUnitInterface.obj = me;
me->fSuspended = false;
me->fHighPerfAVCCommands = false;
pthread_mutex_init( &me->fACObjectArrayLock, NULL );
me->fACObjectArray = CFArrayCreateMutable( kCFAllocatorDefault,
2, IOFireWireAVCLibConsumer::getCFArrayCallbacks() );
pthread_mutex_init( &me->fAVCAsyncCommandArrayLock, NULL );
me->fAVCAsyncCommandArray = CFArrayCreateMutable(kCFAllocatorDefault,
0, NULL);
me->fFactoryId = kIOFireWireAVCLibUnitFactoryID;
CFRetain( me->fFactoryId );
CFPlugInAddInstanceForFactory( me->fFactoryId );
interface = (IOCFPlugInInterface **) &me->fIOCFPlugInInterface.pseudoVTable;
}
return interface;
}
void *IOFireWireAVCLibUnitFactory( CFAllocatorRef allocator, CFUUIDRef typeID )
{
if( CFEqual(typeID, kIOFireWireAVCLibUnitTypeID) )
return (void *) alloc();
else
return NULL;
}