// Copyright (c) 2008-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\usbcsc\usbcsc_bil.cpp
// 
//

#include <e32std.h>
#include <e32std_private.h>
#include <d32usbcsc.h>
#include <e32debug.h>

/** @file usbcsc_bil.cpp

	Buffer Interface Layer for USB Client Device driver stack, using shared chunks.

	@internalTechnology
*/

EXPORT_C TInt RDevUsbcScClient::FinalizeInterface()
	{
	TInt errorOrhandle = DoControl(EControlRealizeInterface); //returns a error value or chunk handle
	TInt r = iSharedChunk.SetReturnedHandle(errorOrhandle);
	iEndpointStatus = 0x00; //all endpoints are closed at the moment
	iAlternateSetting = 0;
	iNewAltSetting = 0;
	iAltSettingSeq = 0;
	return r;
	}


EXPORT_C TInt RDevUsbcScClient::FinalizeInterface(RChunk*& aChunk)
	{
	TInt errorOrhandle = DoControl(EControlRealizeInterface);
	iSharedChunk.SetReturnedHandle(errorOrhandle);
	iEndpointStatus = 0x00; //all endpoints are closed at the moment
	iAlternateSetting = 0;
	return aChunk->SetReturnedHandle(errorOrhandle);
	}


EXPORT_C TInt RDevUsbcScClient::OpenEndpoint(TEndpointBuffer& aEpB, TInt aEpI)
	{
	TUsbcScHdrEndpointRecord* endpointInf = NULL;
	TBuf8<KUsbDescSize_Endpoint> descriptor;
	TUsbcScChunkHeader chunkHeader(iSharedChunk);
	//Do some validity checks
	if((aEpB.iInState != TEndpointBuffer::ENotValid) && (aEpB.iOutState != TEndpointBuffer::ENotValid))
		return KErrArgument;

	TInt nEndpoints = chunkHeader.GetNumberOfEndpoints(iAlternateSetting);	
	if ((aEpI < KEp0Number) && (aEpI > nEndpoints)) // Check endpoint number range 
		return KErrNotFound;

	if(iEndpointStatus & (1 << aEpI)) // Check that endpoint isn't already opene
		return KErrInUse;

	if(aEpI == KEp0Number) //endpoint 0
		{
		TUsbcScHdrEndpointRecord ep0=  TUsbcScHdrEndpointRecord(KUsbcScEndpointZero, KUsbScHdrEpDirectionBiDir | KUsbScHdrEpTypeControl);
		aEpB.Construct(this,iSharedChunk.Base(), &ep0 ,aEpI, 
		(SUsbcScBufferHeader*) ((TUint)iSharedChunk.Base() + (chunkHeader.iBuffers)->Ep0Out()->Offset()));

		aEpB.iBufferStartAddr = (TUint8*) ((TUint)iSharedChunk.Base() + (chunkHeader.iBuffers)->Ep0In()->Offset());
		aEpB.iSize = chunkHeader.iBuffers->Ep0In()->Size();
		}
	else  // If normal endpoint (!ep0)
		{
		TUsbcScBufferRecord* buf = 	chunkHeader.GetBuffer(iAlternateSetting,aEpI,endpointInf);
		if (!buf)
			return KErrGeneral;
		// Set up endpoint members
		aEpB.iBufferStartAddr = (TUint8*)  (buf->Offset() + (TUint)iSharedChunk.Base());
		aEpB.iSize = buf->Size();
		TInt r = GetEndpointDescriptor(iAlternateSetting, aEpI, descriptor);
		if(r != KErrNone) // We need this to be able to calculate alignment
			{
			return r;
			}

		if (endpointInf->Direction()&KUsbScHdrEpDirectionIn)
			{  							//in case of IN endpoints, first endpoint buffer location points to end offset
			aEpB.Construct(this,iSharedChunk.Base(),endpointInf,aEpI);
			if (iInAltSetting==KErrEof)
			aEpB.iInState=TEndpointBuffer::EEOF;

			}
		else
			{
			SUsbcScBufferHeader *endpointHdr = (SUsbcScBufferHeader *) aEpB.iBufferStartAddr;
			//In this case,SUsbcScBufferHeader points to full OUT endpoint header 
			aEpB.Construct(this,iSharedChunk.Base(),endpointInf,aEpI, endpointHdr);
			}
		}
	iEndpointStatus |= (1 << aEpI);	

#ifdef _DEBUG
	aEpB.Dump();
	RDebug::Printf("iEndpointStatus: %x \n",iEndpointStatus);
#endif
	return KErrNone;
	}


//Internal, used by RDevUsbcScClient::StartNextOutAlternateSetting(...)
//This drains any old data from an OUT buffer, and gets it ready for reading an ep.
//aBufferOffset - The offset, into the chunk, that the buffer in question, may be found.
 
TInt RDevUsbcScClient::Drain(TUint aBufferOffset)
{
	TUint8* base = iSharedChunk.Base();
	SUsbcScBufferHeader* endpointHdr = (SUsbcScBufferHeader*) (aBufferOffset+base);
	TUint localTail = endpointHdr->iBilTail;
	TUsbcScTransferHeader* currentTransfer;
	TUint16 next = (iAltSettingSeq+1)&0xFFFF;
	TInt err=KErrNone;

	while (ETrue)
		{
		if (localTail == (TUint) endpointHdr->iHead)
			{
			err = KErrNotReady;
			break;
			}
		currentTransfer = (TUsbcScTransferHeader*) (base + localTail);

		if (currentTransfer->iAltSettingSeq == next)
			{
			iNewAltSetting=currentTransfer->iAltSetting; // record new alt setting
			break;
			}
		else
			{
			localTail = currentTransfer->iNext;
			}
		} // end while
	endpointHdr->iBilTail = localTail;
	endpointHdr->iTail = localTail;
	return err;
}

//Internal, used by RDevUsbcScClient::StartNextOutAlternateSetting(...)
//This method checks that the OUT buffer is ready for reading an ep.
//aBufferOffset - The offset, into the chunk, that the buffer in question, may be found.

TInt RDevUsbcScClient::Peek(TUint aBufferOffset)
{
	TUint8* base = iSharedChunk.Base();
	SUsbcScBufferHeader* endpointHdr = (SUsbcScBufferHeader*) (aBufferOffset+base);
	TUint localTail = endpointHdr->iBilTail;
	TUsbcScTransferHeader* currentTransfer = (TUsbcScTransferHeader*) (base + localTail);

	if ((localTail == (TUint)endpointHdr->iHead) || (currentTransfer->iAltSettingSeq != ((iAltSettingSeq+1)&0xFFFF)))
		// if alternate setting has not changed
		return KErrNotReady;
	else
		{
		iNewAltSetting=currentTransfer->iAltSetting;
		return KErrNone;
		}
}

//Internal, used by RDevUsbcScClient::StartNextOutAlternateSetting(...)
//This method is called if an alternate setting change happens from a set of ONLY IN endpoints.
//Used to find the least possible alternate setting it can return to the user, stored in iNewAltSetting
//Returns the sequence number of the 'latest' alternate setting it can switch to

TInt RDevUsbcScClient::FindNextAlternateSetting()
	{
	TUsbcScChunkHeader chunkHeader(iSharedChunk);
	TUsbcScHdrEndpointRecord* endpointInf = NULL;
	TUint bufOff;
	TInt altSet;
	TInt ep;
	TInt bufNum;

	RArray <TInt> bufferOffset;	// Array to contain all OUT enpoint buffer offsets
	// Populate array
	for (altSet = 0; altSet < chunkHeader.iAltSettings->iNumOfAltSettings ; altSet++)
		{
		TInt numEndpoints = chunkHeader.GetNumberOfEndpoints(altSet);
		for (ep =  1; ep  <= numEndpoints ; ep ++)
			{
			bufOff = chunkHeader.GetBuffer(altSet, ep, endpointInf)->Offset();	
			if ((endpointInf->Direction() & KUsbScHdrEpDirectionOut) && (bufferOffset.Find(bufOff) == KErrNotFound))
				{
				bufferOffset.Append(bufOff);
				}
			}
		}

	TInt err = KErrNotFound;
	TUint16 altSetSeqDelta = 0;
	TUint16 currentaltSetSeqDelta = 0;
	TBool noNewSettingFound = ETrue;
	TInt altSetSeq = iAltSettingSeq;
	TUint8* base = iSharedChunk.Base();

	for (bufNum = 0; bufNum < bufferOffset.Count(); bufNum++) // Scan all OUT buffers
		{	
		SUsbcScBufferHeader* endpointHdr = (SUsbcScBufferHeader*) (bufferOffset[bufNum] + base);
		TUint localTail = endpointHdr->iBilTail;
		TUsbcScTransferHeader* currentTransfer;
		TUint16 next = (iAltSettingSeq+1)&0xFFFF;
		
		while (ETrue)
			{
			if (localTail == (TUint) endpointHdr->iHead)
				{
				break;	// This OUT endpoint buffer has no data, proceed checking with other OUT endpoint buffers
				}
			currentTransfer = (TUsbcScTransferHeader*) (base + localTail);

			if (currentTransfer->iAltSettingSeq != iAltSettingSeq) 
				{
				if (currentTransfer->iAltSettingSeq == next)
					{
					altSetSeq = currentTransfer->iAltSettingSeq;
					iNewAltSetting = currentTransfer->iAltSetting; // record new alt setting
					err = KErrNone;
					break;
					}

				if (noNewSettingFound)
					{
					altSetSeqDelta = Abs(iAltSettingSeq - currentTransfer->iAltSettingSeq);
					altSetSeq = currentTransfer->iAltSettingSeq;
					iNewAltSetting = currentTransfer->iAltSetting; // record new alt setting
					noNewSettingFound = EFalse;
					}
				else
					{
					currentaltSetSeqDelta = Abs(iAltSettingSeq - currentTransfer->iAltSettingSeq);
					if (currentaltSetSeqDelta < altSetSeqDelta)
						{
						altSetSeqDelta = currentaltSetSeqDelta;
						altSetSeq = currentTransfer->iAltSettingSeq;
						iNewAltSetting = currentTransfer->iAltSetting;
						}
					}
				break;
				}
			
			localTail = currentTransfer->iNext;
			} // end while

		if (!err) // Found an alt set sequence one after iAltSettingSeq
			{
			break; // found 'the next' alternate setting, exit for loop
			}
		
		}// for loop

	return altSetSeq;
	}

EXPORT_C TInt RDevUsbcScClient::StartNextOutAlternateSetting(TBool aFlush)
	{
	TUsbcScChunkHeader chunkHeader(iSharedChunk);
	
	//if endpoints are still open, return KErrInUse 
	if((iEndpointStatus&~1) != 0)
		{
		return KErrInUse;
		}

	TInt r;
	TInt ep;
	TInt noEp;
	TUint bufOff;
	TBool inEndpointSet = ETrue;
	TUsbcScHdrEndpointRecord* endpointInf = NULL;

	// check if alternate setting contains all IN endpoints
	noEp = chunkHeader.GetNumberOfEndpoints(iAlternateSetting);

	// for each used buffer. 
	for (ep=1;ep<=noEp;ep++)
		{
		bufOff = chunkHeader.GetBuffer(iAlternateSetting,ep,endpointInf)->Offset();	
	
		if (endpointInf->Direction() & KUsbScHdrEpDirectionOut) 
			{
			inEndpointSet = EFalse;
			if (aFlush)
				r = Drain(bufOff); // we need to remove anythng in the way, and get it ready for reading.
			else
				r = Peek(bufOff); // we need to check it is ready for reading!
			if (r) 
				return r;
			}
		}


	TInt altSeq = 0;
	if (inEndpointSet)	// If all endpoints in the current alternate setting are IN endpoints
		{	// go through all OUT buffers for alternate setting change
		altSeq = FindNextAlternateSetting();
		}

	if((iNewAltSetting == iAlternateSetting) && (!inEndpointSet))
			{
			return KErrNotReady;
			}

	// Find/Set IN alternate setting
	TInt ret = StartNextInAlternateSetting();
	SUsbcScAlternateSetting* altrec = ((SUsbcScAlternateSetting*) (&ret));

	if (altrec->iSequence==iAltSettingSeq+1)
		{
		if (altrec->iSetting!=iNewAltSetting)
			return KErrGeneral;
		iInAltSetting=iNewAltSetting;
		}
	else
		{
		if (inEndpointSet)
			{
			if ((altSeq == iAltSettingSeq) || (iAltSettingSeq == altrec->iSequence))
				{
				return KErrNotReady;
				}
			else if (altSeq != altrec->iSequence)
				{
				iInAltSetting=KErrEof;
				}
			}
		iInAltSetting=KErrEof;
		}

	iAlternateSetting = iNewAltSetting;
	iAltSettingSeq += 1;

	return iAlternateSetting;
	}


EXPORT_C TInt RDevUsbcScClient::GetDataTransferChunk(RChunk* & aChunk)
	{
	aChunk = &iSharedChunk;
	return KErrNone;
	}

// Constructor

EXPORT_C TEndpointBuffer::TEndpointBuffer()
		:iInState(ENotValid),
		iOutState(ENotValid),
		iEndpointNumber(-1),
		iBufferNum(-1),
		iBufferStartAddr(0),
		iSize(0)
	{
	}

// Internal, called by RDevUsbcScClient::OpenEndpoint.
void TEndpointBuffer::Construct(RDevUsbcScClient* aClient, TUint8* aBaseAddr, const TUsbcScHdrEndpointRecord* aEpType , TInt aEndpointNumber,SUsbcScBufferHeader* aEndpointHdr)
	{
	iClient		= aClient;
	iBaseAddr	= (TUint) aBaseAddr;
	iInState 	= (((aEpType->Direction())&KUsbScHdrEpDirectionIn) ? EValid :  ENotValid);
	iOutState	= (((aEpType->Direction())&KUsbScHdrEpDirectionOut) ? EValid :  ENotValid);
	iBufferNum	= (aEpType->iBufferNo==(KUsbcScEndpointZero&0xFF))?KUsbcScEndpointZero:aEpType->iBufferNo;
	iEndpointNumber = aEndpointNumber;

	iEndpointHdr = aEndpointHdr;
	};

EXPORT_C TInt TEndpointBuffer::GetInBufferRange(TAny*& aStart, TUint& aSize)
	{
	if ((iInState))
		{
		return iInState;
		}
	aStart= iBufferStartAddr;
	aSize= iSize;
	return KErrNone;
	};

EXPORT_C TInt TEndpointBuffer::GetInBufferRange(TUint& aStart, TUint& aSize)
	{
	if ((iInState))
		return iInState;
	aStart=	(TUint) iBufferStartAddr - iBaseAddr;
	aSize= iSize;
	return KErrNone;
	}


EXPORT_C TInt TEndpointBuffer::GetBuffer(TAny*& aBuffer,TUint& aSize,TBool& aZLP,TRequestStatus& aStatus,TUint aLength)
	{
	if (iOutState)
		return iOutState;

	TUsbcScTransferHeader* currentTransfer;
	TInt r;
	do // until we have a transfer with data.
		{
		iEndpointHdr->iTail = iEndpointHdr->iBilTail; 
		if(iEndpointHdr->iBilTail == iEndpointHdr->iHead)  //If no new data, create request
			{
			r = iClient->ReadDataNotify(iBufferNum,aStatus);
			if (r!=KErrCompletion)  // Data could arrive since we checked.
				return r;
			}
		currentTransfer = (TUsbcScTransferHeader*) (iBaseAddr + iEndpointHdr->iBilTail);

		iEndpointHdr->iBilTail = currentTransfer->iNext;
		aZLP = (currentTransfer->iFlags & KUsbcScShortPacket)!=EFalse;

		if(currentTransfer->iAltSettingSeq != (iClient->iAltSettingSeq))  // if alternate setting has changed
			{
			if (currentTransfer->iAltSettingSeq == (iClient->iAltSettingSeq+1))	   //Note- KIS ATM, if multiple alternate setting changes happen
				iClient->iNewAltSetting = currentTransfer->iAltSetting; //before StartNextOutAlternateSetting is called, 		   
																	   //this variable will reflect the latest requested AlternateSetting


			if (iEndpointNumber != KEp0Number)
				{
//				iOutState =  EEOF;	
				return KErrEof;
				}
			else if ((currentTransfer->iBytes==0) && (!aZLP)) 
				{
				return KErrAlternateSettingChanged;
				}
			}						

		}
	while ((currentTransfer->iBytes==0) && (!aZLP)); // ignore empty transfers

	aBuffer = currentTransfer->iData.i;
	aSize = currentTransfer->iBytes;	
	return (currentTransfer->iFlags & KUsbcScStateChange)?KStateChange:KErrCompletion;	
	}

EXPORT_C TInt TEndpointBuffer::TakeBuffer(TAny*& aBuffer,TUint& aSize,TBool& aZLP,TRequestStatus& aStatus,TUint aLength)
	{
	if (iOutState)
		return iOutState;

	TUsbcScTransferHeader* currentTransfer;
	TInt r;
	do // until we have a transfer with data.
		{
		if(iEndpointHdr->iBilTail == iEndpointHdr->iHead)  //If no new data, create request
			{
			r = iClient->ReadDataNotify(iBufferNum,aStatus);
			if (r!=KErrCompletion)  // Data could arrive since we checked.
				{
				return r;
				}
			}

		currentTransfer = (TUsbcScTransferHeader*) (iBaseAddr + iEndpointHdr->iBilTail);
		iEndpointHdr->iBilTail = currentTransfer->iNext;
		aZLP = (currentTransfer->iFlags & KUsbcScShortPacket)!=EFalse; // True if short packet else false 

		if(currentTransfer->iAltSettingSeq != (iClient->iAltSettingSeq))  // if alternate setting has changed
			{
			if (currentTransfer->iAltSettingSeq == (iClient->iAltSettingSeq+1))	   //Note- KIS ATM, if multiple alternate setting changes happen
				iClient->iNewAltSetting = currentTransfer->iAltSetting; //before StartNextOutAlternateSetting is called, 		   
																	   //this variable will reflect the latest requested AlternateSetting
			Expire(currentTransfer->iData.i);
			if (iEndpointNumber != KEp0Number)
				{
//				iOutState = EEOF;
				return KErrEof;
				}
			else if ((currentTransfer->iBytes==0) && (!aZLP)) 
				{
				return KErrAlternateSettingChanged;
				}

			}	

		if ((currentTransfer->iBytes==0) && (!aZLP)) // here , if empty transfer with alt setting information, Call expire 
			{
			Expire(currentTransfer->iData.i);
			}
		}
	while ((currentTransfer->iBytes==0) && (!aZLP)); // ignore empty transfers

	aBuffer = currentTransfer->iData.i;
	aSize = currentTransfer->iBytes;
	return (currentTransfer->iFlags & KUsbcScStateChange)?KStateChange:KErrCompletion;	
	}

EXPORT_C TInt TEndpointBuffer::Expire()
	{
	if (!(iOutState != ENotValid))
		return iOutState;

	if (iEndpointHdr->iTail != iEndpointHdr->iBilTail)
		{
		TUsbcScTransferHeader* currentTransfer = (TUsbcScTransferHeader*) (iBaseAddr + iEndpointHdr->iTail);
		iEndpointHdr->iTail = currentTransfer->iNext;
		}
	return KErrNone;
	}

EXPORT_C TInt TEndpointBuffer::Expire(TAny* aAddress)
	{
	if (!(iOutState != ENotValid))
		return iOutState;

	TUint headerSize = sizeof(TUsbcScTransferHeader)-4; // TransferHeader includes 4 bytes of data.
	TInt transferToExpire = ((TUint) aAddress - headerSize);
	TInt offsetToExpire = transferToExpire - iBaseAddr; 

	TInt currentTail = iEndpointHdr->iTail;

	TInt prevTail = NULL;
	TBool found = EFalse;
	while (currentTail != iEndpointHdr->iBilTail)
		{
		TUsbcScTransferHeader* currentTransfer = (TUsbcScTransferHeader*) (iBaseAddr + currentTail);
		if (currentTail == offsetToExpire)		// found which to expire
			{
			found = ETrue;
			// This offset is to be expired
			if (prevTail == NULL)
				{
				// The offset is at the list head
				iEndpointHdr->iTail = currentTransfer->iNext;	
				}
			else
				{
				// The offset is NOT at the list head
				// This leaves a GAP in the buffer which will not be filled unless the 'transfers' before 'currentTail' are expired
				currentTail = currentTransfer->iNext;
				TUsbcScTransferHeader* prevTransfer = (TUsbcScTransferHeader*) (iBaseAddr + prevTail);
				prevTransfer->iNext = currentTail;
				}
			break;
			}
		prevTail = currentTail;
		currentTail = currentTransfer->iNext;
		}
	return found ? KErrNone : KErrNotFound;
	}

	
EXPORT_C TInt TEndpointBuffer::WriteBuffer(TAny* aBuffer,TUint aSize,TBool aZLP,TRequestStatus& aStatus)
	{
	if (iInState)
		return iInState;

	iClient->WriteData(iBufferNum, ((TUint)aBuffer - (TUint)iBaseAddr),aSize,aZLP,aStatus);
	return KErrNone;
	}


EXPORT_C TInt TEndpointBuffer::WriteBuffer(TUint aOffset,TUint aSize,TBool aZLP,TRequestStatus& aStatus)
	{
	if (iInState)
		return iInState;

	iClient->WriteData(iBufferNum,aOffset,aSize,aZLP,aStatus);
	return KErrNone;
	}


/**
Closes the endpoint buffer
@return			KErrNone if close is successfull
*/	
EXPORT_C TInt TEndpointBuffer::Close()
	{
	if ((iInState == ENotValid) && (iOutState == ENotValid))
		return KErrNotFound;
	if (iOutState != ENotValid)
		{
		TUsbcScTransferHeader* currentTransfer = (TUsbcScTransferHeader*) (iBaseAddr + iEndpointHdr->iTail);
		//Incase of AlternateSetting changes and using TEndpointBuffer::GetBuffer, iTail is always one 'transfer' behind iBilTail
		//Incase of AlternateSetting changes and using TEndpointBuffer::TakeBuffer, this shuold force the user to update iTail & only then closes the endpoint buffer
		if (((TInt) currentTransfer->iNext != iEndpointHdr->iBilTail) && (iEndpointHdr->iTail != iEndpointHdr->iBilTail))
			return KErrNotReady;
		}
	iClient->iEndpointStatus &= ~(1 << iEndpointNumber); //reset the bit corresponding to endpoint
	iInState = ENotValid;
	iOutState = ENotValid;
	return KErrNone;
	}


  
EXPORT_C TUsbcScChunkHeader::TUsbcScChunkHeader(RChunk aChunk)
	{
	iChunk = aChunk;
	iBuffers     = (TUsbcScChunkBuffersHeader*)    (aChunk.Base()+((TUsbcScChunkHdrOffs*)iChunk.Base())->iBuffers);
	iAltSettings = (TUsbcScChunkAltSettingHeader*) (aChunk.Base()+((TUsbcScChunkHdrOffs*)iChunk.Base())->iAltSettings);
	}

EXPORT_C TInt TUsbcScChunkHeader::GetNumberOfEndpoints(TInt aAltSetting)
	{
	if ((aAltSetting<0) || (aAltSetting>=iAltSettings->iNumOfAltSettings))
		return KErrArgument;
	return  *((TInt*) (iAltSettings->iAltTableOffset[aAltSetting] + (TInt) iChunk.Base()));
	}


EXPORT_C TUsbcScBufferRecord* TUsbcScChunkHeader::GetBuffer(TInt aAltSetting, TInt aEndpoint, TUsbcScHdrEndpointRecord*& aEndpointInf)
	{
	if ((aAltSetting<0) || (aAltSetting>=iAltSettings->iNumOfAltSettings))
		return NULL;
	TInt8* iEndpoint = (TInt8*) (iAltSettings->iAltTableOffset[aAltSetting] + (TInt) iChunk.Base());
	if ((aEndpoint<=0) || (aEndpoint>*iEndpoint))
		return NULL;
	aEndpointInf = (TUsbcScHdrEndpointRecord*) &(iEndpoint[aEndpoint*iAltSettings->iEpRecordSize]);
	return iBuffers->Buffers(aEndpointInf->iBufferNo);
	}


/* Debug functions */

EXPORT_C void TEndpointBuffer::Dump()
	{
	RDebug::Printf("TEndpointBuffer::Dump iBufferStart: 0x%x, iSize: 0x%x, iEndpointNumber: 0x%x, iBufferNum: %d, iInState: 0x%x iOutState: 0x%x\n",
							iBufferStartAddr,iSize,iEndpointNumber,iBufferNum,iInState,iOutState);
	}

