IOFireWireAVCUnit.cpp [plain text]
#include "IOFireWireAVCUnit.h"
#include "IOFireWireAVCCommand.h"
#include "IOFireWireAVCConsts.h"
#include <IOKit/IOMessage.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/firewire/IOFireWireUnit.h>
#include <IOKit/firewire/IOFireWireBus.h>
#include <IOKit/firewire/IOFWAddressSpace.h>
#include "IOFireWirePCRSpace.h"
const OSSymbol *gIOFireWireAVCUnitType;
const OSSymbol *gIOFireWireAVCSubUnitType;
const OSSymbol *gIOFireWireAVCSubUnitCount[kAVCNumSubUnitTypes];
OSDefineMetaClass(IOFireWireAVCNub, IOService)
OSDefineAbstractStructors(IOFireWireAVCNub, IOService)
OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 1);
OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 2);
OSMetaClassDefineReservedUnused(IOFireWireAVCNub, 3);
OSDefineMetaClassAndStructors(IOFireWireAVCUnit, IOFireWireAVCNub)
OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 0);
OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 1);
OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 2);
OSMetaClassDefineReservedUnused(IOFireWireAVCUnit, 3);
UInt32 IOFireWireAVCUnit::AVCResponse(void *refcon, UInt16 nodeID, IOFWSpeed &speed,
FWAddress addr, UInt32 len, const void *buf, IOFWRequestRefCon requestRefcon)
{
IOFireWireAVCUnit *me = (IOFireWireAVCUnit *)refcon;
UInt32 res = kFWResponseAddressError;
if(addr.addressLo == kFCPResponseAddress && me->fCommand) {
res = me->fCommand->handleResponse(nodeID, len, buf);
}
return res;
}
void IOFireWireAVCUnit::rescanSubUnits(void *arg)
{
IOFireWireAVCUnit *me = (IOFireWireAVCUnit *)arg;
me->updateSubUnits(false);
}
void IOFireWireAVCUnit::updateSubUnits(bool firstTime)
{
IOReturn res;
UInt32 size;
UInt8 cmd[8],response[8];
OSObject *prop;
bool hasFCP = true;
cmd[kAVCCommandResponse] = kAVCStatusInquiryCommand;
cmd[kAVCAddress] = kAVCUnitAddress;
cmd[kAVCOpcode] = kAVCSubunitInfoOpcode;
cmd[kAVCOperand0] = 7;
cmd[4] = cmd[5] = cmd[6] = cmd[7] = 0xff;
size = 8;
for(int i = 0; i<10; i++) {
res = AVCCommand(cmd, 8, response, &size);
if(res == (kIOFireWireResponseBase + kFWResponseConflictError)) {
IOSleep(10);
continue; }
else if(res == kIOReturnSuccess && response[kAVCOperand2] == 0xff) {
IOSleep(10);
continue; }
else
break; }
if(res != kIOReturnSuccess || response[kAVCCommandResponse] != kAVCImplementedStatus) {
if(firstTime) {
if(res != kIOReturnSuccess)
hasFCP = false;
response[kAVCOperand1] = 0x20; response[kAVCOperand2] = 0xff;
response[kAVCOperand3] = 0xff;
response[kAVCOperand4] = 0xff;
}
else
return; }
else if(size == 5) {
size = 8;
response[kAVCOperand1] = 0x08; response[kAVCOperand2] = 0xff;
response[kAVCOperand3] = 0xff;
response[kAVCOperand4] = 0xff;
}
if(firstTime)
setProperty("supportsFCP", hasFCP);
bzero(fSubUnitCount, sizeof(fSubUnitCount));
for(int i=0; i<kAVCNumSubUnitTypes; i++) {
removeProperty(gIOFireWireAVCSubUnitCount[i]);
}
for(int i=0; i<4; i++) {
UInt8 val = response[kAVCOperand1+i];
if(val != 0xff) {
UInt8 type, num;
type = val >> 3;
num = (val & 0x7)+1;
fSubUnitCount[type] = num;
setProperty(gIOFireWireAVCSubUnitCount[type]->getCStringNoCopy(), num, 8);
IOFireWireAVCSubUnit *sub = NULL;
OSDictionary * propTable = 0;
do {
propTable = OSDictionary::withCapacity(6);
if(!propTable)
break;
prop = OSNumber::withNumber(type, 32);
propTable->setObject(gIOFireWireAVCSubUnitType, prop);
prop->release();
if(!firstTime) {
OSIterator *childIterator;
IOFireWireAVCSubUnit * found = NULL;
childIterator = getClientIterator();
if(childIterator) {
OSObject *child;
while( (child = childIterator->getNextObject())) {
found = OSDynamicCast(IOFireWireAVCSubUnit, child);
if(found && found->matchPropertyTable(propTable)) {
break;
}
else
found = NULL;
}
childIterator->release();
if(found) {
break;
}
}
}
sub = new IOFireWireAVCSubUnit;
if(!sub)
break;
if (!sub->init(propTable, this))
break;
if (!sub->attach(this))
break;
sub->setProperty("supportsFCP", hasFCP);
sub->registerService();
} while (0);
if(sub)
sub->release();
if(propTable)
propTable->release();
}
}
if(!firstTime) {
OSIterator *childIterator;
IOFireWireAVCSubUnit * sub = NULL;
childIterator = getClientIterator();
if(childIterator) {
OSObject *child;
while( (child = childIterator->getNextObject())) {
sub = OSDynamicCast(IOFireWireAVCSubUnit, child);
if(sub) {
OSNumber *type;
type = OSDynamicCast(OSNumber, sub->getProperty(gIOFireWireAVCSubUnitType));
if(type && !fSubUnitCount[type->unsigned32BitValue()])
sub->terminate();
}
}
childIterator->release();
}
}
}
bool IOFireWireAVCUnit::start(IOService *provider)
{
OSObject *prop;
UInt32 type;
fDevice = OSDynamicCast(IOFireWireNub, provider);
if(!fDevice)
return false;
if(!gIOFireWireAVCUnitType)
gIOFireWireAVCUnitType = OSSymbol::withCString("Unit_Type");
if(!gIOFireWireAVCUnitType)
return false;
if(!gIOFireWireAVCSubUnitType)
gIOFireWireAVCSubUnitType = OSSymbol::withCString("SubUnit_Type");
if(!gIOFireWireAVCSubUnitType)
return false;
for(int i=0; i<kAVCNumSubUnitTypes; i++) {
char buff[16];
if(!gIOFireWireAVCSubUnitCount[i]) {
sprintf(buff, "AVCSubUnit_%x", i);
gIOFireWireAVCSubUnitCount[i] = OSSymbol::withCString(buff);
if(!gIOFireWireAVCSubUnitCount[i])
return false;
}
}
if( !IOService::start(provider))
return (false);
fFCPResponseSpace = fDevice->getBus()->createInitialAddressSpace(kFCPResponseAddress, 512,
NULL, AVCResponse, this);
if(!fFCPResponseSpace)
return false;
fFCPResponseSpace->activate();
avcLock = IOLockAlloc();
if (avcLock == NULL) {
IOLog("IOAVCUnit::start avcLock failed\n");
return false;
}
cmdLock = IOLockAlloc();
if (cmdLock == NULL) {
IOLog("IOAVCUnit::start avcLock failed\n");
return false;
}
IOReturn res;
UInt32 size;
UInt8 cmd[8],response[8];
cmd[kAVCCommandResponse] = kAVCStatusInquiryCommand;
cmd[kAVCAddress] = kAVCUnitAddress;
cmd[kAVCOpcode] = kAVCUnitInfoOpcode;
cmd[3] = cmd[4] = cmd[5] = cmd[6] = cmd[7] = 0xff;
size = 8;
res = AVCCommand(cmd, 8, response, &size);
if(kIOReturnSuccess != res) {
IOSleep(2000); size = 8;
res = AVCCommand(cmd, 8, response, &size);
}
if(kIOReturnSuccess != res || response[kAVCCommandResponse] != kAVCImplementedStatus)
type = kAVCVideoCamera; else
type = IOAVCType(response[kAVCOperand1]);
prop = provider->getProperty(gFireWireVendor_ID);
if(prop)
setProperty(gFireWireVendor_ID, prop);
prop = provider->getProperty(gFireWire_GUID);
if(prop)
setProperty(gFireWire_GUID, prop);
prop = provider->getProperty(gFireWireProduct_Name);
if(prop)
setProperty(gFireWireProduct_Name, prop);
setProperty("Unit_Type", type, 32);
fStarted = true;
updateSubUnits(true);
registerService();
return true;
}
void IOFireWireAVCUnit::free(void)
{
if (fFCPResponseSpace) {
fFCPResponseSpace->deactivate();
fFCPResponseSpace->release();
}
if (avcLock) {
IOLockFree(avcLock);
}
IOService::free();
}
bool IOFireWireAVCUnit::matchPropertyTable(OSDictionary * table)
{
if (!IOService::matchPropertyTable(table)) return false;
bool res = compareProperty(table, gIOFireWireAVCUnitType) &&
compareProperty(table, gFireWireVendor_ID) &&
compareProperty(table, gFireWire_GUID);
if(res) {
int i;
for(i=0; i<kAVCNumSubUnitTypes; i++) {
OSNumber * value;
value = OSDynamicCast(OSNumber, table->getObject( gIOFireWireAVCSubUnitCount[i] ));
if(value) {
res = value->unsigned8BitValue() <= fSubUnitCount[i];
if(!res)
break;
}
}
}
return res;
}
IOReturn IOFireWireAVCUnit::AVCCommand(const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
{
IOReturn res;
IOFireWireAVCCommand *cmd;
if(len == 0 || len > 512) {
IOLog("Loopy AVCCmd, len %ld, respLen %ld\n", len, *size);
return kIOReturnBadArgument;
}
cmd = IOFireWireAVCCommand::withNub(fDevice, in, len, out, size);
if(!cmd)
return kIOReturnNoMemory;
IOTakeLock(avcLock);
fCommand = cmd;
res = fCommand->submit();
if(res != kIOReturnSuccess) {
}
IOTakeLock(cmdLock);
fCommand = NULL;
IOUnlock(cmdLock);
cmd->release();
IOUnlock(avcLock);
return res;
}
IOReturn IOFireWireAVCUnit::AVCCommandInGeneration(UInt32 generation, const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
{
IOReturn res;
IOFireWireAVCCommand *cmd;
if(len == 0 || len > 512) {
IOLog("Loopy AVCCmd, len %ld, respLen %ld\n", len, *size);
return kIOReturnBadArgument;
}
cmd = IOFireWireAVCCommand::withNub(fDevice, generation, in, len, out, size);
if(!cmd)
return kIOReturnNoMemory;
IOTakeLock(avcLock);
fCommand = cmd;
res = fCommand->submit();
if(res != kIOReturnSuccess) {
}
IOTakeLock(cmdLock);
fCommand = NULL;
IOUnlock(cmdLock);
cmd->release();
IOUnlock(avcLock);
return res;
}
bool IOFireWireAVCUnit::handleOpen( IOService * forClient, IOOptionBits options, void * arg )
{
bool ok = false;
if( !isOpen() )
{
ok = fDevice->open(this, options, arg);
if(ok)
ok = IOService::handleOpen(forClient, options, arg);
}
return ok;
}
void IOFireWireAVCUnit::handleClose( IOService * forClient, IOOptionBits options )
{
if( isOpen( forClient ) )
{
IOService::handleClose(forClient, options);
fDevice->close(this, options);
}
}
IOReturn IOFireWireAVCUnit::message(UInt32 type, IOService *provider, void *argument)
{
if( fStarted == true && type == kIOMessageServiceIsResumed ) {
IOCreateThread(rescanSubUnits, this);
}
messageClients(type);
return kIOReturnSuccess;
}
IOReturn IOFireWireAVCUnit::updateAVCCommandTimeout()
{
IOTakeLock(cmdLock);
if(fCommand != NULL)
fCommand->resetInterimTimeout();
IOUnlock(cmdLock);
return kIOReturnSuccess;
}
OSDefineMetaClassAndStructors(IOFireWireAVCSubUnit, IOFireWireAVCNub)
OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 0);
OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 1);
OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 2);
OSMetaClassDefineReservedUnused(IOFireWireAVCSubUnit, 3);
bool IOFireWireAVCSubUnit::init(OSDictionary *propTable, IOFireWireAVCUnit *provider)
{
OSObject *prop;
if(!IOFireWireAVCNub::init(propTable))
return false;
fAVCUnit = provider;
if(!fAVCUnit)
return false;
fDevice = fAVCUnit->getDevice();
if(!fDevice)
return false;
prop = provider->getProperty(gFireWireVendor_ID);
if(prop)
setProperty(gFireWireVendor_ID, prop);
prop = provider->getProperty(gFireWire_GUID);
if(prop)
setProperty(gFireWire_GUID, prop);
prop = provider->getProperty(gFireWireProduct_Name);
if(prop)
setProperty(gFireWireProduct_Name, prop);
prop = provider->getProperty(gIOUserClientClassKey);
if(prop)
setProperty(gIOUserClientClassKey, prop);
prop = provider->getProperty(kIOCFPlugInTypesKey);
if(prop)
setProperty(kIOCFPlugInTypesKey, prop);
return true;
}
bool IOFireWireAVCSubUnit::matchPropertyTable(OSDictionary * table)
{
if (!IOService::matchPropertyTable(table)) return false;
return compareProperty(table, gIOFireWireAVCSubUnitType) &&
compareProperty(table, gFireWireVendor_ID) &&
compareProperty(table, gFireWire_GUID);
}
IOReturn IOFireWireAVCSubUnit::AVCCommand(const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
{
return fAVCUnit->AVCCommand(in, len, out, size);
}
IOReturn IOFireWireAVCSubUnit::AVCCommandInGeneration(UInt32 generation, const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
{
return fAVCUnit->AVCCommandInGeneration(generation, in, len, out, size);
}
IOReturn IOFireWireAVCSubUnit::updateAVCCommandTimeout()
{
return fAVCUnit->updateAVCCommandTimeout();
}
bool IOFireWireAVCSubUnit::handleOpen( IOService * forClient, IOOptionBits options, void * arg )
{
bool ok = false;
if( !isOpen() )
{
ok = fAVCUnit->open(this, options, arg);
if(ok)
ok = IOService::handleOpen(forClient, options, arg);
}
return ok;
}
void IOFireWireAVCSubUnit::handleClose( IOService * forClient, IOOptionBits options )
{
if( isOpen( forClient ) )
{
IOService::handleClose(forClient, options);
fAVCUnit->close(this, options);
}
}
IOReturn IOFireWireAVCSubUnit::message(UInt32 type, IOService *provider, void *argument)
{
messageClients(type);
return kIOReturnSuccess;
}