//******************************************************************************
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//******************************************************************************

/*++

Module Name:  

UsbServ.cpp

Abstract:  
    USB Driver Service.

Functions:

Notes: 

--*/

#define __THIS_FILE__   TEXT("UsbServ.cpp")

#include <windows.h>
#include <memory.h>
#include "usbtest.h"
#include "UsbServ.h"
UsbBasic::UsbBasic(LPCUSB_FUNCS UsbFuncsPtr,LPCUSB_INTERFACE pInterface, LPCWSTR uniqueDriverId,
				   LPCUSB_DRIVER_SETTINGS lpDriverSettings): lpUsbFuncs(UsbFuncsPtr)
{
	if(UsbFuncsPtr == NULL || pInterface == NULL || uniqueDriverId == NULL || lpDriverSettings == NULL)
	    return;

	lpInterface=(LPUSB_INTERFACE) malloc(sizeof(USB_INTERFACE));
	memcpy(lpInterface,pInterface,sizeof(USB_INTERFACE));
	szUniqueDriverId=(LPWSTR)malloc(sizeof(WCHAR)*(wcslen(uniqueDriverId)+1));
	wcscpy(szUniqueDriverId,uniqueDriverId);

}
UsbBasic::~UsbBasic()
{
	free(lpInterface);
	free(szUniqueDriverId);
}
void UsbBasic::debug(LPCTSTR szFormat, ...) {
	TCHAR szBuffer[1024] = TEXT("USBClass: ");
	
	va_list pArgs; 
	va_start(pArgs, szFormat);
	_vsntprintf(szBuffer + 10, countof(szBuffer) - 13, szFormat, pArgs);
	va_end(pArgs);
	
	_tcscat(szBuffer, TEXT("\r\n"));
	
	OutputDebugString(szBuffer);
}


VOID UsbBasic::GetUSBDVersion(LPDWORD lpdwMajorVersion, LPDWORD lpdwMinorVersion)
{
	if(lpUsbFuncs != NULL)
	    (*lpUsbFuncs->lpGetUSBDVersion)(lpdwMajorVersion,lpdwMinorVersion);
}
//Helper commands
BOOL UsbBasic::TranslateStringDesc(LPCUSB_STRING_DESCRIPTOR lpStringDesc,
								   LPWSTR szString,DWORD cchStringLength)
{
	if(lpUsbFuncs != NULL)
	    return (*lpUsbFuncs->lpTranslateStringDesc)(lpStringDesc,szString,cchStringLength);

	return FALSE;

}
LPCUSB_INTERFACE UsbBasic::FindInterface(LPCUSB_DEVICE lpUsbDevice, 
										 UCHAR bInterfaceNumber, UCHAR bAlternateSetting)
{
	if(lpUsbFuncs != NULL)
	    return (*lpUsbFuncs->lpFindInterface)(lpUsbDevice,bInterfaceNumber,bAlternateSetting);

       return NULL;

};
//---------------------------------Usb Transfer--------------------------------
DWORD UsbTransfer::dwCurrentID=0;
UsbTransfer::UsbTransfer(USB_TRANSFER usbTransfer,LPCUSB_FUNCS pUsbFuncs,
						 LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr):
			hUsbTransfer(usbTransfer),lpUsbFuncs(pUsbFuncs),
			transferEvent(FALSE,TRUE,NULL,NULL),
			lpAttachedBuffer(lpBuffer),dwAttachedSize(dwSize),dwAttachedPhysicalAddr(dwPhysicalAddr)
{
	ASSERT(pUsbFuncs);
	++dwCurrentID;
	dwID=dwCurrentID;
	transferEvent.ResetEvent();
}

UsbTransfer::~UsbTransfer()
{
	if (hUsbTransfer)
		CloseTransfer();
};
VOID UsbTransfer::ReInitial(USB_TRANSFER usbTransfer,LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr)
{
	if (hUsbTransfer)
		CloseTransfer();
	hUsbTransfer=usbTransfer;
	transferEvent.ResetEvent();
	++dwCurrentID;
	dwID=dwCurrentID;
	lpAttachedBuffer=lpBuffer;
	dwAttachedSize=dwSize;
	dwAttachedPhysicalAddr=dwPhysicalAddr;
}
// get info on Transfers
BOOL UsbTransfer::IsTransferComplete ()
{
	ASSERT(hUsbTransfer);
	return (*lpUsbFuncs->lpIsTransferComplete)(hUsbTransfer);
}
BOOL UsbTransfer::IsCompleteNoError()
{
	if (hUsbTransfer && IsTransferComplete()) {
		DWORD dwBytesTransfered=0;
		DWORD dwError;
		if (GetTransferStatus(&dwBytesTransfered,&dwError) && dwError==USB_NO_ERROR) 
			return TRUE;
	}
	return FALSE;
}
BOOL UsbTransfer::GetTransferStatus (LPDWORD lpdwBytesTransfered, LPDWORD lpdwError)
{
	ASSERT(hUsbTransfer);
	return  (*lpUsbFuncs->lpGetTransferStatus)(hUsbTransfer,lpdwBytesTransfered,lpdwError);
}
BOOL UsbTransfer::GetIsochResults(DWORD cFrames, LPDWORD lpdwBytesTransfered, 
								  LPDWORD lpdwErrors)
{
	ASSERT(hUsbTransfer);
	return (*lpUsbFuncs->lpGetIsochResults)(hUsbTransfer,cFrames,
		lpdwBytesTransfered, lpdwErrors);

}
// transfer maniuplators
BOOL UsbTransfer::AbortTransfer(DWORD dwFlags)
{
	ASSERT(hUsbTransfer);
	return (*lpUsbFuncs->lpAbortTransfer)(hUsbTransfer,dwFlags);
}
BOOL UsbTransfer::CloseTransfer()
{
	ASSERT(hUsbTransfer);
	BOOL rt= (*lpUsbFuncs->lpCloseTransfer)(hUsbTransfer);
	hUsbTransfer=NULL;
	return rt;
}


// transfer callback.

DWORD WINAPI TransferNotify(LPVOID lpvNotifyParameter)
{
	PTransStatus pTransStatus=(PTransStatus)lpvNotifyParameter;
	if (!pTransStatus)
		return -1;

	if (pTransStatus->hTransfer!=0) {
		if ((*(pTransStatus->lpUsbFuncs->lpIsTransferComplete))(pTransStatus->hTransfer))
			pTransStatus->IsFinished=TRUE;
		else
			pTransStatus->IsFinished=FALSE;
	}
	else
		pTransStatus->IsFinished=FALSE;
	pTransStatus->cEvent.SetEvent();
	return 1;
}

//------------------------------------Usb Class-------------------------------------

UsbClass::UsbClass(USB_HANDLE usbHandle,LPCUSB_FUNCS UsbFuncsPtr,
			LPCUSB_INTERFACE lpInterface, LPCWSTR szUniqueDriverId,
            LPCUSB_DRIVER_SETTINGS lpDriverSettings) :
		UsbBasic(UsbFuncsPtr,lpInterface,szUniqueDriverId,lpDriverSettings),
		hUsb(usbHandle)
{
	DEBUGMSG(DBG_FUNC,(TEXT("UsbClass")));
	if(lpInterface == NULL)
	    return;
	m_dwSyncFrameNumber=0;
	DefaultDeviceDesc=(lpInterface->lpEndpoints)->Descriptor;
};
UsbClass::~UsbClass()
{

}


BOOL UsbClass::DisableDevice(BOOL fReset, BYTE bInterfaceNumber)
{
	return (*lpUsbFuncs->lpDisableDevice)(hUsb, fReset, bInterfaceNumber);
}

BOOL UsbClass::SuspendDevice(BYTE bInterfaceNumber)
{
	return (*lpUsbFuncs->lpSuspendDevice)(hUsb,bInterfaceNumber);
}

BOOL UsbClass::ResumeDevice(BYTE bInterfaceNumber)
{
	return (*lpUsbFuncs->lpResumeDevice)(hUsb,bInterfaceNumber);
}



//USB Subsystem Commands
BOOL UsbClass::GetFrameNumber(LPDWORD lpdwFrameNumber)
{
	return (*lpUsbFuncs->lpGetFrameNumber)(hUsb,lpdwFrameNumber);
}
BOOL UsbClass::GetFrameLength(LPUSHORT lpuFrameLength)
{
	return (*lpUsbFuncs->lpGetFrameLength)(hUsb,lpuFrameLength);
};
//Enables Device to Adjust the USB SOF period on OHCI or UHCI cards
BOOL UsbClass::TakeFrameLengthControl()
{
	return (*lpUsbFuncs->lpTakeFrameLengthControl)(hUsb);
}
BOOL UsbClass::SetFrameLength(HANDLE hEvent, USHORT uFrameLength)
{
	return (*lpUsbFuncs->lpSetFrameLength)(hUsb,hEvent,uFrameLength);
}
BOOL UsbClass::ReleaseFrameLengthControl()
{
	return (*lpUsbFuncs->lpReleaseFrameLengthControl)(hUsb);
}

// Gets info on a device
LPCUSB_DEVICE UsbClass::GetDeviceInfo()
{
	return (*lpUsbFuncs->lpGetDeviceInfo)(hUsb);
};

//Device commands
USB_TRANSFER UsbClass::IssueVendorTransfer(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter,DWORD dwFlags,
		LPCUSB_DEVICE_REQUEST lpControlHeader, LPVOID lpvBuffer,ULONG uBufferPhysicalAddress)
{
	return (*lpUsbFuncs->lpIssueVendorTransfer)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,lpControlHeader,lpvBuffer,uBufferPhysicalAddress);
}
USB_TRANSFER UsbClass::GetInterface(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter, DWORD dwFlags, UCHAR bInterfaceNumber, PUCHAR lpvAlternateSetting)
{
	return (*lpUsbFuncs->lpGetInterface)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
				dwFlags,bInterfaceNumber,lpvAlternateSetting);
}
USB_TRANSFER UsbClass::SetInterface(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter,DWORD dwFlags, UCHAR bInterfaceNumber, UCHAR bAlternateSetting)
{
	return (*lpUsbFuncs->lpSetInterface)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,bInterfaceNumber,bAlternateSetting);
}
USB_TRANSFER UsbClass::GetDescriptor(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter, DWORD dwFlags, UCHAR bType, UCHAR bIndex, WORD wLanguage,
                                          WORD wLength, LPVOID lpvBuffer)
{
	return (*lpUsbFuncs->lpGetDescriptor)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,bType,bIndex,wLanguage,wLength,lpvBuffer);
}
USB_TRANSFER UsbClass::SetDescriptor(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter, DWORD dwFlags, UCHAR bType, UCHAR bIndex, WORD wLanguage,
                                          WORD wLength, LPVOID lpvBuffer)
{
	return (*lpUsbFuncs->lpSetDescriptor)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,bType,bIndex,wLanguage,wLength,lpvBuffer);
}
USB_TRANSFER UsbClass::SetFeature(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter,DWORD dwFlags, WORD wFeature, UCHAR bIndex)
{
	return (*lpUsbFuncs->lpSetFeature)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,wFeature,bIndex);
}
USB_TRANSFER UsbClass::ClearFeature(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter,DWORD dwFlags, WORD wFeature, UCHAR bIndex)
{
	return (*lpUsbFuncs->lpClearFeature)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,wFeature,bIndex);
}
USB_TRANSFER UsbClass::GetStatus(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter, DWORD dwFlags, UCHAR bIndex, LPWORD lpwStatus)
{
	return (*lpUsbFuncs->lpGetStatus)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,bIndex,lpwStatus);
}
USB_TRANSFER UsbClass::SyncFrame(LPTRANSFER_NOTIFY_ROUTINE lpNodifyAddress,LPVOID lpNodifyParameter, DWORD dwFlags, UCHAR bEndPoint, LPWORD lpwFrame)
{
	return (*lpUsbFuncs->lpSyncFrame)(hUsb,
			((dwFlags & USB_NO_WAIT)!=0)?lpNodifyAddress:NULL,
			lpNodifyParameter,
			dwFlags,bEndPoint,lpwFrame);
}
//-----default pipe----------------------
BOOL UsbClass::ResetDefaultPipe()
{
	return (*lpUsbFuncs->lpResetDefaultPipe)(hUsb);
}
BOOL UsbClass::IsDefaultPipeHalted(LPBOOL lpbHalted)
{
	return (*lpUsbFuncs->lpIsDefaultPipeHalted)(hUsb,lpbHalted);
}

//-----------------------------------UsbPipeService--------------------------------

UsbPipeService::UsbPipeService(LPCUSB_FUNCS ptrUsbFuncs,USB_HANDLE usbHandle,LPCUSB_ENDPOINT_DESCRIPTOR lpEndPointDescriptor,BOOL Async)
{
	DEBUGMSG(DBG_FUNC,(TEXT("UsbPipeService")));

	if(ptrUsbFuncs == NULL || usbHandle == NULL || lpEndPointDescriptor == NULL)
	    return;

	errorCode=0;
	hPipe=NULL;
	lpUsbFuncs=ptrUsbFuncs;
	DeviceDesc=*lpEndPointDescriptor;
	dwFlags=0;//USB_SEND_TO_ENDPOINT;
	if (Async) {
		dwFlags|=USB_NO_WAIT;
		lpStartAddress=&UsbPipeService::TransferNotify;
	}
	else
		lpStartAddress=NULL;

	if (lpUsbFuncs) {
		hPipe=(*(lpUsbFuncs->lpOpenPipe))(usbHandle,lpEndPointDescriptor);
		if (hPipe==NULL) {
			DEBUGMSG(DBG_ERR,(TEXT("can not open pipe usbHandle=%lx,Descriptor=%lx"),usbHandle,lpEndPointDescriptor));
			errorCode=ERROR_USB_PIPE_OPEN;
		};
		if (isInPipe()) 
			dwFlags|=USB_IN_TRANSFER;
		if (isOutPipe())
			dwFlags|=USB_OUT_TRANSFER;
	}
	else {
		DEBUGMSG(DBG_ERR,(TEXT(" usb func is NULL!!!")));
		errorCode=ERROR_USB_NULL_FUNC;
	};
	DEBUGMSG(DBG_FUNC,(TEXT("End of UsbPipeService")));

}
UsbPipeService::~UsbPipeService()
{
	errorCode =0;
	if (hPipe) {
		if (!(*(lpUsbFuncs->lpClosePipe))(hPipe)) 
			errorCode=ERROR_USB_PIPE_CLOSE;
	};
};


BOOL UsbPipeService::AbortPipeTransfers(DWORD dwFlags)
{
	ASSERT(hPipe);
	if ((*lpUsbFuncs->lpAbortPipeTransfers)(hPipe,dwFlags))
		return TRUE;
	else {
		errorCode=ERROR_USB_PIPE;
		return FALSE;
	};

}
BOOL UsbPipeService::ResetPipe()
{
	ASSERT(hPipe);
	if ((*lpUsbFuncs->lpResetPipe)(hPipe))
		return TRUE;
	else {
		errorCode=ERROR_USB_PIPE;
		return FALSE;
	};
}
BOOL UsbPipeService::IsPipeHalted(LPBOOL lpbHalted)
{
	ASSERT(hPipe);
	if ((*lpUsbFuncs->lpIsPipeHalted)(hPipe,lpbHalted))
		return TRUE;
	else {
		errorCode=ERROR_USB_PIPE;
		return FALSE;
	};
}
#define ISSUE_RETRY 5
#define RETRY_SLEEP 20
USB_TRANSFER UsbPipeService::IssueControlTransfer(UsbTransfer * uTransfer, LPCVOID lpvControlHeader,DWORD dwBufferSize,
		LPVOID lpvBuffer,ULONG uBufferPhysicalAddress)
{
	if(uTransfer == NULL || hPipe == NULL)
	    return NULL;
	USB_TRANSFER hTransfer;
	DWORD dwRetry=0;
	do {
	pipeMutex.Lock();
	DEBUGMSG(DBG_CONTROL,(TEXT("+IssueControlTransfer(buffersize %lx,buffer %lx, phAddr %lx) TransferID %lx \n"),
		dwBufferSize,lpvBuffer,uBufferPhysicalAddress,
		uTransfer->GetTransferID()));
	uTransfer->SetPipe(this);
	hTransfer=
		(*lpUsbFuncs->lpIssueControlTransfer)(hPipe,
				lpStartAddress,uTransfer,
				dwFlags,lpvControlHeader,dwBufferSize,lpvBuffer,uBufferPhysicalAddress);
	DEBUGMSG(DBG_CONTROL,(TEXT("-IssueControlTransfer(TransferID %lx \n"),
		uTransfer->GetTransferID()));
	uTransfer->SetTransferHandle(hTransfer);
	pipeMutex.Unlock();
	if (hTransfer==NULL) {
		DEBUGMSG(DBG_CONTROL|DBG_WARN,(TEXT("IssueControlTransfer(buffersize %lx,buffer %lx, phAddr %lx) Failed, TransferID %lx \n"),
			dwBufferSize,lpvBuffer,uBufferPhysicalAddress,
			uTransfer->GetTransferID()));
		Sleep(RETRY_SLEEP);
	};
	} while (hTransfer==NULL && (dwRetry++<ISSUE_RETRY));

	if (hTransfer==NULL) {
		errorCode=ERROR_USB_PIPE_TRANSFER;
		ASSERT(FALSE);
	}
	else
	if ((dwFlags & USB_NO_WAIT)==0)
		ActionCompleteNotify(uTransfer);
	return hTransfer;
}
USB_TRANSFER UsbPipeService::IssueBulkTransfer(UsbTransfer * uTransfer, DWORD dwBufferSize, LPVOID lpvBuffer,
		ULONG uBufferPhysicalAddress)
{
	if(uTransfer == NULL || hPipe == NULL)
	    return NULL;

	USB_TRANSFER hTransfer;
	DWORD	dwRetry=0;
	do {
	pipeMutex.Lock();
	DEBUGMSG(DBG_BULK,(TEXT("+IssueBulkTransfer(buffersize %lx,buffer %lx, dwFirstData %lx, phAddr %lx) TransferID %lx \n"),
		dwBufferSize, lpvBuffer,*(PDWORD)lpvBuffer, uBufferPhysicalAddress,
		uTransfer->GetTransferID()));
	uTransfer->SetPipe(this);
	hTransfer=(*lpUsbFuncs->lpIssueBulkTransfer)(hPipe,
				lpStartAddress,uTransfer,
				dwFlags,dwBufferSize,(LPVOID)lpvBuffer,uBufferPhysicalAddress);
	DEBUGMSG(DBG_BULK,(TEXT("-IssueBulkTransfer TransferID %lx \n"),
		uTransfer->GetTransferID()));
	uTransfer->SetTransferHandle(hTransfer);
	pipeMutex.Unlock();
	if (hTransfer==NULL) {
		DEBUGMSG(DBG_BULK|DBG_WARN,(TEXT("IssueBulkTransfer(buffersize %lx,buffer %lx, phAddr %lx) Failed, TransferID %lx \n"),
			dwBufferSize,lpvBuffer,uBufferPhysicalAddress,
			uTransfer->GetTransferID()));
		Sleep(RETRY_SLEEP);
	};
	} while (hTransfer==NULL && (dwRetry++<ISSUE_RETRY));

	if (hTransfer==NULL) {
		errorCode=ERROR_USB_PIPE_TRANSFER;
		ASSERT(FALSE);
	}
	else {
		DEBUGMSG(DBG_BULK,(TEXT("Transfer IssueBulkTransfer, hTransfer=%u, TransferID %lx \n"),
				hTransfer, uTransfer->GetTransferID()));
		if ((dwFlags & USB_NO_WAIT)==0) {
			ActionCompleteNotify(uTransfer);
		}
	}
	return hTransfer;
}

USB_TRANSFER UsbPipeService::IssueInterruptTransfer(UsbTransfer * uTransfer, DWORD dwBufferSize, LPVOID lpvBuffer,
		ULONG uBufferPhysicalAddress)
{
	if(uTransfer == NULL || hPipe == NULL)
	    return NULL;
	USB_TRANSFER hTransfer;
	DWORD dwRetry=0;
	do {
	pipeMutex.Lock();
	DEBUGMSG(DBG_INTERRUPT,(TEXT("+IssueInterruptTransfer(buffersize %lx,buffer %lx, phAddr %lx) TransferID %lx \n"),
		dwBufferSize,lpvBuffer,uBufferPhysicalAddress,
		uTransfer->GetTransferID()));
	uTransfer->SetPipe(this);
	hTransfer=(*lpUsbFuncs->lpIssueInterruptTransfer)(hPipe,
				lpStartAddress,uTransfer,
				dwFlags,dwBufferSize,lpvBuffer,uBufferPhysicalAddress);
	DEBUGMSG(DBG_INTERRUPT,(TEXT("-IssueInterruptTransfer TransferID %lx \n"),
		uTransfer->GetTransferID()));
	uTransfer->SetTransferHandle(hTransfer);
	pipeMutex.Unlock();
	if (hTransfer==NULL) {
		DEBUGMSG(DBG_INTERRUPT|DBG_WARN,(TEXT("+IssueInterruptTransfer(buffersize %lx,buffer %lx, phAddr %lx) Failed, TransferID %lx \n"),
			dwBufferSize,lpvBuffer,uBufferPhysicalAddress,
			uTransfer->GetTransferID()));
		Sleep(RETRY_SLEEP);
	};
	} while (hTransfer==NULL && (dwRetry++<ISSUE_RETRY));

	if (hTransfer==NULL) {
		errorCode=ERROR_USB_PIPE_TRANSFER;
		ASSERT(FALSE);
	}
	else
	if ((dwFlags & USB_NO_WAIT)==0)
		ActionCompleteNotify(uTransfer);
	return hTransfer;

}
USB_TRANSFER UsbPipeService::IssueIsochTransfer(UsbTransfer * uTransfer, 
		DWORD dwStartingFrame,DWORD dwFrames,
		LPCDWORD lpdwLengths,LPVOID lpvBuffer,
		ULONG uBufferPhysicalAddress)
{
	if(uTransfer == NULL || hPipe == NULL)
	    return NULL;
	DWORD dwRetry=0;
	USB_TRANSFER hTransfer;
	do {
	pipeMutex.Lock();
	DEBUGMSG(DBG_ISOCH,(TEXT("+IssueIsochTransfer(buffer %lx, phAddr %lx) TransferID %lx \n"),
		lpvBuffer,uBufferPhysicalAddress,
		uTransfer->GetTransferID()));
	uTransfer->SetPipe(this);
	hTransfer=(*lpUsbFuncs->lpIssueIsochTransfer)(hPipe,
				lpStartAddress,uTransfer,
				dwFlags,dwStartingFrame,dwFrames,lpdwLengths,lpvBuffer,uBufferPhysicalAddress);
	DEBUGMSG(DBG_ISOCH,(TEXT("+IssueIsochTransfer startingframe=%d, dwFrames=%d \n"),
		dwStartingFrame,dwFrames));
	DEBUGMSG(DBG_ISOCH,(TEXT("-IssueIsochTransfer TransferID %lx \n"),
		uTransfer->GetTransferID()));
	uTransfer->SetTransferHandle(hTransfer);
	pipeMutex.Unlock();
	if (hTransfer==NULL) {
		DEBUGMSG(DBG_ISOCH|DBG_WARN,(TEXT("+IssueIsochTransfer(buffer %lx, phAddr %lx) TransferID %lx, failed, retrying \n"),
			lpvBuffer,uBufferPhysicalAddress,
			uTransfer->GetTransferID()));
		Sleep(RETRY_SLEEP);
	};
	}while (hTransfer==NULL && (dwRetry++<ISSUE_RETRY));

	if (hTransfer==NULL) {
		errorCode=ERROR_USB_PIPE_TRANSFER;
		ASSERT(FALSE);
	}
	else
	if ((dwFlags & USB_NO_WAIT)==0)
		ActionCompleteNotify(uTransfer);
	return hTransfer;

};
BOOL UsbPipeService::isInPipe()
{
	return USB_ENDPOINT_DIRECTION_IN(DeviceDesc.bEndpointAddress);
}
BOOL UsbPipeService::isOutPipe()
{
	return USB_ENDPOINT_DIRECTION_OUT(DeviceDesc.bEndpointAddress);
}
DWORD WINAPI UsbPipeService::TransferNotify(LPVOID lpvNotifyParameter)
{
	UsbPipeService * pPipeService=
		(UsbPipeService *)(((UsbTransfer *)lpvNotifyParameter)->GetPipe());
	return pPipeService->ActionCompleteNotify((UsbTransfer *)lpvNotifyParameter);
}

DWORD UsbPipeService::ActionCompleteNotify(UsbTransfer * uTransfer)
{
	uTransfer->CompleteNotify();
	return TRUE;
}
BOOL UsbPipeService::FormatData(LPBYTE pDataBuffer, DWORD dwDataLen)
{
	DWORD dwPacketSize=GetMaxPacketSize();
	ASSERT(dwPacketSize);
	ASSERT(pDataBuffer);
	ASSERT(dwDataLen);
	WORD wSeqNumber=0;
	if (dwPacketSize>=4 && (dwPacketSize&3)==0) {
		while (dwDataLen>=dwPacketSize) {
			WORD *pwDataBuffer=(WORD *)pDataBuffer;
			*pwDataBuffer=wSeqNumber++;
			*(pwDataBuffer+1)=0;
			WORD wCheckSum=0;
			for (DWORD i=0;i<dwPacketSize/2;i++) 
				wCheckSum +=*(pwDataBuffer+i);
			*(pwDataBuffer+1)=~wCheckSum;
			// advance to next packet.
			dwDataLen-=dwPacketSize;
			pDataBuffer+=dwPacketSize;
		}
		return TRUE;
	}
	else
		return FALSE;
}
BOOL UsbPipeService::CheckData(LPBYTE pDataBuffer, DWORD dwDataLen, LPCHECK_RESULT pResult)
{
	DWORD dwPacketSize=GetMaxPacketSize();
	ASSERT(pResult);
	ASSERT(dwPacketSize);
	ASSERT(pDataBuffer);
	ASSERT(dwDataLen);
	
	WORD wSeqNumber=0;
	if (dwPacketSize>=4 && (dwPacketSize&3)==0) {
		DWORD dwSeqMax=dwDataLen/dwPacketSize;
		PBYTE pbFlag=(PBYTE)malloc(dwSeqMax);
		if (pbFlag==NULL) // no memory
			return FALSE;
		memset(pResult,0,sizeof(CHECK_RESULT));
		memset(pbFlag,0,dwSeqMax);
		while (dwDataLen>=dwPacketSize) {
			(pResult->dwTotal)++;
			WORD *pwDataBuffer=(WORD *)pDataBuffer;
			WORD wCheckSum=0;
			for (DWORD i=0;i<dwPacketSize/2;i++) 
				wCheckSum +=*(pwDataBuffer+i);
			if (wCheckSum!=(WORD)(-1))  // check sum fail.
				pResult->dwJunk++;
			else {
				if (wSeqNumber>*pwDataBuffer) {
					pResult->dwSeqError++;
					wSeqNumber=*(pwDataBuffer);
				}
				else {
					wSeqNumber= *pwDataBuffer;
					if (wSeqNumber<(WORD)dwSeqMax && pbFlag[wSeqNumber]==0) {
						pbFlag[wSeqNumber]=1;
						pResult->dwSuccess++;
						wSeqNumber++;
					}
					else 
						pResult->dwSeqError++;
				};
			};
			dwDataLen-=dwPacketSize;
			pDataBuffer+=dwPacketSize;
		}
		for (DWORD dwIndex=0;dwIndex<dwSeqMax;dwIndex++) {
			if (pbFlag[dwIndex]==0)  // un-touched.
				pResult->dwLost++;
		}
		free(pbFlag);
		return TRUE;
	}
	else
		return FALSE;
}

UsbClientDrv::UsbClientDrv(USB_HANDLE usbHandle,LPCUSB_FUNCS UsbFuncsPtr,
			LPCUSB_INTERFACE lpInterface, LPCWSTR szUniqueDriverId,
            LPCUSB_DRIVER_SETTINGS lpDriverSettings):
	UsbClass(usbHandle,UsbFuncsPtr,lpInterface,szUniqueDriverId,lpDriverSettings)
{
	DEBUGMSG(DBG_FUNC, (TEXT("UsbClientDrv")));
	if(lpInterface == NULL)
	    return;
	    
	dwNumEndPoints=lpInterface->Descriptor.bNumEndpoints;
	lpEndPoints=(LPUSB_ENDPOINT)malloc(sizeof(USB_ENDPOINT)*dwNumEndPoints);
	if(lpEndPoints == NULL)
	    return;
	memcpy((PVOID)lpEndPoints,(PVOID)lpInterface->lpEndpoints,sizeof(USB_ENDPOINT)*dwNumEndPoints);
	DEBUGMSG(DBG_FUNC,(TEXT("End of UsbClientDrv")));
	hDevice=NULL;
			
}
LPCUSB_ENDPOINT UsbClientDrv::GetDescriptorPoint(DWORD dwIndex)
{
	if (dwIndex<dwNumEndPoints)
		return (lpEndPoints+dwIndex);
	else
		return NULL;
}
UsbClientDrv::~UsbClientDrv()
{
	if (lpEndPoints)
		free((PVOID)lpEndPoints);
	if (hDevice)
		DeregisterDevice(hDevice);
};

