/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1998-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include "AppleUSBEHCI.h" #include #include #include #define super IOUSBControllerV2 static IOReturn TranslateStatusToUSBError(UInt32 status); // Add to USB.h IOReturn AppleUSBEHCI::UIMCreateControlEndpoint(UInt8 functionAddress, UInt8 endpointNumber, UInt16 maxPacketSize, UInt8 speed) { IOLog("EHCIUIM -- UIMCreateControlEndpoint old version called with no split params\n"); return(kIOReturnInternalError); } IOReturn AppleUSBEHCI::UIMCreateControlEndpoint(UInt8 functionAddress, UInt8 endpointNumber, UInt16 maxPacketSize, UInt8 speed, USBDeviceAddress highSpeedHub, int highSpeedPort) { AppleEHCIQueueHead *pEHCIEndpointDescriptor; AppleEHCIQueueHead *pED; USBLog(7, "%s[%p]::UIMCreateControlEndpoint(%d, %d, %d, %d @(%d, %d))\n", getName(), this, functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort); if (_rootHubFuncAddress == functionAddress) { return(kIOReturnSuccess); } if( (speed == kUSBDeviceSpeedLow) && (maxPacketSize > 8) ) { if(functionAddress != 0) { USBError(1, "%s[%p]::UIMCreateControlEndpoint - incorrect max packet size (%d) for low speed", getName(), this, maxPacketSize); return(kIOReturnBadArgument); } USBLog(3, "%s[%p]::UIMCreateControlEndpoint - changing low speed max packet from %d to 8 for dev 0", getName(), this, maxPacketSize); maxPacketSize = 8; } pED = _AsyncHead; pEHCIEndpointDescriptor = AddEmptyCBEndPoint(functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort, kEHCIEDDirectionTD, pED); if(pED == NULL) { _AsyncHead = pEHCIEndpointDescriptor; _pEHCIRegisters->AsyncListAddr = HostToUSBLong( pEHCIEndpointDescriptor->_sharedPhysical ); } if (pEHCIEndpointDescriptor == NULL) return(-1); //FIXME return (kIOReturnSuccess); } AppleEHCIQueueHead * AppleUSBEHCI::MakeEmptyEndPoint( UInt8 functionAddress, UInt8 endpointNumber, UInt16 maxPacketSize, UInt8 speed, USBDeviceAddress highSpeedHub, int highSpeedPort, UInt8 direction) { UInt32 myFunctionAddress; UInt32 myEndPointNumber; UInt32 myMaxPacketSize; UInt32 mySpeed = 0; AppleEHCIQueueHead * pED; EHCIGeneralTransferDescriptorPtr pTD; USBLog(7, "%s[%p]::MakeEmptyEndPoint - Addr: %d, EPT#: %d, MPS: %d, speed: %d, hubAddr: %d, hubPort: %d, dir: %d", getName(), this, functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort, direction); if( (highSpeedHub == 0) && (speed != kUSBDeviceSpeedHigh) ) { USBLog(1, "%s[%p]::MakeEmptyEndPoint - new endpoint NOT fixing up speed", getName(), this); // speed = kUSBDeviceSpeedHigh; } pED = FindControlBulkEndpoint(functionAddress, endpointNumber, NULL, direction); if(pED != NULL) { USBLog(1, "%s[%p]::MakeEmptyEndPoint - old endpoint found, abusing %p", getName(), this, pED); pED->GetSharedLogical()->flags = 0xffffffff; } pED = AllocateQH(); USBLog(7, "%s[%p]::MakeEmptyEndPoint - new endpoint %p", getName(), this, pED); myFunctionAddress = ((UInt32) functionAddress) << kEHCIEDFlags_FAPhase; myEndPointNumber = ((UInt32) endpointNumber) << kEHCIEDFlags_ENPhase; // Note: unlike in 9, the speed bit patterns between USB.h and the EHCI spec, don't match // From USB.h // kUSBDeviceSpeedLow = 0, // low speed device // kUSBDeviceSpeedFull = 1, // full speed device // kUSBDeviceSpeedHigh = 2 // high speed device // EHCI: // 00b Full-Speed (12Mbs) // 01b Low-Speed (1.5Mbs) // 10b High-Speed (480 Mb/s) if(speed == kUSBDeviceSpeedFull) { mySpeed = 0; } else if(speed == kUSBDeviceSpeedLow) { mySpeed = 1 << kEHCIEDFlags_SPhase; } else if(speed == kUSBDeviceSpeedHigh) { mySpeed = 2 << kEHCIEDFlags_SPhase; } USBLog(7, "%s[%p]::MakeEmptyEndPoint - mySpeed %d", getName(), this, mySpeed); myMaxPacketSize = ((UInt32) maxPacketSize) << kEHCIEDFlags_MPSPhase; pED->_direction = direction; USBLog(7, "%s[%p]::MakeEmptyEndPoint - MPS = %d, setting flags to %p", getName(), this, maxPacketSize, myFunctionAddress | myEndPointNumber | myMaxPacketSize | mySpeed); pED->GetSharedLogical()->flags = HostToUSBLong(myFunctionAddress | myEndPointNumber | myMaxPacketSize | mySpeed); pED->GetSharedLogical()->splitFlags = HostToUSBLong( (1<< kEHCIEDSplitFlags_MultPhase) // 1 transaction per traversal | (highSpeedHub << kEHCIEDSplitFlags_HubAddrPhase) | (highSpeedPort << kEHCIEDSplitFlags_PortPhase) ); pED->GetSharedLogical()->CurrqTDPtr = 0; pED->GetSharedLogical()->AltqTDPtr = HostToUSBLong(kEHCITermFlag); // Invalid address pED->GetSharedLogical()->qTDFlags = 0; // this sets the active and halted bits to zero. pED->GetSharedLogical()->BuffPtr[0] = 0; pED->GetSharedLogical()->BuffPtr[1] = 0; pED->GetSharedLogical()->BuffPtr[2] = 0; pED->GetSharedLogical()->BuffPtr[3] = 0; pED->GetSharedLogical()->BuffPtr[4] = 0; // ?? pED->GetSharedLogical()->qTDFlags = 0; // this sets the active and halted bits to zero. // Put in a blank TD now, this will be fetched everytime the endpoint is accessed // so we can use it to drag in a real transaction. pTD = AllocateTD(); // ***** What if this fails?????? see also OHCI pTD->pShared->flags = 0; // make sure active is 0 pTD->pShared->nextTD = HostToUSBLong(kEHCITermFlag); pTD->pShared->altTD = HostToUSBLong(kEHCITermFlag); pTD->command = NULL; USBLog(7, "%s[%p]::MakeEmptyEndPoint - pointing NextqTDPtr to %lx", getName(), this, pTD->pPhysical); pED->GetSharedLogical()->NextqTDPtr = HostToUSBLong( (UInt32)pTD->pPhysical & ~0x1F); pED->_qTD = pTD; pED->_TailTD = pED->_qTD; pED->_responseToStall = 0; return pED; } AppleEHCIQueueHead * AppleUSBEHCI::MakeEmptyIntEndPoint(UInt8 functionAddress, UInt8 endpointNumber, UInt16 maxPacketSize, UInt8 speed, USBDeviceAddress highSpeedHub, int highSpeedPort, UInt8 direction) { AppleEHCIQueueHead * intED; UInt32 mySMask; intED = MakeEmptyEndPoint(functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort, direction); if (intED == NULL) return(NULL); return intED; } void AppleUSBEHCI::linkAsyncEndpoint(AppleEHCIQueueHead *CBED, AppleEHCIQueueHead *pEDHead) { IOPhysicalAddress newHorizPtr; if(pEDHead == NULL) // No previous EDs, make this head of queue. { CBED->GetSharedLogical()->flags |= HostToUSBLong(1 << kEHCIEDFlags_HPhase); } // Point first endpoint to itself newHorizPtr = HostToUSBLong(CBED->GetPhysicalAddrWithType()); // Its a queue head USBLog(7, "%s[%p]::linkAsynEndpoint pEDHead %p", getName(), this, pEDHead); if(pEDHead == NULL) { CBED->GetSharedLogical()->nextQH = newHorizPtr; CBED->_logicalNext = NULL; } else { // New endpoints are inserted just after queuehead (not at beginning or end). // Point new endpoint at same endpoint old queuehead was pointing CBED->GetSharedLogical()->nextQH = pEDHead->GetSharedLogical()->nextQH; CBED->_logicalNext = pEDHead->_logicalNext; // Point queue head to new endpoint pEDHead->_logicalNext = CBED; pEDHead->GetSharedLogical()->nextQH = newHorizPtr; } } AppleEHCIQueueHead * AppleUSBEHCI::AddEmptyCBEndPoint( UInt8 functionAddress, UInt8 endpointNumber, UInt16 maxPacketSize, UInt8 speed, USBDeviceAddress highSpeedHub, int highSpeedPort, UInt8 direction, AppleEHCIQueueHead * pED) { AppleEHCIQueueHead * CBED; UInt32 cFlag; UInt32 myDataToggleCntrl; USBLog(7, "%s[%p]::AddEmptyCBEndPoint speed %d @(%d, %d)", getName(), this, speed, highSpeedHub, highSpeedPort); CBED = MakeEmptyEndPoint(functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort, direction); if (CBED == NULL) return(NULL); cFlag = 0; if(kEHCIEDDirectionTD == direction) { myDataToggleCntrl = 1 << kEHCIEDFlags_DTCPhase; if(speed != kUSBDeviceSpeedHigh) { cFlag = 1 << kEHCIEDFlags_CPhase; } } else { myDataToggleCntrl = 0; } CBED->GetSharedLogical()->flags |= HostToUSBLong(myDataToggleCntrl | cFlag); linkAsyncEndpoint(CBED, pED); return CBED; } IOReturn AppleUSBEHCI::UIMCreateControlTransfer( short functionAddress, short endpointNumber, IOUSBCompletion completion, void* CBP, bool bufferRounding, UInt32 bufferSize, short direction) { // this is the old 1.8/1.8.1 method. It should not be used any more. USBLog(1, "%s(%p)UIMCreateControlTransfer- calling the wrong method!", getName(), this); return kIOReturnIPCError; } void AppleUSBEHCI::printAsyncQueue() { AppleEHCIQueueHead *pED = _AsyncHead; if (pED) { USBLog(7, "%s[%p]::printAsyncQueue", getName(), this); while (pED) { //pED->print(); pED = OSDynamicCast(AppleEHCIQueueHead, pED->_logicalNext); } } else { USBLog(7, "%s[%p]::printAsyncQueue: NULL Async Queue", getName(), this); } } void AppleUSBEHCI::printTD(EHCIGeneralTransferDescriptorPtr pTD) { #if 1 IOLog("print TD turned off\n"); #else if(pTD == 0) { IOLog("Attempt to print null TD\n"); return; } USBLog(5, "%s[%p]::printED: ------pTD at %lx", getName(), this, (UInt32)pTD); USBLog(5, "%s[%p]::printED: nextTD: %lx", getName(), this, USBToHostLong(pTD->pShared->nextTD)); USBLog(5, "%s[%p]::printED: altTD: %lx", getName(), this, USBToHostLong(pTD->pShared->altTD)); USBLog(5, "%s[%p]::printED: flags: %lx", getName(), this, USBToHostLong(pTD->pShared->flags)); USBLog(5, "%s[%p]::printED: BuffPtr0: %lx", getName(), this, USBToHostLong(pTD->pShared->BuffPtr[0])); USBLog(5, "%s[%p]::printED: pEndpt: %lx", getName(), this, (UInt32)(pTD->pQH)); // USBLog(5, "%s[%p]::printED: bufSiz: %lx", getName(), this, (UInt32)(pTD->bufferSize)); USBLog(5, "%s[%p]::printED: pPhys: %lx", getName(), this, (UInt32)(pTD->pPhysical)); USBLog(5, "%s[%p]::printED: pLogNxt: %lx", getName(), this, (UInt32)(pTD->pLogicalNext)); USBLog(5, "%s[%p]::printED: logBf: %lx", getName(), this, (UInt32)(pTD->logicalBuffer)); #endif } IOReturn AppleUSBEHCI::UIMCreateControlTransfer( short functionAddress, short endpointNumber, IOUSBCommand* command, IOMemoryDescriptor* CBP, bool bufferRounding, UInt32 bufferSize, short direction) { AppleEHCIQueueHead * pEDQueue; AppleEHCIQueueHead * pEDDummy; IOReturn status; IOUSBCompletion completion = command->GetUSLCompletion(); USBLog(7, "%s[%p]::UIMCreateControlTransfer adr=%d:%d cbp=%lx:%lx br=%s cback=[%p:%p] dir=%d)", getName(), this, functionAddress, endpointNumber, (UInt32)CBP, bufferSize, bufferRounding ? "YES":"NO", completion.target, completion.parameter, direction); // search for endpoint descriptor pEDQueue = FindControlBulkEndpoint(functionAddress, endpointNumber, &pEDDummy, kEHCIEDDirectionTD); USBLog(7, "%s[%p]::UIMCreateControlTransfer -- found endpoint at %p", getName(), this, pEDQueue); if (pEDQueue == NULL) { USBLog(3, "%s[%p]::UIMCreateControlTransfer- Could not find endpoint!", getName(), this); return kIOUSBEndpointNotFound; } status = allocateTDs(pEDQueue, command, CBP, bufferSize, direction, true); if(status == kIOReturnSuccess) { USBLog(7, "%s[%p]::UIMCreateControlTransfer allocateTDS done - CMD = %p, STS = %p", getName(), this, USBToHostLong(_pEHCIRegisters->USBCMD), USBToHostLong(_pEHCIRegisters->USBSTS)); printAsyncQueue(); EnableAsyncSchedule(); } else { USBError(1, "%s[%p]::UIMCreateControlTransfer - allocateTDs returned error %x", getName(), this, (UInt32)status); } return status; } AppleEHCIQueueHead * AppleUSBEHCI::FindControlBulkEndpoint ( short functionNumber, short endpointNumber, AppleEHCIQueueHead * *pEDBack, short direction) { UInt32 unique; AppleEHCIQueueHead * pEDQueue; AppleEHCIQueueHead * pEDQueueBack; short EDDirection; // search for endpoint descriptor unique = (UInt32) ((((UInt32) endpointNumber) << kEHCIEDFlags_ENPhase) | ((UInt32) functionNumber)); pEDQueue = _AsyncHead; pEDQueueBack = NULL; if(pEDQueue == NULL) { USBLog(7, "%s[%p]::FindControlBulkEndpoint - pEDQueue is NULL", getName(), this); return NULL; } do { EDDirection = pEDQueue->_direction; if( ( (USBToHostLong(pEDQueue->GetSharedLogical()->flags) & kUniqueNumNoDirMask) == unique) && ( ((EDDirection == kEHCIEDDirectionTD) || (EDDirection) == direction)) ) { if (pEDBack) *pEDBack = pEDQueueBack; return pEDQueue; } else { pEDQueueBack = pEDQueue; pEDQueue = OSDynamicCast(AppleEHCIQueueHead, pEDQueue->_logicalNext); } } while (pEDQueue != NULL); return NULL; } IOReturn AppleUSBEHCI::allocateTDs(AppleEHCIQueueHead* pEDQueue, IOUSBCommand *command, IOMemoryDescriptor* CBP, UInt32 bufferSize, UInt16 direction, Boolean controlTransaction) { EHCIGeneralTransferDescriptorPtr pTD1, pTD, pTDnew, pTDLast; UInt32 myToggle = 0; UInt32 myDirection = 0; IOByteCount transferOffset; UInt32 pageCount; UInt32 flags; IOReturn status = kIOReturnSuccess; UInt32 maxPacket; UInt32 bytesThisTD, segment; UInt32 curTDsegment; UInt32 totalPhysLength; IOPhysicalAddress dmaStartAddr; UInt32 dmaStartOffset; UInt32 bytesToSchedule; bool needNewTD = false; UInt32 maxTDLength; /* *********** Note: Always put the flags in the TD last. ************** */ /* *********** This is what kicks off the transaction if ************** */ /* *********** the next and alt pointers are not set up ************** */ /* *********** then the controller will pick up and cache ************** */ /* *********** crap for the TD. ************** */ if(controlTransaction) { myToggle = 0; // Use data0 for setup if(direction != kEHCIEDDirectionTD) { // Setup uses Data 0, data & status use Data1 myToggle |= kEHCITDFlags_DT; // use Data1 } } myDirection = (UInt32) direction << kEHCITDFlags_PIDPhase; maxPacket = (USBToHostLong(pEDQueue->GetSharedLogical()->flags) & kEHCIEDFlags_MPS) >> kEHCIEDFlags_MPSPhase; // Slight change of plan. // I was filling in the tail, then pointing that to a new TD which then // became the tail. Repeat until entire buffer is covered. // The problem with that is AltNextqTDptr. I was leaving this as invalid // in the theory that a short packet would halt the queue and it would // then get kicked back into life by scavengeAnEnpoint. It was abit tricky // to make it point to the new tail TD when you don't know where that's // going to be in a multi TD situation. // New plan. Allocate all the TDs in a chain unlinked to the endpoint // then link them in by copying the data from the first new TD to the // tail TD, then making the first new TD the tail TD. All along // AltNextqTDs will be pointed to this first new TD (qTD1), // Its easy to point to something when you know where it is. // First allocate the first of the new bunch pTD1 = AllocateTD(); if (pTD1 == NULL) { USBError(1, "%s[%p]::allocateTDs can't allocate 1st new TD", getName(), this); return kIOReturnNoMemory; } pTD = pTD1; // We'll be working with pTD if (bufferSize != 0) { transferOffset = 0; curTDsegment = 0; bytesThisTD = 0; while (transferOffset < bufferSize) { // first, calculate the maximum possible transfer of the given segment. note that this was already checked for // being disjoint, so we don't have to worry about that. dmaStartAddr = CBP->getPhysicalSegment(transferOffset, &totalPhysLength); USBLog(7, "%s[%p]::allocateTDs - getPhysicalSegment returned length of %d (out of %d) and start of %p", getName(), this, totalPhysLength, bufferSize, dmaStartAddr); dmaStartOffset = (dmaStartAddr & (kEHCIPageSize-1)); bytesToSchedule = 0; // only the first segment can start on a non page boundary if ((curTDsegment == 0) || (dmaStartOffset == 0)) { needNewTD = false; if (totalPhysLength > bufferSize) { USBLog(4, "%s[%p]::allocateTDs - segment physical length > buffer size - truncating", getName(), this); totalPhysLength = bufferSize; } // each TD can transfer at most four full pages plus from the initial offset to the end of the first page maxTDLength = ((kEHCIPagesPerTD-curTDsegment) * kEHCIPageSize) - dmaStartOffset; if (totalPhysLength > maxTDLength) { if ((curTDsegment == 0) && (dmaStartOffset != 0)) { // truncate this TD to exactly 4 pages, which will always be a multiple of MPS USBLog(7, "%s[%p]::allocateTDs - segment won't fit - using 4 pages for this TD", getName(), this); bytesToSchedule = (kEHCIPagesPerTD-1) * kEHCIPageSize; } else { // truncate this TD to however many pages are left, which will always be a multiple of MPS USBLog(7, "%s[%p]::allocateTDs - segment is larger than 1 TD - using %d pages for this TD", getName(), this, kEHCIPagesPerTD - curTDsegment); bytesToSchedule = (kEHCIPagesPerTD - curTDsegment) * kEHCIPageSize; } } else bytesToSchedule = totalPhysLength; bytesThisTD += bytesToSchedule; transferOffset += bytesToSchedule; // If our transfer for this TD does not end on a page boundary, we need to close the TD // if ( (transferOffset < bufferSize) && ((dmaStartOffset+bytesToSchedule) & kEHCIPageOffsetMask) ) { USBLog(6, "%s[%p]::allocateTDs - non-last transfer didn't end at end of page (%d, %d)", getName(), this, dmaStartOffset+bytesToSchedule); needNewTD = true; } // now schedule all of the bytes I just discovered while (bytesToSchedule) { pTD->pShared->BuffPtr[curTDsegment++] = HostToUSBLong(dmaStartAddr); dmaStartAddr += (kEHCIPageSize - dmaStartOffset); if (bytesToSchedule > (kEHCIPageSize - dmaStartOffset)) bytesToSchedule -= (kEHCIPageSize - dmaStartOffset); else bytesToSchedule = 0; dmaStartOffset = 0; } if ( ((curTDsegment < kEHCIPagesPerTD) && (transferOffset < bufferSize)) && !needNewTD ) { USBLog(7, "%s[%p]::allocateTDs - didn't fill up this TD (segment %d) - going back for more", getName(), this, curTDsegment); continue; } } flags = kEHCITDioc; // Want to interrupt on completion USBLog(7, "%s[%p]::allocateTDs - i have %d bytes in %d segments", getName(), this, bytesThisTD, curTDsegment); for (segment = 0; segment < curTDsegment; segment++) { USBLog(7, "%s[%p]::allocateTDs - addr[%d]:%p", getName(), this, segment, USBToHostLong(pTD->pShared->BuffPtr[segment])); } flags |= (bytesThisTD << kEHCITDFlags_BytesPhase); pTD->pShared->altTD = HostToUSBLong(pTD1->pPhysical); // point alt to first TD, will be fixed up later pTD->pType = (UInt32) kEHCIBulkTransferOutType; flags |= myDirection | myToggle | kEHCITDStatus_Active | (3<traceFlag = trace; pTD->traceFlag = false; pTD->pQH = pEDQueue; // only supply a callback when the entire buffer has been // transfered. USBLog(7, "%s[%p]::allocateTDs - putting command into TD (%p) on ED (%p)", getName(), this, pTD, pEDQueue); pTD->command = command; // Do like OHCI, link to command from each TD if (transferOffset >= bufferSize) { myToggle = 0; // Only set toggle on first TD pTD->lastTDofTransaction = true; pTD->logicalBuffer = CBP; pTD->pShared->flags = HostToUSBLong(flags); //if(trace)printTD(pTD); } else { pTD->lastTDofTransaction = false; myToggle = 0; // Only set toggle on first TD pTDnew = AllocateTD(); if (pTDnew == NULL) { status = kIOReturnNoMemory; USBError(1, "%s[%p]::allocateTDs can't allocate new TD", getName(), this); } else { pTD->pShared->nextTD = HostToUSBLong(pTDnew->pPhysical); pTD->pLogicalNext = pTDnew; pTD->pShared->flags = HostToUSBLong(flags); // Doesn't matter about flags, not linked in yet // if(trace)printTD(pTD); pTD = pTDnew; curTDsegment = 0; bytesThisTD = 0; USBLog(7, "%s[%p]::allocateTDs - got another TD - going to fill it up too (%d, %d)", getName(), this, transferOffset, bufferSize); } } } } else { // no buffer to transfer pTD->pShared->altTD = HostToUSBLong(pTD1->pPhysical); // point alt to first TD, will be fixed up later USBLog(7, "%s[%p]::allocateTDs - (no buffer)- putting command into TD (%p) on ED (%p)", getName(), this, pTD, pEDQueue); pTD->command = command; pTD->lastTDofTransaction = true; pTD->logicalBuffer = CBP; // pTD->traceFlag = trace; pTD->traceFlag = false; pTD->pQH = pEDQueue; flags = kEHCITDioc | kEHCITDioc | myDirection | myToggle | kEHCITDStatus_Active | (3<pShared->flags = HostToUSBLong(flags); } pTDLast = pEDQueue->_TailTD; pTD->pShared->nextTD = HostToUSBLong(pTD1->pPhysical); pTD->pLogicalNext = pTD1; // We now have a new chain of TDs. link it in. // pTD1, pointer to first TD // pTD, pointer to last TD // pTDLast is last currently on endpoint, will be made first flags = pTD1->pShared->flags; pTD1->pShared->flags = 0; // Copy contents of first TD to old tail pTDLast->pShared->nextTD = pTD1->pShared->nextTD; pTDLast->pShared->altTD = pTD1->pShared->altTD; pTDLast->pShared->flags = 0; pTDLast->pShared->BuffPtr[0] = pTD1->pShared->BuffPtr[0]; pTDLast->pShared->BuffPtr[1] = pTD1->pShared->BuffPtr[1]; pTDLast->pShared->BuffPtr[2] = pTD1->pShared->BuffPtr[2]; pTDLast->pShared->BuffPtr[3] = pTD1->pShared->BuffPtr[3]; pTDLast->pShared->BuffPtr[4] = pTD1->pShared->BuffPtr[4]; pTDLast->pQH = pTD1->pQH; USBLog(7, "%s[%p]::allocateTDs - transfering command from TD (%p) to TD (%p)", getName(), this, pTD1, pTDLast); pTDLast->command = pTD1->command; pTDLast->lastTDofTransaction = pTD1->lastTDofTransaction; //pTDLast->bufferSize = pTD1->bufferSize; pTDLast->traceFlag = pTD1->traceFlag; pTDLast->pType = pTD1->pType; pTDLast->pLogicalNext = pTD1->pLogicalNext; pTDLast->logicalBuffer = pTD1->logicalBuffer; // Note not copying // flags // unused // index (pertains to TD) // pPhysical (pretains to TD) // Point end of new TDs to first TD, now new tail pTD->pShared->nextTD = HostToUSBLong(pTD1->pPhysical); pTD->pLogicalNext = pTD1; // squash pointers on new tail pTD1->pShared->nextTD = HostToUSBLong(kEHCITermFlag); pTD1->pShared->altTD = HostToUSBLong(kEHCITermFlag); pTD1->pLogicalNext = 0; USBLog(7, "%s[%p]::allocateTDs - zeroing out command in TD (%p)", getName(), this, pTD1); pTD1->command = NULL; // This is (of course) copied from the 9 UIM. It has this cryptic note. // **** THIS NEEDS TO BE CHANGED, SEE NOTE // we have good status, so let's kick off the machine // Make new descriptor the tail pEDQueue->_TailTD = pTD1; pTDLast->pShared->flags = flags; if (status) { USBLog(3, "%s[%p] CreateGeneralTransfer: returning status 0x%x", getName(), this, status); } return status; } static UInt32 mungeECHIStatus(UInt32 flags, EHCIGeneralTransferDescriptorPtr pHCDoneTD) { AppleEHCIQueueHead * pEndpoint; #if 0 /* Mail from John Howard */ Active CErr Halted XactErr Bytes2Xfer -- These will be no error, halted not set 0 >0 0 0 0 normal (nc) 0 >0 0 0 >0 short packet (sp) 0 >0 0 1 >=0 (nc or sp) with one or more retries for XactErrors detected -- These will be stalled, CErr still has some left 0 >0 1 0 >0 assuming no other status bits are set, this was a STALL response. Bytes2Xfer is a don''t care. 0 >0 1 1 >0 STALL response (same assumption) during some bus transaction during the buffer a timeout, etc. was encountered -- Finally we have misc transaction error. 0 0 1 1 >=0 three consecutive bus transaction errors (any of bad pid, timeout, data crc, etc.) Also: If a transaction transaltor gets an error on an Int or Isoc it returns an Err handshake. Then you find: Ping State/Err == 1 If a TT gets 3 errors on a control or bulk transaction you get a STALL from the TT. Ugh! how do you cope with that. #endif pEndpoint = pHCDoneTD->pQH; if((flags & kEHCITDStatus_Halted) == 0) { // transaction completed sucessfully // Clear error state flag pEndpoint->_responseToStall = 0; return kIOReturnSuccess; } if( (flags & kEHCITDStatus_BuffErr) != 0) { // Buffer over or under run error - i.e. the controller could not keep up from or to system memory if( (flags & kEHCITDFlags_PID) == (1 << kEHCITDFlags_PIDPhase) ) { // An in token return kOHCIITDConditionBufferOverrun; } else { // OUT/Status, data going out return kOHCIITDConditionBufferUnderrun; } } if ( (flags & kEHCITDStatus_Babble) != 0) { // Babble means that we had a data overrun // Buffer over or under run error if( (flags & kEHCITDFlags_PID) == (1 << kEHCITDFlags_PIDPhase) ) { // An in token return kOHCIITDConditionDataOverrun; } // for out token, we let the other error processing handle it, since the data over/underrun conditions are IN only } if( (flags & kEHCITDFlags_Cerr) != 0) { // A STALL // Check endpoint to see if we need to send a not responding instead if( ((USBToHostLong(pEndpoint->GetSharedLogical()->flags) & kEHCIEDFlags_S) >> kEHCIEDFlags_SPhase) != 2) { // A full/low speed transaction, must be behind a transaction translator if( (USBToHostLong(pEndpoint->GetSharedLogical()->splitFlags) & kEHCIEDSplitFlags_SMask) == 0) { // Not an Int transaction // Commute first and every other STALL to a not responding, you can't tell the differernce // according to the 2.0 spec where it goes completely off the rails. // See section 11.17.1 and appendicies A-14 and A-23 pEndpoint->_responseToStall = 1 - pEndpoint->_responseToStall; // Switch 0 - 1 if(pEndpoint->_responseToStall == 1) { return kOHCIITDConditionStall; } else { return kOHCIITDConditionDeviceNotResponding; } } } return kOHCIITDConditionStall; } if( (flags & kEHCITDStatus_XactErr) != 0) { if( ((USBToHostLong(pEndpoint->GetSharedLogical()->flags) & kEHCIEDFlags_S) >> kEHCIEDFlags_SPhase) != 2) { // A split transaction return(kIOUSBHighSpeedSplitError); } return kOHCIITDConditionDeviceNotResponding; // Can't tell this from any other transaction err } if( (flags & kEHCITDStatus_PingState) != 0) // Int/Isoc gave error to transaction translator { return kOHCIITDConditionDeviceNotResponding; } USBLog(1, "mungeECHIStatus condition we're not expecting %x", flags); return kOHCIITDConditionCRC; } IOReturn TranslateStatusToUSBError(UInt32 status) { static const UInt32 statusToErrorMap[] = { /* OHCI Error */ /* USB Error */ /* 0 */ kIOReturnSuccess, /* 1 */ kIOUSBCRCErr, /* 2 */ kIOUSBBitstufErr, /* 3 */ kIOUSBDataToggleErr, /* 4 */ kIOUSBPipeStalled, /* 5 */ kIOReturnNotResponding, /* 6 */ kIOUSBPIDCheckErr, /* 7 */ kIOUSBWrongPIDErr, /* 8 */ kIOReturnOverrun, /* 9 */ kIOReturnUnderrun, /* 10 */ kIOUSBReserved1Err, /* 11 */ kIOUSBReserved2Err, /* 12 */ kIOUSBBufferOverrunErr, /* 13 */ kIOUSBBufferUnderrunErr, /* 14 */ kIOUSBNotSent1Err, /* 15 */ kIOUSBNotSent2Err }; if (status > 15) { if(status == (UInt32) kIOUSBHighSpeedSplitError) { return(kIOUSBHighSpeedSplitError); } return kIOReturnInternalError; } return statusToErrorMap[status]; } IOReturn AppleUSBEHCI::EHCIUIMDoDoneQueueProcessing(EHCIGeneralTransferDescriptorPtr pHCDoneTD, OSStatus forceErr, IOUSBCompletionAction safeAction, EHCIGeneralTransferDescriptorPtr stopAt) { UInt32 flags, transferStatus; UInt32 bufferSizeRemaining = 0; EHCIGeneralTransferDescriptorPtr nextTD; OSStatus accumErr = kIOReturnSuccess; USBLog(7, "+%s[%p]::EHCIUIMDoDoneQueueProcessing", getName(), this); while (pHCDoneTD != NULL) { IOReturn errStatus; if(pHCDoneTD == stopAt) { // Don't process this one or any further USBLog(5, "%s[%p]::EHCIUIMDoDoneQueueProcessing stop at %p",getName(), this, pHCDoneTD); break; } nextTD = (EHCIGeneralTransferDescriptorPtr)pHCDoneTD->pLogicalNext; flags = USBToHostLong(pHCDoneTD->pShared->flags); if (forceErr != kIOReturnSuccess) { errStatus = forceErr; } else if (accumErr != kIOReturnSuccess) { errStatus = accumErr; } else { transferStatus = mungeECHIStatus(flags, pHCDoneTD); // forceErr = transferStatus; // Give this to the whole transaction if (transferStatus) { USBLog(4, "%s[%p]::EHCIUIMDoDoneQueueProcessing - TD (%p) - got transferStatus %p with flags (%p)", getName(), this, pHCDoneTD, transferStatus, flags); } errStatus = TranslateStatusToUSBError(transferStatus); accumErr = errStatus; if (errStatus) { USBLog(4, "%s[%p]::EHCIUIMDoDoneQueueProcessing - got errror %p", getName(), this, errStatus); } } if (pHCDoneTD->pType == kEHCIIsochronousType) { USBError (1, "%s[%p]::EHCIUIMDoDoneQueueProcessing we're not expecting this (pHCDoneTD->pType == kEHCIIsochronousType)", getName(), this); // cast to a isoc type //pITD = (OHCIIsochTransferDescriptorPtr) pHCDoneTD; //ProcessCompletedITD(pITD, errStatus); // deallocate td //DeallocateITD(pITD); } else { bufferSizeRemaining += (flags & kEHCITDFlags_Bytes) >> kEHCITDFlags_BytesPhase; if (pHCDoneTD->lastTDofTransaction) { if ( pHCDoneTD->command == NULL ) { // IOPanic("pHCDoneTD->command is NULL in EHCIUIMDoneQueueProcessing"); USBError (1, "%s[%p]::EHCIUIMDoDoneQueueProcessing pHCDoneTD->command is NULL (%p)", getName(), this, pHCDoneTD); } else { IOUSBCompletion completion = pHCDoneTD->command->GetUSLCompletion(); if(!safeAction || (safeAction == completion.action)) { // remove flag before completing pHCDoneTD->lastTDofTransaction = 0; Complete(completion, errStatus, bufferSizeRemaining); bufferSizeRemaining = 0; // So next transaction starts afresh. accumErr = kIOReturnSuccess; } else { USBError(1, "The EHCI driver has detected an error [safeAction != NULL]"); } } } pHCDoneTD->logicalBuffer = NULL; USBLog(7, "%s[%p]::EHCIUIMDoDoneQueueProcessing - deallocating TD (%p)", getName(), this, pHCDoneTD); DeallocateTD(pHCDoneTD); } pHCDoneTD = nextTD; // New qHead } USBLog(7, "-%s[%p]::EHCIUIMDoDoneQueueProcessing", getName(), this); return(kIOReturnSuccess); } IOReturn AppleUSBEHCI::scavengeIsocTransactions(IOUSBCompletionAction safeAction) { AppleEHCIIsochListElement *pDoneEl; UInt32 cachedProducer; UInt32 cachedConsumer; AppleEHCIIsochEndpointPtr pEP; AppleEHCIIsochListElement *prevEl; AppleEHCIIsochListElement *nextEl; IOInterruptState intState; // Get the values of the Done Queue Head and the producer count. We use a lock and disable interrupts // so that the filter routine does not preempt us and updates the values while we're trying to read them. // intState = IOSimpleLockLockDisableInterrupt( _wdhLock ); pDoneEl = (AppleEHCIIsochListElement*)_savedDoneQueueHead; cachedProducer = _producerCount; IOSimpleLockUnlockEnableInterrupt( _wdhLock, intState ); cachedConsumer = _consumerCount; if (pDoneEl && (cachedConsumer != cachedProducer)) { // there is real work to do - first reverse the list prevEl = NULL; USBLog(7, "%s[%p]::scavengeIsocTransactions - before reversal, cachedConsumer = 0x%x", getName(), this, cachedConsumer); while (true) { pDoneEl->_logicalNext = prevEl; prevEl = pDoneEl; cachedConsumer++; if ( cachedProducer == cachedConsumer) break; pDoneEl = pDoneEl->_doneQueueLink; } // update the consumer count _consumerCount = cachedConsumer; USBLog(7, "%s[%p]::scavengeIsocTransactions - after reversal, cachedConsumer = 0x%x", getName(), this, cachedConsumer); // now cachedDoneQueueHead points to the head of the done queue in the right order while (pDoneEl) { nextEl = OSDynamicCast(AppleEHCIIsochListElement, pDoneEl->_logicalNext); pDoneEl->_logicalNext = NULL; USBLog(7, "%s[%p]::scavengeIsocTransactions - about to scavenge TD %p", getName(), this, pDoneEl); scavengeAnIsocTD(pDoneEl, safeAction); pDoneEl = nextEl; } } pEP = _isochEPList; while (pEP) { AddIsocFramesToSchedule(pEP); pEP = pEP->nextEP; } return kIOReturnSuccess; } IOReturn AppleUSBEHCI::scavengeAnIsocTD(AppleEHCIIsochListElement *pTD, IOUSBCompletionAction safeAction) { AppleEHCIIsochEndpointPtr pEP; IOReturn ret; AbsoluteTime timeStamp; pEP = pTD->_pEndpoint; clock_get_uptime(&timeStamp); if(pEP == NULL) { USBError(1, "%s[%p]::scavengeAnIsocEndPoint - could not find endpoint associated with iTD (%p)", getName(), this, pTD->_pEndpoint); } else { if (!pTD->_lowLatency) ret = pTD->UpdateFrameList(timeStamp); // TODO - accumulate the return values pEP->scheduledTDs--; PutTDonDoneQueue(pEP, pTD, true); } if(pTD->_completion.action != NULL) { ReturnIsocDoneQueue(pEP); } return(kIOReturnSuccess); } void AppleUSBEHCI::PutTDonToDoList(AppleEHCIIsochEndpointPtr pED, AppleEHCIIsochListElement *pTD) { USBLog(7, "%s[%p]::PutTDonToDoList - pED (%p) pTD (%p) frameNumber(%Lx)", getName(), this, pED, pTD, pTD->_frameNumber); // Link TD into todo list if(pED->toDoList == NULL) { // as the head of a new list pED->toDoList = pTD; } else { // at the tail of the old list pED->toDoEnd->_logicalNext = pTD; } // no matter what we are the new tail pED->toDoEnd = pTD; pED->activeTDs++; } AppleEHCIIsochListElement * AppleUSBEHCI::GetTDfromToDoList(AppleEHCIIsochEndpointPtr pED) { AppleEHCIIsochListElement *pTD; pTD = pED->toDoList; if (pTD) { if (pTD == pED->toDoEnd) pED->toDoList = pED->toDoEnd = NULL; else pED->toDoList = OSDynamicCast(AppleEHCIIsochListElement, pTD->_logicalNext); USBLog(7, "%s[%p]::GetTDfromToDoList - pED (%p) pTD (%p) frameNumber(%Lx)", getName(), this, pED, pTD, pTD->_frameNumber); } return pTD; } void AppleUSBEHCI::PutTDonDoneQueue(AppleEHCIIsochEndpointPtr pED, AppleEHCIIsochListElement *pTD, bool checkDeferred) { AppleEHCIIsochListElement *deferredTD; if (checkDeferred) { while (pED->deferredQueue && (pED->deferredQueue->_frameNumber < pTD->_frameNumber)) { deferredTD = GetTDfromDeferredQueue(pED); PutTDonDoneQueue(pED, deferredTD, false); } } if(pED->doneQueue == NULL) { // as the head of a new list pED->doneQueue = pTD; } else { // at the tail of the old list pED->doneEnd->_logicalNext = pTD; } // and not matter what we are now the new tail pED->doneEnd = pTD; } AppleEHCIIsochListElement * AppleUSBEHCI::GetTDfromDoneQueue(AppleEHCIIsochEndpointPtr pED) { AppleEHCIIsochListElement *pTD; pTD = pED->doneQueue; if (pTD) { if (pTD == pED->doneEnd) pED->doneQueue = pED->doneEnd = NULL; else pED->doneQueue = OSDynamicCast(AppleEHCIIsochListElement, pTD->_logicalNext); pED->activeTDs--; } return pTD; } void AppleUSBEHCI::PutTDonDeferredQueue(AppleEHCIIsochEndpointPtr pED, AppleEHCIIsochListElement *pTD) { USBLog(7, "%s[%p]::PutTDonDeferredQueue(%p, %p)", getName(), this, pED, pTD); if(pED->deferredQueue == NULL) { // as the head of a new list pED->deferredQueue = pTD; } else { // at the tail of the old list pED->deferredEnd->_logicalNext = pTD; } // and not matter what we are now the new tail pED->deferredEnd = pTD; } AppleEHCIIsochListElement * AppleUSBEHCI::GetTDfromDeferredQueue(AppleEHCIIsochEndpointPtr pED) { AppleEHCIIsochListElement *pTD; pTD = pED->deferredQueue; if (pTD) { if (pTD == pED->deferredEnd) pED->deferredQueue = pED->deferredEnd = NULL; else pED->deferredQueue = OSDynamicCast(AppleEHCIIsochListElement, pTD->_logicalNext); } USBLog(7, "%s[%p]::GetTDfromDeferredQueue(%p) returning %p", getName(), this, pED, pTD); return pTD; } IOReturn AppleUSBEHCI::scavengeAnEndpointQueue(AppleEHCIListElement *pListElem, IOUSBCompletionAction safeAction) { EHCIGeneralTransferDescriptorPtr doneQueue = NULL, doneTail= NULL, qHead, qTD, qEnd; UInt32 flags, count = 0; Boolean TDisHalted, shortTransfer; AppleEHCIQueueHead *pQH; while( (pListElem != NULL) && (count++ < 150000) ) { pQH = OSDynamicCast(AppleEHCIQueueHead, pListElem); if(pQH) { qTD = qHead = pQH->_qTD; qEnd = pQH->_TailTD; if (((qTD == NULL) || (qEnd == NULL)) && (qTD != qEnd)) { USBError(1, "The EHCI driver found a device queue with invalid head (%p) or tail (%p) - flags %p", qTD, qEnd, pQH->GetSharedLogical()->flags); } TDisHalted = false; shortTransfer = false; while( qTD && (qTD != qEnd) && (count++ < 150000) ) { // This end point has transactions flags = USBToHostLong(qTD->pShared->flags); if(!TDisHalted && !shortTransfer) { if((flags & kEHCITDStatus_Active) != 0) { // Command is still alive, go to next queue break; } // check for halted TDisHalted = ((flags & kEHCITDStatus_Halted) != 0) ; if (!TDisHalted) { // this TD is not active, and was not halted, so check to see if it was short // if so - we can ignore that state of the remaining TDs until the lastTD // since the harwdare skipped them shortTransfer = ((flags & kEHCITDFlags_Bytes) >> kEHCITDFlags_BytesPhase) ? true : false; } } if(qTD->lastTDofTransaction) { // We have the complete command USBLog(7, "%s[%p]::scavengeAnEndpointQueue - TD (%p) on ED (%p)", getName(), this, qTD, pQH); if(doneQueue == NULL) { doneQueue = qHead; } else { doneTail->pLogicalNext = qHead; } doneTail = qTD; qTD = qTD->pLogicalNext; qHead = qTD; doneTail->pLogicalNext = NULL; pQH->_qTD = qTD; if (qTD == NULL) { USBError(1, "The EHCI driver found a NULL Transfer Descriptor - Queue flags %p", pQH->GetSharedLogical()->flags); break; } if(TDisHalted) { flags = USBToHostLong(qTD->pShared->flags); pQH->GetSharedLogical()->NextqTDPtr = HostToUSBLong( qTD->pPhysical); if((flags & kEHCITDStatus_Active) != 0) // Next command is still active { break; } } // Reset our loop variables // TDisHalted = false; shortTransfer = false; } else { USBLog(7, "%s[%p]::scavengeAnEndpointQueue - looking past TD (%p) on ED (%p)", getName(), this, qTD, pQH); qTD = qTD->pLogicalNext; if (qTD == NULL) { USBError(1, "The EHCI driver found a NULL Transfer Descriptor - Queue flags %p", pQH->GetSharedLogical()->flags); break; } } } } pListElem = pListElem->_logicalNext; } if(doneQueue != NULL) { EHCIUIMDoDoneQueueProcessing(doneQueue, kIOReturnSuccess, safeAction, NULL); } if(count > 1000) { USBLog(1, "%s[%p]::scavengeAnEndpointQueue looks like bad ed queue %lx", getName(), this, count); } return kIOReturnSuccess; } void AppleUSBEHCI::scavengeCompletedTransactions(IOUSBCompletionAction safeAction) { IOReturn err, err1; int i; safeAction = 0; err = scavengeIsocTransactions(safeAction); if(err != kIOReturnSuccess) { USBLog(1, "%s[%p]::scavengeCompletedTransactions err isoch list %x", getName(), this, err); } if ( _AsyncHead != NULL ) { err = scavengeAnEndpointQueue(_AsyncHead, safeAction); if(err != kIOReturnSuccess) { USBLog(1, "%s[%p]::scavengeCompletedTransactions err async queue %x", getName(), this, err); } } if ( _logicalPeriodicList != NULL ) { for(i= 0; iGetUSLCompletion(); USBLog(7, "%s[%p]::UIMCreateControlTransfer - CrntlTx: adr=%d:%d cbp=%lx:%lx br=%s cback=[%lx:%lx] dir=%d)", getName(), this, functionAddress, endpointNumber, (UInt32)CBP, bufferSize, bufferRounding?"YES":"NO", (UInt32)completion.target, (UInt32)completion.parameter, direction); if (direction == kUSBOut) descDirection = kIODirectionOut; else if (direction == kUSBIn) descDirection = kIODirectionIn; else descDirection = kIODirectionOut; if(bufferSize != 0) { desc = IOMemoryDescriptor::withAddress(CBP, bufferSize, descDirection); if(!desc) { USBError(1, "%s[%p]::UIMCreateControlTransfer - IOMemoryDescriptor::withAddress failed", getName(), this); return kIOReturnNoMemory; } } USBLog(7, "%s[%p]::UIMCreateControlTransfer - calling UIMCreateControlTransfer0 with mem desc", getName(), this); status = UIMCreateControlTransfer(functionAddress, endpointNumber, command, desc, bufferRounding, bufferSize, direction); if(desc) desc->release(); return status; } IOReturn AppleUSBEHCI::UIMCreateControlTransfer( short functionAddress, short endpointNumber, IOUSBCompletion completion, IOMemoryDescriptor* CBP, bool bufferRounding, UInt32 bufferSize, short direction) { USBLog(1, "%s[%p] UIMCreateControlTransfer- calling the wrong method!", getName(), this); return kIOReturnIPCError; } IOReturn AppleUSBEHCI::UIMCreateBulkEndpoint( UInt8 functionAddress, UInt8 endpointNumber, UInt8 direction, UInt8 speed, UInt8 maxPacketSize) { USBLog(1, "%s[%p] UIMCreateBulkEndpoint- calling the wrong method!", getName(), this); return kIOReturnInternalError; } IOReturn AppleUSBEHCI::UIMCreateBulkEndpoint( UInt8 functionAddress, UInt8 endpointNumber, UInt8 direction, UInt8 speed, UInt16 maxPacketSize, USBDeviceAddress highSpeedHub, int highSpeedPort) { AppleEHCIQueueHead *pEHCIEndpointDescriptor; AppleEHCIQueueHead *pED; USBLog(7, "%s[%p]::UIMCreateBulkEndpoint(adr=%d:%d, max=%d, dir=%d)", getName(), this, functionAddress, endpointNumber, maxPacketSize, direction); if( (direction != kUSBOut) && (direction != kUSBIn) ) { USBLog(1, "%s[%p]::UIMCreateBulkEndpoint - wrong direction %d", getName(), this, direction); return kIOReturnBadArgument; } if(highSpeedHub == 0) speed = kUSBDeviceSpeedHigh; else speed = kUSBDeviceSpeedFull; pED = _AsyncHead; if ((speed == kUSBDeviceSpeedHigh) && (maxPacketSize != 512)) { // This shouldn't happen any more, this has been fixed. USBError(1, "%s[%p]::UIMCreateBulkEndpoint: USB 2.0 Spec (5.8.3) converting MPS from %d to 512", getName(), this, maxPacketSize); maxPacketSize = 512; } pEHCIEndpointDescriptor = AddEmptyCBEndPoint(functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort, direction, pED); if (pEHCIEndpointDescriptor == NULL) return kIOReturnNoResources; if(pED == NULL) { _AsyncHead = pEHCIEndpointDescriptor; _pEHCIRegisters->AsyncListAddr = USBToHostLong( pEHCIEndpointDescriptor->_sharedPhysical ); } return kIOReturnSuccess; } IOReturn AppleUSBEHCI::UIMCreateBulkTransfer(IOUSBCommand* command) { AppleEHCIQueueHead *pEDQueue; AppleEHCIQueueHead *pEDDummy; IOReturn status; IOMemoryDescriptor* buffer = command->GetBuffer(); short direction = command->GetDirection(); USBLog(7, "%s[%p]::UIMCreateBulkTransfer - adr=%d:%d cbp=%lx:%x cback=[%lx:%lx:%lx] dir=%d)", getName(), this, command->GetAddress(), command->GetEndpoint(), (UInt32)buffer, (int)command->GetReqCount(), (UInt32)command->GetUSLCompletion().action, (UInt32)command->GetUSLCompletion().target, (UInt32)command->GetUSLCompletion().parameter, direction); pEDQueue = FindControlBulkEndpoint(command->GetAddress(), command->GetEndpoint(), &pEDDummy, direction); if (pEDQueue == NULL) { USBLog(3, "%s[%p]::UIMCreateControlTransfer- Could not find endpoint!", getName(), this); return kIOUSBEndpointNotFound; } status = allocateTDs(pEDQueue, command, buffer, command->GetReqCount(), direction, false ); if(status == kIOReturnSuccess) EnableAsyncSchedule(); else { USBLog(1, "%s[%p]::UIMCreateControlTransfer- allocateTDs returned error %x", getName(), this, (UInt32)status); } return status; } IOReturn AppleUSBEHCI::UIMCreateBulkTransfer( short functionAddress, short endpointNumber, IOUSBCompletion completion, IOMemoryDescriptor * CBP, bool bufferRounding, UInt32 bufferSize, short direction) { // this is the old 1.8/1.8.1 method. It should not be used any more. USBLog(1, "%s(%p)UIMCreateBulkTransfer- calling the wrong method!", getName(), this); return kIOReturnIPCError; } UInt16 AppleUSBEHCI::validatePollingRate(short rawPollingRate, short speed, int *offset, UInt16 *bytesAvailable) { UInt16 pollingRate; int i; UInt16 availableBandwidth[kEHCIMaxPoll]; pollingRate = rawPollingRate; if(speed == kUSBDeviceSpeedHigh) { USBLog(5,"%s[%p]::validatePollingRate HS pollingRate raw: %d", getName(), this, pollingRate); if(pollingRate <= 3) { // this would be a sub frame polling rate (per microframe) and we will only do frame resolution for now pollingRate = 1; } else { if(pollingRate > 16) { int count = 0; // this is an illegal polling rate. however, some HS devices have it // so we will treat this like an OLD polling rate, i.e. a # of ms to poll at // we need to find the lowest power of 2 to accomodate this, just like FS USBLog(1,"%s[%p]::validatePollingRate: illegal HS polling rate (%d), cooking like FS", getName(), this, pollingRate); while( (pollingRate >> count) != 1) count++; pollingRate = (1 << count); USBLog(1,"%s[%p]::validatePollingRate: (illegal)new cooked polling rate (%d)", getName(), this, pollingRate); } else { // the polling rate is in microframes and is the exponent in 2^(rate-1) pollingRate = 1 << (pollingRate - 4); } } if(pollingRate > kEHCIMaxPoll) { pollingRate = kEHCIMaxPoll; } USBLog(5,"%s[%p]::validatePollingRate HS pollingRate cooked: %d", getName(), this, pollingRate); } else { // full/low speed device int count = 0; // pollingRate is good USBLog(5,"%s[%p]::validatePollingRate LS/FS pollingRate raw: %d", getName(), this, pollingRate); // Find the next lowest power of 2 for the polling rate while( (pollingRate >> count) != 1) count++; pollingRate = (1 << count); // The following is a fix for rdar://3645770. OHCI controllers had a limit on the polling rate that they poll at -- 32ms. Anything above that // would get clamped to 32 ms. The USB spec indicates that the controller should poll at the polling rate OR lower, so, for FS/LS devices // that are attached to a HS Hub, we are going to clamp the polling rate to 32ms so that those devices behave the same as when attached to // an OHCI controller. The following line used to be: if(pollingRate > 255) and then set the rate to 128. // if(pollingRate > 32) { USBLog(5,"%s[%p]::validatePollingRate - Oops LS/FS pollingRate too high: %d, setting to 128", getName(), this, pollingRate); pollingRate = 32; } else if( (pollingRate < 8) && (speed == kUSBDeviceSpeedLow) ) { USBLog(5,"%s[%p]::validatePollingRate Oops LS pollingRate too low: %d, setting to 8.", getName(), this, pollingRate); pollingRate = 8; } else if(pollingRate == 0) { USBLog(5,"%s[%p]::validatePollingRate Oops HS pollingRate too low: %d, setting to 1.", getName(), this, pollingRate); pollingRate = 1; } USBLog(5, "%s[%p]::validatePollingRate LS/FS pollingRate cooked: %d", getName(), this, pollingRate); } // now that we have the "cooked" polling rate, we need to "balance" the load // among the [kEHCIMaxPoll % pollingrate] nodes which might contain this endpoint for (i=0; i < pollingRate; i++) availableBandwidth[i] = kEHCIMaxPeriodicBandwidth; for (i=0; i < kEHCIMaxPoll; i++) if (availableBandwidth[i % pollingRate] > _periodicBandwidth[i]) availableBandwidth[i % pollingRate] = _periodicBandwidth[i]; *offset = 0; // start at 0 *bytesAvailable = 0; for (i=0; i < pollingRate; i++) if (availableBandwidth[i] > *bytesAvailable) { *offset = i; *bytesAvailable = availableBandwidth[i]; } return pollingRate; } static AppleEHCIListElement* FindIntEDqueue(AppleEHCIListElement *start, UInt8 pollingRate) { AppleEHCIQueueHead *pQH; AppleEHCIIsochListElement *pIsoch = OSDynamicCast(AppleEHCIIsochListElement, start); if (pIsoch) { // the list starts with some Isoch elements and we need to find the end of them while (OSDynamicCast(AppleEHCIIsochListElement, start->_logicalNext)) { pIsoch = OSDynamicCast(AppleEHCIIsochListElement, start->_logicalNext); start = start->_logicalNext; } // now pIsoch and start both point to the final Isoch element in the list start = start->_logicalNext; } // so either we went through the above loop or pIsoch is NULL. if start is also NULL, then we are // at a list which consists of 0 or more Isoch elements if (!start) return pIsoch; pQH = OSDynamicCast(AppleEHCIQueueHead, start); pollingRate--; if(pQH && (pQH->_pollM1 < pollingRate)) { return pIsoch; } while(start->_logicalNext != NULL) { pQH = OSDynamicCast(AppleEHCIQueueHead, pQH->_logicalNext); if(pQH && (pQH->_pollM1 < pollingRate)) { break; } start = start->_logicalNext; } return start; } void AppleUSBEHCI::linkInterruptEndpoint(AppleEHCIQueueHead *pEHCIEndpointDescriptor) { short pollingRate; UInt16 maxPacketSize; int offset; AppleEHCIListElement * pLE; UInt32 newHorizPtr; maxPacketSize = pEHCIEndpointDescriptor->_maxPacketSize; offset = pEHCIEndpointDescriptor->_offset; pollingRate = pEHCIEndpointDescriptor->_pollM1+1; newHorizPtr = HostToUSBLong( pEHCIEndpointDescriptor->GetPhysicalAddrWithType()); while( offset < kEHCIPeriodicListEntries) { pLE = FindIntEDqueue(_logicalPeriodicList[offset], pollingRate); if(pLE == NULL) { // Insert as first in queue if(pEHCIEndpointDescriptor->_logicalNext == NULL) { pEHCIEndpointDescriptor->_logicalNext = _logicalPeriodicList[offset]; pEHCIEndpointDescriptor->GetSharedLogical()->nextQH = _periodicList[offset]; } else { USBError(1, "%s[%p]::linkInterruptEndpoint(%p) - _logicalNext is not NULL! (%p)", getName(), this, pEHCIEndpointDescriptor, pEHCIEndpointDescriptor->_logicalNext); } _logicalPeriodicList[offset] = pEHCIEndpointDescriptor; _periodicList[offset] = newHorizPtr; USBLog(7, "%s[%p]::linkInterruptEndpoint - inserted at top of list %d - next logical (%p) next physical (%p)", getName(), this, offset, pEHCIEndpointDescriptor->_logicalNext, USBToHostLong(pEHCIEndpointDescriptor->GetSharedLogical()->nextQH)); } else if (pEHCIEndpointDescriptor != pLE) { // Insert in middle/end of queue if(pEHCIEndpointDescriptor->_logicalNext == NULL) // JRH - again - how could this not be NULL? { pEHCIEndpointDescriptor->_logicalNext = pLE->_logicalNext; pEHCIEndpointDescriptor->GetSharedLogical()->nextQH = pLE->GetPhysicalLink(); } else { USBError(1, "%s[%p]::linkInterruptEndpoint(%p) - _logicalNext is not NULL! (%p)", getName(), this, pEHCIEndpointDescriptor, pEHCIEndpointDescriptor->_logicalNext); } // Point queue element to new endpoint pLE->_logicalNext = pEHCIEndpointDescriptor; pLE->SetPhysicalLink(newHorizPtr); USBLog(7, "%s[%p]::linkInterruptEndpoint - inserted into list %d - next logical (%p) next physical (%p)", getName(), this, offset, pEHCIEndpointDescriptor->_logicalNext, USBToHostLong(pEHCIEndpointDescriptor->GetSharedLogical()->nextQH)); } else { // Else was already linked USBLog(7, "%s[%p]::linkInterruptEndpoint - (%p) already linked into %d (%p)", getName(), this, pEHCIEndpointDescriptor, offset, pLE); } // this gets repeated, but we only subtract up to MaxPoll if (offset < kEHCIMaxPoll) _periodicBandwidth[offset] -= maxPacketSize; offset += pollingRate; } _periodicEDsInSchedule++; } IOReturn AppleUSBEHCI::UIMCreateInterruptEndpoint( short functionAddress, short endpointNumber, UInt8 direction, short speed, UInt16 maxPacketSize, short pollingRate) { USBError(1, "%s[%p]::UIMCreateInterruptEndpoint - old version called with no split params"); return kIOReturnInternalError; } IOReturn AppleUSBEHCI::UIMCreateInterruptEndpoint( short functionAddress, short endpointNumber, UInt8 direction, short speed, UInt16 maxPacketSize, short pollingRate, USBDeviceAddress highSpeedHub, int highSpeedPort) { int offset; UInt16 availableBandwidth; AppleEHCIQueueHead * pEHCIEndpointDescriptor; AppleEHCIListElement * pLE; AppleEHCIListElement * temp; AppleUSBEHCIHubInfo * hiPtr = NULL; if (_rootHubFuncAddress == functionAddress) return kIOReturnSuccess; USBLog(7, "%s[%p]::UIMCreateInterruptEndpoint (%d, %d, %s, %d, %d)", getName(), this, functionAddress, endpointNumber, (speed == kUSBDeviceSpeedLow) ? "lo" : "full", maxPacketSize, pollingRate); if( (speed == kUSBDeviceSpeedLow) && (maxPacketSize > 8) ) { USBLog (1, "%s[%p]::UIMCreateInterruptEndpoint - incorrect max packet size (%d) for low speed", getName(), this, maxPacketSize); return kIOReturnBadArgument; } if(pollingRate == 0) return(kIOReturnBadArgument); // If the interrupt already exists, then we need to delete it first, as we're probably trying // to change the Polling interval via SetPipePolicy(). // pEHCIEndpointDescriptor = FindInterruptEndpoint(functionAddress, endpointNumber, direction, &temp); if ( pEHCIEndpointDescriptor != NULL ) { IOReturn ret; USBLog(3, "%s[%p]: UIMCreateInterruptEndpoint endpoint already existed -- deleting it",getName(), this); ret = UIMDeleteEndpoint(functionAddress, endpointNumber, direction); if ( ret != kIOReturnSuccess) { USBLog(3, "%s[%p]: UIMCreateInterruptEndpoint deleting endpoint returned %p",getName(), this, ret); return ret; } } else USBLog(3, "%s[%p]: UIMCreateInterruptEndpoint endpoint does NOT exist",getName(), this); // find my offset and check the HIGH SPEED bandwidth to the hub pollingRate = validatePollingRate(pollingRate, speed, &offset, &availableBandwidth); if (maxPacketSize > availableBandwidth) { USBLog (1, "%s[%p]::UIMCreateInterruptEndpoint - no bandwidth", getName(), this); return kIOReturnNoBandwidth; } if (highSpeedHub) { // get the "master" hub Info for this hub to check the flags hiPtr = AppleUSBEHCIHubInfo::GetHubInfo(&_hsHubs, highSpeedHub, highSpeedPort); if (!hiPtr) { USBLog (1, "%s[%p]::UIMCreateInterruptEndpoint - no hub in list", getName(), this); return kIOReturnInternalError; } if (maxPacketSize > hiPtr->AvailableInterruptBandwidth()) { USBLog (1, "%s[%p]::UIMCreateInterruptEndpoint - split transaction - no bandwidth on hub or port (MPS %d, bw %d)", getName(), this, maxPacketSize, hiPtr->AvailableInterruptBandwidth()); return kIOReturnNoBandwidth; } } else { USBLog(5, "%s[%p]::UIMCreateInterruptEndpoint - using offset %d with HS bandwidth of %d", getName(), this, offset, availableBandwidth); } pEHCIEndpointDescriptor = MakeEmptyIntEndPoint(functionAddress, endpointNumber, maxPacketSize, speed, highSpeedHub, highSpeedPort, direction); if (pEHCIEndpointDescriptor == NULL) { USBError(1, "%s[%p]::UIMCreateInterruptEndpoint - could not create empty endpoint", getName(), this); return kIOReturnNoResources; } if (hiPtr) hiPtr->AllocateInterruptBandwidth(pEHCIEndpointDescriptor, maxPacketSize); else pEHCIEndpointDescriptor->GetSharedLogical()->splitFlags |= HostToUSBLong(0x00000001); // HS interrupt goes on frame 1 pEHCIEndpointDescriptor->_pollM1 = pollingRate-1; pEHCIEndpointDescriptor->_offset = offset; pEHCIEndpointDescriptor->_maxPacketSize = maxPacketSize; if(_greatestPeriod < pollingRate) { _greatestPeriod = pollingRate; } linkInterruptEndpoint(pEHCIEndpointDescriptor); USBLog(5, "-%s[%p]::UIMCreateInterruptEndpoint", getName(), this); return kIOReturnSuccess; } IOReturn AppleUSBEHCI::UIMCreateIsochEndpoint(short functionAddress, short endpointNumber, UInt32 maxPacketSize, UInt8 direction) { USBError(1, "%s[%p]::UIMCreateIsochEndpoint -- old version called with no split params", getName(), this); return kIOReturnBadArgument; } IOReturn AppleUSBEHCI::UIMCreateIsochEndpoint(short functionAddress, short endpointNumber, UInt32 maxPacketSize, UInt8 direction, USBDeviceAddress highSpeedHub, int highSpeedPort) { USBError(1, "%s[%p]::UIMCreateIsochEndpoint -- old version called with no interval", getName(), this); return kIOReturnBadArgument; } IOReturn AppleUSBEHCI::UIMCreateIsochEndpoint(short functionAddress, short endpointNumber, UInt32 maxPacketSize, UInt8 direction, USBDeviceAddress highSpeedHub, int highSpeedPort, UInt8 interval) { AppleEHCIIsochEndpointPtr pEP; UInt32 curMaxPacketSize; AppleUSBEHCIHubInfo *hiPtr = NULL; UInt32 xtraRequest; // we do not create an isoch endpoint in the hardware itself. isoch transactions are handled by // TDs linked firectly into the Periodic List. There are two types of isoch TDs - split (siTD) // and high speed (iTD). This method will allocate an internal data structure which will be used // to track bandwidth, etc. USBLog(7, "%s[%p]::UIMCreateIsochEndpoint(%d, %d, %d, %d, %d, %d)", getName(), this, functionAddress, endpointNumber, maxPacketSize, direction, highSpeedHub, highSpeedPort); if (highSpeedHub == 0) { // Use a UInt16 to keep track of isoc bandwidth, then you can use // a pointer to it or to hiPtr->bandwidthAvailable for full speed. if ((interval != 1) && (interval != 8)) { USBError(1, "%s[%p]::UIMCreateIsochEndpoint: bad interval %d", getName(), this, interval); return kIOReturnBadArgument; } } else { // in this case we have a FS/LS device connected through a HS hub hiPtr = AppleUSBEHCIHubInfo::GetHubInfo(&_hsHubs, highSpeedHub, highSpeedPort); if (!hiPtr) { USBLog (1, "%s[%p]::UIMCreateIsochEndpoint - setting new hub info(%p) bandwidth to kUSBMaxIsocFrameReqCount", getName(), this, hiPtr); return kIOReturnInternalError; } } // see if the endpoint already exists - if so, this is a SetPipePolicy pEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL); if (pEP) { // this is the case where we have already created this endpoint, and now we are adjusting the maxPacketSize // USBLog(6,"%s[%p]::UIMCreateIsochEndpoint endpoint already exists, attempting to change maxPacketSize to %d",getName(), this, maxPacketSize); // if this is a Split transaction device, then let the hubInfo code handle the reallocation if (hiPtr) return hiPtr->ReallocateIsochBandwidth(pEP, maxPacketSize); // this is for High Speed devices curMaxPacketSize = pEP->maxPacketSize; if (maxPacketSize == curMaxPacketSize) { USBLog(4, "%s[%p]::UIMCreateIsochEndpoint maxPacketSize (%d) the same, no change",getName(), this, maxPacketSize); return kIOReturnSuccess; } if (maxPacketSize > curMaxPacketSize) { // client is trying to get more bandwidth xtraRequest = maxPacketSize - curMaxPacketSize; if (xtraRequest > _isochBandwidthAvail) { USBLog(1,"%s[%p]::UIMCreateIsochEndpoint out of bandwidth, request (extra) = %d, available: %d",getName(), this, xtraRequest, _isochBandwidthAvail); return kIOReturnNoBandwidth; } _isochBandwidthAvail -= xtraRequest; USBLog(5, "%s[%p]::UIMCreateIsochEndpoint grabbing additional bandwidth: %d, new available: %d",getName(), this, xtraRequest, _isochBandwidthAvail); } else { // client is trying to return some bandwidth xtraRequest = curMaxPacketSize - maxPacketSize; _isochBandwidthAvail += xtraRequest; USBLog(5, "%s[%p]::UIMCreateIsochEndpoint returning some bandwidth: %d, new available: %d",getName(), this, xtraRequest, _isochBandwidthAvail); } pEP->maxPacketSize = maxPacketSize; if(maxPacketSize >1024) { pEP->mult = ((maxPacketSize-1)/1024)+1; pEP->oneMPS = (maxPacketSize+2)/pEP->mult; } else { pEP->mult = 1; pEP->oneMPS = maxPacketSize; } USBLog(5,"%s[%p]::UIMCreateIsochEndpoint high speed size %d, mult %d: %d",getName(), this, maxPacketSize, pEP->mult, pEP->oneMPS); return kIOReturnSuccess; } // we neeed to create a new EP structure if (maxPacketSize > (hiPtr ? hiPtr->AvailableIsochBandwidth(direction) : _isochBandwidthAvail)) { USBLog(1,"%s[%p]::UIMCreateIsochEndpoint out of bandwidth, request (extra) = %d, available: %d",getName(), this, maxPacketSize, (hiPtr ? hiPtr->AvailableIsochBandwidth(direction) : _isochBandwidthAvail)); return kIOReturnNoBandwidth; } pEP = CreateIsochronousEndpoint(functionAddress, endpointNumber, direction, highSpeedHub, highSpeedPort); if (pEP == NULL) return kIOReturnNoMemory; if (hiPtr) { if (hiPtr->AllocateIsochBandwidth(pEP, maxPacketSize) != kIOReturnSuccess) { USBError(1, "%s[%p]::UIMCreateIsochEndpoint - could not allocate bandwidth", getName(), this); return kIOReturnNoMemory; } } else { // This is the High Speed Case if(maxPacketSize >1024) { pEP->mult = ((maxPacketSize-1)/1024)+1; pEP->oneMPS = (maxPacketSize+2)/pEP->mult; } else { pEP->mult = 1; pEP->oneMPS = maxPacketSize; } pEP->maxPacketSize = maxPacketSize; USBLog(5,"%s[%p]::UIMCreateIsochEndpoint high speed 2 size %d, mult %d: %d",getName(), this, maxPacketSize, pEP->mult, pEP->oneMPS); } pEP->inSlot = kEHCIPeriodicListEntries+1; pEP->interval = interval; pEP->hiPtr = hiPtr; _isochBandwidthAvail -= maxPacketSize; USBLog(4, "%s[%p]::UIMCreateIsochEndpoint success. bandwidth used = %d, new available: %d",getName(), this, maxPacketSize, _isochBandwidthAvail); return kIOReturnSuccess; } IOReturn AppleUSBEHCI::AbortIsochEP(AppleEHCIIsochEndpointPtr pEP) { UInt32 slot; IOReturn err; AppleEHCIIsochListElement *pTD; AbsoluteTime timeStamp; USBLog(7, "%s[%p]::AbortIsochEP (%p)", getName(), this, pEP); // we need to make sure that the interrupt routine is not processing the periodic list DisablePeriodicSchedule(); clock_get_uptime(&timeStamp); // now make sure we finish any periodic processing we were already doing (for MP machines) while (_filterInterruptActive) ; // now scavange any transactions which were already on the done queue err = scavengeIsocTransactions(NULL); if (err) { USBLog(1, "%s[%p]::AbortIsochEP - err (%p) from scavengeIsocTransactions", getName(), this, err); } if ((_outSlot < kEHCIPeriodicListEntries) && (pEP->inSlot < kEHCIPeriodicListEntries)) { // now scavenge the remaining transactions on the periodic list slot = _outSlot; while (slot != pEP->inSlot) { AppleEHCIListElement *thing; AppleEHCIListElement *nextThing; AppleEHCIListElement *prevThing; thing = _logicalPeriodicList[slot]; prevThing = NULL; while(thing != NULL) { nextThing = thing->_logicalNext; pTD = OSDynamicCast(AppleEHCIIsochListElement, thing); if(pTD) { if (pTD->_pEndpoint == pEP) { // first unlink it if (prevThing) { prevThing->_logicalNext = thing->_logicalNext; prevThing->SetPhysicalLink(thing->GetPhysicalLink()); thing = prevThing; // to cause prevThing to remain unchanged at the bottom of the loop } else { _logicalPeriodicList[slot] = nextThing; _periodicList[slot] = thing->GetPhysicalLink(); pEP->scheduledTDs--; thing = NULL; // to cause prevThing to remain unchanged (NULL) at the bottom of the loop } err = pTD->UpdateFrameList(timeStamp); // TODO - accumulate the return values or force abort err PutTDonDoneQueue(pEP, pTD, true); } } else { // only care about Isoch in this list - if we get here we are at the Interrupt list, which means no more Isoch break; } prevThing = thing; thing = nextThing; } slot = (slot + 1) & (kEHCIPeriodicListEntries-1); } } // now transfer any transactions from the todo list to the done queue pTD = GetTDfromToDoList(pEP); while (pTD) { err = pTD->UpdateFrameList(timeStamp); PutTDonDoneQueue(pEP, pTD, true); pTD = GetTDfromToDoList(pEP); } // since we have no Isoch xactions on the endpoint, we can reset the counter pEP->firstAvailableFrame = 0; pEP->inSlot = kEHCIPeriodicListEntries + 1; // we can go back to processing now EnablePeriodicSchedule(); pEP->accumulatedStatus = kIOReturnAborted; ReturnIsocDoneQueue(pEP); pEP->accumulatedStatus = kIOReturnSuccess; return kIOReturnSuccess; } void AppleUSBEHCI::returnTransactions(AppleEHCIQueueHead *pED, EHCIGeneralTransferDescriptor *untilThisOne, IOReturn error) { EHCIGeneralTransferDescriptorPtr doneQueue = NULL, doneTail= NULL; bool removedSome = false; USBLog(5, "%s[%p]::returnTransactions, pED(%p) until (%p)", getName(), this, pED, untilThisOne); pED->print(6); if (!(USBToHostLong(pED->GetSharedLogical()->qTDFlags) & kEHCITDStatus_Halted)) { USBError(1, "%s[%p]::returnTransactions, pED (%p) NOT HALTED (qTDFlags = %p)", getName(), this, pED, USBToHostLong(pED->GetSharedLogical()->qTDFlags)); // HaltAsyncEndpoint(pED); } if ((pED->_qTD != pED->_TailTD) && (pED->_qTD != untilThisOne)) // There are transactions on this queue { USBLog(5, "%s[%p] returnTransactions: removing TDs", getName(), this); removedSome = true; if(untilThisOne == NULL) { untilThisOne = pED->_TailTD; // Return all the transactions on this queue } doneQueue = pED->_qTD; doneTail = doneQueue; pED->_qTD = pED->_qTD->pLogicalNext; while (pED->_qTD != untilThisOne) { doneTail->pLogicalNext = pED->_qTD; doneTail = pED->_qTD; pED->_qTD = pED->_qTD->pLogicalNext; } doneTail->pLogicalNext = NULL; pED->GetSharedLogical()->NextqTDPtr = HostToUSBLong(untilThisOne->pPhysical); pED->_qTD = untilThisOne; } // Kill the overlay transaction and leave the EP enabled (NOT halted) USBLog(5, "%s[%p]::returnTransactions, pED->qTD (L:%p P:%p) pED->TailTD (L:%p P:%p)", getName(), this, pED->_qTD, pED->_qTD->pPhysical, pED->_TailTD, pED->_TailTD->pPhysical); USBLog(5, "%s[%p]::returnTransactions: clearing ED bit, qTDFlags = %x", getName(), this, USBToHostLong(pED->GetSharedLogical()->qTDFlags)); if (removedSome) pED->GetSharedLogical()->qTDFlags = 0; // Ensure that next TD is fetched (not the ALT) and reset the data toggle else pED->GetSharedLogical()->qTDFlags &= HostToUSBLong(kEHCITDFlags_DT); // Ensure that next TD is fetched (not the ALT) but keep the data toggle if (doneQueue) { USBLog(5, "%s[%p]::returnTransactions: calling back the done queue (after ED is made active)", getName(), this); EHCIUIMDoDoneQueueProcessing(doneQueue, error, NULL, NULL); } USBLog(5, "%s[%p]::returnTransactions: after bit clear, qTDFlags = %x", getName(), this, USBToHostLong(pED->GetSharedLogical()->qTDFlags)); } void AppleUSBEHCI::HaltAsyncEndpoint(AppleEHCIQueueHead *pED, AppleEHCIQueueHead *pEDBack) { // check to see if the endpoint is halted, and if not, delink it from the ASYNC // queue, halt it, and put it back if (!(USBToHostLong(pED->GetSharedLogical()->qTDFlags) & kEHCITDStatus_Halted)) { // we must halt the endpoint before we can touch it // but we want the TD on the list when we go to return the transactions USBLog(6, "%s[%p]::HaltAsyncEndpoint - unlinking, halting, and relinking (%p)", getName(), this, pED); unlinkAsyncEndpoint(pED, pEDBack); pED->GetSharedLogical()->qTDFlags |= HostToUSBLong(kEHCITDStatus_Halted); linkAsyncEndpoint(pED, _AsyncHead); } } void AppleUSBEHCI::HaltInterruptEndpoint(AppleEHCIQueueHead *pED) { // check to see if the endpoint is halted, and if not, delink it from the ASYNC // queue, halt it, and put it back if (!(USBToHostLong(pED->GetSharedLogical()->qTDFlags) & kEHCITDStatus_Halted)) { // we must halt the endpoint before we can touch it // but we want the TD on the list when we go to return the transactions USBLog(6, "%s[%p]::HaltInterruptEndpoint - unlinking, halting, and relinking (%p)", getName(), this, pED); unlinkIntEndpoint(pED); pED->GetSharedLogical()->qTDFlags |= HostToUSBLong(kEHCITDStatus_Halted); linkInterruptEndpoint(pED); } } IOReturn AppleUSBEHCI::HandleEndpointAbort( short functionAddress, short endpointNumber, short direction, bool clearToggle) { AppleEHCIQueueHead *pED; AppleEHCIQueueHead *pEDQueueBack; AppleEHCIIsochEndpointPtr piEP; USBLog(5, "%s[%p] AppleUSBEHCI::HandleEndpointAbort: Addr: %d, Endpoint: %d,%d",getName(), this, functionAddress, endpointNumber, direction); if (functionAddress == _rootHubFuncAddress) { if ( (endpointNumber != 1) && (endpointNumber != 0) ) { USBLog(1, "%s[%p::HandleEndpointAbort: bad params - endpNumber: %d", getName(), this, endpointNumber ); return kIOReturnBadArgument; } // We call SimulateEDDelete (endpointNumber, direction) in 9 // USBLog(5, "%s[%p]::HandleEndpointAbort: Attempting operation on root hub", getName(), this); return SimulateEDAbort( endpointNumber, direction); } piEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL); if (piEP) { return AbortIsochEP(piEP); } pED = FindControlBulkEndpoint (functionAddress, endpointNumber, &pEDQueueBack, direction); if(pED != NULL) { HaltAsyncEndpoint(pED, pEDQueueBack); returnTransactions(pED, NULL, kIOUSBTransactionReturned); // this will unhalt the EP if (clearToggle) pED->GetSharedLogical()->qTDFlags &= HostToUSBLong(~kEHCITDFlags_DT); // Ensure that next TD is fetched (not the ALT) but keep the data toggle } else { pED = FindInterruptEndpoint(functionAddress, endpointNumber, direction, NULL); if(pED == NULL) { USBLog(1, "AppleUSBEHCI::HandleEndpointAbort, endpoint not found"); return kIOUSBEndpointNotFound; } HaltInterruptEndpoint(pED); returnTransactions(pED, NULL, kIOUSBTransactionReturned); } return kIOReturnSuccess; } IOReturn AppleUSBEHCI::UIMAbortEndpoint( short functionAddress, short endpointNumber, short direction) { // this is probably not correct, but still waiting clarification on the EHCI spec section 4.8.2 return HandleEndpointAbort(functionAddress, endpointNumber, direction, false); } IOReturn AppleUSBEHCI::UIMClearEndpointStall( short functionAddress, short endpointNumber, short direction) { USBLog(7, "%s[%p]::UIMClearEndpointStall - endpoint %d:%d", getName(), this, functionAddress, endpointNumber); return HandleEndpointAbort(functionAddress, endpointNumber, direction, true); } AppleEHCIQueueHead * AppleUSBEHCI::FindInterruptEndpoint(short functionNumber, short endpointNumber, short direction, AppleEHCIListElement * *pLEBack) { UInt32 unique; AppleEHCIQueueHead * pEDQueue; AppleEHCIListElement * pListElementBack; AppleEHCIListElement * pListElem; int i; unique = (UInt32) ((((UInt32) endpointNumber) << kEHCIEDFlags_ENPhase) | ((UInt32) functionNumber)); pListElementBack = NULL; for(i= 0; i < _greatestPeriod; i++) { pListElem = OSDynamicCast(AppleEHCIListElement, _logicalPeriodicList[i]); while ( pListElem != NULL) { pEDQueue = OSDynamicCast(AppleEHCIQueueHead, pListElem); if (pEDQueue) { if( ( (USBToHostLong(pEDQueue->GetSharedLogical()->flags) & kUniqueNumNoDirMask) == unique) && ( pEDQueue->_direction == (UInt8)direction) ) { if (pLEBack) *pLEBack = pListElementBack; return pEDQueue; } } pListElementBack = pListElem; pListElem = OSDynamicCast(AppleEHCIListElement, pListElem->_logicalNext); } } return NULL; } IOReturn AppleUSBEHCI::UIMCreateInterruptTransfer(IOUSBCommand* command) { IOReturn status = kIOReturnSuccess; AppleEHCIQueueHead * pEDQueue; IOUSBCompletion completion = command->GetUSLCompletion(); IOMemoryDescriptor* buffer = command->GetBuffer(); short direction = command->GetDirection(); // our local copy may change USBLog(7, "%s[%p]::UIMCreateInterruptTransfer - adr=%d:%d cbp=%p:%lx br=%s cback=[%lx:%lx:%lx])", getName(), this, command->GetAddress(), command->GetEndpoint(), command->GetBuffer(), command->GetBuffer()->getLength(), command->GetBufferRounding()?"YES":"NO", (UInt32)completion.action, (UInt32)completion.target, (UInt32)completion.parameter); if (_rootHubFuncAddress == command->GetAddress()) { SimulateRootHubInt(command->GetEndpoint(), buffer, buffer->getLength(), completion); return kIOReturnSuccess; } pEDQueue = FindInterruptEndpoint(command->GetAddress(), command->GetEndpoint(), direction, NULL); if (!pEDQueue) { USBLog(1, "%s[%p]::UIMCreateInterruptTransfer - Endpoint not found", getName(), this); return kIOUSBEndpointNotFound; } status = allocateTDs(pEDQueue, command, buffer, command->GetBuffer()->getLength(), direction, false ); if(status != kIOReturnSuccess) { USBLog(1, "%s[%p]::UIMCreateInterruptTransfer allocateTDs failed %x", getName(), this, status); } else EnablePeriodicSchedule(); return status; } IOReturn AppleUSBEHCI::UIMCreateInterruptTransfer( short functionAddress, short endpointNumber, IOUSBCompletion completion, IOMemoryDescriptor * CBP, bool bufferRounding, UInt32 bufferSize, short direction) { // this is the old 1.8/1.8.1 method. It should not be used any more. USBLog(1, "%s(%p)UIMCreateInterruptTransfer- calling the wrong method!", getName(), this); return kIOReturnIPCError; } void AppleUSBEHCI::unlinkIntEndpoint(AppleEHCIQueueHead * pED) { int i; AppleEHCIListElement * pListElem; int maxPacketSize; Boolean foundED = false; USBLog(7, "+%s[%p]::unlinkIntEndpoint(%p)", getName(), this, pED); maxPacketSize = (USBToHostLong(pED->GetSharedLogical()->flags) & kEHCIEDFlags_MPS) >> kEHCIEDFlags_MPSPhase; for(i= pED->_offset; i < kEHCIPeriodicListEntries; i += pED->_pollM1+1) { pListElem = OSDynamicCast(AppleEHCIListElement, _logicalPeriodicList[i]); if(pED == pListElem) { _logicalPeriodicList[i] = pED->_logicalNext; _periodicList[i] = pED->GetPhysicalLink(); foundED = true; USBLog(6, "%s[%p]::unlinkIntEndpoint- found ED at top of list %d, new logical=%p, new physical=%p", getName(), this, i, _logicalPeriodicList[i], USBToHostLong(_periodicList[i])); } else { while(pListElem != NULL) { if(pListElem->_logicalNext == pED) { pListElem->_logicalNext = pED->_logicalNext; pListElem->SetPhysicalLink(pED->GetPhysicalLink()); foundED = true; USBLog(6, "%s[%p]::unlinkIntEndpoint- found ED in list %d, new logical=%p, new physical=%p", getName(), this, i, pED->_logicalNext, USBToHostLong(pED->GetPhysicalLink())); break; } pListElem = OSDynamicCast(AppleEHCIListElement, pListElem->_logicalNext); } if(pListElem == NULL) { USBLog(2, "%s[%p]::unlinkIntEndpoint endpoint not found in list %d", getName(), this, i); } } if (i < kEHCIMaxPoll) _periodicBandwidth[i] += maxPacketSize; } IOSleep(1); // make sure to clear the period list traversal cache pED->_logicalNext = NULL; if (foundED) _periodicEDsInSchedule--; USBLog(7, "-%s[%p]::unlinkIntEndpoint(%p)", getName(), this, pED); } void AppleUSBEHCI::unlinkAsyncEndpoint(AppleEHCIQueueHead * pED, AppleEHCIQueueHead * pEDQueueBack) { UInt32 CMD, STS, count; if( (pEDQueueBack == NULL) && (pED->_logicalNext == NULL) ) { USBLog(7, "%s[%p]::unlinkAsyncEndpoint: removing sole endpoint %lx", getName(),this, (long)pED); // this is the only endpoint in the queue. we will leave list processing disabled DisableAsyncSchedule(); printAsyncQueue(); _AsyncHead = NULL; _pEHCIRegisters->AsyncListAddr = 0; printAsyncQueue(); } else { USBLog(7, "%s[%p]::unlinkAsyncEndpoint: removing endpoint from queue %lx", getName(),this, (long)pED); // have to take this out of the queue if(_AsyncHead == pED) { // this is the case where we are taking the head of the queue, but it is not the // only element left in the queue if (pEDQueueBack) { USBError(1, "%s[%p]::unlinkAsyncEndpoint: ERROR - pEDQueueBack should be NULL at this point", getName(),this ); } // we need to find the last ED in the logical list, so that we can link in the "wrap around" physical pointer pEDQueueBack = OSDynamicCast(AppleEHCIQueueHead, pED->_logicalNext); while (pEDQueueBack->_logicalNext) pEDQueueBack = OSDynamicCast(AppleEHCIQueueHead, pEDQueueBack->_logicalNext); // DisableAsyncSchedule(); printAsyncQueue(); _AsyncHead = OSDynamicCast(AppleEHCIQueueHead, pED->_logicalNext); _pEHCIRegisters->AsyncListAddr = HostToUSBLong(pED->_logicalNext->_sharedPhysical); pEDQueueBack->GetSharedLogical()->nextQH = HostToUSBLong(pED->_logicalNext->GetPhysicalAddrWithType()); pED = OSDynamicCast(AppleEHCIQueueHead , pED->_logicalNext); pED->GetSharedLogical()->flags |= HostToUSBLong(kEHCIEDFlags_H); printAsyncQueue(); // EnableAsyncSchedule(); } else if(pEDQueueBack != NULL) { // DisableAsyncSchedule(); printAsyncQueue(); pEDQueueBack->GetSharedLogical()->nextQH = pED->GetSharedLogical()->nextQH; pEDQueueBack->_logicalNext = pED->_logicalNext; printAsyncQueue(); // EnableAsyncSchedule(); } else { USBLog(7, "%s[%p]::unlinkAsyncEndpoint: ED not head, but pEDQueueBack not NULL", getName(),this); } // ED is unlinked, now tell controller CMD = USBToHostLong(_pEHCIRegisters->USBCMD); _pEHCIRegisters->USBCMD = HostToUSBLong(CMD | kEHCICMDAsyncDoorbell); // Wait for controller to acknowledge STS = USBToHostLong(_pEHCIRegisters->USBSTS); count = 0; while((STS & kEHCIAAEIntBit) == 0) { IOSleep(1); STS = USBToHostLong(_pEHCIRegisters->USBSTS); count++; if ( count > 10000) { // Bail out after 10 seconds break; } }; USBLog(5, "%s[%p]::unlinkAsyncEndpoint: delayed for %d", getName(),this, count); // Clear request _pEHCIRegisters->USBSTS = HostToUSBLong(kEHCIAAEIntBit); } } IOReturn AppleUSBEHCI::DeleteIsochEP(AppleEHCIIsochEndpointPtr pEP) { AppleEHCIIsochEndpointPtr curEP, prevEP; USBLog(7, "%s[%p]::DeleteIsochEP (%p)", getName(), this, pEP); if (pEP->activeTDs) { USBLog(6, "%s[%p]::DeleteIsochEP- there are still %d active TDs - aborting", getName(), this, pEP->activeTDs); AbortIsochEP(pEP); if (pEP->activeTDs) { USBError(1, "%s[%p]::DeleteIsochEP- after abort there are STILL %d active TDs", getName(), this, pEP->activeTDs); } } prevEP = NULL; curEP = _isochEPList; while (curEP) { if (curEP == pEP) { if (prevEP) prevEP->nextEP = curEP->nextEP; else _isochEPList = curEP->nextEP; break; } prevEP = curEP; curEP = curEP->nextEP; } // need to return the bandwidth used if (pEP->highSpeedHub == 0) { _isochBandwidthAvail += pEP->maxPacketSize; } else { AppleUSBEHCIHubInfo *hiPtr; hiPtr = AppleUSBEHCIHubInfo::GetHubInfo(&_hsHubs, pEP->highSpeedHub, pEP->highSpeedPort); if (hiPtr) { hiPtr->DeallocateIsochBandwidth(pEP); } } return kIOReturnSuccess; } IOReturn AppleUSBEHCI::UIMDeleteEndpoint( short functionAddress, short endpointNumber, short direction) { AppleEHCIQueueHead *pED; AppleEHCIQueueHead *pEDQueueBack; AppleEHCIIsochEndpointPtr piEP; USBLog(7, "%s[%p] AppleUSBEHCI::UIMDeleteEndpoint: Addr: %d, Endpoint: %d,%d",getName(), this, functionAddress,endpointNumber,direction); if (functionAddress == _rootHubFuncAddress) { if ( (endpointNumber != 1) && (endpointNumber != 0) ) { USBLog(1, "%s[%p] UIMDeleteEndpoint: bad params - endpNumber: %d", getName(), this, endpointNumber ); return kIOReturnBadArgument; } // We call SimulateEDDelete (endpointNumber, direction) in 9 // USBLog(5, "%s[%p] UIMDeleteEndpoint: Attempting operation on root hub", getName(), this); return SimulateEDDelete( endpointNumber, direction); } piEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL); if (piEP) return DeleteIsochEP(piEP); pED = FindControlBulkEndpoint (functionAddress, endpointNumber, &pEDQueueBack, direction); if(pED == NULL) { UInt32 splitFlags; USBDeviceAddress highSpeedHub; UInt8 highSpeedPort; AppleUSBEHCIHubInfo *hiPtr; pED = FindInterruptEndpoint(functionAddress, endpointNumber, direction, NULL); if(pED == NULL) { USBLog(2, "AppleUSBEHCI::UIMDeleteEndpoint, endpoint not found"); return kIOUSBEndpointNotFound; } USBLog(5, "%s[%p]::UIMDeleteEndpoint: deleting Int endpoint", getName(), this); unlinkIntEndpoint(pED); if (!_periodicEDsInSchedule) { USBLog(3, "%s[%p]::UIMDeleteEndpoint: $$$$$$ all EDs gone from periodic schedule, disabling", getName(), this, _periodicEDsInSchedule); DisablePeriodicSchedule(); } splitFlags = USBToHostLong(pED->GetSharedLogical()->splitFlags); highSpeedHub = (splitFlags & kEHCIEDSplitFlags_HubAddr) >> kEHCIEDSplitFlags_HubAddrPhase; highSpeedPort = (splitFlags & kEHCIEDSplitFlags_Port) >> kEHCIEDSplitFlags_PortPhase; if (highSpeedHub) { hiPtr = AppleUSBEHCIHubInfo::GetHubInfo(&_hsHubs, highSpeedHub, highSpeedPort); if (hiPtr) hiPtr->DeallocateInterruptBandwidth(pED); } } else { USBLog(5, "%s[%p] UIMDeleteEndpoint: unlinking async endpoint", getName(), this); unlinkAsyncEndpoint(pED, pEDQueueBack); } if(pED->_qTD != pED->_TailTD) // There are transactions on this queue { USBLog(5, "%s[%p] UIMDeleteEndpoint: removing TDs", getName(), this); EHCIUIMDoDoneQueueProcessing(pED->_qTD, kIOUSBTransactionReturned, NULL, pED->_TailTD); pED->_qTD = pED->_TailTD; pED->GetSharedLogical()->NextqTDPtr = HostToUSBLong(pED->_qTD->pPhysical); } if ( pED->_qTD != NULL ) { // I need to delete the dummy TD USBLog(6, "%s[%p]::UIMDeleteEndpoint - deallocating the dummy TD", getName(), this); DeallocateTD(pED->_qTD); pED->_qTD = NULL; } USBLog(5, "%s[%p] AppleUSBEHCI::UIMDeleteEndpoint: Deallocating %p", getName(), this, pED); DeallocateED(pED); return kIOReturnSuccess; } IOReturn AppleUSBEHCI::CreateHSIsochTransfer( AppleEHCIIsochEndpointPtr pEP, IOUSBIsocCompletion completion, UInt64 frameNumberStart, IOMemoryDescriptor *pBuffer, UInt32 frameCount, IOUSBLowLatencyIsocFrame *pFrames, UInt32 updateFrequency, Boolean lowLatency) { UInt64 curFrameNumber = GetFrameNumber(); UInt64 maxOffset; UInt32 bufferSize; AppleEHCIIsochTransferDescriptor *pNewITD = NULL; IOByteCount transferOffset; unsigned i,j; UInt32 *buffP, *TransactionP = 0; UInt32 pageOffset; UInt32 page; UInt32 frames; UInt32 trLen; UInt64 frameDiff; UInt32 diff32; IOUSBIsocFrame *pHLFrames; IOPhysicalAddress dmaStartAddr; IOByteCount segLen; #if 0 // JRH - 9/21/04 - this is a bogus check if( (frameCount % 8) != 0 ) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer frameCount not whole frame: %d",getName(), this, frameCount); return kIOReturnBadArgument; } #endif maxOffset = _frameListSize; if (frameNumberStart <= curFrameNumber) { if (frameNumberStart < (curFrameNumber - maxOffset)) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer request frame WAY too old. frameNumberStart: %ld, curFrameNumber: %ld. Returning 0x%x",getName(), this, (UInt32) frameNumberStart, (UInt32) curFrameNumber, kIOReturnIsoTooOld); return kIOReturnIsoTooOld; } USBLog(5,"%s[%p]::UIMCreateIsochTransfer WARNING! curframe later than requested, expect some notSent errors! frameNumberStart: %ld, curFrameNumber: %ld. USBIsocFrame Ptr: %p, First ITD: %p",getName(), this, (UInt32) frameNumberStart, (UInt32) curFrameNumber, pFrames, pEP->toDoEnd); } else { // frameNumberStart > curFrameNumber if (frameNumberStart > (curFrameNumber + maxOffset)) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer request frame too far ahead! frameNumberStart: %ld, curFrameNumber: %ld",getName(), this, (UInt32) frameNumberStart, (UInt32) curFrameNumber); return kIOReturnIsoTooNew; } frameDiff = frameNumberStart - curFrameNumber; diff32 = (UInt32)frameDiff; if (diff32 < 2) { USBLog(5,"%s[%p]::UIMCreateIsochTransfer WARNING! - frameNumberStart less than 2 ms (is %ld)! frameNumberStart: %ld, curFrameNumber: %ld",getName(), this, (UInt32) diff32, (UInt32) frameNumberStart, (UInt32) curFrameNumber); } } // Format all the TDs, attach them to the pseudo endpoint. // let the frame interrupt routine put them in the periodic list frames = 100; transferOffset = 0; pHLFrames = (IOUSBIsocFrame *)pFrames; // Do this one frame, 8 micro frames, at a time while(frameCount > 0) { // // Get the size of buffer for this frame // bufferSize = 0; for ( i = 0; i < 8; i += pEP->interval) { if(frameCount < i) { break; } if(lowLatency) { if (pFrames[i].frReqCount > kUSBMaxHSIsocFrameReqCount) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer Isoch frame too big %d",getName(), this, pFrames[i].frReqCount); return kIOReturnBadArgument; } bufferSize += pFrames[i].frReqCount; } else { if (pHLFrames[i].frReqCount > kUSBMaxHSIsocFrameReqCount) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer Isoch frame too big %d",getName(), this, pHLFrames[i].frReqCount); return kIOReturnBadArgument; } bufferSize += pHLFrames[i].frReqCount; } } // // go ahead and make sure we can grab at least ONE TD, before we lock the buffer // pNewITD = AllocateITD(); USBLog(7, "%s[%p]::UIMCreateIsochTransfer - new iTD %p", getName(), this, pNewITD); if (pNewITD == NULL) { USBLog(1,"%s[%p]::UIMCreateIsochTransfer Could not allocate a new iTD",getName(), this); return kIOReturnNoMemory; } pNewITD->_lowLatency = lowLatency; // initialize the page pointers to zero length pNewITD->GetSharedLogical()->Transaction0 = NULL; pNewITD->GetSharedLogical()->Transaction1 = NULL; pNewITD->GetSharedLogical()->Transaction2 = NULL; pNewITD->GetSharedLogical()->Transaction3 = NULL; pNewITD->GetSharedLogical()->Transaction4 = NULL; pNewITD->GetSharedLogical()->Transaction5 = NULL; pNewITD->GetSharedLogical()->Transaction6 = NULL; pNewITD->GetSharedLogical()->Transaction7 = NULL; // set up all the physical page pointers buffP = &pNewITD->GetSharedLogical()->bufferPage0; dmaStartAddr = pBuffer->getPhysicalSegment(transferOffset, &segLen); pageOffset = dmaStartAddr & kEHCIPageOffsetMask; USBLog(4, "%s[%p]::CreateHSIsochTransfer - Addr (%p) Length (%d) BufferSize (%d)", getName(), this, dmaStartAddr, segLen, bufferSize); if(segLen > bufferSize) { segLen = bufferSize; } if(segLen > (kEHCIPageSize-pageOffset)) { segLen = kEHCIPageSize-pageOffset; } *(buffP++) = HostToUSBLong( dmaStartAddr & kEHCIPageMask); USBLog(4, "%s[%p]::CreateHSIocTransfer - getPhysicalSegment returned start of %p; length:%ld ; Buff Ptr0:%lx", getName(), this, dmaStartAddr, segLen, *(buffP-1)); transferOffset += segLen; bufferSize -= segLen; for(j=1; j <= 6; j++) { if(bufferSize == 0) { *(buffP++) = 0; continue; } dmaStartAddr = pBuffer->getPhysicalSegment(transferOffset, &segLen); *(buffP++) = HostToUSBLong( dmaStartAddr & kEHCIPageMask); // pageOffset = dmaStartAddr & kEHCIPageOffsetMask; if((dmaStartAddr & kEHCIPageOffsetMask) != 0) { USBError(1, "%s[%p]::CreateHSIocTransfer - pageOffset not zero in %dth buff: %p", getName(), this, j, dmaStartAddr); } if(segLen > bufferSize) { segLen = bufferSize; } if(segLen > kEHCIPageSize) { segLen = kEHCIPageSize; } transferOffset += segLen; bufferSize -= segLen; USBLog(4, "%s[%p]::CreateHSIocTransfer - getPhysicalSegment returned start of %p; length:%d ; Buff Ptr%d:%p", getName(), this, dmaStartAddr, segLen, j, USBToHostLong(*(buffP-1))); } // return kIOReturnNoMemory; page = 0; TransactionP = &pNewITD->GetSharedLogical()->Transaction0; if(lowLatency) { pNewITD->_pFrames = (IOUSBIsocFrame *)pFrames; // Points to the 8 frames structure for the 8 frames in this micro frame } else { pNewITD->_pFrames = pHLFrames; // Points to the 8 frames structure for the 8 frames in this micro frame } for ( i = 0; i< 8; i += pEP->interval) { if(frameCount < i) { break; } if(lowLatency) { trLen = (pFrames++)->frReqCount; } else { trLen = (pHLFrames++)->frReqCount; } // Len already checked above USBLog(4, "%s[%p]::CreateHSIsochTransfer - forming transaction length (%d), pageOffset (%p), page (%d)", getName(), this, trLen, pageOffset, page); *(TransactionP++) = HostToUSBLong(kEHCI_ITDStatus_Active | (trLen<< kEHCI_ITDTr_LenPhase) | (pageOffset << kEHCI_ITDTr_OffsetPhase) | (page << kEHCI_ITDTr_PagePhase) ); // This gets filled in later for the last one. pNewITD->_completion.action = NULL; pageOffset += trLen; if(pageOffset > kEHCIPageSize) { pageOffset -= kEHCIPageSize; page++; } } if(frameCount > (UInt32)(8 / pEP->interval)) { frameCount -= (8 / pEP->interval); } else { frameCount = 0; } // We're no longer using IOC as end of transaction marker. There's a iTD->completion field for that if(frames-- == 0) { // We've gone 100 frames, about time to set an IOC TransactionP[-1] |= HostToUSBLong(kEHCI_ITDTr_IOC); frames = 100; } pNewITD->_frameNumber = frameNumberStart++; pNewITD->GetSharedLogical()->bufferPage0 |= HostToUSBLong((pEP->functionAddress << kEHCI_ITDBuf_FnAddrPhase) | (pEP->endpointNumber << kEHCI_ITDBuf_EPPhase) ); pNewITD->GetSharedLogical()->bufferPage1 |= HostToUSBLong( (pEP->oneMPS << kEHCI_ITDBuf_MPSPhase) | ((pEP->direction == kUSBIn) ? (UInt32) kEHCI_ITDBuf_IO : 0) ); pNewITD->GetSharedLogical()->bufferPage2 |= HostToUSBLong( (pEP->mult << kEHCI_ITDBuf_MultPhase) ); pNewITD->_pEndpoint = pEP; pNewITD->print(4); PutTDonToDoList(pEP, pNewITD); } // Rember to put IOC on the last one at least. TransactionP[-1] |= HostToUSBLong(kEHCI_ITDTr_IOC); // An the completion action. USBLog(3,"%s[%p]::UIMCreateIsochTransfer completion completion.action %p",getName(), this, completion.action); pNewITD->_completion = completion; AddIsocFramesToSchedule(pEP); EnablePeriodicSchedule(); return kIOReturnSuccess; } IOReturn AppleUSBEHCI::CreateSplitIsochTransfer( AppleEHCIIsochEndpointPtr pEP, IOUSBIsocCompletion completion, UInt64 frameNumberStart, IOMemoryDescriptor *pBuffer, UInt32 frameCount, IOUSBIsocFrame *pFrames, UInt32 updateFrequency, bool lowLatency) { AppleEHCISplitIsochTransferDescriptor *pNewSITD=NULL, *pDummySITD=NULL; UInt64 maxOffset; UInt64 curFrameNumber = GetFrameNumber(); UInt64 frameDiff; UInt32 diff32; IOByteCount transferOffset; UInt32 frames; IOUSBLowLatencyIsocFrame *pLLFrames; UInt32 bufferSize; UInt8 startSplitFlags; UInt8 completeSplitFlags = 0; UInt8 transactionPosition; UInt8 transactionCount; UInt8 ioc = 0; UInt32 i; UInt32 pageOffset; IOPhysicalAddress dmaStartAddr; IOByteCount segLen; UInt32 myStatFlags; IOPhysicalAddress prevPhysLink = kEHCITermFlag; USBLog(6, "%s[%p]::CreateSplitIsochTransfer - frameCount %d - frameNumberStart 0x%Lx - curFrameNumber 0x%Lx", getName(), this, frameCount, frameNumberStart, curFrameNumber); USBLog(6, "%s[%p]::CreateSplitIsochTransfer - updateFrequency %d - lowLatency %d", getName(), this, updateFrequency, lowLatency); maxOffset = _frameListSize; if (frameNumberStart < pEP->firstAvailableFrame) { USBLog(3,"%s[%p]::CreateSplitIsochTransfer: no overlapping frames - EP (%p) frameNumberStart: %Ld, pEP->firstAvailableFrame: %Ld. Returning 0x%x",getName(), this, pEP, frameNumberStart, pEP->firstAvailableFrame, kIOReturnIsoTooOld); return kIOReturnIsoTooOld; } pEP->firstAvailableFrame = frameNumberStart; if (frameNumberStart <= curFrameNumber) { if (frameNumberStart < (curFrameNumber - maxOffset)) { USBLog(3,"%s[%p]::CreateSplitIsochTransfer request frame WAY too old. frameNumberStart: %Ld, curFrameNumber: %Ld. Returning 0x%x",getName(), this, frameNumberStart, curFrameNumber, kIOReturnIsoTooOld); return kIOReturnIsoTooOld; } USBLog(5,"%s[%p]::UIMCreateIsochTransfer WARNING! curframe later than requested, expect some notSent errors! frameNumberStart: %Ld, curFrameNumber: %Ld. USBIsocFrame Ptr: %p, First ITD: %p",getName(), this, frameNumberStart, curFrameNumber, pFrames, pEP->toDoEnd); } else { // frameNumberStart > curFrameNumber if (frameNumberStart > (curFrameNumber + maxOffset)) { USBLog(3,"%s[%p]::CreateSplitIsochTransfer request frame too far ahead! frameNumberStart: %Ld, curFrameNumber: %Ld",getName(), this, frameNumberStart, curFrameNumber); return kIOReturnIsoTooNew; } frameDiff = frameNumberStart - curFrameNumber; diff32 = (UInt32)frameDiff; if (diff32 < 2) { USBLog(5,"%s[%p]::UIMCreateIsochTransfer WARNING! - frameNumberStart less than 2 ms (is %d)! frameNumberStart: %Ld, curFrameNumber: %Ld",getName(), this, diff32, frameNumberStart, curFrameNumber); } } pLLFrames = (IOUSBLowLatencyIsocFrame *)pFrames; // // Get the total size of buffer // bufferSize = 0; for ( i = 0; i < frameCount; i++) { if (!lowLatency) { if (pFrames[i].frReqCount > pEP->maxPacketSize) { USBLog(1,"%s[%p]::CreateSplitIsochTransfer - Isoch frame (%d) too big (%d) MPS (%d)",getName(), this, i + 1, pFrames[i].frReqCount, pEP->maxPacketSize); return kIOReturnBadArgument; } bufferSize += pFrames[i].frReqCount; } else { if (pLLFrames[i].frReqCount > kUSBMaxFSIsocEndpointReqCount) { USBLog(1,"%s[%p]::CreateSplitIsochTransfer(LL) - Isoch frame (%d) too big (%d) MPS (%d)",getName(), this, i + 1, pLLFrames[i].frReqCount, pEP->maxPacketSize); return kIOReturnBadArgument; } bufferSize += pLLFrames[i].frReqCount; // Make sure our frStatus field has a known value (debugging aid) // pLLFrames[i].frStatus = (IOReturn) kUSBLowLatencyIsochTransferKey; } } // Format all the TDs, attach them to the pseudo endpoint. // let the frame interrupt routine put them in the periodic list frames = 100; transferOffset = 0; // Do this one frame at a time for ( i = 0; i < frameCount; i++) { UInt16 reqCount, reqLeft; pNewSITD = AllocateSITD(); if (lowLatency) reqCount = pLLFrames[i].frReqCount; else reqCount = pFrames[i].frReqCount; USBLog(7, "%s[%p]::CreateSplitIsochTransfer - new iTD %p size (%d)", getName(), this, pNewSITD, reqCount); if (!pNewSITD) { USBLog(1,"%s[%p]::CreateSplitIsochTransfer Could not allocate a new iTD",getName(), this); return kIOReturnNoMemory; } pEP->firstAvailableFrame++; pNewSITD->_lowLatency = lowLatency; // set up the physical page pointers dmaStartAddr = pBuffer->getPhysicalSegment(transferOffset, &segLen); pageOffset = dmaStartAddr & kEHCIPageOffsetMask; if(segLen > reqCount) { segLen = reqCount; } if(segLen > (kEHCIPageSize-pageOffset)) { segLen = kEHCIPageSize-pageOffset; } pNewSITD->GetSharedLogical()->buffPtr0 = HostToUSBLong(dmaStartAddr); USBLog(7, "%s[%p]::CreateSplitIocTransfer - getPhysicalSegment returned start of %p; length %ld", getName(), this, dmaStartAddr, segLen); transferOffset += segLen; bufferSize -= segLen; reqLeft = reqCount - segLen; if(reqLeft==0) { pNewSITD->GetSharedLogical()->buffPtr1 = 0; } else { dmaStartAddr = pBuffer->getPhysicalSegment(transferOffset, &segLen); pNewSITD->GetSharedLogical()->buffPtr1 = HostToUSBLong(dmaStartAddr & kEHCIPageMask); if(segLen > reqLeft) { segLen = reqLeft; } if(segLen > kEHCIPageSize) { segLen = kEHCIPageSize; } USBLog(7, "%s[%p]::CreateSplitIocTransfer - getPhysicalSegment returned start of %p; length %ld", getName(), this, dmaStartAddr, segLen); transferOffset += segLen; bufferSize -= segLen; } pNewSITD->_pFrames = pFrames; pNewSITD->_frameNumber = frameNumberStart + i; pNewSITD->_frameIndex = i; // NEW 11-15-04 SS and CS calculated by AllocateIsochBandwidth if (pEP->direction == kUSBOut) { completeSplitFlags = 0; // dont use complete split for OUT transactions if (reqCount > kUSBEHCIMaxSSOUTsection) { transactionCount = (reqCount + (kUSBEHCIMaxSSOUTsection-1)) / kUSBEHCIMaxSSOUTsection; // number of 180 byte transfers transactionPosition = 1; // beginning of a multi-part transfer startSplitFlags = pEP->startSplitFlags; // bitmask of bits to send SSplit on (created in HubInfo.cpp) } else { startSplitFlags = pEP->startSplitFlags; // bitmask of bits to send SSplit on (created in HubInfo.cpp) transactionPosition = 0; // total transfer is in this microframe transactionCount = 1; // only need one transfer } } else { // IN transactions startSplitFlags = pEP->startSplitFlags; // issue the SSplit on microframe 0 transactionPosition = 0; // only used for OUT transactionCount = 0; // only used for OUT completeSplitFlags = pEP->completeSplitFlags; // allow completes on 2,3,4,5,6,7 USBLog(7, "%s[%p]::CreateSplitIsochTransfer IN - SS (%x) CS (%x)", getName(), this, startSplitFlags, completeSplitFlags); } // calculate IOC and completion if necessary if (i == (frameCount-1)) { // only ioc on the last frame if we are not using the backPtr if (pEP->useBackPtr) { ioc = 0; // will put it on the dummy } else { ioc = 1; pNewSITD->_completion = completion; } } else if (lowLatency) { if (!updateFrequency) ioc = (((i+1) % 8) == 0) ? 1 : 0; else ioc = (((i+1) % updateFrequency) == 0) ? 1 : 0; } else ioc = 0; pNewSITD->GetSharedLogical()->nextSITD = HostToUSBLong(kEHCITermFlag); pNewSITD->GetSharedLogical()->routeFlags = HostToUSBLong(((pEP->direction == kUSBOut) ? 0 : (1 << kEHCIsiTDRouteDirectionPhase)) | (pEP->highSpeedPort << kEHCIsiTDRoutePortNumberPhase) | (pEP->highSpeedHub << kEHCIsiTDRouteHubAddrPhase) | (pEP->endpointNumber << kEHCIsiTDRouteEndpointPhase) | (pEP->functionAddress << kEHCIsiTDRouteDeviceAddrPhase) ); pNewSITD->GetSharedLogical()->timeFlags = HostToUSBLong((completeSplitFlags << kEHCIsiTDTimeCMaskPhase) | (startSplitFlags << kEHCIsiTDTimeSMaskPhase) ); myStatFlags = (ioc << kEHCIsiTDStatIOCPhase) | (reqCount << kEHCIsiTDStatLengthPhase) | kEHCIsiTDStatStatusActive; if ((i > 0) && pEP->useBackPtr) { myStatFlags |= kEHCUsiTDStatStatusSplitXState; // every TD after the first starts in DoCompleteSplit state pNewSITD->GetSharedLogical()->backPtr = HostToUSBLong(prevPhysLink); } else { pNewSITD->GetSharedLogical()->backPtr = HostToUSBLong(kEHCITermFlag); } prevPhysLink = pNewSITD->_sharedPhysical; pNewSITD->GetSharedLogical()->statFlags = HostToUSBLong(myStatFlags); pNewSITD->GetSharedLogical()->buffPtr1 |= HostToUSBLong( // Buff pointer already set up (transactionPosition << kEHCIsiTDBuffPtr1TPPhase) | (transactionCount)); pNewSITD->_pEndpoint = pEP; PutTDonToDoList(pEP, pNewSITD); } // if we are wrapping around, we need to add one more link to wrap things up if (pEP->useBackPtr) { pDummySITD = AllocateSITD(); // most of the fields get copied from the last SITD pDummySITD->GetSharedLogical()->nextSITD = HostToUSBLong(kEHCITermFlag); pDummySITD->GetSharedLogical()->routeFlags = pNewSITD->GetSharedLogical()->routeFlags; pDummySITD->GetSharedLogical()->timeFlags = HostToUSBLong((completeSplitFlags & 0x03) << kEHCIsiTDTimeCMaskPhase); // no SS on the dummy TD and CS only on microframes 0 and 1 pDummySITD->GetSharedLogical()->statFlags = HostToUSBLong(kEHCIsiTDStatIOC | kEHCIsiTDStatStatusActive | kEHCUsiTDStatStatusSplitXState); pDummySITD->GetSharedLogical()->buffPtr0 = pNewSITD->GetSharedLogical()->buffPtr0; pDummySITD->GetSharedLogical()->buffPtr1 = pNewSITD->GetSharedLogical()->buffPtr1; pDummySITD->GetSharedLogical()->backPtr = HostToUSBLong(prevPhysLink); pDummySITD->_completion = completion; pDummySITD->_pEndpoint = pEP; pDummySITD->_pFrames = NULL; pDummySITD->_frameNumber = pNewSITD->_frameNumber+1; pDummySITD->_isDummySITD = true; PutTDonToDoList(pEP, pDummySITD); } AddIsocFramesToSchedule(pEP); EnablePeriodicSchedule(); return kIOReturnSuccess; } // this is the non Low Latency case IOReturn AppleUSBEHCI::UIMCreateIsochTransfer( short functionAddress, short endpointNumber, IOUSBIsocCompletion completion, UInt8 direction, UInt64 frameStart, IOMemoryDescriptor *pBuffer, UInt32 frameCount, IOUSBIsocFrame *pFrames) { AppleEHCIIsochEndpointPtr pEP; USBLog(7, "%s[%p]::UIMCreateIsochTransfer - adr=%d:%d cbp=%p:%lx (cback=[%lx:%lx:%lx])", getName(), this, functionAddress, endpointNumber, pBuffer, pBuffer->getLength(), (UInt32)completion.action, (UInt32)completion.target, (UInt32)completion.parameter); if ( (frameCount == 0) || (frameCount > 1000) ) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer bad frameCount: %d",getName(), this, frameCount); return kIOReturnBadArgument; } pEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL); if(pEP == NULL) { USBLog(1, "%s[%p]::UIMCreateIsochTransfer - Endpoint not found", getName(), this); return kIOUSBEndpointNotFound; } if (pEP->highSpeedHub) return CreateSplitIsochTransfer(pEP, completion, frameStart, pBuffer, frameCount, pFrames); else return CreateHSIsochTransfer(pEP, completion, frameStart, pBuffer, frameCount, (IOUSBLowLatencyIsocFrame*)pFrames); } // this is the Low Latency case IOReturn AppleUSBEHCI::UIMCreateIsochTransfer( short functionAddress, short endpointNumber, IOUSBIsocCompletion completion, UInt8 direction, UInt64 frameNumberStart, IOMemoryDescriptor *pBuffer, UInt32 frameCount, IOUSBLowLatencyIsocFrame *pFrames, UInt32 updateFrequency) { AppleEHCIIsochEndpointPtr pEP; USBLog(7, "%s[%p]::UIMCreateIsochTransfer - adr=%d:%d cbp=%p:%lx (cback=[%lx:%lx:%lx])", getName(), this, functionAddress, endpointNumber, pBuffer, pBuffer->getLength(), (UInt32)completion.action, (UInt32)completion.target, (UInt32)completion.parameter); if ( (frameCount == 0) || (frameCount > 1000) ) { USBLog(3,"%s[%p]::UIMCreateIsochTransfer bad frameCount: %d",getName(), this, frameCount); return kIOReturnBadArgument; } pEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL); if(pEP == NULL) { USBLog(1, "%s[%p]::UIMCreateIsochTransfer - Endpoint not found", getName(), this); return kIOUSBEndpointNotFound; } if (pEP->highSpeedHub) return CreateSplitIsochTransfer(pEP, completion, frameNumberStart, pBuffer, frameCount, (IOUSBIsocFrame*)pFrames, updateFrequency, true); else return CreateHSIsochTransfer(pEP, completion, frameNumberStart, pBuffer, frameCount, pFrames, updateFrequency, true); } void AppleUSBEHCI::AddIsocFramesToSchedule(AppleEHCIIsochEndpointPtr pEP) { UInt64 currFrame; AppleEHCIIsochListElement *pTD; AppleEHCISplitIsochTransferDescriptor *pSITD; UInt16 nextSlot; AbsoluteTime timeStamp; pTD = GetTDfromToDoList(pEP); if(pTD == NULL) { USBLog(7, "%s[%p]::AddIsocFramesToSchedule - no frames to add fn:%d EP:%d", getName(), this, pEP->functionAddress, pEP->endpointNumber); return; } // Don't get GetFrameNumber() unless we're going to use it // currFrame = GetFrameNumber(); USBLog(7, "%s[%p]::AddIsocFramesToSchedule - fn:%d EP:%d inSlot (0x%x), currFrame: 0x%Lx", getName(), this, pEP->functionAddress, pEP->endpointNumber, pEP->inSlot, currFrame); clock_get_uptime(&timeStamp); while(pTD->_frameNumber <= (currFrame+1)) // Add 1, and use <= so you never put in a new frame // at less than 2 ahead of now. (EHCI spec, 7.2.1) { IOReturn ret; // this transaction is old before it began, move to done queue USBLog(7, "%s[%p]::AddIsocFramesToSchedule - ignoring TD(%p) because it is too old (%Lx) vs (%Lx) ", getName(), this, pTD, pTD->_frameNumber, currFrame); ret = pTD->UpdateFrameList(timeStamp); // TODO - accumulate the return values if (pEP->scheduledTDs) PutTDonDeferredQueue(pEP, pTD); else { USBLog(7, "%s[%p]::AddIsocFramesToSchedule - putting TD(%p) on Done Queue instead of Deferred Queue ", getName(), this, pTD); PutTDonDoneQueue(pEP, pTD, true); } pTD = GetTDfromToDoList(pEP); USBLog(7, "%s[%p]::AddIsocFramesToSchedule - pTD = %p", getName(), this, pTD); if(pTD == NULL) { // Run out of transactions to move. Call this on a separate thread so that we return to the caller right away // // ReturnIsocDoneQueue(pEP); USBLog(7, "%s[%p]::AddIsocFramesToSchedule - calling the ReturnIsocDoneQueue on a separate thread", getName(), this); thread_call_enter1(_processDoneQueueThread, (thread_call_param_t) pEP); return; } } currFrame = pTD->_frameNumber; // start looking at the first available number // This needs to be fixed up when we have variable length lists. pEP->inSlot = currFrame & (kEHCIPeriodicListEntries-1); do { nextSlot = (pEP->inSlot + 1) & (kEHCIPeriodicListEntries-1); if( nextSlot == _outSlot) // weve caught up with our tail { USBLog(7, "%s[%p]::AddIsocFramesToSchedule - caught up nextSlot (0x%x) _outSlot (0x%x)", getName(), this, nextSlot, _outSlot); break; } USBLog(7, "%s[%p]::AddIsocFramesToSchedule - checking TD(%p) FN(0x%Lx) against currFrame (0x%Lx)", getName(), this, pTD, pTD->_frameNumber, currFrame); if(currFrame == pTD->_frameNumber) { AppleEHCIListElement *linkAfter = NULL; AppleEHCIIsochListElement *prevIsochLE; // could be split or HS AppleEHCISplitIsochTransferDescriptor *prevSITD; if(_outSlot > kEHCIPeriodicListEntries) { _outSlot = pEP->inSlot; } // Place TD in list USBLog(6, "%s[%p]::AddIsocFramesToSchedule - linking TD (%p) with frame (0x%Lx) into slot (0x%x) - curr next log (%p) phys (%p)", getName(), this, pTD, pTD->_frameNumber, pEP->inSlot, _logicalPeriodicList[pEP->inSlot], USBToHostLong(_periodicList[pEP->inSlot])); pTD->print(6); prevIsochLE = OSDynamicCast(AppleEHCIIsochListElement, _logicalPeriodicList[pEP->inSlot]); while (prevIsochLE) { prevSITD = OSDynamicCast(AppleEHCISplitIsochTransferDescriptor, prevIsochLE); if (prevSITD) { if (pEP->direction == kUSBIn) { // IN SITDs have to be linked AFTER // a) any DUMMY SITDs on the same TT // b) any OUT SITDs on the same TT if (prevSITD->_pEndpoint->hiPtr == pEP->hiPtr) { if (prevSITD->_isDummySITD) { USBLog(7, "%s[%p]::AddIsocFramesToSchedule - found previous DUMMY SITD (%p) on same TT - linking after", getName(), this, prevSITD); linkAfter = prevSITD; } else if (prevSITD->_pEndpoint->direction == kUSBOut) { USBLog(7, "%s[%p]::AddIsocFramesToSchedule - found previous OUT SITD (%p) on same TT - linking after", getName(), this, prevSITD); linkAfter = prevSITD; } } } else { // OUT transaction. check frame ordering UInt8 oldStart = FirstScheduledSSMicroFrame(prevSITD->_pEndpoint); UInt8 myStart = FirstScheduledSSMicroFrame(pTD->_pEndpoint); UInt8 oldEnd = LastScheduledSSMicroFrame(prevSITD->_pEndpoint); UInt8 myEnd = LastScheduledSSMicroFrame(pTD->_pEndpoint); if ((oldStart < myStart) || ((oldStart == myStart) && (oldEnd < myEnd))) { USBLog(6, "%s[%p]::AddIsocFramesToSchedule - me[%d:%d] linking AFTER previous [%d:%d]", getName(), this, myStart, myEnd, oldStart, oldEnd); linkAfter = prevSITD; } else { USBLog(6, "%s[%p]::AddIsocFramesToSchedule - me[%d:%d] ignoring previous [%d:%d]", getName(), this, myStart, myEnd, oldStart, oldEnd); } } } prevIsochLE = OSDynamicCast(AppleEHCIIsochListElement, prevIsochLE->_logicalNext); } if (linkAfter) { pTD->SetPhysicalLink(linkAfter->GetPhysicalLink()); pTD->_logicalNext = linkAfter->_logicalNext; linkAfter->_logicalNext = pTD; linkAfter->SetPhysicalLink(HostToUSBLong(pTD->GetPhysicalAddrWithType())); } else { pTD->SetPhysicalLink(_periodicList[pEP->inSlot]); pTD->_logicalNext = _logicalPeriodicList[pEP->inSlot]; _logicalPeriodicList[pEP->inSlot] = pTD; _periodicList[pEP->inSlot] = HostToUSBLong(pTD->GetPhysicalAddrWithType()); } pEP->scheduledTDs++; USBLog(7, "%s[%p]::AddIsocFramesToSchedule - _periodicList[%x]:%x", getName(), this, pEP->inSlot, USBToHostLong(_periodicList[pEP->inSlot])); // pTD->print(); // and fetch the next one pTD = GetTDfromToDoList(pEP); } currFrame++; pEP->inSlot = nextSlot; USBLog(7, "%s[%p]::AddIsocFramesToSchedule - pEP->inSlot is now 0x%x", getName(), this, pEP->inSlot); } while(pTD != NULL); USBLog(7, "%s[%p]::AddIsocFramesToSchedule - finished, currFrame: %Lx", getName(), this, GetFrameNumber() ); } void AppleUSBEHCI::ProcessDoneQueueEntry(OSObject *target, thread_call_param_t endpointPtr) { AppleUSBEHCI * me = OSDynamicCast(AppleUSBEHCI, target); AppleEHCIIsochEndpointPtr pEP = (AppleEHCIIsochEndpointPtr) endpointPtr; if (!me || !pEP) return; me->retain(); me->ReturnIsocDoneQueue(pEP); me->release(); } void AppleUSBEHCI::ReturnIsocDoneQueue(AppleEHCIIsochEndpointPtr pEP) { AppleEHCIIsochListElement *pTD = GetTDfromDoneQueue(pEP); IOUSBIsocFrame *pFrames = NULL; USBLog(7, "%s[%p]::ReturnIsocDoneQueue (%p)", getName(), this, pEP); if (pTD) { // HS always stores the big frame pointer in the first TD pFrames = pTD->_pFrames; } else { USBLog(7, "%s[%p]::ReturnIsocDoneQueue - no TDs to return", getName(), this); } while(pTD) { USBLog(7, "%s[%p]::ReturnIsocDoneQueue: TD %p", getName(), this, pTD); if( pTD->_completion.action != NULL) { IOUSBIsocCompletionAction pHandler; pHandler = pTD->_completion.action; pTD->_completion.action = NULL; USBLog(7, "%s[%p]::ReturnIsocDoneQueue- calling handler(%p, %p, %p, %p)", getName(), this, pTD->_completion.target, pTD->_completion.parameter, pEP->accumulatedStatus, pFrames); (*pHandler) (pTD->_completion.target, pTD->_completion.parameter, pEP->accumulatedStatus, pFrames); // if the accumulated status is aborted, then we need to keep that status until we are done // otherwise the status will be in the endpoint when we get to the callback case and will // be reset afterwards if (pEP->accumulatedStatus != kIOReturnAborted) { if (pEP->accumulatedStatus != kIOReturnSuccess && (pEP->accumulatedStatus != kIOReturnUnderrun) ) USBLog(6, "%s[%p]::ReturnIsocDoneQueue - resetting status from %p", getName(), this, pEP->accumulatedStatus); pEP->accumulatedStatus = kIOReturnSuccess; } pTD->Deallocate(this); pTD = GetTDfromDoneQueue(pEP); if (pTD) pFrames = pTD->_pFrames; } else { pTD->Deallocate(this); pTD = GetTDfromDoneQueue(pEP); } } } IOReturn AppleUSBEHCI::UIMHubMaintenance(USBDeviceAddress highSpeedHub, UInt32 highSpeedPort, UInt32 command, UInt32 flags) { AppleUSBEHCIHubInfo *hiPtr; switch (command) { case kUSBHSHubCommandAddHub: USBLog(7, "%s[%p]::UIMHubMaintenance - adding hub %d with flags %x", getName(), this, highSpeedHub, flags); hiPtr = AppleUSBEHCIHubInfo::GetHubInfo(&_hsHubs, highSpeedHub, 0); if (hiPtr) { USBLog(7, "%s[%p]::UIMHubMaintenance - adding hub which already exists (%d)", getName(), this, highSpeedHub); AppleUSBEHCIHubInfo::DeleteHubInfoZero(&_hsHubs, highSpeedHub); } hiPtr = AppleUSBEHCIHubInfo::NewHubInfoZero(&_hsHubs, highSpeedHub, flags); if (!hiPtr) return kIOReturnNoMemory; USBLog(7, "%s[%p]::UIMHubMaintenance - done creating new hub (%p) for address (%d)", getName(), this, hiPtr, highSpeedHub); break; case kUSBHSHubCommandRemoveHub: USBLog(7, "%s[%p]::UIMHubMaintenance - deleting hub %d", getName(), this, highSpeedHub); AppleUSBEHCIHubInfo::DeleteHubInfoZero(&_hsHubs, highSpeedHub); // remove master device and all sub devices break; default: return kIOReturnBadArgument; } return kIOReturnSuccess; } #define kEHCIUIMScratchFirstActiveFrame 0 void AppleUSBEHCI::ReturnOneTransaction( EHCIGeneralTransferDescriptor *transaction, AppleEHCIQueueHead *pED, AppleEHCIQueueHead *pEDBack, IOReturn err) { // make sure it is halted, since we should leave it linked HaltAsyncEndpoint(pED, pEDBack); // USBLog(6, "ReturnOneTransaction Enter with transaction %p",transaction); while(transaction!= NULL) { if(transaction->lastTDofTransaction) { transaction = transaction->pLogicalNext; break; } transaction = transaction->pLogicalNext; // USBLog(7, "ReturnOneTransaction next transaction %p",transaction); } // USBLog(6, "ReturnOneTransaction going with transaction %p",transaction); if(transaction == NULL) { // This works, sort of, NULL for an end transction means remove them all. // But there will be no callback USBLog(1, "%s[%p]::ReturnOneTransaction - returning all TDs on the queue", getName(), this); } else { } returnTransactions(pED, transaction, err); } UInt32 AppleUSBEHCI::findBufferRemaining(AppleEHCIQueueHead *pED) { UInt32 flags, bufferSizeRemaining; flags = USBToHostLong(pED->GetSharedLogical()->qTDFlags); bufferSizeRemaining = (flags & kEHCITDFlags_Bytes) >> kEHCITDFlags_BytesPhase; return(bufferSizeRemaining); } void AppleUSBEHCI::CheckEDListForTimeouts(AppleEHCIQueueHead *head) { AppleEHCIQueueHead *pED = head; AppleEHCIQueueHead *pEDBack = NULL, *pEDBack1 = NULL; IOPhysicalAddress pTDPhys; EHCIGeneralTransferDescriptor *pTD; UInt32 noDataTimeout; UInt32 completionTimeout; UInt32 rem; UInt32 curFrame = GetFrameNumber32(); for (; pED != 0; pED = (AppleEHCIQueueHead *)pED->_logicalNext) { USBLog(7, "%s[%p]::CheckEDListForTimeouts - checking ED [%p]", getName(), this, pED); pED->print(7); // Need to keep a note of the previous ED for back links. Usually I'd // put a pEDBack = pED at the end of the loop, but there are lots of // continues in this loop so it was getting skipped (and unlinking the // entire async schedule). These lines get the ED from the previous // interation in pEDBack. pEDBack = pEDBack1; pEDBack1 = pED; // OHCI gets phys pointer and logicals that, that seems a little complicated, so // I'll get the logical pointer and compare it to the phys. If they're different, // this transaction has only just got to the head and the previous one(s) haven't // been scavenged yet. Assume its not a good candidate for a timeout. // get the top TD pTDPhys = USBToHostLong(pED->GetSharedLogical()->CurrqTDPtr) & kEHCIEDTDPtrMask; pTD = pED->_qTD; if (!pTD) { USBLog(6, "%s[%p]::CheckEDListForTimeouts - no TD", getName(), this); continue; } if (!pTD->command) { USBLog(7, "%s[%p]::CheckEDListForTimeouts - found a TD without a command - moving on", getName(), this); continue; } if (pTD == pED->_TailTD) { USBLog(1, "%s[%p]::CheckEDListForTimeouts - ED (%p) - TD is TAIL but there is a command - pTD (%p)", getName(), this, pED, pTD); pED->print(5); } if(pTDPhys != pTD->pPhysical) { USBLog(5, "%s[%p]::CheckEDListForTimeouts - pED (%p) - mismatched logical and physical - TD (%p) will be scavenged later", getName(), this, pED, pTD); pED->print(5); continue; } noDataTimeout = pTD->command->GetNoDataTimeout(); completionTimeout = pTD->command->GetCompletionTimeout(); if (completionTimeout) { UInt32 firstActiveFrame = pTD->command->GetUIMScratch(kEHCIUIMScratchFirstActiveFrame); if (!firstActiveFrame) { pTD->command->SetUIMScratch(kEHCIUIMScratchFirstActiveFrame, curFrame); continue; } if ((curFrame - firstActiveFrame) >= completionTimeout) { USBLog(2, "%s[%p]::CheckEDListForTimeout - Found a TD [%p] on QH [%p] past the completion deadline, timing out! (%x - %x)", getName(), this, pTD, pED, curFrame, firstActiveFrame); USBError(1, "%s[%p]::Found a transaction past the completion deadline on bus %d, timing out!", getName(), this, _busNumber); pED->print(2); ReturnOneTransaction(pTD, pED, pEDBack, kIOUSBTransactionTimeout); continue; } } if (!noDataTimeout) continue; if (!pTD->lastFrame || (pTD->lastFrame > curFrame)) { // this pTD is not a candidate yet, remember the frame number and go on pTD->lastFrame = curFrame; pTD->lastRemaining = findBufferRemaining(pED /*pTD get value from overlay area*/); continue; } rem = findBufferRemaining(pED /*pTD get value from overlay area*/); if (pTD->lastRemaining != rem) { // there has been some activity on this TD. update and move on pTD->lastRemaining = rem; continue; } if ((curFrame - pTD->lastFrame) >= noDataTimeout) { USBLog(2, "%s[%p]CheckEDListForTimeout: Found a transaction which hasn't moved in 5 seconds, timing out! (%x - %x)", getName(), this, pTD, curFrame, pTD->lastFrame); USBError(1, "%s[%p]::Found a transaction which hasn't moved in 5 seconds on bus %d, timing out!", getName(), this, _busNumber); //printED(pED); //printTD(pTD); //printAsyncQueue(); ReturnOneTransaction(pTD, pED, pEDBack, kIOUSBTransactionTimeout); //printED(pED); //printTD(pTD); //printAsyncQueue(); continue; } } } //============================================================================================= // // UIMCheckForTimeouts // // This routine is called every kUSBWatchdogTimeoutMS by the controller. It is useful for // periodic checks in the UIM // //============================================================================================= // void AppleUSBEHCI::UIMCheckForTimeouts(void) { AbsoluteTime currentTime; AbsoluteTime lastRootHubChangeTime; UInt64 elapsedTime = 0; bool allPortsDisconnected = false; // If we are not active anymore or if we're in ehciBusStateOff, then don't check for timeouts // if ( isInactive() || (_onCardBus && _pcCardEjected) || !_ehciAvailable || (_ehciBusState != kEHCIBusStateRunning)) return; // Check to see if our control or bulk lists have a TD that has timed out // CheckEDListForTimeouts(_AsyncHead); // See if it's time to check for Root Hub inactivity // if ( !_idleSuspend ) { // Check to see if it's been kEHCICheckForRootHubConnectionsPeriod seconds // since we last checked this port // clock_get_uptime( ¤tTime ); SUB_ABSOLUTETIME(¤tTime, &_lastCheckedTime ); absolutetime_to_nanoseconds(currentTime, &elapsedTime); elapsedTime /= 1000000000; // Convert to seconds from nanoseconds if ( elapsedTime >= kEHCICheckForRootHubConnectionsPeriod ) { USBLog(6,"%s[%p] Time to check for root hub inactivity on bus %d", getName(), this, _busNumber); clock_get_uptime( &_lastCheckedTime ); // Check to see if the root hub has been inactive for kEHCICheckForRootHubInactivityPeriod seconds // allPortsDisconnected = RootHubAreAllPortsDisconnected(); if ( allPortsDisconnected ) { // Find the last time we had a change in the root hub. If it's been 30 secs or // more, then we are ready to suspend the ports // lastRootHubChangeTime = LastRootHubPortStatusChanged( false ); clock_get_uptime( ¤tTime ); SUB_ABSOLUTETIME(¤tTime, &lastRootHubChangeTime ); absolutetime_to_nanoseconds(currentTime, &elapsedTime); elapsedTime /= 1000000000; if ( elapsedTime >= kEHCICheckForRootHubInactivityPeriod ) { // Yes, nothing connected to this root hub and it's been more than kEHCICheckForRootHubInactivityPeriod secs since // we last saw something happen on it, so let's suspend that bus // USBLog(5,"%s[%p] Time to suspend the ports of bus %d", getName(), this, _busNumber); setPowerState( kEHCISetPowerLevelIdleSuspend, this); } } } } } UInt8 AppleUSBEHCI::FirstScheduledSSMicroFrame(AppleEHCIIsochEndpointPtr pEP) { UInt8 ssFlags = pEP->startSplitFlags; UInt8 ret = 0; if (!ssFlags) return 0; while ((ssFlags & 1) == 0) { ret++; ssFlags >>= 1; } USBLog(6, "%s[%p]::FirstScheduledSSMicroFrame(%p): returning %d", getName(), this, pEP, ret); return ret; } UInt8 AppleUSBEHCI::LastScheduledSSMicroFrame(AppleEHCIIsochEndpointPtr pEP) { UInt8 ssFlags = pEP->startSplitFlags; UInt8 ret = 7; if (!ssFlags) return 0; while ((ssFlags & 0x80) == 0) { ret--; ssFlags <<= 1; } USBLog(6, "%s[%p]::LastScheduledSSMicroFrame(%p): returning %d", getName(), this, pEP, ret); return ret; } #if 0 void AppleUSBEHCI::printED(AppleEHCIQueueHead * pED) { if(pED == NULL) { USBLog(7, "%s[%p]::printED: Attempt to print null ED", getName(), this); return; } USBLog(5, "%s[%p]::printED: ----------------pED at %p", getName(), this, pED); USBLog(5, "%s[%p]::printED: nextQH: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->nextQH)); USBLog(5, "%s[%p]::printED: flags: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->flags)); USBLog(5, "%s[%p]::printED: slags: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->splitFlags)); USBLog(5, "%s[%p]::printED: CurrqTD: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->CurrqTDPtr)); USBLog(5, "%s[%p]::printED: NextqTD: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->NextqTDPtr)); USBLog(5, "%s[%p]::printED: AltqTD: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->AltqTDPtr)); USBLog(5, "%s[%p]::printED: TDFlags: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->qTDFlags)); USBLog(5, "%s[%p]::printED: BuffPtr[0]: %p", getName(), this, USBToHostLong(pED->GetSharedLogical()->BuffPtr[0])); USBLog(5, "%s[%p]::printED: pLgNext: %p", getName(), this, pED->_logicalNext); USBLog(5, "%s[%p]::printED: pEDSharedPhys: %p", getName(), this, pED->_sharedPhysical); USBLog(5, "%s[%p]::printED: head qTD: %p", getName(), this, pED->qTD); USBLog(5, "%s[%p]::printED: last qTD: %p", getName(), this, pED->TailTD); if(pED->qTD != NULL) { USBLog(5, "%s[%p]::printED: head qTD physical: %p", getName(), this, pED->qTD->pPhysical); } USBLog(5, "%s[%p]::printED: -----------------------------", getName(), this); } #endif