Example #1
0
IOReturn CLASS::UIMCreateInterruptTransfer(IOUSBCommand* command)
{
    IOUSBCompletion comp;
    IOMemoryDescriptor* md;
    USBDeviceAddress addr;
    IODMACommand* dmac;
    uint32_t speed, tag;

    if (!command)
        return kIOReturnBadArgument;
    addr = command->GetAddress();
    if (addr == _hub3Address || addr == _hub2Address) {
        comp = command->GetUSLCompletion();
        dmac = command->GetDMACommand();
        if (dmac && dmac->getMemoryDescriptor())
            dmac->clearMemoryDescriptor();
        if (command->GetEndpoint() == 1U) {
            md = command->GetBuffer();
            if (!md)
                return kIOReturnInternalError;
            speed = (addr == _hub3Address ? kUSBDeviceSpeedSuper : kUSBDeviceSpeedHigh);
            tag = ((static_cast<uint32_t>(addr) << kUSBAddress_Shift) & kUSBAddress_Mask);
            tag |= ((speed << kUSBSpeed_Shift) & kUSBSpeed_Mask);
            md->setTag(tag);
            return RootHubQueueInterruptRead(md, static_cast<uint32_t>(command->GetReqCount()), comp);
        } else {
            Complete(comp, kIOUSBEndpointNotFound, static_cast<uint32_t>(command->GetReqCount()));
            return kIOUSBEndpointNotFound;
        }
    }
    return CreateTransfer(command, 0U);
}
AppleEHCIitdMemoryBlock*
AppleEHCIitdMemoryBlock::NewMemoryBlock(void)
{
    AppleEHCIitdMemoryBlock 	*me = new AppleEHCIitdMemoryBlock;
    IOByteCount					len;
	IODMACommand				*dmaCommand = NULL;
	UInt64						offset = 0;
	IODMACommand::Segment32		segments;
	UInt32						numSegments = 1;
	IOReturn					status = kIOReturnSuccess;
    
    if (me)
	{
		// Use IODMACommand to get the physical address
		dmaCommand = IODMACommand::withSpecification(kIODMACommandOutputHost32, 32, PAGE_SIZE, (IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly));
		if (!dmaCommand)
		{
			USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not create IODMACommand");
			return NULL;
		}
		USBLog(6, "AppleEHCIitdMemoryBlock::NewMemoryBlock - got IODMACommand %p", dmaCommand);
		
		// allocate one page on a page boundary below the 4GB line
		me->_buffer = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, kIOMemoryUnshared | kIODirectionInOut, kEHCIPageSize, kEHCIStructureAllocationPhysicalMask);
		
		// allocate exactly one physical page
		if (me->_buffer) 
		{
			status = me->_buffer->prepare();
			if (status)
			{
				USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not prepare buffer");
				me->_buffer->release();
				me->_buffer = NULL;
				me->release();
				dmaCommand->release();
				return NULL;
			}
			me->_sharedLogical = (EHCIIsochTransferDescriptorSharedPtr)me->_buffer->getBytesNoCopy();
			bzero(me->_sharedLogical, kEHCIPageSize);
			status = dmaCommand->setMemoryDescriptor(me->_buffer);
			if (status)
			{
				USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not set memory descriptor");
				me->_buffer->complete();
				me->_buffer->release();
				me->_buffer = NULL;
				me->release();
				dmaCommand->release();
				return NULL;
			}
			status = dmaCommand->gen32IOVMSegments(&offset, &segments, &numSegments);
			dmaCommand->clearMemoryDescriptor();
			dmaCommand->release();
			if (status || (numSegments != 1) || (segments.fLength != kEHCIPageSize))
			{
				USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not get physical segment");
				me->_buffer->complete();
				me->_buffer->release();
				me->_buffer = NULL;
				me->release();
				return NULL;
			}
			me->_sharedPhysical = segments.fIOVMAddr;
		}
		else
		{
			USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock, could not allocate buffer!");
			me->release();
			me = NULL;
		}
	}
	else
	{
		USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock, constructor failed!");
    }
    return me;
}
Example #3
0
IOReturn
IOUSBCommandPool::gatedReturnCommand(IOCommand * command)
{
	IOUSBCommand		*usbCommand		= OSDynamicCast(IOUSBCommand, command);					// only one of these should be non-null
	IOUSBIsocCommand	*isocCommand	= OSDynamicCast(IOUSBIsocCommand, command);

	USBLog(7,"IOUSBCommandPool[%p]::gatedReturnCommand %p", this, command);
	if (!command)
	{
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
		panic("IOUSBCommandPool::gatedReturnCommand( NULL )");
#endif
		return kIOReturnBadArgument;
	}
	
	if (command->fCommandChain.next &&
	    (&command->fCommandChain != command->fCommandChain.next || 
		 &command->fCommandChain != command->fCommandChain.prev))
	{
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
		kprintf("WARNING: gatedReturnCommand(%p) already on queue [next=%p prev=%p]\n", command, command->fCommandChain.next, command->fCommandChain.prev);
		panic("IOUSBCommandPool::gatedReturnCommand already on queue");
#endif
		char*		bt[8];
		
		OSBacktrace((void**)bt, 8);
		
		USBError(1,"IOUSBCommandPool::gatedReturnCommand  command already in queue, not putting it back into the queue, bt: [%p][%p][%p][%p][%p][%p][%p][%p]", bt[0], bt[1], bt[2], bt[3], bt[4], bt[5], bt[6], bt[7]);
		return kIOReturnBadArgument;
	}
	
	if (usbCommand)
	{
		IODMACommand *dmaCommand = usbCommand->GetDMACommand();
		if (dmaCommand)
		{
			if (dmaCommand->getMemoryDescriptor())
			{
				USBError(1, "IOUSBCommandPool::gatedReturnCommand - command (%p) still has dmaCommand(%p) with an active memory descriptor(%p)", usbCommand, dmaCommand, dmaCommand->getMemoryDescriptor());
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
				panic("IOUSBCommandPool::gatedReturnCommand -dmaCommand still has active IOMD");
#endif
			}
		}
		else
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - missing dmaCommand in IOUSBCommand");
		}
		
		// Test to poison the IOUSBCommand when returning it
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
		{
		char*					bt[kUSBCommandScratchBuffers];
	
		OSBacktrace((void**)bt, kUSBCommandScratchBuffers);
		for ( int i=0; i < kUSBCommandScratchBuffers; i++)
			usbCommand->SetBT(i, bt[i]);
		}
#endif
		// Clean up the command before returning it
		IOUSBCompletion			nullCompletion;
		nullCompletion.target = (void *) POISONVALUE;
		nullCompletion.action = (IOUSBCompletionAction) NULL;
		nullCompletion.parameter = (void *) POISONVALUE;
				
		usbCommand->SetSelector(INVALID_SELECTOR);
		usbCommand->SetRequest((IOUSBDeviceRequestPtr) POISONVALUE);
		usbCommand->SetAddress(0xFF);
		usbCommand->SetEndpoint(0xFF);
		usbCommand->SetDirection(0xFF);
		usbCommand->SetType(0xFF);
		usbCommand->SetBufferRounding(false);
		usbCommand->SetBuffer((IOMemoryDescriptor *) POISONVALUE);
		usbCommand->SetUSLCompletion(nullCompletion);
		usbCommand->SetClientCompletion(nullCompletion);
		usbCommand->SetDataRemaining(POISONVALUE);
		usbCommand->SetStage(0xFF);
		usbCommand->SetStatus(POISONVALUE);
		usbCommand->SetOrigBuffer((IOMemoryDescriptor *) POISONVALUE);
		usbCommand->SetDisjointCompletion(nullCompletion);
		usbCommand->SetDblBufLength(POISONVALUE);
		usbCommand->SetNoDataTimeout(POISONVALUE);
		usbCommand->SetCompletionTimeout(POISONVALUE);
		usbCommand->SetReqCount(POISONVALUE);
		usbCommand->SetMultiTransferTransaction(true);
		usbCommand->SetFinalTransferInTransaction(true);
		usbCommand->SetUseTimeStamp(true);
		usbCommand->SetIsSyncTransfer(FALSE);
		for ( int i=0; i < kUSBCommandScratchBuffers; i++)
			usbCommand->SetUIMScratch(i, POISONVALUE);
		usbCommand->SetStreamID(POISONVALUE);
		
		if ( usbCommand->GetBufferUSBCommand() != NULL )
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - GetBufferUSBCommand() is not NULL");
		}
		if ( usbCommand->GetRequestMemoryDescriptor() != NULL )
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - GetRequestMemoryDescriptor() is not NULL");
		}
		if ( usbCommand->GetBufferMemoryDescriptor() != NULL )
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - GetBufferMemoryDescriptor() is not NULL");
		}
		
		// Do not see these to anything but NULL as a lot of the code depends on checking for NULLness
		usbCommand->SetBufferUSBCommand(NULL);
		usbCommand->SetRequestMemoryDescriptor(NULL);
		usbCommand->SetBufferMemoryDescriptor(NULL);
	}
	
	if (isocCommand)
	{
		IODMACommand *dmaCommand = isocCommand->GetDMACommand();
		if (dmaCommand)
		{
			if (dmaCommand->getMemoryDescriptor())
			{
				USBError(1, "IOUSBCommandPool::gatedReturnCommand - isocCommand (%p) still has dmaCommand(%p) with an active memory descriptor(%p)", isocCommand, dmaCommand, dmaCommand->getMemoryDescriptor());
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
				panic("IOUSBCommandPool::gatedReturnCommand - dmaCommand still has active IOMD (isoc)");
#endif
			}
		}
		else
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - missing dmaCommand in IOUSBIsocCommand");
		}
	}
	return IOCommandPool::gatedReturnCommand(command);
}
Example #4
0
IOReturn AppleSamplePCI::generateDMAAddresses( IOMemoryDescriptor * memDesc )
{
    // Get the physical segment list. These could be used to generate a scatter gather
    // list for hardware.

    // This is the old getPhysicalSegment() loop calling IOMemoryDescriptor,
    // it will fail (panic) on new machines with memory above the 4Gb line

    IODMACommand *	cmd;
    IOReturn            err = kIOReturnSuccess;
    IOByteCount		offset = 0;
    IOPhysicalAddress	physicalAddr;
    IOPhysicalLength	segmentLength;
    UInt32              index = 0;

    while( (physicalAddr = memDesc->getPhysicalSegment( offset, &segmentLength ))) {
	IOLog("Physical segment(%ld) %08lx:%08lx\n", index, physicalAddr, segmentLength);
	offset += segmentLength;
	index++;
    }

    // 64 bit physical address generation using IODMACommand
    do
    {
	cmd = IODMACommand::withSpecification(
	    // outSegFunc - Host endian since we read the address data with the cpu
	    // and 64 bit wide quantities
	    kIODMACommandOutputHost64, 
	    // numAddressBits
	    64, 
	    // maxSegmentSize - zero for unrestricted physically contiguous chunks
	    0,
	    // mappingOptions - kMapped for DMA addresses
	    IODMACommand::kMapped,
	    // maxTransferSize - no restriction
	    0,
	    // alignment - no restriction
	    1 );
	if (!cmd)
	{
	    IOLog("IODMACommand::withSpecification failed\n");
	    break;
	}

	// point at the memory descriptor and use the auto prepare option
	// to prepare the entire range
	err = cmd->setMemoryDescriptor(memDesc);
	if (kIOReturnSuccess != err)
	{
	    IOLog("setMemoryDescriptor failed (0x%x)\n", err);
	    break;
	}

	UInt64 offset = 0;
	while ((kIOReturnSuccess == err) && (offset < memDesc->getLength()))
	{
	    // use the 64 bit variant to match outSegFunc
	    IODMACommand::Segment64 segments[1];
	    UInt32 numSeg = 1;

	    // use the 64 bit variant to match outSegFunc
	    err = cmd->gen64IOVMSegments(&offset, &segments[0], &numSeg);
	    IOLog("gen64IOVMSegments(%x) addr 0x%qx, len 0x%qx, nsegs %ld\n",
		    err, segments[0].fIOVMAddr, segments[0].fLength, numSeg);
	}

	// if we had a DMA controller, kick off the DMA here

	// when the DMA has completed,
	
	// clear the memory descriptor and use the auto complete option
	// to complete the transaction
	err = cmd->clearMemoryDescriptor();
	if (kIOReturnSuccess != err)
	{
	    IOLog("clearMemoryDescriptor failed (0x%x)\n", err);
	}
    }
    while (false);
    if (cmd)
	cmd->release();
    // end 64 bit loop


    // 32 bit physical address generation using IODMACommand
    // any memory above 4Gb in the memory descriptor will be buffered
    // to memory below the 4G line, on machines without remapping HW support
    do
    {
	cmd = IODMACommand::withSpecification(
	    // outSegFunc - Host endian since we read the address data with the cpu
	    // and 32 bit wide quantities
	    kIODMACommandOutputHost32, 
	    // numAddressBits
	    32, 
	    // maxSegmentSize - zero for unrestricted physically contiguous chunks
	    0,
	    // mappingOptions - kMapped for DMA addresses
	    IODMACommand::kMapped,
	    // maxTransferSize - no restriction
	    0,
	    // alignment - no restriction
	    1 );
	if (!cmd)
	{
	    IOLog("IODMACommand::withSpecification failed\n");
	    break;
	}

	// point at the memory descriptor and use the auto prepare option
	// to prepare the entire range
	err = cmd->setMemoryDescriptor(memDesc);
	if (kIOReturnSuccess != err)
	{
	    IOLog("setMemoryDescriptor failed (0x%x)\n", err);
	    break;
	}

	UInt64 offset = 0;
	while ((kIOReturnSuccess == err) && (offset < memDesc->getLength()))
	{
	    // use the 32 bit variant to match outSegFunc
	    IODMACommand::Segment32 segments[1];
	    UInt32 numSeg = 1;

	    // use the 32 bit variant to match outSegFunc
	    err = cmd->gen32IOVMSegments(&offset, &segments[0], &numSeg);
	    IOLog("gen32IOVMSegments(%x) addr 0x%lx, len 0x%lx, nsegs %ld\n",
		    err, segments[0].fIOVMAddr, segments[0].fLength, numSeg);
	}

	// if we had a DMA controller, kick off the DMA here

	// when the DMA has completed,
	
	// clear the memory descriptor and use the auto complete option
	// to complete the transaction
	err = cmd->clearMemoryDescriptor();
	if (kIOReturnSuccess != err)
	{
	    IOLog("clearMemoryDescriptor failed (0x%x)\n", err);
	}
    }
    while (false);
    if (cmd)
	cmd->release();
    // end 32 bit loop

    return (err);
}
static void 
DisjointCompletion(IOUSBController *me, IOUSBCommand *command, IOReturn status, UInt32 bufferSizeRemaining)
{
    IOBufferMemoryDescriptor	*buf = NULL;
	IODMACommand				*dmaCommand = NULL;

	USBTrace_Start( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)me, (uintptr_t)command, status, bufferSizeRemaining );
	
    if (!me || !command)
    {
		USBError(1, "DisjointCompletion sanity check failed - me(%p) command (%p)", me, command);
		return;
    }
	
	buf = OSDynamicCast(IOBufferMemoryDescriptor, command->GetBuffer());
	dmaCommand = command->GetDMACommand();
	
	if (!dmaCommand || !buf)
	{
		USBLog(1, "%s[%p]::DisjointCompletion - no dmaCommand, or buf(%p) is not an IOBMD", me->getName(), me, command->GetBuffer());
		USBTrace( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)me, (uintptr_t)command->GetBuffer(), 0, 1 );
		return;
	}
	
	if (dmaCommand->getMemoryDescriptor())
	{
		if (dmaCommand->getMemoryDescriptor() != buf)
		{
			USBLog(1, "%s[%p]::DisjointCompletion - buf(%p) doesn't match getMemoryDescriptor(%p)", me->getName(), me, buf, dmaCommand->getMemoryDescriptor());
			USBTrace( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)me, (uintptr_t)buf, (uintptr_t)dmaCommand->getMemoryDescriptor(), 2 );
		}
		
		// need to complete the dma command
		USBLog(6, "%s[%p]::DisjointCompletion - clearing memory descriptor (%p) from dmaCommand (%p)", me->getName(), me, dmaCommand->getMemoryDescriptor(), dmaCommand);
		dmaCommand->clearMemoryDescriptor();
	}
	
    if (command->GetDirection() == kUSBIn)
    {
		USBLog(5, "%s[%p]::DisjointCompletion, copying %d out of %d bytes to desc %p from buffer %p", me->getName(), me, (int)(command->GetDblBufLength()-bufferSizeRemaining), (int)command->GetDblBufLength(), command->GetOrigBuffer(), buf);
		command->GetOrigBuffer()->writeBytes(0, buf->getBytesNoCopy(), (command->GetDblBufLength()-bufferSizeRemaining));
    }
	
    buf->complete();
	buf->release();								// done with this buffer
	command->SetBuffer(NULL);
	
    // now call through to the original completion routine
    IOUSBCompletion completion = command->GetDisjointCompletion();
	
	if ( !command->GetIsSyncTransfer() )
	{
		// Free our command now that we have the completion and we are not going to use it anymore
		me->ReturnUSBCommand(command);
	}
	
   	if (completion.action)
	{
		USBLog(status == kIOReturnSuccess ? 7 : 3, "%s[%p]::DisjointCompletion calling through to %p - status 0x%x!", me->getName(), me, completion.action, (uint32_t)status);
		(*completion.action)(completion.target, completion.parameter, status, bufferSizeRemaining);
	}
	
	USBTrace_End( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)completion.target, (uintptr_t)completion.parameter, status, bufferSizeRemaining);
}
IOReturn 
IOUSBController::IsocIO(IOMemoryDescriptor *			buffer,
						UInt64							frameStart,
						UInt32							numFrames,
						IOUSBLowLatencyIsocFrame *		frameList,
						USBDeviceAddress				address,
						Endpoint *						endpoint,
						IOUSBLowLatencyIsocCompletion * completion,
						UInt32							updateFrequency)
{
    IOReturn				err = kIOReturnSuccess;
    IOUSBIsocCommand *		command = NULL;
    bool					crossEndianRequest = false;
	IODMACommand *			dmaCommand = NULL;
	bool					syncTransfer = false;
    
	// Validate the completion
	//
	USBLog(7, "%s[%p]::IsocIO(LL)", getName(), this);
	if (completion == 0)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) - No completion.  Returning kIOReturnNoCompletion(0x%x)", getName(), this, kIOReturnNoCompletion);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnNoCompletion, 0, 3 );		
		return kIOReturnNoCompletion;
	}
	
	// Validate the commandGate
	//
	if (_commandGate == 0)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) - Could not get _commandGate.  Returning kIOReturnInternalError(0x%x)", getName(), this, kIOReturnInternalError);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnInternalError, 0, 4 );
		return kIOReturnInternalError;
	}
	
	// If the high order bit of the endpoint transfer type is set, then this means it's a request from an Rosetta client
	if ( endpoint->direction & 0x80 )
	{
		endpoint->direction &= ~0x80;
		crossEndianRequest = true;
	}
	
	// Validate the direction of the endpoint -- it has to be kUSBIn or kUSBOut
	if ( (endpoint->direction != kUSBOut) && ( endpoint->direction != kUSBIn) )
	{		
		USBLog(1, "%s[%p]::IsocIO(LL) - Direction is not kUSBOut or kUSBIn (%d).  Returning kIOReturnBadArgument(0x%x)", getName(), this, endpoint->direction, kIOReturnBadArgument);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, endpoint->direction, kIOReturnBadArgument, 6 );
		return kIOReturnBadArgument;
	}
	
    if ( (uintptr_t)completion->action == (uintptr_t)&IOUSBSyncIsoCompletion )
	{
		syncTransfer = true;
        if ( _workLoop->onThread() )
        {
            USBError(1,"IOUSBController(%s)[%p]::DoIsocTransfer sync request on workloop thread.  Use async!", getName(), this);
            return kIOUSBSyncRequestOnWLThread;
        }
		
	}
	
	command = (IOUSBIsocCommand *)_freeUSBIsocCommandPool->getCommand(false);
    // If we couldn't get a command, increase the allocation and try again
    //
    if ( command == NULL )
    {
        IncreaseIsocCommandPool();
        
        command = (IOUSBIsocCommand *)_freeUSBIsocCommandPool->getCommand(false);
        if ( command == NULL )
        {
            USBLog(1, "%s[%p]::IsocIO(LL) Could not get a IOUSBIsocCommand", getName(), this);
			USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnNoResources, 0, 5 );
            return kIOReturnNoResources;
        }
    }

	dmaCommand = command->GetDMACommand();
	if (!dmaCommand)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) no IODMACommand in the IOUSBCommand", getName(), this);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnNoResources, 0, 1 );		
		return kIOReturnNoResources;
	}
	
	USBLog(7, "%s[%p]::IsocIO(LL) - putting buffer %p into dmaCommand %p which has getMemoryDescriptor %p", getName(), this, buffer, command->GetDMACommand(), command->GetDMACommand()->getMemoryDescriptor());
	err = dmaCommand->setMemoryDescriptor(buffer);								// this automatically calls prepare()
	if (err)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) - dmaCommand[%p]->setMemoryDescriptor(%p) failed with status (%p)", getName(), this, command->GetDMACommand(), buffer, (void*)err);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, err, 0, 2 );		
		_freeUSBIsocCommandPool->returnCommand(command);
		return err;
	}
	
	// If the high order bit of the endpoint transfer type is set, then this means it's a request from an Rosetta client
	command->SetRosettaClient(crossEndianRequest);
	
	command->SetIsSyncTransfer(syncTransfer);
	
	// Setup the direction
	if (endpoint->direction == kUSBOut) 
	{
		command->SetSelector(WRITE);
		command->SetDirection(kUSBOut);
	}
	else if (endpoint->direction == kUSBIn) 
	{
		command->SetSelector(READ);
		command->SetDirection(kUSBIn);
	}

	command->SetUseTimeStamp(false);
	command->SetAddress(address);
	command->SetEndpoint(endpoint->number);
	command->SetBuffer(buffer);
	command->SetCompletion( * ((IOUSBIsocCompletion *) completion) );
	command->SetStartFrame(frameStart);
	command->SetNumFrames(numFrames);
	command->SetFrameList( (IOUSBIsocFrame *) frameList);
	command->SetStatus(kIOReturnBadArgument);
	command->SetUpdateFrequency(updateFrequency);
	command->SetLowLatency(true);

	err = _commandGate->runAction(DoIsocTransfer, command);
	
	// If we have a sync request, then we always return the command after the DoIsocTransfer.  If it's an async request, we only return it if 
	// we get an immediate error
	//
	if ( syncTransfer || (kIOReturnSuccess != err) )
	{
		IODMACommand		*dmaCommand = command->GetDMACommand();
		IOMemoryDescriptor	*memDesc = dmaCommand ? (IOMemoryDescriptor	*)dmaCommand->getMemoryDescriptor() : NULL;
		
		if (memDesc)
		{
			USBLog(7, "%s[%p]::IsocIO(LL) - sync xfer or err return - clearing memory descriptor (%p) from dmaCommand (%p)", getName(), this, memDesc, dmaCommand);
			dmaCommand->clearMemoryDescriptor();
		}
		_freeUSBIsocCommandPool->returnCommand(command);
	}

    return err;
}
IOReturn 
IOUSBController::Write(IOMemoryDescriptor *buffer, USBDeviceAddress address, Endpoint *endpoint, IOUSBCompletion *completion, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount)
{
    IOReturn				err = kIOReturnSuccess;
    IOUSBCommand *			command = NULL;
	IODMACommand *			dmaCommand = NULL;
    IOUSBCompletion			nullCompletion;
    int						i;
	bool					isSyncTransfer = false;
	
    USBLog(7, "%s[%p]::Write - reqCount = %qd", getName(), this, (uint64_t)reqCount);
    
    // Validate its a outty pipe and that we have a buffer
    if ((endpoint->direction != kUSBOut) || !buffer || (buffer->getLength() < reqCount))
    {
        USBLog(5, "%s[%p]::Write - direction is not kUSBOut (%d), No Buffer, or buffer length < reqCount (%qd < %qd). Returning kIOReturnBadArgument(0x%x)", getName(), this, endpoint->direction,  (uint64_t)buffer->getLength(), (uint64_t)reqCount, kIOReturnBadArgument);
		return kIOReturnBadArgument;
    }

    if ((endpoint->transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
    {
        USBLog(5, "%s[%p]::Write - Pipe is NOT kUSBBulk (%d) AND specified a timeout (%d, %d).  Returning kIOReturnBadArgument(0x%x)", getName(), this, endpoint->transferType, (uint32_t)noDataTimeout, (uint32_t)completionTimeout, kIOReturnBadArgument);
		return kIOReturnBadArgument;							// timeouts only on bulk pipes
    }
	
    // Validate the command gate
    if (!_commandGate)
    {
        USBLog(5, "%s[%p]::Write - Could not get _commandGate.  Returning kIOReturnInternalError(0x%x)", getName(), this, kIOReturnInternalError);
		return kIOReturnInternalError;
    }

    if (  (uintptr_t) completion->action == (uintptr_t) &IOUSBSyncCompletion )
	{
		isSyncTransfer = true;
		// 7889995 - check to see if we are on the workloop thread before setting up the IOUSBCommand
		if ( _workLoop->onThread() )
		{
            USBError(1,"IOUSBController(%s)[%p]::Write sync request on workloop thread.  Use async!", getName(), this);
            return kIOUSBSyncRequestOnWLThread;
		}
	}
	
	
    // allocate the command
    command = (IOUSBCommand *)_freeUSBCommandPool->getCommand(false);
    
    // If we couldn't get a command, increase the allocation and try again
    //
    if ( command == NULL )
    {
        IncreaseCommandPool();
        
        command = (IOUSBCommand *)_freeUSBCommandPool->getCommand(false);
        if ( command == NULL )
        {
            USBLog(3,"%s[%p]::Write Could not get a IOUSBCommand",getName(),this);
            return kIOReturnNoResources;
        }
    }

	// 7455477: from this point forward, we have the command object, and we need to be careful to put it back if there is an error..
	if (reqCount)
	{
		IOMemoryDescriptor	*memDesc;
		
		dmaCommand = command->GetDMACommand();
		
		if (!dmaCommand)
		{
			USBLog(1, "%s[%p]::Write - no DMA COMMAND", getName(), this);
			USBTrace( kUSBTController, kTPControllerWrite, (uintptr_t)this, kIOReturnNoResources, 0, 1 );
            err = kIOReturnNoResources;
		}
		else
		{
			memDesc = (IOMemoryDescriptor *)dmaCommand->getMemoryDescriptor();
			if (memDesc)
			{
				USBLog(1, "%s[%p]::Write - dmaCommand (%p) already contains memory descriptor (%p) - clearing", getName(), this, dmaCommand, memDesc);
				USBTrace( kUSBTController, kTPControllerWrite, (uintptr_t)this, (uintptr_t)dmaCommand, (uintptr_t)memDesc, 2 );
				dmaCommand->clearMemoryDescriptor();
			}
			USBLog(7, "%s[%p]::Write - setting memory descriptor (%p) into dmaCommand (%p)", getName(), this, buffer, dmaCommand);
			err = dmaCommand->setMemoryDescriptor(buffer);
			if (err)
			{
				USBTrace( kUSBTController, kTPControllerWrite, (uintptr_t)this, err, 0, 4 );
				USBLog(1, "%s[%p]::Write - err(%p) attempting to set the memory descriptor to the dmaCommand", getName(), this, (void*)err);
			}
		}

	}
	
	if (!err)
	{
		command->SetIsSyncTransfer(isSyncTransfer);
		command->SetUseTimeStamp(false);
		command->SetSelector(WRITE);
		command->SetRequest(0);            // Not a device request
		command->SetAddress(address);
		command->SetEndpoint(endpoint->number);
#ifdef SUPPORTS_SS_USB
    	command->SetStreamID(0);
#endif
		command->SetDirection(kUSBOut);
		command->SetType(endpoint->transferType);
		command->SetBuffer(buffer);
		command->SetReqCount(reqCount);
		command->SetClientCompletion(*completion);
		command->SetNoDataTimeout(noDataTimeout); 
		command->SetCompletionTimeout(completionTimeout);
		command->SetMultiTransferTransaction(false);
		command->SetFinalTransferInTransaction(false);
		for (i=0; i < 10; i++)
			command->SetUIMScratch(i, 0);

		nullCompletion.target = (void *) NULL;
		nullCompletion.action = (IOUSBCompletionAction) NULL;
		nullCompletion.parameter = (void *) NULL;
		command->SetDisjointCompletion(nullCompletion);

		err = CheckForDisjointDescriptor(command, endpoint->maxPacketSize);
		if (!err)
		{			
			err = _commandGate->runAction(DoIOTransfer, command);
		}
	}
	
	// 7455477: handle and and all errors which may have occured above
	// If we have a sync request, then we always return the command after the DoIOTransfer.  If it's an async request, we only return it if 
	// we get an immediate error
	//
	if ( isSyncTransfer || (kIOReturnSuccess != err) )
	{
		IOMemoryDescriptor	*memDesc = dmaCommand ? (IOMemoryDescriptor	*)dmaCommand->getMemoryDescriptor() : NULL;

		if (!isSyncTransfer)
		{
			USBLog(2, "%s[%p]::Write - General error (%p) - cleaning up - command(%p) dmaCommand(%p)", getName(), this, (void*)err, command, dmaCommand);
		}

		if (memDesc)
		{
			USBLog(7, "%s[%p]::Write - General error (%p) - clearing memory descriptor (%p) from dmaCommand (%p)", getName(), this, (void*)err, memDesc, dmaCommand);
			dmaCommand->clearMemoryDescriptor();
		}
		nullCompletion = command->GetDisjointCompletion();
		if (nullCompletion.action)
		{
			USBLog(2, "%s[%p]::Write - SYNC xfer or immediate error with Disjoint Completion", getName(), this);
		}
		_freeUSBCommandPool->returnCommand(command);
	}
	
    return err;
}
IOReturn
IOUSBController::CheckForDisjointDescriptor(IOUSBCommand *command, UInt16 maxPacketSize)
{
    IOMemoryDescriptor			*buf = command->GetBuffer();
    IOBufferMemoryDescriptor	*newBuf = NULL;
    IOByteCount					length = command->GetReqCount();
	IODMACommand				*dmaCommand = command->GetDMACommand();
    IOByteCount					segLength = 0;
    IOByteCount					offset = 0;
    IOReturn					err;
	UInt64						offset64;
	IODMACommand::Segment64		segment64;
	UInt32						numSegments;
	
	// USBTrace_Start( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this );
	
    // Zero length buffers are valid, but they are surely not disjoint, so just return success.  
    //
    if ( length == 0 )
        return kIOReturnSuccess;
	
	if (!dmaCommand)
	{
		USBLog(1, "%s[%p]::CheckForDisjointDescriptor - no dmaCommand", getName(), this);
		USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this, kIOReturnBadArgument, 0, 1 );
		return kIOReturnBadArgument;
	}
	
	if (dmaCommand->getMemoryDescriptor() != buf)
	{
		USBLog(1, "%s[%p]::CheckForDisjointDescriptor - mismatched memory descriptor (%p) and dmaCommand memory descriptor (%p)", getName(), this, buf, dmaCommand->getMemoryDescriptor());
		USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, kIOReturnBadArgument, (uintptr_t)buf, (uintptr_t)dmaCommand->getMemoryDescriptor(), 2 );
		return kIOReturnBadArgument;
	}
	
    while (length)
    {
		offset64 = offset;
		numSegments = 1;
		
		err = dmaCommand->gen64IOVMSegments(&offset64, &segment64, &numSegments);
        if (err || (numSegments != 1))
        {
            USBLog(1, "%s[%p]::CheckForDisjointDescriptor - err (%p) trying to generate segments at offset (%qd), length (%d), segLength (%d), total length (%d), buf (%p), numSegments (%d)", getName(), this, (void*)err, offset64, (int)length, (int)segLength, (int)command->GetReqCount(), buf, (int)numSegments);
			USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, offset64, length, segLength, 3 );
			USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, segLength, command->GetReqCount(), numSegments, 4 );
            return kIOReturnBadArgument;
        }

		
		// 3036056 since length might be less than the length of the descriptor, we are OK if the physical
		// segment is longer than we need
        if (segment64.fLength >= length)
            return kIOReturnSuccess;		// this is the last segment, so we are OK
		
		// since length is a 32 bit quantity, then we know from the above statement that if we are here we are 32 bit only
		segLength = (IOByteCount)segment64.fLength;

        // so the segment is less than the rest of the length - we need to check against maxPacketSize
        if (segLength % maxPacketSize)
        {
            // this is the error case. I need to copy the descriptor to a new descriptor and remember that I did it
            USBLog(6, "%s[%p]::CheckForDisjointDescriptor - found a disjoint segment of length (%d) MPS (%d)", getName(), this, (int)segLength, maxPacketSize);
			length = command->GetReqCount();		// we will not return to the while loop, so don't worry about changing the value of length
													// allocate a new descriptor which is the same total length as the old one
			newBuf = IOBufferMemoryDescriptor::withOptions((command->GetDirection() == kUSBIn) ? kIODirectionIn : kIODirectionOut, length);
			if (!newBuf)
			{
				USBLog(1, "%s[%p]::CheckForDisjointDescriptor - could not allocate new buffer", getName(), this);
				USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this, kIOReturnNoMemory, 0, 5 );
				return kIOReturnNoMemory;
			}
			USBLog(7, "%s[%p]::CheckForDisjointDescriptor, obtained buffer %p of length %d", getName(), this, newBuf, (int)length);
			
			// first close out (and complete) the original dma command descriptor
			USBLog(7, "%s[%p]::CheckForDisjointDescriptor, clearing memDec (%p) from dmaCommand (%p)", getName(), this, dmaCommand->getMemoryDescriptor(), dmaCommand);
			dmaCommand->clearMemoryDescriptor();
			
			// copy the bytes to the buffer if necessary
			if (command->GetDirection() == kUSBOut)
			{
				USBLog(7, "%s[%p]::CheckForDisjointDescriptor, copying %d bytes from desc %p to buffer %p", getName(), this, (int)length, buf, newBuf->getBytesNoCopy());
				if (buf->readBytes(0, newBuf->getBytesNoCopy(), length) != length)
				{
					USBLog(1, "%s[%p]::CheckForDisjointDescriptor - bad copy on a write", getName(), this);
					USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this, 0, 0, 6 );
					newBuf->release();
					return kIOReturnNoMemory;
				}
			}
			err = newBuf->prepare();
			if (err)
			{
				USBLog(1, "%s[%p]::CheckForDisjointDescriptor - err 0x%x in prepare", getName(), this, err);
				USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this, err, 0, 7 );
				newBuf->release();
				return err;
			}
			err = dmaCommand->setMemoryDescriptor(newBuf);
			if (err)
			{
				USBLog(1, "%s[%p]::CheckForDisjointDescriptor - err 0x%x in setMemoryDescriptor", getName(), this, err);
				USBTrace( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this, err, 0, 8 );
				newBuf->complete();
				newBuf->release();
				return err;
			}
			
			command->SetOrigBuffer(command->GetBuffer());
			command->SetDisjointCompletion(command->GetClientCompletion());
			USBLog(7, "%s[%p]::CheckForDisjointDescriptor - changing buffer from (%p) to (%p) and putting new buffer in dmaCommand (%p)", getName(), this, command->GetBuffer(), newBuf, dmaCommand);
			command->SetBuffer(newBuf);
			
			
			IOUSBCompletion completion;
			completion.target = this;
			completion.action = (IOUSBCompletionAction)DisjointCompletion;
			completion.parameter = command;
			command->SetClientCompletion(completion);
			
			command->SetDblBufLength(length);			// for the IOFree - the other buffer may change size
            return kIOReturnSuccess;
		}
        length -= segLength;		// adjust our master length pointer
		offset += segLength;
	}
	
	USBLog(5, "%s[%p]::CheckForDisjointDescriptor - returning kIOReturnBadArgument(0x%x)", getName(), this, kIOReturnBadArgument);
	// USBTrace_End( kUSBTController, kTPControllerCheckForDisjointDescriptor, (uintptr_t)this, kIOReturnBadArgument);
	
    return kIOReturnBadArgument;
}
Example #9
0
IOReturn XHCIAsyncEndpoint::CreateTDs(IOUSBCommand* command, uint16_t streamId, uint32_t mystery, uint8_t immediateDataSize, uint8_t const* pImmediateData)
{
	XHCIAsyncTD* pTd;
	size_t transferRequestBytes, numBytesLeft, bytesDone;
	uint32_t maxBytesPerTD, currentTDBytes;
	uint16_t tdIndex;
	bool haveImmediateData;
	bool usingMultipleTDs;

	if (aborting)
		return kIOReturnNotPermitted;
	transferRequestBytes = command->GetReqCount();
	if (transferRequestBytes >= (1U << 23) && (provider->_errataBits & kErrataAbsoluteEDTLA)) {
		IOLog("%s: xHC with absolute EDTLA limited to 8MB per transaction\n", __FUNCTION__);
		return kIOReturnUnsupported;
	}
	if (transferRequestBytes) {
		IODMACommand* dmac = command->GetDMACommand();
		if (!dmac || !dmac->getMemoryDescriptor()) {
			IOLog("%s: no DMA Command or missing memory descriptor\n", __FUNCTION__);
			return kIOReturnBadArgument;
		}
	}
	command->SetUIMScratch(9U, 0U);
	if (immediateDataSize <= sizeof pTd->immediateData) {
		transferRequestBytes = immediateDataSize;
		haveImmediateData = true;
		usingMultipleTDs = false;
		maxBytesPerTD = immediateDataSize;
	} else if (transferRequestBytes <= maxTDBytes) {
		haveImmediateData = false;
		usingMultipleTDs = false;
		maxBytesPerTD = static_cast<uint32_t>(transferRequestBytes);
	} else {
		haveImmediateData = false;
		usingMultipleTDs = true;
		maxBytesPerTD = maxTDBytes;
	}

	numBytesLeft = transferRequestBytes;
	bytesDone = 0U;
	tdIndex = 1U;
	currentTDBytes = maxBytesPerTD;
	do {
		pTd = GetTDFromFreeQueue(true);
		if (!pTd)
			return kIOReturnNoMemory;
		pTd->command = command;
		pTd->interruptThisTD = !(bytesDone % maxTDBytes);
		pTd->multiTDTransaction = usingMultipleTDs;
		pTd->bytesPreceedingThisTD = bytesDone;
		pTd->bytesThisTD = currentTDBytes;
		pTd->mystery = mystery;
		pTd->maxNumPagesInTD = static_cast<uint16_t>((currentTDBytes / PAGE_SIZE) + 2U);
		pTd->streamId = streamId;
		pTd->shortfall = currentTDBytes;
		if (haveImmediateData) {
			pTd->haveImmediateData = haveImmediateData;
			if (immediateDataSize)
				bcopy(pImmediateData, &pTd->immediateData[0], immediateDataSize);
		}
		PutTD(&queuedHead, &queuedTail, pTd, &numTDsQueued);
		bytesDone += currentTDBytes;
		if (bytesDone >= transferRequestBytes)
			break;
		++tdIndex;
		numBytesLeft -= currentTDBytes;
		pTd->bytesFollowingThisTD = numBytesLeft;
		currentTDBytes = (numBytesLeft < maxBytesPerTD) ? static_cast<uint32_t>(numBytesLeft) : maxBytesPerTD;
	} while (true);
	pTd->numTDsThisTransaction = tdIndex;
	pTd->interruptThisTD = true;
	pTd->finalTDInTransaction = true;
	pTd->bytesFollowingThisTD = 0U;
	return kIOReturnSuccess;
}
Example #10
0
IOReturn CLASS::UIMCreateIsochTransfer(IOUSBIsocCommand* command)
{
    uint64_t curFrameNumber, frameNumberStart;
    IODMACommand* dmac;
    GenericUSBXHCIIsochEP* pIsochEp;
    GenericUSBXHCIIsochTD* pIsochTd;
    IOUSBIsocFrame* pFrames;
    IOUSBLowLatencyIsocFrame* pLLFrames;
    size_t transferOffset;
    uint32_t transferCount, updateFrequency, epInterval, transfersPerTD, frameNumberIncrease, frameCount, framesBeforeInterrupt, transfer;
    bool lowLatency, newFrame;

    /*
     * Note: See UIMCreateIsochTransfer in AppleUSBXHCIUIM.cpp for reference
     */
    if (!command)
        return kIOReturnBadArgument;
    curFrameNumber = GetFrameNumber();
#if 0
    /*
     * Note: Added Mavericks
     */
    if (!IsStillConnectedAndEnabled(GetSlotID(command->GetAddress())))
        return kIOReturnNoDevice;
#endif
    transferCount = command->GetNumFrames();
    if (!transferCount || transferCount > 1000U)
        return kIOReturnBadArgument;
    lowLatency = command->GetLowLatency();
    updateFrequency = command->GetUpdateFrequency();
    pFrames = command->GetFrameList();
    pLLFrames = reinterpret_cast<IOUSBLowLatencyIsocFrame*>(pFrames);
    frameNumberStart = command->GetStartFrame();
    pIsochEp = OSDynamicCast(GenericUSBXHCIIsochEP,
                             FindIsochronousEndpoint(command->GetAddress(),
                                     command->GetEndpoint(),
                                     command->GetDirection(),
                                     0));
    if (!pIsochEp)
        return kIOUSBEndpointNotFound;
    if (pIsochEp->aborting)
        return kIOReturnNotPermitted;
    if (pIsochEp->pRing->isInactive())
        return kIOReturnBadArgument;
    if (pIsochEp->pRing->deleteInProgress)
        return kIOReturnNoDevice;
    if (frameNumberStart == kAppleUSBSSIsocContinuousFrame)
        pIsochEp->continuousStream = true;
    else {
        if (frameNumberStart < pIsochEp->firstAvailableFrame)
            return kIOReturnIsoTooOld;
        if (pIsochEp->continuousStream)
            return kIOReturnBadArgument;
    }
    newFrame = false;
    if (!pIsochEp->continuousStream) {
        if (frameNumberStart != pIsochEp->firstAvailableFrame)
            newFrame = true;
        pIsochEp->firstAvailableFrame = frameNumberStart;
        if (static_cast<int64_t>(frameNumberStart - curFrameNumber) < -1024)
            return kIOReturnIsoTooOld;
        else if (static_cast<int64_t>(frameNumberStart - curFrameNumber) > 1024)
            return kIOReturnIsoTooNew;
    }
    dmac = command->GetDMACommand();
    if (!dmac || !dmac->getMemoryDescriptor()) {
        IOLog("%s: no DMA Command or missing memory descriptor\n", __FUNCTION__);
        return kIOReturnBadArgument;
    }
    epInterval = pIsochEp->interval;
    if (epInterval >= 8U) {
        transfersPerTD = 1U;
        frameNumberIncrease = epInterval / 8U;
    } else {
        transfersPerTD = 8U / epInterval;
        frameNumberIncrease = 1U;
    }
    if (!pIsochEp->continuousStream) {
        if (newFrame &&
                frameNumberIncrease > 1U &&
                (frameNumberStart % frameNumberIncrease))
            return kIOReturnBadArgument;
    }
    if (transferCount % transfersPerTD)
        return kIOReturnBadArgument;
    if (!updateFrequency)
        updateFrequency = 8U;
    if (lowLatency && updateFrequency < 8U)
        framesBeforeInterrupt = updateFrequency;
    else
        framesBeforeInterrupt = 8U;
    frameCount = 0U;
    transferOffset = 0U;
    pIsochTd = 0;
    for (uint32_t baseTransferIndex = 0U; baseTransferIndex < transferCount; baseTransferIndex += transfersPerTD) {
        pIsochTd = GenericUSBXHCIIsochTD::ForEndpoint(pIsochEp);
        if (!pIsochTd)
            return kIOReturnNoMemory;
        pIsochTd->_lowLatency = lowLatency;
        pIsochTd->_framesInTD = 0U;
        pIsochTd->newFrame = newFrame;
        pIsochTd->interruptThisTD = false;
        if (frameCount > framesBeforeInterrupt) {
            pIsochTd->interruptThisTD = true;
            frameCount -= framesBeforeInterrupt;
        }
        pIsochEp->firstAvailableFrame += frameNumberIncrease;
        if (frameNumberIncrease == 1U)
            newFrame = false;
        pIsochTd->transferOffset = transferOffset;
        for (transfer = 0U;
                transfer < transfersPerTD && (baseTransferIndex + transfer) < transferCount;
                ++transfer) {
            if (lowLatency) {
                pLLFrames[baseTransferIndex + transfer].frStatus = kUSBLowLatencyIsochTransferKey;
                transferOffset += pLLFrames[baseTransferIndex + transfer].frReqCount;
            } else
                transferOffset += pFrames[baseTransferIndex + transfer].frReqCount;
        }
        pIsochTd->_framesInTD = static_cast<uint8_t>(transfer);
        pIsochTd->_pFrames = pFrames;
        pIsochTd->_frameNumber = frameNumberStart;
        pIsochTd->_frameIndex = baseTransferIndex;
        pIsochTd->_completion.action = 0;
        pIsochTd->_pEndpoint = pIsochEp;
        pIsochTd->command = command;
        PutTDonToDoList(pIsochEp, pIsochTd);
        frameNumberStart += frameNumberIncrease;
        frameCount += frameNumberIncrease;
    }
    if (!pIsochTd)
        return kIOReturnInternalError;
    pIsochTd->_completion = command->GetUSLCompletion();
    pIsochTd->interruptThisTD = true;
    AddIsocFramesToSchedule(pIsochEp);
    return kIOReturnSuccess;
}
Example #11
0
IOReturn CLASS::UIMCreateControlTransfer(short functionNumber, short endpointNumber, IOUSBCommand* command,
        IOMemoryDescriptor* CBP, bool /* bufferRounding */, UInt32 bufferSize,
        short direction)
{
    IOReturn rc;
    uint32_t mystery;	// Note: structure as fourth uint32_t of Transfer TRB
    uint8_t slot, immediateDataSize;
    ringStruct* pRing;
    SetupStageHeader smallbuf1;
    SetupStageHeader smallbuf2;

    slot = GetSlotID(functionNumber);
    if (!slot)
        return kIOUSBEndpointNotFound;
    if (endpointNumber)
        return kIOReturnBadArgument;
#if 0
    /*
     * Note: Added Mavericks
     */
    if (!IsStillConnectedAndEnabled(slot))
        return kIOReturnNoDevice;
#endif
    pRing = GetRing(slot, 1, 0U);
    if (pRing->isInactive())
        return kIOReturnBadArgument;
    if (GetNeedsReset(slot))
        return AddDummyCommand(pRing, command);
    if (pRing->deleteInProgress)
        return kIOReturnNoDevice;
    if (pRing->epType != CTRL_EP)
        return kIOUSBEndpointNotFound;
    XHCIAsyncEndpoint* pAsyncEp = pRing->asyncEndpoint;
    if (!pAsyncEp)
        return kIOUSBEndpointNotFound;
    if (pAsyncEp->aborting)
        return kIOReturnNotPermitted;
    if (CBP && bufferSize) {
        IODMACommand* dmac = command->GetDMACommand();
        if (!dmac) {
            IOLog("%s: no dmaCommand\n", __FUNCTION__);
            return kIOReturnNoMemory;
        }
        IOMemoryDescriptor const* dmac_md = dmac->getMemoryDescriptor();
        if (dmac_md != CBP) {
            IOLog("%s: mismatched CBP (%p) and dmaCommand memory descriptor (%p)\n", __FUNCTION__,
                  CBP, dmac_md);
            return kIOReturnInternalError;
        }
    }
    bzero(&smallbuf1, sizeof smallbuf1);
    if (direction == kUSBNone) {
        if (bufferSize != sizeof smallbuf2)
            return kIOReturnBadArgument;
        if (CBP->readBytes(0U, &smallbuf2, sizeof smallbuf2) != sizeof smallbuf2)
            return kIOReturnInternalError;
        if (smallbuf2.bmRequestType == 0U && smallbuf2.bRequest == 5U) { /* kSetAddress */
            uint16_t deviceAddress = smallbuf2.wValue;
            ContextStruct* pContext = GetSlotContext(slot, 1);
            uint16_t maxPacketSize = static_cast<uint16_t>(XHCI_EPCTX_1_MAXP_SIZE_GET(pContext->_e.dwEpCtx1));
            pContext = GetSlotContext(slot);
            _deviceZero.isBeingAddressed = true;
            rc = AddressDevice(slot,
                               maxPacketSize,
                               true,
                               GetSlCtxSpeed(pContext),
                               XHCI_SCTX_2_TT_HUB_SID_GET(pContext->_s.dwSctx2),
                               XHCI_SCTX_2_TT_PORT_NUM_GET(pContext->_s.dwSctx2));
            if (rc != kIOReturnSuccess)
                return rc;
            _addressMapper.HubAddress[deviceAddress] = static_cast<uint8_t>(_deviceZero.HubAddress);
            _addressMapper.PortOnHub[deviceAddress] = static_cast<uint8_t>(_deviceZero.PortOnHub);
            _addressMapper.Slot[deviceAddress] = static_cast<uint8_t>(slot);
            _addressMapper.Active[deviceAddress] = true;
            _deviceZero.HubAddress = 0U;
            _deviceZero.PortOnHub = 0U;
            mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NOOP) | XHCI_TRB_3_IOC_BIT;
            immediateDataSize = 0U;
        } else {
            mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) | XHCI_TRB_3_IDT_BIT | XHCI_TRB_3_IOC_BIT;
            /*
             * Set TRT field
             */
            if (smallbuf2.wLength >= 1U)
                /*
                 * Note: Upper bit of bmRequestType is transfer direction (1 - in, 0 - out)
                 */
                mystery |= (smallbuf2.bmRequestType & 0x80U) ? XHCI_TRB_3_TRT_IN : XHCI_TRB_3_TRT_OUT;
            // else XHCI_TRB_3_TRT_NONE
            bcopy(&smallbuf2, &smallbuf1, sizeof smallbuf1);
            immediateDataSize = sizeof smallbuf1;
        }
    } else if (bufferSize) {
        mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE);
        if (direction == kUSBIn)
            mystery |= XHCI_TRB_3_DIR_IN;
        immediateDataSize = 0xFFU;	// Note: means data is not immediate
    } else if (_deviceZero.isBeingAddressed) {
        _addressMapper.Slot[0] = 0U;
        _addressMapper.Active[0] = false;
        _deviceZero.isBeingAddressed = false;
        mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NOOP) | XHCI_TRB_3_IOC_BIT;
        immediateDataSize = 0U;
    } else {
        mystery = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE) | XHCI_TRB_3_IOC_BIT;
        if (direction == kUSBIn)
            mystery |= XHCI_TRB_3_DIR_IN;
        immediateDataSize = 0U;
    }
    rc = pAsyncEp->CreateTDs(command, 0U, mystery, immediateDataSize, reinterpret_cast<uint8_t const*>(&smallbuf1));
    pAsyncEp->ScheduleTDs();
    return rc;
}