kernel/eka/drivers/usbc/usbdma.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 6 0173bcd7697c
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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 <drivers/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 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()"));
	}

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