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; }
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); }
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; }
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; }
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; }
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; }