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
#if 0
#define FWLOG(x) printf x
#else
#define FWLOG(x) do {} while (0)
#endif
__BEGIN_DECLS
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;
CFMutableArrayRef fACObjectArray;
pthread_mutex_t fACObjectArrayLock;
Boolean fSuspended;
} AVCUnit;
#define AVCUnit_getThis( self ) \
(((InterfaceMap *) self)->obj)
static IOReturn stop( void * self );
static void removeIODispatcherFromRunLoop( void * self );
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;
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 );
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) )
{
*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;
}
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);
me->fService = service;
status = IOServiceOpen( me->fService, mach_task_self(),
kIOFireWireAVCLibConnection, &me->fConnection );
if( !me->fConnection )
status = kIOReturnNoDevice;
return status;
}
static IOReturn stop( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
if( me->fConnection )
{
IOServiceClose( me->fConnection );
me->fConnection = MACH_PORT_NULL;
}
return kIOReturnSuccess;
}
static IOReturn open( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
if( !me->fConnection )
return kIOReturnNoDevice;
status = IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientOpen, 0, 0);
return status;
}
static IOReturn openWithSessionRef( void * self, IOFireWireSessionRef sessionRef )
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
if( !me->fConnection )
return kIOReturnNoDevice;
status = IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientOpenWithSessionRef,
1, 0, sessionRef);
return status;
}
static IOFireWireSessionRef getSessionRef(void * self)
{
AVCUnit *me = AVCUnit_getThis(self);
IOReturn status = kIOReturnSuccess;
IOFireWireSessionRef sessionRef = 0;
if( !me->fConnection )
return sessionRef;
status = IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientGetSessionRef,
0, 1, (int*)&sessionRef);
if( status != kIOReturnSuccess )
sessionRef = 0;
return sessionRef;
}
static void close( void * self )
{
AVCUnit *me = AVCUnit_getThis(self);
if( !me->fConnection )
return;
IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientClose, 0, 0);
}
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;
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;
}
}
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;
IOByteCount outputCnt = *responseLen;
if( !me->fConnection )
return kIOReturnNotOpen;
status = IOConnectMethodStructureIStructureO(
me->fConnection, kIOFWAVCUserClientAVCCommand,
cmdLen, &outputCnt, (UInt8 *)command, response);
if(status == kIOReturnSuccess)
*responseLen = outputCnt;
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;
IOByteCount outputCnt = *responseLen;
if( !me->fConnection )
return kIOReturnNotOpen;
*(UInt32 *)annoying = generation;
bcopy(command, annoying+sizeof(UInt32), cmdLen);
status = IOConnectMethodStructureIStructureO(
me->fConnection, kIOFWAVCUserClientAVCCommandInGen,
cmdLen+sizeof(UInt32), &outputCnt, annoying, response);
if(status == kIOReturnSuccess)
*responseLen = outputCnt;
return status;
}
static void *GetAncestorInterface( void * self, char * object_class, REFIID pluginType, REFIID iid)
{
io_registry_entry_t parent;
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) )
err = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
if(err)
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 );
return resultInterface;
}
static void *GetProtocolInterface( void * self, REFIID pluginType, REFIID iid)
{
io_registry_entry_t parent;
io_registry_entry_t child;
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") )
err = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
if(err)
break;
err = IORegistryEntryGetChildIterator(parent, kIOServicePlane, &iterator );
if(err)
break;
while(child = IOIteratorNext(iterator)) {
if(IOObjectConformsTo(child, "IOFireWireLocalNode"))
break;
IOObjectRelease(child);
}
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 );
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);
if( !me->fConnection )
return kIOReturnNotOpen;
return IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientUpdateAVCCommandTimeout, 0, 0);
}
static IOReturn makeP2PInputConnection(void * self, UInt32 inputPlug, UInt32 chan)
{
AVCUnit *me = AVCUnit_getThis(self);
return IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientMakeP2PInputConnection, 2, 0, inputPlug, chan);
}
static IOReturn breakP2PInputConnection(void * self, UInt32 inputPlug)
{
AVCUnit *me = AVCUnit_getThis(self);
return IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientBreakP2PInputConnection, 1, 0, inputPlug);
}
static IOReturn makeP2POutputConnection(void * self, UInt32 outputPlug, UInt32 chan, IOFWSpeed speed)
{
AVCUnit *me = AVCUnit_getThis(self);
return IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientMakeP2POutputConnection, 3, 0, outputPlug, chan, speed);
}
static IOReturn breakP2POutputConnection(void * self, UInt32 outputPlug)
{
AVCUnit *me = AVCUnit_getThis(self);
return IOConnectMethodScalarIScalarO( me->fConnection, kIOFWAVCUserClientBreakP2POutputConnection, 1, 0, outputPlug);
}
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
};
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;
pthread_mutex_init( &me->fACObjectArrayLock, NULL );
me->fACObjectArray = CFArrayCreateMutable( kCFAllocatorDefault,
2, IOFireWireAVCLibConsumer::getCFArrayCallbacks() );
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;
}