usbdrv/peripheral/ldd/perildd/src/usbdma.cpp
author hgs
Wed, 20 Oct 2010 12:04:53 +0800
changeset 59 bbdce6bffaad
parent 33 089413cdde3c
permissions -rw-r--r--
201041_02

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "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 <usb/usbc.h>


#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 UsbShai::KUsbEpTypeControl:
        iBufSz = KUsbcDmaBufSzControl;
        iNumberofBuffers = KUsbcDmaBufNumControl;
        break;
    case UsbShai::KUsbEpTypeIsochronous:
        iBufSz = KUsbcDmaBufSzIsochronous;
        iNumberofBuffers = KUsbcDmaBufNumIsochronous;
        break;
    case UsbShai::KUsbEpTypeBulk:
        {
        if (aEndpointInfo->iDir == UsbShai::KUsbEpDirOut)
            {
            const TInt priorityOUT = aBandwidthPriority & 0x0f;
            iBufSz = KUsbcDmaBufSizesBulkOUT[priorityOUT];
            }
        else
            {
            const TInt priorityIN = (aBandwidthPriority >> 4) & 0x0f;
            iBufSz = KUsbcDmaBufSizesBulkIN[priorityIN];
            }
        iNumberofBuffers = KUsbcDmaBufNumBulk;
        }
        break;
    case UsbShai::KUsbEpTypeInterrupt:
        iBufSz = KUsbcDmaBufSzInterrupt;
        iNumberofBuffers = KUsbcDmaBufNumInterrupt;
        break;
    default:
        iBufSz = 0;
        iNumberofBuffers = 0;
        }

    if (aEndpointInfo->iDir == UsbShai::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 != UsbShai::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()"));
    }

TInt TDmaBuf::BufferTotalSize() const
    {
    return iBufSz * iNumberofBuffers;
    }

TInt TDmaBuf::BufferSize() const
    {
    return iBufSz;
    }

TInt TDmaBuf::SetBufferAddr(TInt aBufInd, TUint8* aBufAddr)
    {
    __ASSERT_DEBUG((aBufInd < iNumberofBuffers),
                       Kern::Fault(KUsbPanicLdd, __LINE__));
    iDrainable[aBufInd] = iCanBeFreed[aBufInd] = EFalse;
    iBuffers[aBufInd] = aBufAddr;
    iBufferPhys[aBufInd] = Epoc::LinearToPhysical((TLinAddr)aBufAddr);
    __KTRACE_OPT(KUSB, Kern::Printf("TDmaBuf::SetBufferAddr() iBuffers[%d]=0x%08x", aBufInd, iBuffers[aBufInd]));
    return KErrNone;
    }

TInt TDmaBuf::BufferNumber() const
    {
    return iNumberofBuffers;
    }

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 == UsbShai::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 == UsbShai::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 == UsbShai::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"));
    
    TInt remainTxLength = aTxLength;
    TUint32 bufferOffset = aBufferOffset;
    // Store each buffer separately
    for( TInt i=0;(i<iNumberofBuffers)&&(remainTxLength>0);i++)
        {
        TUint8* logicalDest = iBuffers[i];
        TInt xferSz = Min(remainTxLength, iBufSz);
        TPtr8 des(logicalDest, xferSz, xferSz);
        TInt r = Kern::ThreadBufRead(aThread, aTcb, des, bufferOffset, KChunkShiftBy0);
        if(r != KErrNone)
            {
            Kern::ThreadKill(aThread, EExitPanic, r, KUsbLDDKillCat);
            return r;
            }
        remainTxLength -= iBufSz;
        bufferOffset += iBufSz;
        }

    return KErrNone;
    }


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