diff -r 000000000000 -r a41df078684a kernel/eka/drivers/usbc/usbdma.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/drivers/usbc/usbdma.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,974 @@ +// Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32\drivers\usbc\usbdma.cpp +// LDD for USB Device driver stack: +// Management of DMA-capable data buffers. +// +// + +/** + @file usbdma.cpp + @internalTechnology +*/ + +#include + + +#if defined(_DEBUG) +static const char KUsbPanicLdd[] = "USB LDD"; +#endif + + +TDmaBuf::TDmaBuf(TUsbcEndpointInfo* aEndpointInfo, TInt aBandwidthPriority) + : iBufBasePtr(NULL), + iCurrentDrainingBuffer(NULL), + iCurrentPacket(0), + iCurrentPacketIndexArray(NULL), + iCurrentPacketSizeArray(NULL) + { + iMaxPacketSize = aEndpointInfo->iSize; + iEndpointType = aEndpointInfo->iType; + + switch (aEndpointInfo->iType) + { + case KUsbEpTypeControl: + iBufSz = KUsbcDmaBufSzControl; + iNumberofBuffers = KUsbcDmaBufNumControl; + break; + case KUsbEpTypeIsochronous: + iBufSz = KUsbcDmaBufSzIsochronous; + iNumberofBuffers = KUsbcDmaBufNumIsochronous; + break; + case KUsbEpTypeBulk: + { + if (aEndpointInfo->iDir == KUsbEpDirOut) + { + const TInt priorityOUT = aBandwidthPriority & 0x0f; + iBufSz = KUsbcDmaBufSizesBulkOUT[priorityOUT]; + } + else + { + const TInt priorityIN = (aBandwidthPriority >> 4) & 0x0f; + iBufSz = KUsbcDmaBufSizesBulkIN[priorityIN]; + } + iNumberofBuffers = KUsbcDmaBufNumBulk; + } + break; + case KUsbEpTypeInterrupt: + iBufSz = KUsbcDmaBufSzInterrupt; + iNumberofBuffers = KUsbcDmaBufNumInterrupt; + break; + default: + iBufSz = 0; + iNumberofBuffers = 0; + } + + if (aEndpointInfo->iDir == KUsbEpDirIn) + { + iNumberofBuffers = 1; // IN endpoints only have 1 buffer + } + + for (TInt i = 0; i < KUsbcDmaBufNumMax; i++) + { + // Buffer logical addresses (pointers) + iBuffers[i] = NULL; + // Buffer physical addresses + iBufferPhys[i] = 0; + // Packet indexes base array + iPacketIndex[i] = NULL; + // Packet sizes base array + iPacketSize[i] = NULL; + } + } + + +TInt TDmaBuf::Construct(TUsbcEndpointInfo* aEndpointInfo) + { + if (aEndpointInfo->iDir != KUsbEpDirIn) + { + // IN endpoints don't need a packet array + + // At most 2 packets (clump of max packet size packets) + possible zlp + TUsbcPacketArray* bufPtr = iPacketInfoStorage; + // this divides up the packet indexing & packet size array over the number of buffers + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::Construct() array base=0x%08x", bufPtr)); + for (TInt i = 0; i < iNumberofBuffers; i++) + { + iPacketIndex[i] = bufPtr; + bufPtr += KUsbcDmaBufMaxPkts; + iPacketSize[i] = bufPtr; + bufPtr += KUsbcDmaBufMaxPkts; + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::Construct() packetIndex[%d]=0x%08x packetSize[%d]=0x%08x", + i, iPacketIndex[i], i, iPacketSize[i])); + } + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::Construct() IN endpoint")); + } + Flush(); + return KErrNone; + } + + +TDmaBuf::~TDmaBuf() + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::~TDmaBuf()")); + } + + +TUint8* TDmaBuf::SetBufferBase(TUint8* aBase) + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::SetBufferBase base=0x%08x size=0x%08x", aBase, iBufSz)); + TUint8* bufPtr = aBase; + iBufBasePtr = aBase; + for (TInt i = 0; i < iNumberofBuffers; i++) + { + iDrainable[i] = iCanBeFreed[i] = EFalse; + iBuffers[i] = bufPtr; + iBufferPhys[i] = Epoc::LinearToPhysical((TLinAddr)bufPtr); + bufPtr += iBufSz; + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::SetBufferBase() iBuffers[%d]=0x%08x", i, iBuffers[i])); + } + return bufPtr; + } + + +TInt TDmaBuf::BufferTotalSize() const + { + return iBufSz * iNumberofBuffers; + } + + +TUint8* TDmaBuf::BufferBase() const + { + return iBufBasePtr; + } + + +void TDmaBuf::SetMaxPacketSize(TInt aSize) + { + iMaxPacketSize = aSize; + } + + +void TDmaBuf::Flush() + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::Flush %x", this)); + iRxActive = EFalse; + iTxActive = EFalse; + iExtractOffset = 0; + iTotalRxBytesAvail = 0; + iTotalRxPacketsAvail = 0; + iCurrentDrainingBufferIndex = KUsbcInvalidBufferIndex; + iCurrentFillingBufferIndex = 0; + iDrainQueueIndex = KUsbcInvalidDrainQueueIndex; + for (TInt i = 0; i < KUsbcDmaBufNumMax; i++) + { + iDrainable[i] = EFalse; + iCanBeFreed[i] = EFalse; + iNumberofBytesRx[i] = 0; + iNumberofPacketsRx[i] = 0; + iError[i] = KErrGeneral; + iDrainQueue[i] = KUsbcInvalidBufferIndex; +#if defined(USBC_LDD_BUFFER_TRACE) + iFillingOrderArray[i] = 0; + iNumberofBytesRxRemain[i] = 0; + iNumberofPacketsRxRemain[i] = 0; +#endif + } + // Drain queue is 1 oversized + iDrainQueue[KUsbcDmaBufNumMax] = KUsbcInvalidBufferIndex; + +#if defined(USBC_LDD_BUFFER_TRACE) + iFillingOrder = 0; + iDrainingOrder = 0; +#endif + } + + +void TDmaBuf::RxSetActive() + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxSetActive %x", this)); + iRxActive = ETrue; + } + + +void TDmaBuf::RxSetInActive() + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxSetInActive %x", this)); + iRxActive = EFalse; + } + + +TBool TDmaBuf::RxIsActive() + { + return iRxActive; + } + + +void TDmaBuf::TxSetActive() + { + iTxActive = ETrue; + } + + +void TDmaBuf::TxSetInActive() + { + iTxActive = EFalse; + } + + +TBool TDmaBuf::TxIsActive() + { + return iTxActive; + } + + +/**************************** Rx DMA Buffer Access *************************/ + +void TDmaBuf::ModifyTotalRxBytesAvail(TInt aVal) + { + iTotalRxBytesAvail += aVal; + } + + +void TDmaBuf::ModifyTotalRxPacketsAvail(TInt aVal) + { + iTotalRxPacketsAvail += aVal; + } + + +TBool TDmaBuf::AdvancePacket() + { + ModifyTotalRxPacketsAvail(-1); + TBool r = ETrue; + __ASSERT_DEBUG((iCurrentDrainingBufferIndex >= 0), + Kern::Fault(KUsbPanicLdd, __LINE__)); + if (++iCurrentPacket >= iNumberofPacketsRx[iCurrentDrainingBufferIndex]) + { + r = NextDrainableBuffer(); + } + iExtractOffset = 0; + __ASSERT_DEBUG((iCurrentDrainingBufferIndex == KUsbcInvalidBufferIndex) || + (iCurrentPacket < KUsbcDmaBufMaxPkts), + Kern::Fault(KUsbPanicLdd, __LINE__)); + return r; + } + + +TInt TDmaBuf::PeekNextPacketSize() + { + TUint pkt = iCurrentPacket; + TInt index = iCurrentDrainingBufferIndex; + TInt size = -1; + if (pkt >= iNumberofPacketsRx[index]) + { + index = PeekNextDrainableBuffer(); + pkt = 0; + } + + if ((index != KUsbcInvalidBufferIndex) && iNumberofPacketsRx[index]) + { + const TUsbcPacketArray* sizeArray = iPacketSize[index]; + size = (TInt)sizeArray[pkt]; + } + + __ASSERT_DEBUG((iCurrentDrainingBufferIndex == KUsbcInvalidBufferIndex) || + (iCurrentPacket < KUsbcDmaBufMaxPkts), + Kern::Fault(KUsbPanicLdd, __LINE__)); + return size; + } + + +inline TInt TDmaBuf::GetCurrentError() + { + // USB bus errors are v.rare. To avoid having an error code attached to every packet since + // almost every errorcode will be KErrNone, we have a single error code per buffer + // If the error code is != KErrNone then it refers to the LAST packet in the buffer + TInt errorCode = KErrNone; + //Check the index, it's not equal to negative (-1) value defined in + //KUsbcInvalidBufferIndex. + __ASSERT_DEBUG((iCurrentDrainingBufferIndex >= 0), + Kern::Fault(KUsbPanicLdd, __LINE__)); + + if (iError[iCurrentDrainingBufferIndex] != KErrNone) + { + // See if we are at the last packet + if ((iCurrentPacket + 1) == iNumberofPacketsRx[iCurrentDrainingBufferIndex]) + { + errorCode = iError[iCurrentDrainingBufferIndex]; + } + } + return errorCode; + } + + +// used to decide whether a client read can complete straight away +TBool TDmaBuf::IsReaderEmpty() + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::IsReaderEmpty iTotalRxPacketsAvail=%d", + iTotalRxPacketsAvail)); + return (iTotalRxPacketsAvail == 0); + } + + +void TDmaBuf::ReadXferComplete(TInt aNoBytesRecv, TInt aNoPacketsRecv, TInt aErrorCode) + { + // Adjust pending packet + if ((aNoBytesRecv == 0) && (aErrorCode != KErrNone)) + { + // Make the buffer available for reuse + iDrainable[iCurrentFillingBufferIndex] = EFalse; + return; + } + + ModifyTotalRxBytesAvail(aNoBytesRecv); + ModifyTotalRxPacketsAvail(aNoPacketsRecv); + iNumberofBytesRx[iCurrentFillingBufferIndex] = aNoBytesRecv; + iNumberofPacketsRx[iCurrentFillingBufferIndex] = aNoPacketsRecv; + +#if defined(USBC_LDD_BUFFER_TRACE) + iNumberofBytesRxRemain[iCurrentFillingBufferIndex] = aNoBytesRecv; + iNumberofPacketsRxRemain[iCurrentFillingBufferIndex] = aNoPacketsRecv; +#endif + + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::ReadXferComplete 2 # of bytes=%d # of packets=%d", + iTotalRxBytesAvail, iTotalRxPacketsAvail)); + iDrainable[iCurrentFillingBufferIndex] = ETrue; + iError[iCurrentFillingBufferIndex] = aErrorCode; + AddToDrainQueue(iCurrentFillingBufferIndex); + if (iCurrentDrainingBufferIndex == KUsbcInvalidBufferIndex) + { + NextDrainableBuffer(); + } + } + + +TInt TDmaBuf::RxGetNextXfer(TUint8*& aBufferAddr, TUsbcPacketArray*& aIndexArray, + TUsbcPacketArray*& aSizeArray, TInt& aLength, TPhysAddr& aBufferPhys) + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxGetNextXfer 1")); + if (RxIsActive()) + { + __KTRACE_OPT(KUSB, Kern::Printf(" ---> RxIsActive, returning")); + return KErrInUse; + } + + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxGetNextXfer Current buffer=%d", + iCurrentFillingBufferIndex)); + if (iDrainable[iCurrentFillingBufferIndex]) + { + // If the controller refused the last read request, then the current buffer will still be marked + // as !Drainable, because the controller never completed the read to the ldd. and therefore the buffer + // can be reused. + if (!NextFillableBuffer()) + { + return KErrNoMemory; + } + } + + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxGetNextXfer New buffer=%d", + iCurrentFillingBufferIndex)); + aBufferAddr = iBuffers[iCurrentFillingBufferIndex]; + aBufferPhys = iBufferPhys[iCurrentFillingBufferIndex]; + aIndexArray = iPacketIndex[iCurrentFillingBufferIndex]; + aSizeArray = iPacketSize[iCurrentFillingBufferIndex]; + aLength = iBufSz; + +#if defined(USBC_LDD_BUFFER_TRACE) + iFillingOrderArray[iCurrentFillingBufferIndex] = ++iFillingOrder; +#endif + + return KErrNone; + } + + +TInt TDmaBuf::RxCopyPacketToClient(DThread* aThread, TClientBuffer *aTcb, TInt aLength) + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxCopyPacketToClient 1")); + +#if defined(USBC_LDD_BUFFER_TRACE) + const TInt numPkts = NoRxPackets(); + const TInt numPktsAlt = NoRxPacketsAlt(); + const TInt numBytes = RxBytesAvailable(); + const TInt numBytesAlt = NoRxBytesAlt(); + + if (numPkts != numPktsAlt) + { + Kern::Printf( + "TDmaBuf::RxCopyPacketToClient: Error: #pkts mismatch global=%d actual=%d", + numPkts, numPktsAlt); + } + if (numBytes != numBytesAlt) + { + Kern::Printf( + "TDmaBuf::RxCopyPacketToClient: Error: #bytes mismatch global=%d actual=%d", + numBytes, numBytesAlt); + } + if ((numPkts == 0) && (numBytes !=0)) + { + Kern::Printf( + "TDmaBuf::RxCopyPacketToClient: Error: global bytes & pkts mismatch pkts=%d bytes=%d", + numPkts, numBytes); + } + if ((numPktsAlt == 0) && (numBytesAlt !=0)) + { + Kern::Printf( + "TDmaBuf::RxCopyPacketToClient: Error: actual bytes & pkts mismatch pkts=%d bytes=%d", + numPktsAlt, numBytesAlt); + } +#endif + + if (!NoRxPackets()) + return KErrNotFound; + + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxCopyPacketToClient 2")); + // the next condition should be true because we have some packets available + // coverity[var_tested_neg] + if (iCurrentDrainingBufferIndex == KUsbcInvalidBufferIndex) + { + // Marked as Coverity "Intentional" as the member variable + // iCurrentDrainingBufferIndex is attentionaly negative, from previous + // initialization to KUsbcInvalidBufferIndex (which equals -1). + if (!NextDrainableBuffer()) + return KErrNotFound; + } + + __ASSERT_DEBUG((iCurrentDrainingBufferIndex >= 0 ), + Kern::Fault(KUsbPanicLdd, __LINE__)); + + if (!iDrainable[iCurrentDrainingBufferIndex]) + return KErrNotFound; + + // Calculate copy-from address & adjust for the fact that + // some data may have already been read from the packet + TUint8* logicalSrc = iCurrentDrainingBuffer + iCurrentPacketIndexArray[iCurrentPacket] + iExtractOffset; + TInt packetSz = iCurrentPacketSizeArray[iCurrentPacket]; + TInt thisPacketSz = packetSz - iExtractOffset; + TInt errorCode; + // try and sort out what a "packet" might mean. + // in a multi-packet dma environment, we might see super-packets + // i.e. we might just see one packet, maybe 4K or so long, made of lots of small packets + // Since we don't know where the packet boundaries will be, we have to assume that + // any 'packet' larger than the max packet size of the ep is, in fact, a conglomeration + // of smaller packets. However, for the purposes of the packet count, this is still regarded + // as a single packet and the packet count only decremented when it is consumed. + // As before, if the user fails to read an entire packet out then the next packet is moved onto anyway + // To be safe the user must always supply a buffer of at least max packet size bytes. + if (thisPacketSz > iMaxPacketSize) + { + // Multiple packets left in buffer + // calculate number of bytes to end of packet + if (iEndpointType == KUsbEpTypeBulk) + { + thisPacketSz = iMaxPacketSize - (iExtractOffset & (iMaxPacketSize - 1)); + } + else + { + thisPacketSz = iMaxPacketSize - (iExtractOffset % iMaxPacketSize); + } + errorCode = KErrNone; + } + else + { + errorCode = GetCurrentError(); // single packet left + } + + iExtractOffset += thisPacketSz; // iExtractOffset is now at the end of the real or notional packet + + ModifyTotalRxBytesAvail(-thisPacketSz); +#if defined(USBC_LDD_BUFFER_TRACE) + iNumberofBytesRxRemain[iCurrentDrainingBufferIndex] -= thisPacketSz; +#endif + // this can only be untrue if the "packet" is a conglomeration of smaller packets: + if (iExtractOffset == packetSz) + { + // packet consumed, advance to next packet in buffer +#if defined(USBC_LDD_BUFFER_TRACE) + iNumberofPacketsRxRemain[iCurrentDrainingBufferIndex] -= 1; +#endif + AdvancePacket(); + } + + TPtrC8 des(logicalSrc, thisPacketSz); + TInt r=Kern::ThreadBufWrite(aThread, aTcb, des, 0, 0, aThread); + if (r == KErrNone) + { + r = errorCode; + } + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxCopyPacketToClient 3")); + + FreeDrainedBuffers(); + + // Use this error code to complete client read request: + return r; + } + + +TInt TDmaBuf::RxCopyDataToClient(DThread* aThread, TClientBuffer *aTcb, TInt aLength, TUint32& aDestOffset, + TBool aRUS, TBool& aCompleteNow) + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxCopyDataToClient 1")); + aCompleteNow = ETrue; + +#if defined(USBC_LDD_BUFFER_TRACE) + const TInt numPkts = NoRxPackets(); + const TInt numPktsAlt = NoRxPacketsAlt(); + const TInt numBytes = RxBytesAvailable(); + const TInt numBytesAlt = NoRxBytesAlt(); + + if (numPkts != numPktsAlt) + { + Kern::Printf( + "TDmaBuf::RxCopyDataToClient: Error: #pkts mismatch global=%d actual=%d", + numPkts, numPktsAlt); + } + if (numBytes != numBytesAlt) + { + Kern::Printf( + "TDmaBuf::RxCopyDataToClient: Error: #bytes mismatch global=%d actual=%d", + numBytes, numBytesAlt); + } + if ((numPkts == 0) && (numBytes != 0)) + { + Kern::Printf( + "TDmaBuf::RxCopyDataToClient: Error: global bytes & pkts mismatch pkts=%d bytes=%d", + numPkts, numBytes); + } + if ((numPktsAlt == 0) && (numBytesAlt != 0)) + { + Kern::Printf( + "TDmaBuf::RxCopyDataToClient: Error: actual bytes & pkts mismatch pkts=%d bytes=%d", + numPktsAlt, numBytesAlt); + } +#endif + + if (!NoRxPackets()) + { + return KErrNotFound; + } + + // coverity[var_tested_neg] + if (iCurrentDrainingBufferIndex == KUsbcInvalidBufferIndex) + { + // Marked as Coverity "Inentional" as the member variable + // iCurrentDrainingBufferIndex is attentionaly negative, from previous + // initialization to KUsbcInvalidBufferIndex (which equals -1). + + if (!NextDrainableBuffer()) + { +#if defined(USBC_LDD_BUFFER_TRACE) + Kern::Printf("TDmaBuf::RxCopyDataToClient: Error: No buffer draining=%d, packets=%d", + iCurrentDrainingBufferIndex, iTotalRxPacketsAvail); +#endif + return KErrNotFound; + } + } +#if defined(USBC_LDD_BUFFER_TRACE) + + __ASSERT_DEBUG((iCurrentDrainingBufferIndex >= 0 ), + Kern::Fault(KUsbPanicLdd, __LINE__)); + + if (iDrainingOrder != iFillingOrderArray[iCurrentDrainingBufferIndex]) + { + Kern::Printf("!!! Out of Order Draining TDmaBuf::RxCopyDataToClient 10 draining=%d", + iCurrentDrainingBufferIndex); + } +#endif + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::RxCopyDataToClient 2")); + + TUint8* blockStartAddr = iCurrentDrainingBuffer + iCurrentPacketIndexArray[iCurrentPacket] + iExtractOffset; + TUint8* lastEndAddr = blockStartAddr; // going to track the contiguity of the memory + TUint8* thisStartAddr = blockStartAddr; + TInt toDo = Min(aLength - (TInt)aDestOffset, iTotalRxBytesAvail); +#if defined(USBC_LDD_BUFFER_TRACE) + TInt bufnum = iCurrentDrainingBufferIndex; +#endif + TInt errorCode = KErrNone; + TBool isShortPacket = EFalse; + const TInt maxPacketSizeMask = iMaxPacketSize - 1; + do + { +#if defined(USBC_LDD_BUFFER_TRACE) + if (bufnum != iCurrentDrainingBufferIndex) + { + bufnum = iCurrentDrainingBufferIndex; + if (iDrainingOrder != iFillingOrderArray[iCurrentDrainingBufferIndex]) + { + Kern::Printf("!!! Out of Order Draining TDmaBuf::RxCopyDataToClient 20 draining=%d", + iCurrentDrainingBufferIndex); + } + } +#endif + if (errorCode == KErrNone) + { + errorCode = GetCurrentError(); + } + thisStartAddr = iCurrentDrainingBuffer + iCurrentPacketIndexArray[iCurrentPacket] + iExtractOffset; + const TInt thisPacketSize = iCurrentPacketSizeArray[iCurrentPacket]; + const TInt size = thisPacketSize - iExtractOffset; + if (aRUS) + { + if (iEndpointType == KUsbEpTypeBulk) + { + isShortPacket = (size < iMaxPacketSize) || (size & maxPacketSizeMask); + } + else + { + // this 'if' block is arranged to avoid a division on packet sizes <= iMaxPacketSize + isShortPacket = (size < iMaxPacketSize) || + ((size > iMaxPacketSize) && (size % iMaxPacketSize)); + } + } + TInt copySize = Min(size, toDo); + iExtractOffset += copySize; + toDo -= copySize; + if (thisStartAddr != lastEndAddr) + { + TInt bytesToCopy = lastEndAddr - blockStartAddr; + TInt r=CopyToUser(aThread, blockStartAddr, bytesToCopy, aTcb, aDestOffset); + if(r != KErrNone) + Kern::ThreadKill(aThread, EExitPanic, r, KUsbLDDKillCat); + blockStartAddr = thisStartAddr; + } + + ModifyTotalRxBytesAvail(-copySize); +#if defined(USBC_LDD_BUFFER_TRACE) + iNumberofBytesRxRemain[iCurrentDrainingBufferIndex] -= copySize; +#endif + lastEndAddr = thisStartAddr + copySize; + if (iExtractOffset == thisPacketSize) + { + // More data to copy, so need to access new packet +#if defined(USBC_LDD_BUFFER_TRACE) + iNumberofPacketsRxRemain[iCurrentDrainingBufferIndex] -= 1; +#endif + if (!AdvancePacket()) + { + break; // no more packets left + } + } + } while (toDo > 0 && !isShortPacket); + + if (thisStartAddr != lastEndAddr) + { + TInt bytesToCopy = lastEndAddr - blockStartAddr; + TInt r=CopyToUser(aThread, blockStartAddr, bytesToCopy, aTcb, aDestOffset); + if(r != KErrNone) + Kern::ThreadKill(aThread, EExitPanic, r, KUsbLDDKillCat); + } + + // If we have transferred the requested amount of data it is still possible that + // the next packet is a zlp which needs to be bumped over + + if (aRUS && (toDo == 0) && (iExtractOffset == 0) && (!isShortPacket) && (!IsReaderEmpty()) && + (PeekNextPacketSize() == 0)) + { + // swallow a zlp + isShortPacket = ETrue; +#if defined(USBC_LDD_BUFFER_TRACE) + iNumberofPacketsRxRemain[iCurrentDrainingBufferIndex] -= 1; +#endif + AdvancePacket(); + } + aCompleteNow = isShortPacket || (((TInt)aDestOffset) == aLength) || (errorCode != KErrNone); + + FreeDrainedBuffers(); + + // Use this error code to complete client read request + return errorCode; + } + + +inline TInt TDmaBuf::CopyToUser(DThread* aThread, const TUint8* aSourceAddr, + TInt aLength, TClientBuffer *aTcb, TUint32& aDestOffset) + { + TPtrC8 des(aSourceAddr, aLength); + TInt errorCode = Kern::ThreadBufWrite(aThread, aTcb, des, aDestOffset, KChunkShiftBy0, aThread); + if (errorCode == KErrNone) + { + aDestOffset += aLength; + } + return errorCode; + } + + +inline TInt TDmaBuf::NoRxPackets() const + { + return iTotalRxPacketsAvail; + } + + +inline void TDmaBuf::IncrementBufferIndex(TInt& aIndex) + { + if (++aIndex == iNumberofBuffers) + aIndex = 0; + } + + +TBool TDmaBuf::NextDrainableBuffer() + { + TBool r = EFalse; + if (iCurrentDrainingBufferIndex != KUsbcInvalidBufferIndex) + { + iCanBeFreed[iCurrentDrainingBufferIndex] = ETrue; + iNumberofPacketsRx[iCurrentDrainingBufferIndex] = 0; // Current buffer is empty + iNumberofBytesRx[iCurrentDrainingBufferIndex] = 0; // Current buffer is empty + +#if defined(USBC_LDD_BUFFER_TRACE) + TUint& bytesRemain = iNumberofBytesRxRemain[iCurrentDrainingBufferIndex]; + TUint& pktsRemain = iNumberofPacketsRxRemain[iCurrentDrainingBufferIndex]; + if ((bytesRemain != 0) || (pktsRemain != 0)) + { + Kern::Printf( + "TDmaBuf::NextDrainableBuffer: Error: data discarded buffer=%d pkts=%d bytes=%d", + iCurrentDrainingBufferIndex, pktsRemain, bytesRemain); + bytesRemain = 0; + pktsRemain = 0; + } +#endif + + iCurrentDrainingBufferIndex = KUsbcInvalidBufferIndex; + iCurrentPacket = KUsbcInvalidPacketIndex; + } + + if (iDrainQueueIndex != KUsbcInvalidDrainQueueIndex) + { + r = ETrue; + const TInt index = iDrainQueue[0]; + iDrainQueueIndex--; + for (TInt i = 0; i < iNumberofBuffers; i++) + { + iDrainQueue[i] = iDrainQueue[i+1]; + } + +#if defined(USBC_LDD_BUFFER_TRACE) + if (index != KUsbcInvalidBufferIndex) + iDrainingOrder++; +#endif + + iCurrentDrainingBufferIndex = index; + iCurrentDrainingBuffer = iBuffers[index]; + iCurrentPacketIndexArray = iPacketIndex[index]; + iCurrentPacketSizeArray = iPacketSize[index]; + iCurrentPacket = 0; + } + return r; + } + + +TInt TDmaBuf::PeekNextDrainableBuffer() + { + TInt r = KUsbcInvalidBufferIndex; + if (iDrainQueueIndex != KUsbcInvalidDrainQueueIndex) + { + r = iDrainQueue[0]; + } + return r; + } + + +TBool TDmaBuf::NextFillableBuffer() + { + TBool r = EFalse; + TInt index = iCurrentFillingBufferIndex; + IncrementBufferIndex(index); + // the sequence will restart at 0 if a buffer can't be found this time + iCurrentFillingBufferIndex = 0; + for (TInt i = 0; i < iNumberofBuffers; i++) + { + if (!iDrainable[index]) + { + iCurrentFillingBufferIndex = index; + r = ETrue; + break; + } + IncrementBufferIndex(index); + } + return r; + } + + +void TDmaBuf::FreeDrainedBuffers() + { + for (TInt i = 0; i < iNumberofBuffers; i++) + { + if (iDrainable[i] && iCanBeFreed[i]) + { + iDrainable[i] = iCanBeFreed[i] = EFalse; + } + } + } + + +TBool TDmaBuf::ShortPacketExists() + { + // Actually, a short packet or residue data + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::ShortPacketExists 1")); + TInt index = iCurrentDrainingBufferIndex; + TUsbcPacketArray* pktSizeArray = iCurrentPacketSizeArray; + + if (iMaxPacketSize > 0) + { + // No buffers available for draining + if ((iCurrentDrainingBufferIndex == KUsbcInvalidBufferIndex) || + (iCurrentPacket == KUsbcInvalidPacketIndex)) + return EFalse; + + // Zlp waiting at tail + if ((iTotalRxBytesAvail == 0) && (NoRxPackets() == 1)) + return ETrue; + + if (iEndpointType == KUsbEpTypeBulk) + { + const TInt mask = iMaxPacketSize - 1; + if (iTotalRxBytesAvail & mask) + return ETrue; + + // residue==0; this can be because + // zlps exist, or short packets combine to n * max_packet_size + // This means spadework + const TInt s = iCurrentPacketSizeArray[iCurrentPacket] - iExtractOffset; + if ((s == 0) || (s & mask)) + { + return ETrue; + } + + for (TInt i = 0; i < iNumberofBuffers; i++) + { + if (index == KUsbcInvalidBufferIndex) + break; + if (iDrainable[index]) + { + const TInt packetCount = iNumberofPacketsRx[index]; + const TInt lastPacketSize=pktSizeArray[packetCount - 1]; + if ((lastPacketSize < iMaxPacketSize) || (lastPacketSize & mask)) + { + return ETrue; + } + } + index = iDrainQueue[i]; + pktSizeArray = iPacketSize[index]; + } + } + else + { + if (iTotalRxBytesAvail % iMaxPacketSize) + return ETrue; + + // residue==0; this can be because + // zlps exist, or short packets combine to n * max_packet_size + // This means spadework + const TInt s = iCurrentPacketSizeArray[iCurrentPacket] - iExtractOffset; + if ((s == 0) || (s % iMaxPacketSize)) + { + return ETrue; + } + + for (TInt i = 0; i < iNumberofBuffers; i++) + { + if (index == KUsbcInvalidBufferIndex) + break; + if (iDrainable[index]) + { + const TInt packetCount = iNumberofPacketsRx[index]; + const TInt lastPacketSize = pktSizeArray[packetCount - 1]; + if ((lastPacketSize < iMaxPacketSize) || (lastPacketSize % iMaxPacketSize)) + { + return ETrue; + } + } + index = iDrainQueue[i]; + pktSizeArray = iPacketSize[index]; + } + } + } + + return EFalse; + } + + +void TDmaBuf::AddToDrainQueue(TInt aBufferIndex) + { + if (iDrainQueue[iDrainQueueIndex + 1] != KUsbcInvalidBufferIndex) + { +#if defined(USBC_LDD_BUFFER_TRACE) + Kern::Printf("TDmaBuf::AddToDrainQueue: Error: invalid iDrainQueue[x]"); +#endif + } + iDrainQueue[++iDrainQueueIndex] = aBufferIndex; + } + + +#if defined(USBC_LDD_BUFFER_TRACE) +TInt TDmaBuf::NoRxPacketsAlt() const + { + TInt pktCount = 0; + for(TInt i = 0; i < iNumberofBuffers; i++) + { + if (iDrainable[i]) + { + pktCount += iNumberofPacketsRxRemain[i]; + } + } + return pktCount; + } + + +TInt TDmaBuf::NoRxBytesAlt() const + { + TInt byteCount = 0; + for(TInt i = 0; i < iNumberofBuffers; i++) + { + if (iDrainable[i]) + { + byteCount += iNumberofBytesRxRemain[i]; + } + } + return byteCount; + } +#endif + + +// We only store 1 transaction, no other buffering is done +TInt TDmaBuf::TxStoreData(DThread* aThread, TClientBuffer *aTcb, TInt aTxLength, TUint32 aBufferOffset) + { + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::TxStoreData 1")); + if (!IsReaderEmpty()) + return KErrInUse; + + __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::TxStoreData 2")); + TUint8* logicalDest = iBufBasePtr; + TInt xferSz = Min(aTxLength, BufferTotalSize()); + TPtr8 des(logicalDest, xferSz, xferSz); + TInt r = Kern::ThreadBufRead(aThread, aTcb, des, aBufferOffset,KChunkShiftBy0); + if(r != KErrNone) + Kern::ThreadKill(aThread, EExitPanic, r, KUsbLDDKillCat); + return r; + } + + +TInt TDmaBuf::TxGetNextXfer(TUint8*& aBufferAddr, TInt& aTxLength, TPhysAddr& aBufferPhys) + { + if (iTxActive) + return KErrInUse; + + aBufferAddr = iBuffers[0]; // only 1 tx buffer + aBufferPhys = iBufferPhys[0]; + aTxLength = BufferTotalSize(); + + return KErrNone; + } +