kernel/eka/drivers/usbho/usbdi_utils/zerocopytransferstrategy.cpp
changeset 149 d9f1e5bfe28c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/usbho/usbdi_utils/zerocopytransferstrategy.cpp	Thu Jun 10 11:48:01 2010 +0100
@@ -0,0 +1,982 @@
+// Copyright (c) 2007-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:
+//
+
+#include "zerocopytransferstrategy.h"
+
+#include <d32usbtransfers.h>
+#include <d32usbdi.h>
+#include <d32usbdi_errors.h>
+#include "zerocopymetadata.h"
+#include "usbdiutils.h"
+
+
+RUsbZeroCopyTransferStrategy::TUsbTransferDescriptorDetails::TUsbTransferDescriptorDetails(RUsbTransferDescriptor& aTransferDesc, TInt aRequiredSize, TUint aRequiredAlignment, TInt aRequiredMaxPackets)
+	: iTransferDesc(aTransferDesc)
+	, iRequiredSize(aRequiredSize)
+	, iRequiredAlignment(aRequiredAlignment)
+	, iRequiredMaxPackets(aRequiredMaxPackets)
+	{
+	}
+	
+RUsbZeroCopyTransferStrategy::RUsbZeroCopyTransferStrategy()
+	: iInterfaceHandle(NULL)
+	{
+	}
+
+
+void RUsbZeroCopyTransferStrategy::Close()
+	{
+	iInterfaceHandle = NULL;
+	iChunk.Close();
+	iRegisteredTransfers.Close();
+	RUsbTransferStrategy::Close();
+	}
+
+
+TInt RUsbZeroCopyTransferStrategy::RegisterTransferDescriptor(RUsbTransferDescriptor& aTransferDesc, TInt aRequiredSize, TUint aStartAlignment, TInt aRequiredMaxPackets)
+	{
+	__ASSERT_ALWAYS(!iInterfaceHandle, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorsAlreadyRegistered));
+	if (iRegisteredTransfers.Find(aTransferDesc, CompareTransferDescriptor) != KErrNotFound)
+		{
+		return KErrAlreadyExists;
+		}
+	return iRegisteredTransfers.Append(TUsbTransferDescriptorDetails(aTransferDesc, aRequiredSize, aStartAlignment, aRequiredMaxPackets));
+	}
+
+TBool RUsbZeroCopyTransferStrategy::CompareTransferDescriptor(const RUsbTransferDescriptor* aTransferDesc, const TUsbTransferDescriptorDetails& aDetails)
+	{
+	return aTransferDesc == &aDetails.iTransferDesc;
+	}
+
+
+void RUsbZeroCopyTransferStrategy::ResetTransferDescriptors()
+	{
+	__ASSERT_ALWAYS(!iInterfaceHandle, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorsAlreadyRegistered));
+	iRegisteredTransfers.Reset();
+	}
+
+
+TInt RUsbZeroCopyTransferStrategy::InitialiseTransferDescriptors(RUsbInterface& aInterface)
+	{
+	__ASSERT_ALWAYS(!iInterfaceHandle, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorsAlreadyRegistered));
+
+	// This is the equivilent of a standard R-class Open() method, so initialise the references
+	// we are going to use.
+	iInterfaceHandle = &aInterface;
+
+	// First get the page-size as we will need this for isoc transfer calculations.
+	TInt hcdPageSize = 0;
+	TInt err = aInterface.GetHcdPageSize(hcdPageSize);
+	if (err != KErrNone)
+		{
+		Close(); // roll back
+		return err;
+		}
+	iPageSize = hcdPageSize;
+
+	TInt currentOffset = 0;
+	TInt numStandardTransfers = 0;
+	TInt numIsocTransfers = 0;
+	TInt numIsocElements = 0;
+	err = CalculateDataLayout(currentOffset, numStandardTransfers, numIsocTransfers, numIsocElements);
+	if (err != KErrNone)
+		{
+		Close(); // roll back
+		return err;
+		}
+
+	TInt metaDataStart = 0;
+	CalculateMetaDataLayout(currentOffset, metaDataStart, numStandardTransfers, numIsocTransfers, numIsocElements);
+
+	// currentOffset should now be just past the region required for all the data and meta data.
+	// Therefore it equals the total size of the buffer we need to hold them all.
+	err = iInterfaceHandle->AllocateSharedChunk(iChunk, currentOffset, iBaseOffset);
+	if (err != KErrNone)
+		{
+		Close(); // roll back
+		return err;
+		}
+
+	InitialiseMetaData(metaDataStart, numStandardTransfers, numIsocTransfers, numIsocElements);
+
+	return KErrNone;
+	}
+
+TInt RUsbZeroCopyTransferStrategy::CalculateDataLayout(TInt& aCurrentOffset, TInt& aNumStandardTransfers, TInt& aNumIsocTransfers, TInt& aNumIsocElements)
+	{
+	const TUint32 pageAddrBits = iPageSize-1;
+	const TUint32 pageTableMask = ~pageAddrBits;
+
+	//Get the maximum wMaxPacketSize of the associated interface for Bulk/Interrupt EPs
+	TInt maxMaxBulk = 0;
+	TInt maxMaxInterrupt = 0;
+	TInt err = GetMaximumMaxPacketSize(maxMaxBulk, maxMaxInterrupt);
+	if (err != KErrNone)
+		{
+		return err;
+		}
+	
+	// Work out where to place the transfers, and how much space is needed.
+	TInt numTransfers = iRegisteredTransfers.Count();
+	for (TInt i=0; i < numTransfers; ++i)
+		{
+		TUsbTransferDescriptorDetails& details = iRegisteredTransfers[i];
+		
+		err = CaculateAdditionalAlignment(aCurrentOffset, maxMaxBulk, maxMaxInterrupt, details);
+		if (err != KErrNone)
+			{
+			return err;
+			}
+		
+		// only allow intra-page alignment requests that are powers of 2 (so offset agnostic).
+		__ASSERT_ALWAYS(details.iRequiredAlignment <= iPageSize, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorAlignmentOverPageBoundary));
+		__ASSERT_ALWAYS(IsPowerOfTwo(details.iRequiredAlignment), UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorAlignmentNotPowerOfTwo));
+
+		TInt alignPad = IncNeededToAlign(aCurrentOffset, details.iRequiredAlignment);
+		__ASSERT_DEBUG(alignPad < iPageSize, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadAlignment)); // just re-asserting what should be guarded above
+		aCurrentOffset += alignPad; // Align to the start of transfer buffer
+
+		// There are stark differences between isoc transfers and transfer of other types.
+		if (details.iTransferDesc.iType == RUsbTransferDescriptor::EIsochronous)
+			{
+			// First do some Isoc specific checks
+			__ASSERT_ALWAYS(details.iRequiredMaxPackets > 0, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorNoPacketsRequested));
+
+			// For the allocation we have to consider the worse case - that is that the max
+			// number of packets at the max packet size.
+			// We are constrained by the USB stack to not allow transfers across page boundaries.
+
+			// As such we calculate how many packets we can fit into a page to determine the
+			// number of pages for data we need.
+			const TInt packetsPerPage = iPageSize/details.iRequiredSize;
+
+			// Assign the start to an appropriate point.
+			details.iAssignedOffset = aCurrentOffset;
+			TInt packetsToStore = details.iRequiredMaxPackets;
+			TInt numElements = 0; // for counting up the number of pages we need meta-data for.
+			
+			// The size requried to hold a length array for the descriptor
+			const TInt lengthsArrayLength = UsbZeroCopyIsocChunkHeader::KLengthsElementSize * details.iRequiredMaxPackets;
+			// The size required to hold a result array for the descriptor
+			const TInt resultsArrayLength = UsbZeroCopyIsocChunkHeader::KResultsElementSize * details.iRequiredMaxPackets;
+
+			// Determine how much we can fit into the remaining space of the current page.
+			TBool samePage = (pageTableMask & aCurrentOffset) == (pageTableMask & (aCurrentOffset - alignPad));
+			if (samePage)
+				{
+				TInt remainingSpace = iPageSize - (pageAddrBits & aCurrentOffset);
+				TInt packetsThatFit = remainingSpace / details.iRequiredSize;
+				if (packetsThatFit >= packetsToStore)
+					{
+					// We can fit it in this page so we finish here - this is the special case.
+					aCurrentOffset += packetsToStore * details.iRequiredSize;
+					++aNumIsocElements;
+					++aNumIsocTransfers;
+					details.iNumElements = 1;
+					// Do the lengths array
+					aCurrentOffset += IncNeededToAlign(aCurrentOffset, UsbZeroCopyIsocChunkHeader::KLengthsElementSize);
+					details.iLengthsOffset = aCurrentOffset;
+					aCurrentOffset += lengthsArrayLength;
+					// The dual lengths array should be implicitly alligned
+					details.iReqLenOffset = aCurrentOffset;
+					aCurrentOffset += lengthsArrayLength;
+					// Now handle the results array
+					aCurrentOffset += IncNeededToAlign(aCurrentOffset, UsbZeroCopyIsocChunkHeader::KResultsElementSize);
+					details.iResultsOffset = aCurrentOffset;
+					aCurrentOffset += resultsArrayLength;
+					continue;
+					}
+				aCurrentOffset = (pageTableMask & aCurrentOffset) + iPageSize; // Advance to next page
+				packetsToStore -= packetsThatFit;
+				++numElements;
+				}
+			__ASSERT_DEBUG(packetsToStore > 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorNoPacketsLeftToStore));
+
+			// Determine the number of pages extra that are needed (minus one)
+			TInt pagesRequired = packetsToStore / packetsPerPage;
+
+			// Determine how much of the last page is actually needed.
+			TInt trailingPackets = packetsToStore % packetsPerPage;
+			TInt usedSpace = trailingPackets * details.iRequiredSize;
+
+			// Commit the amount for the buffers.
+			aCurrentOffset += usedSpace + pagesRequired*iPageSize;
+			numElements += pagesRequired + /*the final page*/1; // We have already included the first page (if already partially used)
+			aNumIsocElements += numElements;
+			++aNumIsocTransfers;
+
+			// Used to ensure only allocate an appropriate number per-descriptor.
+			details.iNumElements = numElements;
+
+			// We also need an array of lengths for each packet that we use (need to align to even bytes).
+			aCurrentOffset += IncNeededToAlign(aCurrentOffset, UsbZeroCopyIsocChunkHeader::KLengthsElementSize);
+			details.iLengthsOffset = aCurrentOffset;
+			aCurrentOffset += lengthsArrayLength;
+			// Dual length array should be implicitly aligned
+			details.iReqLenOffset = aCurrentOffset;
+			aCurrentOffset += lengthsArrayLength;
+			// Now handle the results array
+			aCurrentOffset += IncNeededToAlign(aCurrentOffset, UsbZeroCopyIsocChunkHeader::KResultsElementSize);
+			details.iResultsOffset = aCurrentOffset;
+			aCurrentOffset += resultsArrayLength;
+			}
+		else
+			{
+			details.iAssignedOffset = aCurrentOffset;
+			aCurrentOffset += details.iRequiredSize;
+			++aNumStandardTransfers;
+			}
+		}
+	
+	return KErrNone;
+	}
+
+
+void RUsbZeroCopyTransferStrategy::CalculateMetaDataLayout(TInt& aCurrentOffset, TInt& aMetaDataStart, TInt aNumStandardTransfers, TInt aNumIsocTransfers, TInt aNumIsocElements)
+	{
+	// Round up to 4 byte alignment for handling the meta-data correctly.
+	aCurrentOffset += IncNeededToAlign(aCurrentOffset, sizeof(TInt));
+
+	aMetaDataStart = aCurrentOffset;
+
+	// Now calculate the size required for the transfer meta-data.
+	aCurrentOffset += aNumStandardTransfers * UsbZeroCopyBulkIntrChunkHeader::HeaderSize();
+	aCurrentOffset += aNumIsocTransfers * UsbZeroCopyIsocChunkHeader::HeaderSize();
+	aCurrentOffset += aNumIsocElements * UsbZeroCopyIsocChunkElement::ElementSize();
+	}
+	
+void RUsbZeroCopyTransferStrategy::InitialiseMetaData(TInt aMetaDataOffset, TInt aNumStandardTransfers, TInt aNumIsocTransfers, TInt aNumIsocElements)
+	{
+	const TUint32 pageAddrBits = iPageSize-1;
+	const TUint32 pageTableMask = ~pageAddrBits;
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	TInt numTransfers = iRegisteredTransfers.Count();
+	for (TInt i=0; i < numTransfers; ++i)
+		{
+		TUsbTransferDescriptorDetails details = iRegisteredTransfers[i];
+
+		if (details.iTransferDesc.iType == RUsbTransferDescriptor::EIsochronous)
+			{
+			// Initialise Meta-data (minus elements).
+			UsbZeroCopyIsocChunkHeader::TransferType(chunkBase, aMetaDataOffset) = details.iTransferDesc.iType;
+			UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aMetaDataOffset) = details.iRequiredMaxPackets;
+			UsbZeroCopyIsocChunkHeader::MaxPacketSize(chunkBase, aMetaDataOffset) = details.iRequiredSize;
+			// Double check that the length array is aligned correctly.
+			__ASSERT_DEBUG(details.iLengthsOffset % UsbZeroCopyIsocChunkHeader::KLengthsElementSize == 0,
+				UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorLengthsArrayBadAlignment));
+			UsbZeroCopyIsocChunkHeader::LengthsOffset(chunkBase, aMetaDataOffset) = details.iLengthsOffset;
+			UsbZeroCopyIsocChunkHeader::ReqLenOffset(chunkBase, aMetaDataOffset) = details.iReqLenOffset;
+			// Double check that the result array is aligned correctly.
+			__ASSERT_DEBUG(details.iResultsOffset % UsbZeroCopyIsocChunkHeader::KResultsElementSize == 0,
+				UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorResultsArrayBadAlignment));
+			UsbZeroCopyIsocChunkHeader::ResultsOffset(chunkBase, aMetaDataOffset) = details.iResultsOffset;
+			// Initialise transfer descriptor
+			SetTransferHandle(details.iTransferDesc, aMetaDataOffset);
+			// Move on to next meta-data slot
+			TInt prevMetaOffset = aMetaDataOffset;
+			aMetaDataOffset += UsbZeroCopyIsocChunkHeader::HeaderSize();
+
+			// Initialise elements for transfers
+			UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, prevMetaOffset) = aMetaDataOffset;
+			
+			TInt isocElementsUnmapped = details.iNumElements;
+			// First element could be anywhere, the others are at the start of (virtually) contiguous pages
+			TInt offset = details.iAssignedOffset;
+			while (isocElementsUnmapped > 0)
+				{
+				// Update the data references
+				UsbZeroCopyIsocChunkElement::DataOffset(chunkBase, aMetaDataOffset) = offset;
+				UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, aMetaDataOffset) = 0; // Default value.
+				// Move on to the next element and bind it to the chain.
+				prevMetaOffset = aMetaDataOffset;
+				aMetaDataOffset += UsbZeroCopyIsocChunkElement::ElementSize();
+				UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, prevMetaOffset) = aMetaDataOffset;
+				// Move to the next page
+				offset = (pageTableMask&offset)+iPageSize;
+				--isocElementsUnmapped;
+				--aNumIsocElements;
+				}
+			// We have reached the end of the list so we should update the next element offset for the
+			// last element to indicate that it is the terminator.
+			UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, prevMetaOffset) = UsbZeroCopyIsocChunkElement::KEndOfList;
+			--aNumIsocTransfers;
+			}
+		else
+			{
+			// Initialise Meta-data.
+			UsbZeroCopyBulkIntrChunkHeader::TransferType(chunkBase, aMetaDataOffset) = details.iTransferDesc.iType;
+			UsbZeroCopyBulkIntrChunkHeader::DataOffset(chunkBase, aMetaDataOffset) = details.iAssignedOffset;
+			UsbZeroCopyBulkIntrChunkHeader::DataLength(chunkBase, aMetaDataOffset) = 0;
+			UsbZeroCopyBulkIntrChunkHeader::DataMaxLength(chunkBase, aMetaDataOffset) = details.iRequiredSize;
+			UsbZeroCopyBulkIntrChunkHeader::ZlpStatus(chunkBase, aMetaDataOffset) = RUsbTransferDescriptor::ESendZlpIfRequired;
+			// Initialise transfer descriptor
+			SetTransferHandle(details.iTransferDesc, aMetaDataOffset);
+			// Move on to next meta-data slot
+			aMetaDataOffset += UsbZeroCopyBulkIntrChunkHeader::HeaderSize();
+			--aNumStandardTransfers;
+			}
+		}
+
+	__ASSERT_DEBUG(aNumStandardTransfers == 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorIncompleteInitialisation));
+	__ASSERT_DEBUG(aNumIsocTransfers == 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorIncompleteInitialisation));
+	__ASSERT_DEBUG(aNumIsocElements == 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorIncompleteInitialisation));
+	}
+
+
+TBool RUsbZeroCopyTransferStrategy::IsPowerOfTwo(TUint aNumber)
+	{
+    return aNumber && !(aNumber & (aNumber - 1)); //this returns true if the integer is a power of two
+    }
+
+
+TInt RUsbZeroCopyTransferStrategy::IncNeededToAlign(TInt aOffset, TUint aAlignment)
+	{
+	if (aAlignment == 0)
+		{
+		return 0;
+		}
+	TInt remain = aOffset % aAlignment;
+	return (aAlignment - remain) % aAlignment;
+	}
+
+
+// Standard Methods
+
+TPtr8 RUsbZeroCopyTransferStrategy::WritableBuffer(TInt aHandle)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+	
+	TUint8* dataPtr = chunkBase + UsbZeroCopyBulkIntrChunkHeader::DataOffset(chunkBase, aHandle);
+	TInt maxLength = UsbZeroCopyBulkIntrChunkHeader::DataMaxLength(chunkBase, aHandle);
+
+	return TPtr8(dataPtr, 0, maxLength);
+	}
+
+void RUsbZeroCopyTransferStrategy::SaveData(TInt aHandle, TInt aLength)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	TInt maxLength = UsbZeroCopyBulkIntrChunkHeader::DataMaxLength(chunkBase, aHandle);
+	__ASSERT_ALWAYS(aLength <= maxLength, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorSavedToMuchData));
+
+	UsbZeroCopyBulkIntrChunkHeader::DataLength(chunkBase, aHandle) = aLength;
+	}
+	
+void RUsbZeroCopyTransferStrategy::SetZlpStatus(TInt aHandle, RUsbTransferDescriptor::TZlpStatus aZlpStatus)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	UsbZeroCopyBulkIntrChunkHeader::ZlpStatus(chunkBase, aHandle) = aZlpStatus;
+	}
+
+TPtrC8 RUsbZeroCopyTransferStrategy::Buffer(TInt aHandle) const
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+	
+	TUint8* dataPtr = chunkBase + UsbZeroCopyBulkIntrChunkHeader::DataOffset(chunkBase, aHandle);
+	TInt length = UsbZeroCopyBulkIntrChunkHeader::DataLength(chunkBase, aHandle);
+
+	return TPtrC8(dataPtr, length);
+	}
+	
+
+
+
+// Isochronous Methods
+	
+void RUsbZeroCopyTransferStrategy::Reset(TInt aHandle)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	// Loop through and reset number of packets in each element as 0
+	TInt elementOffset = UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, aHandle);
+	while (elementOffset != UsbZeroCopyIsocChunkElement::KEndOfList)
+		{
+		UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, elementOffset) = 0;
+		elementOffset = UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, elementOffset);
+		}
+	}
+
+TPacketLengths RUsbZeroCopyTransferStrategy::Lengths(TInt aHandle)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	TInt lengthsOffset = UsbZeroCopyIsocChunkHeader::LengthsOffset(chunkBase, aHandle);
+	TUint16* lengthsPtr = reinterpret_cast<TUint16*>(chunkBase + lengthsOffset);
+	
+	TInt reqLenOffset = UsbZeroCopyIsocChunkHeader::ReqLenOffset(chunkBase, aHandle);
+	TUint16* reqLenPtr = reinterpret_cast<TUint16*>(chunkBase + reqLenOffset);
+
+	TInt& maxNumPackets = UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aHandle);
+
+	return TPacketLengths(lengthsPtr, reqLenPtr, maxNumPackets);
+	}
+	
+TPacketResults RUsbZeroCopyTransferStrategy::Results(TInt aHandle)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	TInt resultsOffset = UsbZeroCopyIsocChunkHeader::ResultsOffset(chunkBase, aHandle);
+	TInt* resultsPtr = reinterpret_cast<TInt*>(chunkBase + resultsOffset);
+
+	TInt& maxNumPackets = UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aHandle);
+
+	return TPacketResults(resultsPtr, maxNumPackets);
+	}
+
+TInt RUsbZeroCopyTransferStrategy::MaxPacketSize(TInt aHandle)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	TInt maxPacketSize = UsbZeroCopyIsocChunkHeader::MaxPacketSize(chunkBase, aHandle);
+	
+	return maxPacketSize;
+	}
+
+TPtr8 RUsbZeroCopyTransferStrategy::WritablePackets(TInt aHandle, TInt aWriteHandle, TInt aNumPacketsRequested, TInt& aMaxNumPacketsAbleToWrite)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+	__ASSERT_DEBUG(aWriteHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadWriteHandle));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+	
+	const TUint32 pageAddrBits = iPageSize-1;
+	const TUint32 pageTableMask = ~pageAddrBits;
+	
+	if (aHandle == aWriteHandle)
+		{
+		// The initial write handle will be the same as the standard handle so we need to find the actual 
+		// element to work correctly.
+		aWriteHandle = UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, aHandle);
+		}
+
+	// Now we have two cases - the number of packets requested is contained in one page, or it crosses the page.
+	// 1) If we cross the page then we get the buffer for upto the end of the page, and inform the user of the number
+	// of packets they are able to write into it (normally this will be quite high as we can consider 0 length
+	// packets.)
+	// 2) If we are on one page then we provide a buffer to the end of the page and return the number of packets
+	// the requested as the max they can write.  However we also now mark it so that an attempt to get a subsequent
+	// writable buffer will return a 0 max length TPtr8 and 0 max number of packets to write.  If they want write
+	// more they need to reset the descriptor and start again.
+
+	if (UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, aWriteHandle) == UsbZeroCopyIsocChunkElement::KInvalidElement)
+		{
+		// Here we are testing the second case, if we previously marked an element as invalid then we must not
+		// return a valid buffer.
+		aMaxNumPacketsAbleToWrite = 0;
+		return TPtr8(NULL, 0);
+		}
+
+	TInt dataOffset = UsbZeroCopyIsocChunkElement::DataOffset(chunkBase, aWriteHandle);
+	
+	TUint8* dataPtr = chunkBase + dataOffset;
+	TInt totalMaxSize = aNumPacketsRequested * UsbZeroCopyIsocChunkHeader::MaxPacketSize(chunkBase, aHandle);
+	// The USB stack requires isoc transfer to be limited to a page (not allowed to cross the boundary).
+	TUint32 dataAddr = reinterpret_cast<TUint32>(dataPtr);
+	TBool samePage = (pageTableMask & dataAddr) == (pageTableMask & (dataAddr + totalMaxSize));
+	TInt allowableSize = samePage ? totalMaxSize : iPageSize - (pageAddrBits & dataAddr);
+
+	TInt numPacketsRemaining = UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aHandle) - UsedPackets(aHandle);
+
+	if (aNumPacketsRequested < numPacketsRemaining)
+		{
+		// This is the 2nd case as documented in the comment.  So we mark the next packet as invalid.
+		aMaxNumPacketsAbleToWrite = aNumPacketsRequested;
+		TInt nextElement = UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, aWriteHandle);
+		if (nextElement != UsbZeroCopyIsocChunkElement::KEndOfList)
+			{
+			UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, nextElement) = UsbZeroCopyIsocChunkElement::KInvalidElement; // Mark as invalid.
+			}
+		// else we are at the end of the list anyway
+		}
+	else
+		{
+		aMaxNumPacketsAbleToWrite = numPacketsRemaining;
+		}
+
+	return TPtr8(dataPtr, allowableSize);
+	}
+
+TInt RUsbZeroCopyTransferStrategy::SaveMultiple(TInt aHandle, TInt aWriteHandle, TInt aNumPackets)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+	__ASSERT_DEBUG(aWriteHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadWriteHandle));
+	__ASSERT_ALWAYS(aNumPackets > 0, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorNoPacketsToSave));
+	
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	if (aHandle == aWriteHandle)
+		{
+		aWriteHandle = UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, aHandle);
+		}
+
+	// if marked invalid then they shouldn't try to save it (they haven't been able to write anything into the data anyway).
+	__ASSERT_ALWAYS(UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, aWriteHandle) != UsbZeroCopyIsocChunkElement::KInvalidElement,
+		UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorInvalidSaveCall));
+
+	// Ensure they've not tried to write in too many packets
+	TInt usedPackets = UsedPackets(aHandle);
+	__ASSERT_ALWAYS(aNumPackets + usedPackets <= UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aHandle),
+		UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorSavedTooManyPackets));
+
+	// Check that the length values have not exceeded the maximum.
+	TInt maxPacketSize = UsbZeroCopyIsocChunkHeader::MaxPacketSize(chunkBase, aHandle);
+	TInt lengthsOffset = UsbZeroCopyIsocChunkHeader::LengthsOffset(chunkBase, aHandle);
+	TUint16* lengthsPtr = reinterpret_cast<TUint16*>(chunkBase + lengthsOffset);
+#ifdef _DEBUG
+	// The requested length is only functionally needed for IN transfers, but it provides an
+	// extra check that the length values that were requested by the user are those that are
+	// been requested on the USB stack.
+	TInt reqLenOffset = UsbZeroCopyIsocChunkHeader::ReqLenOffset(chunkBase, aHandle);
+	TUint16* reqLenPtr = reinterpret_cast<TUint16*>(chunkBase + reqLenOffset);
+#endif // _DEBUG
+	for (TInt i=0; i < aNumPackets; ++i)
+		{
+		__ASSERT_ALWAYS(lengthsPtr[usedPackets + i] <= maxPacketSize, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorSavingTooLargeAPacket));
+		__ASSERT_DEBUG(lengthsPtr[usedPackets + i] == reqLenPtr[usedPackets + i], UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorRequestedLengthDiffers)); // Belt 'n' Braces
+		}
+
+	// Commit the packets to the transfer descriptor.
+	UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, aWriteHandle) = aNumPackets;
+	TInt headerOffset = UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, aWriteHandle);
+	
+	// Return the handle to the next region for writing.
+	return (headerOffset == UsbZeroCopyIsocChunkElement::KEndOfList) ? KErrEof : headerOffset;
+	}
+
+/**
+Used to walk the elements to total up the number of packets that have been saved in the transfer descriptor.
+*/
+TInt RUsbZeroCopyTransferStrategy::UsedPackets(TInt aHeaderOffset)
+	{
+	__ASSERT_DEBUG(aHeaderOffset >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorInvalidHeaderOffset));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+	TInt elementOffset = UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, aHeaderOffset);
+	TInt totalNumPackets = 0;
+	while (elementOffset != UsbZeroCopyIsocChunkElement::KEndOfList)
+		{
+		TInt numPackets = UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, elementOffset);
+		if (numPackets == 0 || numPackets == UsbZeroCopyIsocChunkElement::KInvalidElement)
+			{
+			break;
+			}
+		totalNumPackets += numPackets;
+		elementOffset = UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, elementOffset);
+		}
+	return totalNumPackets;
+	}
+
+/**
+Used to read packets out from the transfer descriptor.
+Note that some of the panics are belt'n'braces, and are used to sanity test result that has been
+provided.  These should be correct (as the results are set by the kernel), however because the user
+has access to length array (for writing out packets) it is possible for them to 'corrupt' the result.
+We panic explicitly in UDEB builds, in UREL the guards are not present and the user may get returned
+a bad descriptor.
+*/
+TPtrC8 RUsbZeroCopyTransferStrategy::Packets(TInt aHandle, TInt aFirstPacketIndex, TInt aNumPacketsRequested, TInt& aNumPacketsReturned) const
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+	__ASSERT_ALWAYS(aFirstPacketIndex >= 0, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorPacketNotInBounds));
+	__ASSERT_ALWAYS(aNumPacketsRequested > 0, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorTooFewPacketsRequested));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+
+	__ASSERT_ALWAYS(aNumPacketsRequested <= UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aHandle),
+		UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorTooManyPacketsRequested));
+
+#ifdef _DEBUG
+	const TUint32 pageAddrBits = iPageSize-1;
+	const TUint32 pageTableMask = ~pageAddrBits;
+#endif // _DEBUG
+	const TInt maxPacketSize = UsbZeroCopyIsocChunkHeader::MaxPacketSize(chunkBase, aHandle);
+
+	TInt elementOffset = UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, aHandle);
+	TInt packetCount = 0;
+	while (elementOffset != UsbZeroCopyIsocChunkElement::KEndOfList)
+		{
+		TInt numPackets = UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, elementOffset);
+		if (numPackets == 0 || numPackets == UsbZeroCopyIsocChunkElement::KInvalidElement)
+			{
+			// We've got to the end of the elements and not found the packets we are after.
+			break;
+			}
+		TInt previousPacketCount = packetCount;
+		packetCount += numPackets;
+		if (aFirstPacketIndex < packetCount) // If true then start packet must be in this element
+			{
+			TInt intraElementIndex = aFirstPacketIndex - previousPacketCount;
+			TInt maxPacketsForReturn = packetCount - aFirstPacketIndex;
+
+			TInt lengthsOffset = UsbZeroCopyIsocChunkHeader::LengthsOffset(chunkBase, aHandle);
+			TUint16* lengthsPtr = reinterpret_cast<TUint16*>(chunkBase + lengthsOffset + previousPacketCount * sizeof(TUint16));
+			TInt reqLenOffset = UsbZeroCopyIsocChunkHeader::ReqLenOffset(chunkBase, aHandle);
+			TUint16* reqLenPtr = reinterpret_cast<TUint16*>(chunkBase + reqLenOffset + previousPacketCount * sizeof(TUint16));
+
+			aNumPacketsReturned = (aNumPacketsRequested < maxPacketsForReturn) ? aNumPacketsRequested : maxPacketsForReturn;
+
+			TInt distanceToReqPacket = 0;
+			for (TInt i=0; i < intraElementIndex; ++i)
+				{
+				TUint16 reqLen = reqLenPtr[i];
+				__ASSERT_DEBUG(reqLen <= maxPacketSize,
+					UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorReceivedTooLargeAPacket)); // Belt'n'Braces
+				distanceToReqPacket += reqLen;
+				}
+			TInt dataOffset = UsbZeroCopyIsocChunkElement::DataOffset(chunkBase, elementOffset);
+			TUint8* dataPtr = chunkBase + dataOffset + distanceToReqPacket;
+
+			TInt totalLengthPackets = 0;
+			for (TInt i=0; i < aNumPacketsReturned; ++i)
+				{
+				TUint16 len = lengthsPtr[intraElementIndex + i];
+				TUint16 reqLen = reqLenPtr[intraElementIndex + i];
+				__ASSERT_DEBUG(len <= maxPacketSize,
+					UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorReceivedTooLargeAPacket)); // Belt'n'Braces
+
+				totalLengthPackets += len;
+				
+				// Here we handle the potential gaps that may appear in the data stream if a short
+				// packet is received.
+				if (len < reqLen)
+					{
+					// if here then we received a short packet, as such we can only return up to here
+					aNumPacketsReturned = i+1;
+					break;
+					}
+				// Otherwise we expect them to be equal (if we got more than requested then something odd has happened.
+				__ASSERT_DEBUG(len == reqLen, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorRequestedLengthDiffers)); // Belt 'n' Braces
+				}
+
+			// The USB stack requires isoc transfer to be limited to a page (not allowed to cross the boundary).
+			// Therefore one of our elements must have data only on one page.
+#ifdef _DEBUG
+			TUint32 dataAddr = reinterpret_cast<TUint32>(dataPtr);
+			TBool samePage = (totalLengthPackets == 0) || (pageTableMask & dataAddr) == (pageTableMask & (dataAddr + totalLengthPackets - 1));
+			__ASSERT_DEBUG(samePage, UsbdiUtils::Panic(UsbdiPanics::EIsocTransferResultCrossesPageBoundary)); // Belt'n'Braces
+#endif // _DEBUG
+
+			return TPtrC8(dataPtr, totalLengthPackets);
+			}
+		
+		// No luck so far, move on to try the next element
+		elementOffset = UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, elementOffset);
+		}
+
+	// No suitable packet range found.
+	aNumPacketsReturned = 0;
+	return TPtrC8(NULL, 0);
+	}
+
+void RUsbZeroCopyTransferStrategy::ReceivePackets(TInt aHandle, TInt aNumPackets)
+	{
+	__ASSERT_DEBUG(aHandle >= 0, UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorBadHandle));
+	__ASSERT_ALWAYS(aNumPackets > 0, UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorTooFewPacketsRequested));
+
+	TUint8* chunkBase = iChunk.Base() + iBaseOffset;
+	
+	__ASSERT_ALWAYS(aNumPackets <= UsbZeroCopyIsocChunkHeader::MaxNumPackets(chunkBase, aHandle),
+		UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorTooManyPacketsRequested));
+
+	const TUint32 pageAddrBits = iPageSize-1;
+	const TUint32 pageTableMask = ~pageAddrBits;
+	const TInt maxPacketSize = UsbZeroCopyIsocChunkHeader::MaxPacketSize(chunkBase, aHandle);
+
+#ifdef _DEBUG
+	// Here we make the best check we can that the user has set-up the requested lengths they require.
+	// If there is a difference, they have either a corrupted metadata chunk, or they are reusing a 
+	// previous buffer without setting the lengths requested.
+	TInt lengthsOffset = UsbZeroCopyIsocChunkHeader::LengthsOffset(chunkBase, aHandle);
+	TUint16* lengthsPtr = reinterpret_cast<TUint16*>(chunkBase + lengthsOffset);
+	TInt reqLenOffset = UsbZeroCopyIsocChunkHeader::ReqLenOffset(chunkBase, aHandle);
+	TUint16* reqLenPtr = reinterpret_cast<TUint16*>(chunkBase + reqLenOffset);
+	for (TInt i=0; i < aNumPackets; ++i)
+		{
+		__ASSERT_DEBUG(lengthsPtr[i] == reqLenPtr[i],
+			UsbdiUtils::Panic(UsbdiPanics::ETransferDescriptorRequestedLengthDiffers)); // Belt 'n' Braces
+		}
+#endif // _DEBUG
+
+	TInt elementOffset = UsbZeroCopyIsocChunkHeader::FirstElementOffset(chunkBase, aHandle);
+	while (aNumPackets)
+		{
+		__ASSERT_DEBUG(elementOffset != UsbZeroCopyIsocChunkElement::KEndOfList,
+			UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorUnexpectedEndOfIsocList));
+
+		TInt totalMaxSize = aNumPackets * maxPacketSize;
+
+		TInt dataOffset = UsbZeroCopyIsocChunkElement::DataOffset(chunkBase, elementOffset);
+		TUint8* dataPtr = chunkBase + dataOffset;
+		TUint32 dataAddr = reinterpret_cast<TUint32>(dataPtr);
+		TBool samePage = (pageTableMask & dataAddr) == (pageTableMask & (dataAddr + totalMaxSize));
+		TInt allowableSize = samePage ? totalMaxSize : iPageSize - (pageAddrBits & dataAddr);
+		TInt numPackets = allowableSize / maxPacketSize;
+
+		// TODO We could assert here in debug as a double check using UsedPackets()
+
+		__ASSERT_DEBUG(numPackets > 0,
+			UsbdiUtils::Fault(UsbdiFaults::EUsbTransferDescriptorUnfillableElement));
+
+		UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, elementOffset) = numPackets;
+		aNumPackets -= numPackets;
+
+		elementOffset = UsbZeroCopyIsocChunkElement::NextElementOffset(chunkBase, elementOffset);
+		}
+
+	if (elementOffset != UsbZeroCopyIsocChunkElement::KEndOfList)
+		{
+		UsbZeroCopyIsocChunkElement::NumPackets(chunkBase, elementOffset) = UsbZeroCopyIsocChunkElement::KInvalidElement; // Mark as invalid.
+		}
+	}
+	
+	
+
+
+
+TPtr8 RUsbZeroCopyTransferStrategy::IntrWritableBuffer(TInt aHandle)
+	{
+	return WritableBuffer(aHandle);
+	}
+
+void RUsbZeroCopyTransferStrategy::IntrSaveData(TInt aHandle, TInt aLength)
+	{
+	SaveData(aHandle, aLength);
+	}
+
+void RUsbZeroCopyTransferStrategy::IntrSetZlpStatus(TInt aHandle, RUsbTransferDescriptor::TZlpStatus aZlpStatus)
+	{
+	SetZlpStatus(aHandle, aZlpStatus);
+	}
+
+TPtrC8 RUsbZeroCopyTransferStrategy::IntrBuffer(TInt aHandle) const
+	{
+	return Buffer(aHandle);
+	}
+
+TPtr8 RUsbZeroCopyTransferStrategy::BulkWritableBuffer(TInt aHandle)
+	{
+	return WritableBuffer(aHandle);
+	}
+
+void RUsbZeroCopyTransferStrategy::BulkSaveData(TInt aHandle, TInt aLength)
+	{
+	SaveData(aHandle, aLength);
+	}
+
+void RUsbZeroCopyTransferStrategy::BulkSetZlpStatus(TInt aHandle, RUsbTransferDescriptor::TZlpStatus aZlpStatus)
+	{
+	SetZlpStatus(aHandle, aZlpStatus);
+	}
+
+TPtrC8 RUsbZeroCopyTransferStrategy::BulkBuffer(TInt aHandle) const
+	{
+	return Buffer(aHandle);
+	}
+
+void RUsbZeroCopyTransferStrategy::IsocReset(TInt aHandle)
+	{
+	Reset(aHandle);
+	}
+
+TPacketLengths RUsbZeroCopyTransferStrategy::IsocLengths(TInt aHandle)
+	{
+	return Lengths(aHandle);
+	}
+	
+TPacketResults RUsbZeroCopyTransferStrategy::IsocResults(TInt aHandle)
+	{
+	return Results(aHandle);
+	}
+
+TInt RUsbZeroCopyTransferStrategy::IsocMaxPacketSize(TInt aHandle)
+	{
+	return MaxPacketSize(aHandle);
+	}
+
+TPtr8 RUsbZeroCopyTransferStrategy::IsocWritablePackets(TInt aHandle, TInt aWriteHandle, TInt aNumPacketsRequested, TInt& aMaxNumPacketsAbleToWrite)
+	{
+	return WritablePackets(aHandle, aWriteHandle, aNumPacketsRequested, aMaxNumPacketsAbleToWrite);
+	}
+
+TInt RUsbZeroCopyTransferStrategy::IsocSaveMultiple(TInt aHandle, TInt aWriteHandle, TInt aNumOfPackets)
+	{
+	return SaveMultiple(aHandle, aWriteHandle, aNumOfPackets);
+	}
+
+TPtrC8 RUsbZeroCopyTransferStrategy::IsocPackets(TInt aHandle, TInt aFirstPacketIndex, TInt aNumPacketsRequested, TInt& aNumPacketsReturned) const
+	{
+	return Packets(aHandle, aFirstPacketIndex, aNumPacketsRequested, aNumPacketsReturned);
+	}
+
+void RUsbZeroCopyTransferStrategy::IsocReceivePackets(TInt aHandle, TInt aNumOfPackets)
+	{
+	ReceivePackets(aHandle, aNumOfPackets);
+	}
+
+
+//Calculate-alignment related methods
+
+/**
+ Scan through all the bulk/interrupt endpoints associated with the particular interface
+ (and all its alternate settings) to find the maximum bMaxPacketSize across all of these.
+ For Interrupt, if there is EP of which the maxPacketSize is not power of 2,
+ the maxmaxpaceketsize will be assigned the first maxPacketSize which is not power of 2.  
+*/
+TInt RUsbZeroCopyTransferStrategy::GetMaximumMaxPacketSize(TInt& aMaxMaxBulk, TInt& aMaxMaxInterrupt)
+	{
+	TUsbInterfaceDescriptor interfaceDesc;
+	TInt err = iInterfaceHandle->GetInterfaceDescriptor(interfaceDesc);
+	if (KErrNone != err)
+		{
+		return err;
+		}
+
+	const TUint8 KEPTransferTypeBulk = 0x02;
+	const TUint8 KEPTransferTypeInterrupt = 0x03;
+	const TUint8 KEPTransferTypeMask = 0x03;
+	
+	TBool ignoreInterruptEP = EFalse;
+	//Traverse all related interface alternate settings
+	TUsbGenericDescriptor* descriptor = &interfaceDesc;
+	while (descriptor)
+		{
+		TUsbInterfaceDescriptor* interface = TUsbInterfaceDescriptor::Cast(descriptor);
+		
+		if (interface)
+			{
+			//Traverse all endpoint descriptor in the interface
+			TUsbGenericDescriptor* subDescriptor = interface->iFirstChild;
+			
+			while (subDescriptor)
+				{
+				TUsbEndpointDescriptor* endpoint = TUsbEndpointDescriptor::Cast(subDescriptor);
+				
+				if (endpoint)
+					{
+					TBool isBulkEP = ((endpoint->Attributes() & KEPTransferTypeMask) == KEPTransferTypeBulk);
+					TBool isInterruptEP = ((endpoint->Attributes() & KEPTransferTypeMask) == KEPTransferTypeInterrupt);
+					TUint maxPacketSize = endpoint->MaxPacketSize();
+
+					//Caculate the maximum maxPacketSize
+					if (isBulkEP)
+						{
+						if (maxPacketSize > aMaxMaxBulk)
+							{
+							aMaxMaxBulk = maxPacketSize;
+							}
+						}
+					else if(isInterruptEP && !ignoreInterruptEP)
+						{
+						if (!IsPowerOfTwo(maxPacketSize))
+							{
+							aMaxMaxInterrupt = maxPacketSize;
+							ignoreInterruptEP = ETrue;
+							}
+						
+						if (maxPacketSize > aMaxMaxInterrupt)
+							{
+							aMaxMaxInterrupt = maxPacketSize;
+							}
+						}
+					}
+
+				subDescriptor = subDescriptor->iNextPeer;
+				}				
+			}
+		
+		descriptor = descriptor->iNextPeer;
+		}
+	
+	return KErrNone;	
+	}
+
+/**
+Calculate the additional alignment requirement on bulk and interrupt transfer.
+For Bulk transfer,
+	Scan through all the bulk/interrupt endpoints associated with the particular interface
+	to find the maximum wMaxPacketSize across all of these. The new alignment for the transfer
+	is the maximum between the maximum bMaxPacketSize and the original alignment
+For Interrupt transfer,
+	Check if there is endpoints of which the wMaxPacketSize is not power of 2,
+	if no, do the same as bulk;
+	if yes, the size of transfer data is limited to one page size, and the additional alignment 
+            calcualted to make the transfer data not to span page boundary
+
+*/
+TInt RUsbZeroCopyTransferStrategy::CaculateAdditionalAlignment(TInt aCurrentOffset, TInt aMaxMaxBulk, TInt aMaxMaxInterrupt, TUsbTransferDescriptorDetails& aTransferDetails)
+	{
+	RUsbTransferDescriptor::TTransferType transferType = aTransferDetails.iTransferDesc.iType;
+	TBool isBulkTransfer = (transferType == RUsbTransferDescriptor::EBulk);
+	TBool isInterruptTransfer = (transferType == RUsbTransferDescriptor::EInterrupt);
+
+	if (isBulkTransfer)
+		{
+		if (aMaxMaxBulk > aTransferDetails.iRequiredAlignment)
+			{
+			aTransferDetails.iRequiredAlignment = aMaxMaxBulk;
+			}
+		}
+	else if (isInterruptTransfer)
+		{
+		if (IsPowerOfTwo(aMaxMaxInterrupt))
+			{
+			if (aMaxMaxInterrupt > aTransferDetails.iRequiredAlignment)
+				{
+				aTransferDetails.iRequiredAlignment = aMaxMaxInterrupt;
+				}
+			}
+		else
+			{
+			if (aTransferDetails.iRequiredSize > iPageSize)
+				{
+				//The transfer data can not span the page boundary
+				//if there is EP of which wMaxPacketSize is not power-of-2,
+				return KErrNotSupported;
+				}
+			else
+				{
+				TInt sizeLeftOfCurrentPage = IncNeededToAlign(aCurrentOffset,iPageSize);
+				TInt alignPad = IncNeededToAlign(aCurrentOffset, aTransferDetails.iRequiredAlignment);
+				
+				//The transfer data can't fit into the current page
+				//Align the trasfer data to the next page
+				if ( sizeLeftOfCurrentPage < (alignPad + aTransferDetails.iRequiredSize) )
+					{
+					aTransferDetails.iRequiredAlignment = iPageSize;
+					}
+				}
+			}
+		}
+	return KErrNone;
+	}