IOFireWireAVCUnit.cpp [plain text]
#include <IOKit/avc/IOFireWireAVCUnit.h>
#include <IOKit/avc/IOFireWireAVCCommand.h>
#include <IOKit/avc/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 <IOKit/firewire/IOFireWireController.h>
#include <IOKit/avc/IOFireWirePCRSpace.h>
#if FIRELOG
#import <IOKit/firewire/FireLog.h>
#define FIRELOG_MSG(x) FireLog x
#else
#define FIRELOG_MSG(x) do {} while (0)
#endif
const OSSymbol *gIOFireWireAVCUnitType;
const OSSymbol *gIOFireWireAVCSubUnitType;
const OSSymbol *gIOFireWireAVCSubUnitCount[kAVCNumSubUnitTypes];
OSDefineMetaClassAndStructors(IOFireWireAVCAsynchronousCommand, IOCommand)
OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 0);
OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 1);
OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 2);
OSMetaClassDefineReservedUnused(IOFireWireAVCAsynchronousCommand, 3);
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);
bool IOFireWireAVCAsynchronousCommand::isPending(void)
{
bool res;
switch (cmdState)
{
case kAVCAsyncCommandStateRequestSent: case kAVCAsyncCommandStateWaitingForResponse: case kAVCAsyncCommandStateReceivedInterimResponse: res = true;
break;
case kAVCAsyncCommandStatePendingRequest: case kAVCAsyncCommandStateRequestFailed: case kAVCAsyncCommandStateReceivedFinalResponse: case kAVCAsyncCommandStateTimeOutBeforeResponse: case kAVCAsyncCommandStateBusReset: case kAVCAsyncCommandStateOutOfMemory: case kAVCAsyncCommandStateCanceled: default:
res = false;
break;
};
return res;
}
void IOFireWireAVCAsynchronousCommand::free()
{
FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::free (this=0x%08X)\n",this));
if ( isPending() )
{
cancel();
}
fWriteNodeID = kIOFWAVCAsyncCmdFreed; cmdState = kAVCAsyncCommandStateCanceled;
if ( fWriteCmd->Busy() )
{
fWriteCmd->cancel(kIOReturnOffline);
}
if ( fWriteCmd )
{
fWriteCmd->release();
fWriteCmd = NULL;
}
if( fDelayCmd->Busy() )
{
fDelayCmd->cancel(kIOReturnOffline);
}
if (fDelayCmd)
{
fDelayCmd->release();
fDelayCmd = NULL;
}
if (fMem)
{
fMem->release();
fMem = NULL;
}
if (pCommandBuf)
{
delete[] pCommandBuf;
pCommandBuf = NULL;
}
if (pInterimResponseBuf)
{
delete[] pInterimResponseBuf;
pInterimResponseBuf = NULL;
}
if (pFinalResponseBuf)
{
delete[] pFinalResponseBuf;
pFinalResponseBuf = NULL;
}
OSObject::free();
}
IOReturn IOFireWireAVCAsynchronousCommand::init(const UInt8 * command,
UInt32 len,
IOFireWireAVCAsynchronousCommandCallback completionCallback,
void *pClientRefCon)
{
FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::init (this=0x%08X, opCode=0x%02X)\n",this,command[kAVCOpcode]));
if(len == 0 || len > 512)
return kIOReturnBadArgument;
pCommandBuf = new UInt8[len];
if (!pCommandBuf)
return kIOReturnNoMemory;
bcopy(command, pCommandBuf, len);
cmdLen = len;
cmdState = kAVCAsyncCommandStatePendingRequest;
fCallback = completionCallback;
pRefCon = pClientRefCon;
pInterimResponseBuf = NULL;
interimResponseLen = 0;
pFinalResponseBuf = NULL;
finalResponseLen = 0;
fAVCUnit = NULL;
fMem = NULL;
fWriteCmd = NULL;
fDelayCmd = NULL;
fWriteNodeID = kFWBadNodeID;
fWriteGen = 0xFFFFFFFF;
return kIOReturnSuccess;
}
IOReturn IOFireWireAVCAsynchronousCommand::submit(IOFireWireAVCNub *pAVCNub)
{
IOReturn res;
FWAddress addr;
IOFireWireAVCUnit *pAVCUnit;
IOFireWireAVCSubUnit *pAVCSubunit;
FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::submit (this=0x%08X, opCode=0x%02X)\n",this,pCommandBuf[kAVCOpcode]));
retain();
if (fWriteNodeID == kIOFWAVCAsyncCmdFreed)
{
release();
return kIOReturnNotPermitted;
}
if (OSDynamicCast(IOFireWireAVCUnit, pAVCNub) != NULL)
{
pAVCUnit = (IOFireWireAVCUnit*) pAVCNub;
}
else if (OSDynamicCast(IOFireWireAVCSubUnit, pAVCNub) != NULL)
{
pAVCSubunit = (IOFireWireAVCSubUnit*) pAVCNub;
pAVCUnit = pAVCSubunit->fAVCUnit;
}
else
{
release();
return kIOReturnBadArgument;
}
addr.addressHi = kCSRRegisterSpaceBaseAddressHi;
addr.addressLo = kFCPCommandAddress;
if (cmdState != kAVCAsyncCommandStatePendingRequest)
{
release();
return kIOReturnNotPermitted;
}
fAVCUnit = pAVCUnit;
if ( not fAVCUnit->available() )
{
release();
return kIOReturnNotPermitted;
}
fMem = IOMemoryDescriptor::withAddress((void *)pCommandBuf,
cmdLen,
kIODirectionOutIn);
if(!fMem)
{
release();
return kIOReturnNoMemory;
}
IOReturn err = fMem->prepare();
if( err != kIOReturnSuccess )
{
release();
return kIOReturnNoMemory;
}
fWriteCmd = fAVCUnit->fDevice->createWriteCommand(addr,
fMem,
IOFireWireAVCUnit::AVCAsynchRequestWriteDone,
this);
if(!fWriteCmd)
{
release();
return kIOReturnNoMemory;
}
fDelayCmd = fAVCUnit->fIOFireWireAVCUnitExpansion->fControl->createDelayedCmd(250000,
IOFireWireAVCUnit::AVCAsynchDelayDone,
this);
if (!fDelayCmd)
{
release();
return kIOReturnNoMemory;
}
fAVCUnit->lockAVCAsynchronousCommandLock();
if(!fAVCUnit->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->setObject(this))
{
res = kIOReturnNoMemory;
}
else
{
res = fWriteCmd->submit();
if (cmdState == kAVCAsyncCommandStatePendingRequest)
{
if (res == kIOReturnSuccess)
cmdState = kAVCAsyncCommandStateRequestSent;
else
cmdState = kAVCAsyncCommandStateRequestFailed;
}
}
fAVCUnit->unlockAVCAsynchronousCommandLock();
release();
return res;
}
IOReturn IOFireWireAVCAsynchronousCommand::reinit(const UInt8 * command, UInt32 len)
{
FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::reinit (this=0x%08X)\n",this));
if (fWriteNodeID == kIOFWAVCAsyncCmdFreed)
return kIOReturnNotPermitted;
if (isPending())
return kIOReturnNotPermitted;
if(len == 0 || len > 512)
return kIOReturnBadArgument;
if (fWriteCmd)
fWriteCmd->release();
if (fDelayCmd)
fDelayCmd->release();
if (fMem)
fMem->release();
if (pCommandBuf)
delete pCommandBuf;
if (pInterimResponseBuf)
delete pInterimResponseBuf;
if (pFinalResponseBuf)
delete pFinalResponseBuf;
pCommandBuf = new UInt8[len];
if (!pCommandBuf)
return kIOReturnNoMemory;
bcopy(command, pCommandBuf, len);
cmdLen = len;
cmdState = kAVCAsyncCommandStatePendingRequest;
pInterimResponseBuf = NULL;
interimResponseLen = 0;
pFinalResponseBuf = NULL;
finalResponseLen = 0;
fAVCUnit = NULL;
fMem = NULL;
fWriteCmd = NULL;
fDelayCmd = NULL;
fWriteNodeID = kFWBadNodeID;
fWriteGen = 0xFFFFFFFF;
return kIOReturnSuccess;
}
IOReturn IOFireWireAVCAsynchronousCommand::cancel(void)
{
UInt32 cmdIndex;
FIRELOG_MSG(("IOFireWireAVCAsynchronousCommand::cancel (this=0x%08X)\n",this));
fAVCUnit->lockAVCAsynchronousCommandLock();
if ((cmdState == kAVCAsyncCommandStateRequestSent) && (fWriteCmd))
fWriteCmd->cancel(kIOReturnAborted);
else if ((cmdState == kAVCAsyncCommandStateWaitingForResponse) && (fDelayCmd))
fDelayCmd->cancel(kIOReturnAborted);
cmdState = kAVCAsyncCommandStateCanceled;
cmdIndex = fAVCUnit->indexOfAVCAsynchronousCommandObject(this);
if (cmdIndex != 0xFFFFFFFF)
{
fAVCUnit->removeAVCAsynchronousCommandObjectAtIndex(cmdIndex);
}
fAVCUnit->unlockAVCAsynchronousCommandLock();
if (fCallback != NULL)
fCallback(pRefCon,this);
return kIOReturnSuccess;
}
IOReturn IOFireWireAVCUnit::setProperties (OSObject * properties )
{
IOReturn result = kIOReturnSuccess ;
OSDictionary* dict = OSDynamicCast( OSDictionary, properties ) ;
if ( dict )
{
OSObject* value = dict->getObject( "RobustAVCResponseMatching" ) ;
if ( value )
{
fIOFireWireAVCUnitExpansion->enableRobustAVCCommandResponseMatching = false;
}
else
{
result = IOFireWireAVCNub::setProperties ( properties ) ;
}
}
else
result = IOFireWireAVCNub::setProperties ( properties ) ;
return result ;
}
UInt32 IOFireWireAVCUnit::AVCResponse(void *refcon, UInt16 nodeID, IOFWSpeed &speed,
FWAddress addr, UInt32 len, const void *buf, IOFWRequestRefCon requestRefcon)
{
IOFireWireAVCUnit *me = (IOFireWireAVCUnit *)refcon;
UInt8 *pResponseBytes = (UInt8*) buf;
UInt32 res = kFWResponseAddressError;
UInt32 i;
IOFireWireAVCAsynchronousCommand *pCmd;
bool foundOutstandingAVCAsynchCommandForNode = false;
bool matchFound = false;
UInt32 matchedCommandIndex;
bool doCallback = false;
FIRELOG_MSG(("IOFireWireAVCUnit::AVCResponse (this=0x%08X)\n",me));
FIRELOG_MSG(("AVCResponse Info: nodeID=0x%04X, opCode=0x%02X, avcAddress=0x%02X respLen=0x%08X\n",nodeID, pResponseBytes[kAVCOpcode], pResponseBytes[kAVCAddress],len));
if ((addr.addressLo != kFCPResponseAddress) || (len < 3))
return res;
me->lockAVCAsynchronousCommandLock();
for (i = 0; i < me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(); i++)
{
pCmd = (IOFireWireAVCAsynchronousCommand*) me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
FIRELOG_MSG(("Evaluating outstanding AVC async cmd %d (%d total): cmd = 0x%08X, nodeID = 0x%04X, opCode=0x%02X, avcAddress=0x%02X pending=%s\n",
i,
me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(),
pCmd,
pCmd->fWriteNodeID,
pCmd->pCommandBuf[kAVCOpcode] ,
pCmd->pCommandBuf[kAVCAddress],
(pCmd->isPending() ? "YES" : "NO")
));
if (pCmd->fWriteNodeID == nodeID)
{
foundOutstandingAVCAsynchCommandForNode = true;
if ((pCmd->pCommandBuf[kAVCAddress] == pResponseBytes[kAVCAddress]) && (pCmd->pCommandBuf[kAVCOpcode] == pResponseBytes[kAVCOpcode]))
{
if ((pCmd->pCommandBuf[kAVCCommandResponse] == kAVCNotifyCommand) &&
((pResponseBytes[kAVCCommandResponse] == kAVCAcceptedStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCInTransitionStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCImplementedStatus)))
{
}
else
{
matchFound = true;
matchedCommandIndex = i;
break;
}
}
}
}
if ((!matchFound) && (foundOutstandingAVCAsynchCommandForNode) && ((pResponseBytes[kAVCAddress] & 0xF8) == 0x20))
{
for (i = 0; i < me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(); i++)
{
pCmd = (IOFireWireAVCAsynchronousCommand*) me->fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
if (pCmd->fWriteNodeID == nodeID)
{
if ((pCmd->pCommandBuf[kAVCAddress] == pResponseBytes[kAVCAddress]) && (pCmd->pCommandBuf[kAVCOpcode] == 0xD0))
{
if (((pResponseBytes[kAVCOpcode] == 0xC1) ||
(pResponseBytes[kAVCOpcode] == 0xC2) ||
(pResponseBytes[kAVCOpcode] == 0xC3) ||
(pResponseBytes[kAVCOpcode] == 0xC4)) && (len == 4))
{
if ((pCmd->pCommandBuf[kAVCCommandResponse] == kAVCNotifyCommand) &&
((pResponseBytes[kAVCCommandResponse] == kAVCAcceptedStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCInTransitionStatus) || (pResponseBytes[kAVCCommandResponse] == kAVCImplementedStatus)))
{
}
else
{
matchFound = true;
matchedCommandIndex = i;
break;
}
}
}
}
}
}
if (matchFound)
{
FIRELOG_MSG(("AVC Async Request/Response Match Found: %d\n",matchedCommandIndex));
if (pCmd->pInterimResponseBuf == NULL)
{
if (pCmd->fDelayCmd)
pCmd->fDelayCmd->cancel(kIOReturnAborted);
}
if (pResponseBytes[kAVCCommandResponse] == 0x0F)
{
pCmd->pInterimResponseBuf = new UInt8[len];
if (pCmd->pInterimResponseBuf)
{
pCmd->interimResponseLen = len;
bcopy(pResponseBytes, pCmd->pInterimResponseBuf, len);
pCmd->cmdState = kAVCAsyncCommandStateReceivedInterimResponse;
}
else
{
pCmd->cmdState = kAVCAsyncCommandStateOutOfMemory;
me->removeAVCAsynchronousCommandObjectAtIndex(matchedCommandIndex);
}
}
else
{
pCmd->pFinalResponseBuf = new UInt8[len];
if (pCmd->pFinalResponseBuf)
{
pCmd->finalResponseLen = len;
bcopy(pResponseBytes, pCmd->pFinalResponseBuf, len);
pCmd->cmdState = kAVCAsyncCommandStateReceivedFinalResponse;
}
else
pCmd->cmdState = kAVCAsyncCommandStateOutOfMemory;
me->removeAVCAsynchronousCommandObjectAtIndex(matchedCommandIndex);
}
doCallback = true;
res = kFWResponseComplete;
}
me->unlockAVCAsynchronousCommandLock();
if (doCallback == true)
{
if (pCmd->fCallback != NULL)
pCmd->fCallback(pCmd->pRefCon,pCmd);
}
if (!matchFound)
{
if(me->fCommand)
{
if (me->fIOFireWireAVCUnitExpansion->enableRobustAVCCommandResponseMatching)
res = me->fCommand->handleResponse(nodeID, len, buf);
else
res = me->fCommand->handleResponseWithSimpleMatching(nodeID, len, buf);
}
}
return res;
}
void IOFireWireAVCUnit::rescanSubUnits(void *arg)
{
IOFireWireAVCUnit *me = (IOFireWireAVCUnit *)arg;
FIRELOG_MSG(("IOFireWireAVCUnit::rescanSubUnits (this=0x%08X)\n",me));
me->updateSubUnits(false);
}
void IOFireWireAVCUnit::updateSubUnits(bool firstTime)
{
FIRELOG_MSG(("IOFireWireAVCUnit::updateSubUnits (this=0x%08X)\n",this));
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[kAVCOperand1] == 0xff) {
IOSleep(10);
continue; }
else if(res == kIOReturnOffline){
FIRELOG_MSG(("IOFireWireAVCUnit %p, bus-reset during subunit scan! firstTime=%s\n",this,firstTime == true ? "true" : "false"));
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
{
this->release(); 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();
if (type == 0)
{
OSObject *prop;
OSNumber *deviceGUID;
unsigned long long guidVal;
prop = getProperty(gFireWire_GUID);
deviceGUID = OSDynamicCast( OSNumber, prop );
guidVal = deviceGUID->unsigned64BitValue();
if ((guidVal & 0xFFFFFF0000000000LL) == 0x0800460000000000LL) {
fDevice->setNodeFlags(kIOFWMustBeRoot);
}
}
} 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();
}
}
if (!firstTime)
this->release(); }
bool IOFireWireAVCUnit::start(IOService *provider)
{
FIRELOG_MSG(("IOFireWireAVCUnit::start (this=0x%08X)\n",this));
OSObject *prop;
UInt32 type;
OSNumber *deviceGUID;
unsigned long long guidVal;
UInt8 series;
fDevice = OSDynamicCast(IOFireWireNub, provider);
if(!fDevice)
return false;
fDevice->retain();
fIOFireWireAVCUnitExpansion = (ExpansionData*) IOMalloc( sizeof(ExpansionData) );
if( fIOFireWireAVCUnitExpansion == NULL )
return false;
else
bzero( fIOFireWireAVCUnitExpansion, sizeof(ExpansionData) );
fIOFireWireAVCUnitExpansion->fControl = fDevice->getController();
if(!fIOFireWireAVCUnitExpansion->fControl)
return false;
fIOFireWireAVCUnitExpansion->enableRobustAVCCommandResponseMatching = true;
fIOFireWireAVCUnitExpansion->fAVCAsyncCommands = OSArray::withCapacity(1);
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]) {
snprintf(buff, sizeof(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 cmdLock failed\n");
return false;
}
IOReturn res;
UInt32 size;
UInt8 cmd[8],response[8];
UInt32 unitInfoRetryCount = 0;
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)
{
do
{
unitInfoRetryCount++;
IOSleep(2000); size = 8;
res = AVCCommand(cmd, 8, response, &size);
}while((kIOReturnSuccess != res) && (unitInfoRetryCount <= 4));
}
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);
deviceGUID = OSDynamicCast( OSNumber, prop );
guidVal = deviceGUID->unsigned64BitValue();
if ((guidVal & 0xFFFFFFFFFF000000LL) == 0x0000850000000000LL)
{
series = (UInt8) ((guidVal & 0x0000000000FF0000LL) >> 16);
if ((series <= 0x13) || ((series >= 0x18) && (series <= 0x23)))
fDevice->setNodeFlags( kIOFWLimitAsyncPacketSize );
series = (UInt8) (((guidVal & 0x00000000FFFFFFFFLL) >> 18) & 0x3f); if(series == 0x19) fDevice->setNodeFlags(kIOFWMustNotBeRoot);
}
if ((guidVal & 0xFFFFFF0000000000LL) == 0x0080450000000000LL) {
series = (UInt8) ((guidVal & 0x0000000000FF0000LL) >> 16);
prop = provider->getProperty(gFireWireProduct_Name);
if(prop)
{
OSString * string = OSDynamicCast ( OSString, prop ) ;
if (string->isEqualTo("PV-GS15"))
{
fDevice->setNodeFlags(kIOFWMustNotBeRoot);
fDevice->setNodeFlags(kIOFWMustHaveGap63);
IOLog("Panasonic guid=%lld series=%x model=%s\n", guidVal, series, string->getCStringNoCopy()); }
else if (string->isEqualTo("PV-GS120 "))
{
fDevice->setNodeFlags(kIOFWMustBeRoot);
IOLog("Panasonic guid=%lld series=%x model=%s\n", guidVal, series, string->getCStringNoCopy()); }
else
{
FIRELOG_MSG(( "Unknown Panasonic series\n" ));
}
}
}
}
prop = provider->getProperty(gFireWireProduct_Name);
if(prop)
setProperty(gFireWireProduct_Name, prop);
setProperty("Unit_Type", type, 32);
fStarted = true;
updateSubUnits(true);
registerService();
return true;
}
bool IOFireWireAVCUnit::available()
{
return fStarted;
}
void IOFireWireAVCUnit::free(void)
{
IOFireWireAVCAsynchronousCommand *pCmd;
FIRELOG_MSG(("IOFireWireAVCUnit::free (this=0x%08X)\n",this));
if ((fIOFireWireAVCUnitExpansion) && (fIOFireWireAVCUnitExpansion->fControl))
{
lockAVCAsynchronousCommandLock();
fStarted = false;
unlockAVCAsynchronousCommandLock();
}
if (fFCPResponseSpace) {
fFCPResponseSpace->deactivate();
fFCPResponseSpace->release();
fFCPResponseSpace = NULL;
}
if (avcLock) {
IOLockFree(avcLock);
avcLock = NULL;
}
if ((fIOFireWireAVCUnitExpansion) && (fIOFireWireAVCUnitExpansion->fControl) && (fIOFireWireAVCUnitExpansion->fAVCAsyncCommands))
{
lockAVCAsynchronousCommandLock();
while (fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount())
{
pCmd = (IOFireWireAVCAsynchronousCommand*) fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(0);
pCmd->fWriteNodeID = kIOFWAVCAsyncCmdFreed; pCmd->cancel();
}
unlockAVCAsynchronousCommandLock();
fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->release();
}
if (cmdLock)
{
IOLockFree(cmdLock);
cmdLock = NULL;
}
if( fDevice )
{
fDevice->release();
fDevice = NULL;
}
if (fIOFireWireAVCUnitExpansion)
{
IOFree ( fIOFireWireAVCUnitExpansion, sizeof(ExpansionData) );
fIOFireWireAVCUnitExpansion = NULL;
}
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)
{
FIRELOG_MSG(("IOFireWireAVCUnit::AVCCommand (this=0x%08X, opCode=0x%02X)\n",this,in[2]));
IOReturn res;
IOFireWireAVCCommand *cmd;
if(len == 0 || len > 512) {
IOLog("Loopy AVCCmd, len %d, respLen %d\n", (uint32_t)len, (uint32_t)*size);
return kIOReturnBadArgument;
}
this->retain();
cmd = IOFireWireAVCCommand::withNub(fDevice, in, len, out, size);
if(!cmd)
{
this->release();
return kIOReturnNoMemory;
}
IOTakeLock(avcLock);
fCommand = cmd;
res = fCommand->submit();
if(res != kIOReturnSuccess) {
}
IOTakeLock(cmdLock);
fCommand = NULL;
IOUnlock(cmdLock);
cmd->release();
IOUnlock(avcLock);
this->release();
return res;
}
IOReturn IOFireWireAVCUnit::AVCCommandInGeneration(UInt32 generation, const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
{
FIRELOG_MSG(("IOFireWireAVCUnit::AVCCommandInGeneration (this=0x%08X)\n",this));
IOReturn res;
IOFireWireAVCCommand *cmd;
if(len == 0 || len > 512) {
IOLog("Loopy AVCCmd, len %d, respLen %d\n", (uint32_t)len, (uint32_t)*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;
}
void IOFireWireAVCUnit::AVCAsynchRequestWriteDone(void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd)
{
IOFireWireAVCAsynchronousCommand *pCmdObject = OSDynamicCast(IOFireWireAVCAsynchronousCommand, (IOFireWireAVCAsynchronousCommand*)refcon);
if(!pCmdObject)
return;
IOFireWireAVCUnit *pAVCUnit = OSDynamicCast(IOFireWireAVCUnit, pCmdObject->fAVCUnit);
if(!pAVCUnit)
return;
UInt32 cmdIndex;
bool doCallback = false;
FIRELOG_MSG(("IOFireWireAVCUnit::AVCAsynchRequestWriteDone (cmd=0x%08X, status=0x%08X)\n",pCmdObject,status));
pAVCUnit->lockAVCAsynchronousCommandLock();
if(status == kIOReturnAborted)
{
pAVCUnit->unlockAVCAsynchronousCommandLock();
return;
}
cmdIndex = pAVCUnit->indexOfAVCAsynchronousCommandObject(pCmdObject);
if (cmdIndex == 0xFFFFFFFF)
{
pAVCUnit->unlockAVCAsynchronousCommandLock();
return;
}
if(status == kIOReturnSuccess)
{
if(device)
device->getNodeIDGeneration(pCmdObject->fWriteGen, pCmdObject->fWriteNodeID);
pCmdObject->fDelayCmd->submit();
pCmdObject->cmdState = kAVCAsyncCommandStateWaitingForResponse;
}
else
{
pCmdObject->cmdState = kAVCAsyncCommandStateRequestFailed;
doCallback = true;
pAVCUnit->removeAVCAsynchronousCommandObjectAtIndex(cmdIndex);
}
pAVCUnit->unlockAVCAsynchronousCommandLock();
if (doCallback == true)
{
if (pCmdObject->fCallback != NULL)
pCmdObject->fCallback(pCmdObject->pRefCon,pCmdObject);
}
}
void IOFireWireAVCUnit::AVCAsynchDelayDone(void *refcon, IOReturn status, IOFireWireBus *bus, IOFWBusCommand *fwCmd)
{
IOFireWireAVCAsynchronousCommand *pCmdObject = OSDynamicCast( IOFireWireAVCAsynchronousCommand, (IOFireWireAVCAsynchronousCommand*)refcon );
if(!pCmdObject)
return;
IOFireWireAVCUnit *pAVCUnit = OSDynamicCast(IOFireWireAVCUnit, (IOFireWireAVCUnit*)pCmdObject->fAVCUnit);
if(!pAVCUnit)
return;
UInt32 cmdIndex;
FIRELOG_MSG(("IOFireWireAVCUnit::AVCAsynchDelayDone, cmd=0x%08X, status = 0x%08X\n",pCmdObject,status));
if (status != kIOReturnTimeout)
return;
pAVCUnit->lockAVCAsynchronousCommandLock();
cmdIndex = pAVCUnit->indexOfAVCAsynchronousCommandObject(pCmdObject);
if (cmdIndex == 0xFFFFFFFF)
{
pAVCUnit->unlockAVCAsynchronousCommandLock();
return;
}
pCmdObject->cmdState = kAVCAsyncCommandStateTimeOutBeforeResponse;
pAVCUnit->removeAVCAsynchronousCommandObjectAtIndex(cmdIndex);
pAVCUnit->unlockAVCAsynchronousCommandLock();
if (pCmdObject->fCallback != NULL)
pCmdObject->fCallback(pCmdObject->pRefCon,pCmdObject);
}
UInt32 IOFireWireAVCUnit::indexOfAVCAsynchronousCommandObject(IOFireWireAVCAsynchronousCommand *pCommandObject)
{
UInt32 res = 0xFFFFFFFF;
int i;
for (i=(fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount()-1);i>=0;i--)
{
IOFireWireAVCAsynchronousCommand *pCmd;
pCmd = (IOFireWireAVCAsynchronousCommand*) fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
if(pCommandObject == pCmd)
{
res = i;
break;
}
}
return res;
}
void IOFireWireAVCUnit::removeAVCAsynchronousCommandObjectAtIndex(UInt32 index)
{
fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->removeObject(index);
}
void IOFireWireAVCUnit::lockAVCAsynchronousCommandLock()
{
FIRELOG_MSG(("IOFireWireAVCUnit::lockAVCAsynchronousCommandLock (this=0x%08X)\n",this));
fIOFireWireAVCUnitExpansion->fControl->closeGate();
}
void IOFireWireAVCUnit::unlockAVCAsynchronousCommandLock()
{
FIRELOG_MSG(("IOFireWireAVCUnit::unlockAVCAsynchronousCommandLock (this=0x%08X)\n",this));
fIOFireWireAVCUnitExpansion->fControl->openGate();
}
bool IOFireWireAVCUnit::handleOpen( IOService * forClient, IOOptionBits options, void * arg )
{
FIRELOG_MSG(("IOFireWireAVCUnit::handleOpen (this=0x%08X)\n",this));
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 )
{
FIRELOG_MSG(("IOFireWireAVCUnit::handleClose (this=0x%08X)\n",this));
if( isOpen( forClient ) )
{
IOService::handleClose(forClient, options);
fDevice->close(this, options);
}
}
IOReturn IOFireWireAVCUnit::message(UInt32 type, IOService *provider, void *argument)
{
UInt32 i;
IOFireWireAVCAsynchronousCommand *pCmd;
OSArray *pTerminatedCommandsArray = NULL;
FIRELOG_MSG(("IOFireWireAVCUnit::message (type = 0x%08X, this=0x%08X)\n",type,this));
if( fStarted == true &&
type == kIOMessageServiceIsSuspended &&
(fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount() > 0))
{
lockAVCAsynchronousCommandLock();
for (i = 0; i < fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getCount(); i++)
{
pCmd = (IOFireWireAVCAsynchronousCommand*) fIOFireWireAVCUnitExpansion->fAVCAsyncCommands->getObject(i);
if (pCmd->cmdState == kAVCAsyncCommandStateRequestSent)
pCmd->fWriteCmd->cancel(kIOReturnAborted);
if (pCmd->cmdState == kAVCAsyncCommandStateWaitingForResponse)
pCmd->fDelayCmd->cancel(kIOReturnAborted);
FIRELOG_MSG(("IOFireWireAVCUnit::message setting pending async AVC command (0x%08X) to bus-reset state\n",pCmd));
pCmd->cmdState = kAVCAsyncCommandStateBusReset;
removeAVCAsynchronousCommandObjectAtIndex(i);
if (pTerminatedCommandsArray == NULL)
pTerminatedCommandsArray = OSArray::withCapacity(1);
if (pTerminatedCommandsArray != NULL)
pTerminatedCommandsArray->setObject(pCmd);
}
unlockAVCAsynchronousCommandLock();
if (pTerminatedCommandsArray != NULL)
{
for (i = 0; i < pTerminatedCommandsArray->getCount(); i++)
{
pCmd = (IOFireWireAVCAsynchronousCommand*) pTerminatedCommandsArray->getObject(i);
if (pCmd->fCallback != NULL)
pCmd->fCallback(pCmd->pRefCon,pCmd);
}
pTerminatedCommandsArray->release();
}
}
if( fStarted == true && type == kIOMessageServiceIsResumed )
{
this->retain(); thread_t thread;
if( kernel_thread_start((thread_continue_t)rescanSubUnits, this, &thread ) == KERN_SUCCESS )
{
thread_deallocate(thread);
}
}
messageClients(type);
return kIOReturnSuccess;
}
IOReturn IOFireWireAVCUnit::updateAVCCommandTimeout()
{
FIRELOG_MSG(("IOFireWireAVCUnit::updateAVCCommandTimeout (this=0x%08X)\n",this));
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)
{
FIRELOG_MSG(("IOFireWireAVCSubUnit::init (this=0x%08X)\n",this));
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)
{
FIRELOG_MSG(("IOFireWireAVCSubUnit::AVCCommand (this=0x%08X)\n",this));
return fAVCUnit->AVCCommand(in, len, out, size);
}
IOReturn IOFireWireAVCSubUnit::AVCCommandInGeneration(UInt32 generation, const UInt8 * in, UInt32 len, UInt8 * out, UInt32 *size)
{
FIRELOG_MSG(("IOFireWireAVCSubUnit::AVCCommandInGeneration (this=0x%08X)\n",this));
return fAVCUnit->AVCCommandInGeneration(generation, in, len, out, size);
}
IOReturn IOFireWireAVCSubUnit::updateAVCCommandTimeout()
{
FIRELOG_MSG(("IOFireWireAVCSubUnit::updateAVCCommandTimeout (this=0x%08X)\n",this));
return fAVCUnit->updateAVCCommandTimeout();
}
bool IOFireWireAVCSubUnit::handleOpen( IOService * forClient, IOOptionBits options, void * arg )
{
FIRELOG_MSG(("IOFireWireAVCSubUnit::handleOpen (this=0x%08X)\n",this));
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 )
{
FIRELOG_MSG(("IOFireWireAVCSubUnit::handleClose (this=0x%08X)\n",this));
if( isOpen( forClient ) )
{
IOService::handleClose(forClient, options);
fAVCUnit->close(this, options);
}
}
IOReturn IOFireWireAVCSubUnit::message(UInt32 type, IOService *provider, void *argument)
{
messageClients(type);
return kIOReturnSuccess;
}