//******************************************************************************
//
//
// 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:  
    PipeTest.h

Abstract:  
    USB driver Pipe Test.
    
Notes: 

--*/

#ifndef __PIPETEST_H_
#define __PIPETEST_H_
#include "UsbServ.h" 
#include "Usbtest.h"
#include "syncobj.h"
#include "MThread.h"
#include <usbdi.h>


#define MAX_ISOCH_BUFFERS 0x400


class UsbClientDrv;
// define for IsochTransfer Sync delay.
#define TRANSFER_OUT_DELAY 500
#define TRANSFER_IN_DELAY 1000
typedef enum {
	ISSUECONTROLTRANSFER,
	ISSUEBULKTRANSFER,
	ISSUEINTERRUPTTRANSFER,
	ISSUEISOCHTRANSFER,
	ISSUETRANSFER
} PipeCommandType;
typedef struct _PipeCommandBlock {
	DWORD PipeCommandBlockLength;
	PipeCommandType ePipeCommandType;
	DWORD dwFlags;
	DWORD dwStartingFrame,dwFrames; // for Isochron
	LPCVOID lpvControlHeader;
	DWORD dwBufferSize;
	LPVOID lpvBuffer;
	ULONG uBufferPhysicalAddress;
} PipeCommandBlock,* PPipeCommandBlock, * LPPipeCommandBlock;


class CommandSyncPipe : public UsbPipeService {
public:
	CommandSyncPipe(UsbClientDrv * usbClientDriver,
		LPCUSB_ENDPOINT_DESCRIPTOR lpEndPointDescriptor) :
			pDriverPtr(usbClientDriver),
			UsbPipeService(usbClientDriver->lpUsbFuncs,usbClientDriver->GetUsbHandle(),lpEndPointDescriptor){
		errorCode=0;
	};
	BOOL SetEndpoint(BOOL bStalled);
	BOOL IssueCommand(PUSB_DEVICE_REQUEST pControlHead, LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr );
	virtual DWORD GetErrorCode() {return (errorCode!=0?errorCode:GetPipeLastError());};
private:
	DWORD errorCode;
	UsbClientDrv * pDriverPtr;
	int iDevID;
	TCHAR szCEDeviceName[10];
};


class SyncPipeThread : public MiniThread,public UsbPipeService {
public:
	SyncPipeThread(UsbClientDrv * usbClientDriver,
		LPCUSB_ENDPOINT_DESCRIPTOR lpEndPointDescriptor, int iID,
		LPVOID lpBuffer,DWORD dwBufferSize,DWORD dwBlockSize, 
		CSemaphore * pSync=NULL,
		DWORD dwPhysicalAddr=0) :
			MiniThread (0,THREAD_PRIORITY_NORMAL,TRUE),
			pDriverPtr(usbClientDriver),
			UsbPipeService(usbClientDriver->lpUsbFuncs,usbClientDriver->GetUsbHandle(),lpEndPointDescriptor),
			lpDataBuffer(lpBuffer),
			dwDataSize(dwBufferSize),
			m_dwBlockSize(dwBlockSize),
			m_pSync(pSync), 
                    iDevID(iID),
			m_dwPhysicalAddr(dwPhysicalAddr){
		DEBUGMSG(DBG_FUNC,(TEXT("SyncPipeThread addr@%lx length=%lx"),lpDataBuffer,dwBufferSize));
            UCHAR uAddr = (lpEndPointDescriptor!= NULL) ?lpEndPointDescriptor->bEndpointAddress:0xFF;
             wsprintf(szCEDeviceName, _T("Dev%2d (Pipe0x%x):"), iID, uAddr);

            m_bShortStress = FALSE;
		errorCode=0;
	};
	BOOL SetEndpoint(BOOL bStalled);
	virtual BOOL SubmitRequest(LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr );
	virtual BOOL SubmitIsochRequest(LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr, PDWORD pdwLengths);
	virtual DWORD GetErrorCode() {return (errorCode!=0?errorCode:GetPipeLastError());};
	VOID SetBlockSize(DWORD dwSize){m_dwBlockSize = dwSize;};
       VOID SetShortStress(BOOL bSS){m_bShortStress = bSS;};
	//only used for short transfer test
	VOID SetShortBlockSize(DWORD dwSize){m_dwBlockSize = dwSize; dwDataSize = dwSize * 15;};

private:
	virtual DWORD ThreadRun();
       VOID RunShortStress();
	const LPVOID lpDataBuffer;
	const DWORD m_dwPhysicalAddr;
	DWORD dwDataSize;
	DWORD m_dwBlockSize;
	DWORD m_dwCurrentSyncFrame;
	CSemaphore * m_pSync;
	DWORD errorCode;
	UsbClientDrv * pDriverPtr;
	int iDevID;
	TCHAR szCEDeviceName[20];
      BOOL m_bShortStress;
};
class AsyncTransFIFO;

#define MAX_FIFO_SIZE 0x40
class AsyncTransFIFO:public MiniThread {
public:
	AsyncTransFIFO(LPCUSB_FUNCS lpUsbFuncs,DWORD dwFifoSize=MAX_FIFO_SIZE);
	~AsyncTransFIFO();

	BOOL IsEmpty() { return(dwOutput==dwInput); };
	BOOL IsFull() { return (NextPosition(dwInput)==dwOutput); };
	BOOL IsSignalEmpty() { return (dwSignal==dwInput); };
	BOOL IsHeadSignaled() { return (dwOutput!=dwSignal); };
	DWORD CountTransferFromUnsignaled(UsbTransfer * pTransfer);
	void WaitAndLockPositionToken() { cSemaphore->Lock(); };
	void ReleasePositionToken() { cSemaphore->Unlock(); };
	BOOL AddTransferHandle(UsbTransfer * pTransfer);
	UsbTransfer *RemoveTransferHandle();
	BOOL SignalAndAdvance();
	UsbTransfer * FirstTransferHandle() {
		UsbTransfer *lpTransRet = NULL;
		cMutex.Lock(10000);

		if (!IsEmpty())
			lpTransRet = TransferArray[dwOutput]; 

		cMutex.Unlock();
		return lpTransRet;
	};
	DWORD GetErrorCode() { return errorCode; };
	UsbPipeService * SetPipe (UsbPipeService * pPipe) { return m_pPipe=pPipe; };
	UsbPipeService * GetPipe() { return m_pPipe; };
private:
	LPCUSB_FUNCS m_lpUsbFuncs;
	UsbPipeService * m_pPipe;
	CMutex cMutex;
	CSemaphore * cSemaphore;
	DWORD dwArraySize;
	UsbTransfer * TransferArray[MAX_FIFO_SIZE];
	DWORD dwOutput,dwInput,dwSignal;
	DWORD dwNumTrans;

	TCHAR szCEDeviceName[10];
	DWORD NextPosition(DWORD Position) {
		if (++Position>=dwArraySize)
			Position=0;
		return Position;
	};

	virtual DWORD ThreadRun();
	DWORD errorCode;
};

class AsyncUsbPipeService:public UsbPipeService {
public:
	AsyncUsbPipeService(UsbClientDrv * usbClientDrv,LPCUSB_ENDPOINT_DESCRIPTOR lpEndPointDescriptor, int iID):
		UsbPipeService(usbClientDrv->lpUsbFuncs,usbClientDrv->GetUsbHandle(),lpEndPointDescriptor,TRUE),
		pDriverPtr(usbClientDrv), 
		transStorage(usbClientDrv->lpUsbFuncs,0x40)
	{
		DEBUGMSG(DBG_FUNC,(TEXT("+ASyncPipeThread")));
             iDevID = iID;  
             UCHAR uAddr = (lpEndPointDescriptor!= NULL) ?lpEndPointDescriptor->bEndpointAddress:0xFF;
             wsprintf(szCEDeviceName, _T("Dev%2d (Pipe0x%x):"), iDevID, uAddr);
		pdwLengths = NULL;
		if( GetEndPointAttribute() == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
			pdwLengths = new DWORD[MAX_ISOCH_BUFFERS];
			if (pdwLengths == NULL) 
				DEBUGMSG(DBG_ERR,(TEXT("Fail at Isoch Transfer, new returned NULL for pdwLengths")));
		}
		transStorage.SetPipe(this);
		transStorage.ThreadStart();
	};
	~AsyncUsbPipeService()
	{
		if (!transStorage.ThreadTerminated(30000)) {
			transStorage.ForceTerminated();
		}
		if(pdwLengths != NULL)
			delete [] pdwLengths;
	};
	virtual DWORD ActionCompleteNotify(UsbTransfer * pTransfer);
	virtual BOOL SubmitRequest(LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr);
	virtual BOOL SubmitIsochRequest(LPVOID lpBuffer,DWORD dwSize,DWORD dwPhysicalAddr,PDWORD pdwLengths);
	BOOL WaitForComplete(DWORD dwTicks) { 
		DWORD dwStartTicks = GetTickCount();

		while (GetTickCount() - dwStartTicks <= dwTicks) {
			if (transStorage.IsEmpty())
				return TRUE;
			Sleep(100);
		}
		if (transStorage.IsEmpty())
			return TRUE;
		else {
			DEBUGMSG(DBG_DETAIL, (TEXT("WaitForComplete Time Out")));
			return FALSE;
		}
	}
	DWORD GetErrorCode() {return transStorage.GetErrorCode();}
       LPTSTR GetDeviceString(){ return szCEDeviceName;}
	DWORD m_dwCurrentSyncFrame;
private:
	UsbClientDrv * pDriverPtr;
	AsyncTransFIFO transStorage;
	int iDevID;
	TCHAR szCEDeviceName[20];
protected:
	PDWORD pdwLengths;
};


class AsyncPipeThread : public MiniThread,public AsyncUsbPipeService {
public:
	AsyncPipeThread(UsbClientDrv * usbClientDrv,
		LPCUSB_ENDPOINT_DESCRIPTOR lpEndPointDescriptor, int iID,
		LPVOID lpBuffer,DWORD dwBufferSize,DWORD dwBlockSize,
		CSemaphore * pSync=NULL,
		DWORD dwPhysicalAddr=0) 
		:       MiniThread (0,THREAD_PRIORITY_NORMAL,TRUE),
			AsyncUsbPipeService(usbClientDrv,lpEndPointDescriptor, iID),
			pDriverPtr(usbClientDrv),
			lpDataBuffer(lpBuffer),
			dwDataSize(dwBufferSize),m_dwBlockSize(dwBlockSize),
			m_pSync(pSync),
			m_dwPhysicalAddr(dwPhysicalAddr), iDevID(iID)
	{
		DEBUGMSG(DBG_FUNC,(TEXT("AsyncPipeThread addr@%lx length=%lx"),lpBuffer,dwBufferSize));
        	szCEDeviceName = GetDeviceString();
		errorCode=0;
	};
	virtual DWORD GetErrorCode() 
	{       if (errorCode) 
			return errorCode;
		else 
			return AsyncUsbPipeService::GetErrorCode();
	};
	BOOL SetEndpoint(BOOL bStalled);
	virtual DWORD ActionCompleteNotify(UsbTransfer *pTransfer);
	DWORD CallThreadRun() { return AsyncPipeThread::ThreadRun(); };
	VOID SetBlockSize(DWORD dwSize){m_dwBlockSize = dwSize;};
	//only used for short transfer test
	VOID SetShortBlockSize(DWORD dwSize){m_dwBlockSize = dwSize; dwDataSize = dwSize * 15;};
private:
	virtual DWORD ThreadRun();
	const LPVOID lpDataBuffer;
	DWORD dwDataSize;
	const DWORD m_dwPhysicalAddr;
	DWORD m_dwBlockSize;
	CSemaphore * m_pSync;
	DWORD errorCode;
	UsbClientDrv * pDriverPtr;
	int	iDevID;
	LPTSTR szCEDeviceName;
};

BOOL SinglePairLoopback(UsbClientDrv *pDriverPtr, DWORD dwTransType, UCHAR uEPType, BOOL fAsync, PPhysMemNode pMem, int iID);
BOOL AllPairLoopback(UsbClientDrv *pDriverPtr, DWORD dwTransType, BOOL fAsync, PPhysMemNode pMem, int iID);
BOOL  AllTypeLoopback(UsbClientDrv *pDriverPtr, DWORD dwTransType, BOOL fAsync, PPhysMemNode pMemNode, int iID);

extern DWORD PipeNormalForCommand(UsbClientDrv *pDriverPtr, LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, BOOL fAsync, int iID);
extern BOOL PipeNormalPhyMemory(UsbClientDrv *pDriverPtr, PPhysMemNode PMemN,  LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, BOOL fAsync, int iID);
extern BOOL PipeAlignPhyMemory(UsbClientDrv *pDriverPtr, PPhysMemNode PMemN,  LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, BOOL fAsync, int iID);
extern DWORD PipeNormalShortTransfer(UsbClientDrv *pDriverPtr, LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, BOOL fAsync, int iID);
extern DWORD PipeNormalShortStress(UsbClientDrv *pDriverPtr, LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, int iID);

extern DWORD SinglePipeNormalForCommand(UsbClientDrv *pDriverPtr, LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, BOOL fAsync, int iID);
extern DWORD SinglePipeNormalShortTransfer(UsbClientDrv *pDriverPtr, LPCUSB_ENDPOINT pOutEP, LPCUSB_ENDPOINT pInEP, BOOL fAsync, int iID);

#endif
