#include "USB_control_transfer.h"
#include "common.h"
#include "usb20.h"
#include "isp1582.h"
#include "fa_usb.h"

#pragma DATA_ALIGN(USBControlTransferInfo, 4)


#define USBFSM4DCP_IDLE							0x00
#define USBFSM4DCP_SETUPPROC					0x01
#define USBFSM4DCP_REQUESTPROC					0x02
#define USBFSM4DCP_DATAIN						0x03
#define USBFSM4DCP_DATAOUT						0x04
#define USBFSM4DCP_CONTROLREADHANDSHAKE			0x05
#define USBFSM4DCP_CONTROLWRITEHANDSHAKE		0x06
#define USBFSM4DCP_STALL						0x07

TUSBControlTransferInfo USBControlTransferInfo;
void ControlTransfer_Init(void);
void SetupToken_Handler(void);
int DeviceRequest_Handler(unsigned char **responseDataAddr);
unsigned short ReadControlTransferSetupPacket(unsigned short *buf);
unsigned short ControlTransfer_ReadControlEndpoint(unsigned char *buf, unsigned short len);
void ControlTransfer_WriteControlEndpoint(unsigned char *buf, unsigned short len);

void ControlTransfer_BurstTransmitEP0(PUCHAR pData, USHORT len);
void ControlTransfer_Stall(void);
//----------------------------------------------------------
void ControlTransfer_Init(void)
{	
	USBControlTransferInfo.TXAddr = NULL;
	USBControlTransferInfo.DCP_state = USBFSM4DCP_IDLE;
	USBControlTransferInfo.SetupPacketDone = FALSE;
	USBControlTransferInfo.RxPacketDone = FALSE;
	USBControlTransferInfo.TxPacketDone = FALSE;

	USBControlTransferInfo.RxDataExists = FALSE;
	USBControlTransferInfo.TxIdle = TRUE;

	USBControlTransferInfo.USBRxIORequestState = USB_IO_REQUEST_STATE_IDLE;
	USBControlTransferInfo.USBTxIORequestState = USB_IO_REQUEST_STATE_IDLE;
	USBControlTransferInfo.USBSetupIORequestState = USB_IO_REQUEST_STATE_IDLE;
}
//----------------------------------------------------------
void SetupToken_Handler(void)	// be called from isr
{
	register unsigned char dir;

	USBControlTransferInfo.RequestDataLength = 0;
	USBControlTransferInfo.TXLength = 0;
	USBControlTransferInfo.TXCount = 0;
	USBControlTransferInfo.RXLength = 0;
	USBControlTransferInfo.RXCount = 0;

	dir = BIT8_GET(USBControlTransferInfo.DeviceRequest.bmRequestType, DEVICE_REQUEST_TYPE_DIR);

	if (dir == DEVICE_REQUEST_TYPE_DIR_DEVICE_TO_HOST)	// device to host (IN dir)
	{
		USBControlTransferInfo.RequestDataLength = USBControlTransferInfo.DeviceRequest.wLength;		// DeviceRequest can be changed while processing Control Data
		// enable IN data stage
		USB_IO_Request_SetEndpointStatus(INDEX_ENDPOINT0_IN, EPCTLFC_DATASTAGE);

		if((USBControlTransferInfo.DeviceRequest.bRequest == 0) && (USBControlTransferInfo.DeviceRequest.bmRequestType == 0xc0))
		{
			USBControlTransferInfo.DCP_state = USBFSM4DCP_CONTROLREADHANDSHAKE; // WORKAROUND FIX CONTROL END WITH OUT 1(NO DATA).
		}
		else
		{
			USBControlTransferInfo.DCP_state = USBFSM4DCP_REQUESTPROC;   // need another transaction
		}
	}
	else
	{
		USBControlTransferInfo.RXLength = USBControlTransferInfo.DeviceRequest.wLength;
		// set command 

		// enable OUT data stage
		USB_IO_Request_SetEndpointStatus(INDEX_ENDPOINT0_OUT, EPCTLFC_DATASTAGE);

		if (USBControlTransferInfo.RXLength == 0)
		{
			// Set command  without Data stage
			USBControlTransferInfo.DCP_state = USBFSM4DCP_REQUESTPROC; // no such command now.
		}
		else
		{
			//
			// Set command  with Data stage
			// get Data Buffer
			//
			if(USBControlTransferInfo.RXLength <= MAX_CONTROLDATA_SIZE)
			{
				// set command with OUT token 
				USBControlTransferInfo.DCP_state = USBFSM4DCP_DATAOUT;
			}
			else
			{
				ControlTransfer_Stall();
			}
		}
	}
}

//----------------------------------------------------------
void ControlTransfer_Process(void)
{
	//unsigned char readCount;
	short remainReadLength, remainWriteLength;
	int responseDataCount;
	unsigned char *responseDataAddr;

	unsigned int ie;
	ie = IRQ_disable(IRQ_EVT_USB);


	if (USBControlTransferInfo.TxPacketDone)
	{
		switch (USBControlTransferInfo.DCP_state)
		{
			case USBFSM4DCP_CONTROLREADHANDSHAKE:
				USB_IO_Request_Read_Handshake();
				USBControlTransferInfo.DCP_state = USBFSM4DCP_CONTROLREADHANDSHAKE;
				break;
			case USBFSM4DCP_CONTROLWRITEHANDSHAKE:
				USBControlTransferInfo.DCP_state = USBFSM4DCP_IDLE;
				break;
			case USBFSM4DCP_DATAIN:
				USBControlTransferInfo.TxIdle = TRUE;
				break;			
			case USBFSM4DCP_STALL:
				USBControlTransferInfo.DCP_state = USBFSM4DCP_IDLE;
				break;

			case USBFSM4DCP_REQUESTPROC:
			case USBFSM4DCP_SETUPPROC:
			case USBFSM4DCP_DATAOUT:	
			case USBFSM4DCP_IDLE:
			default:
				// do nothing, maybe just 1st nak for control in.
				ControlTransfer_Stall();
				break;
		}
		USBControlTransferInfo.TxPacketDone = FALSE;
	}
	else if (USBControlTransferInfo.RxPacketDone)
	{
		switch (USBControlTransferInfo.DCP_state)
		{
			case USBFSM4DCP_DATAOUT:
				// Normal Path
				// Data out Stage of In Control Write Transfer.
				USBControlTransferInfo.RxDataExists = TRUE;
				break;
			case USBFSM4DCP_CONTROLREADHANDSHAKE:
			case USBFSM4DCP_IDLE:
				USBControlTransferInfo.DCP_state = USBFSM4DCP_IDLE;
				break;		
			case USBFSM4DCP_STALL:
				// After STALL,Except SETUP Token, No other token is allowed
				break;
			case USBFSM4DCP_SETUPPROC:
				//In SETUPPROC, no Data-out phase shall occur.
			case USBFSM4DCP_REQUESTPROC:
			case USBFSM4DCP_DATAIN:
			default:
				// Anyway Stall it
				ControlTransfer_Stall();
				break;
		}
		USBControlTransferInfo.RxPacketDone = FALSE;
	}
	else if (USBControlTransferInfo.SetupPacketDone)
	{
		// Getting Setup Packet
		switch(USBControlTransferInfo.DCP_state)
		{
			case USBFSM4DCP_SETUPPROC:
			case USBFSM4DCP_DATAOUT:
			case USBFSM4DCP_DATAIN:
			case USBFSM4DCP_REQUESTPROC:
			case USBFSM4DCP_CONTROLREADHANDSHAKE:
			case USBFSM4DCP_CONTROLWRITEHANDSHAKE:
			case USBFSM4DCP_STALL:
			case USBFSM4DCP_IDLE :
			default:	// unknown state
				USBControlTransferInfo.DCP_state = USBFSM4DCP_SETUPPROC;
				break;
		} /* not a setup packet, just a Data Out Packet */
		USBControlTransferInfo.SetupPacketDone = FALSE;
	}


	if (USBControlTransferInfo.DCP_state != USBFSM4DCP_IDLE)
	{
		if (USBControlTransferInfo.DCP_state == USBFSM4DCP_SETUPPROC)
		{
			if (USBControlTransferInfo.USBSetupIORequestState == USB_IO_REQUEST_STATE_IDLE)
			{
				USB_IO_Request_ReadControlBuffer(INDEX_ENDPOINT0_SETUP, &(USBControlTransferInfo.DeviceRequest),
					8, &(USBControlTransferInfo.SetupIORequestControlData));
				USBControlTransferInfo.USBSetupIORequestState = USB_IO_REQUEST_STATE_PENDING;
			}
			else if (USBControlTransferInfo.USBSetupIORequestState == USB_IO_REQUEST_STATE_PENDING)
			{
				if (USBControlTransferInfo.SetupIORequestControlData.IOState == USB_IO_STATE_COMPLETE)
				{
					if (USBControlTransferInfo.SetupIORequestControlData.ReadCount == sizeof(TDeviceRequestData))
					{
						SetupToken_Handler();		// call handler (process setup token)
					}
					else
					{
						ControlTransfer_Stall();
					}
					USBControlTransferInfo.USBSetupIORequestState = USB_IO_REQUEST_STATE_IDLE;
				}
				else if (USBControlTransferInfo.SetupIORequestControlData.IOState == USB_IO_STATE_ERROR)
				{
					ControlTransfer_Stall();
					USBControlTransferInfo.USBSetupIORequestState = USB_IO_REQUEST_STATE_IDLE;
				}
			}
		}
		else if (USBControlTransferInfo.DCP_state == USBFSM4DCP_DATAOUT)
		{
			if (USBControlTransferInfo.USBRxIORequestState == USB_IO_REQUEST_STATE_IDLE)
			{
				if (USBControlTransferInfo.RxDataExists)
				{
					remainReadLength = USBControlTransferInfo.RXLength - USBControlTransferInfo.RXCount;
					if (remainReadLength > MAX_CONTROLDATA_SIZE - USBControlTransferInfo.RXCount)
					{
						remainReadLength = MAX_CONTROLDATA_SIZE - USBControlTransferInfo.RXCount;
					}
					if (remainReadLength <= 0)
					{
						USB_IO_Request_ClearBuffer(INDEX_ENDPOINT0_OUT);
						USBControlTransferInfo.DCP_state = USBFSM4DCP_REQUESTPROC;
					}
					else
					{
						USB_IO_Request_ReadControlBuffer(INDEX_ENDPOINT0_OUT, USBControlTransferInfo.DataStageBuffer + USBControlTransferInfo.RXCount,
							remainReadLength, &(USBControlTransferInfo.RXIORequestControlData));
						USBControlTransferInfo.USBRxIORequestState = USB_IO_REQUEST_STATE_PENDING;
					}
				}
				USBControlTransferInfo.RxDataExists = FALSE;
			}
			else if (USBControlTransferInfo.USBRxIORequestState == USB_IO_REQUEST_STATE_PENDING)
			{
				if (USBControlTransferInfo.RXIORequestControlData.IOState == USB_IO_STATE_COMPLETE)
				{
					USBControlTransferInfo.RXCount += USBControlTransferInfo.RXIORequestControlData.ReadCount;
					if (USBControlTransferInfo.RXCount >= USBControlTransferInfo.RXLength)
					{
						USBControlTransferInfo.DCP_state = USBFSM4DCP_REQUESTPROC;
					} // else  in state of USBFSM4DCP_DATAOUT 					
					USBControlTransferInfo.USBRxIORequestState = USB_IO_REQUEST_STATE_IDLE;
				}
				else if (USBControlTransferInfo.RXIORequestControlData.IOState == USB_IO_STATE_ERROR)
				{
					ControlTransfer_Stall();
					USBControlTransferInfo.USBRxIORequestState = USB_IO_REQUEST_STATE_IDLE;
				}				
			}
		}
		else if (USBControlTransferInfo.DCP_state == USBFSM4DCP_DATAIN)
		{
			if (USBControlTransferInfo.USBTxIORequestState == USB_IO_REQUEST_STATE_IDLE)
			{
				if (USBControlTransferInfo.TxIdle)
				{
					remainWriteLength = USBControlTransferInfo.TXLength - USBControlTransferInfo.TXCount;
					if (remainWriteLength >= EP0_PACKET_SIZE)
					{
						USB_IO_Request_WriteControlBuffer(INDEX_ENDPOINT0_IN, (unsigned char *)USBControlTransferInfo.TXAddr + USBControlTransferInfo.TXCount,
							EP0_PACKET_SIZE, &(USBControlTransferInfo.TXIORequestControlData));
						USBControlTransferInfo.TXCount += EP0_PACKET_SIZE;
						//USBControlTransferInfo.DCP_state = USBFSM4DCP_DATAIN;
						// State remains at USBFSM4DCP_DATAIN
						USBControlTransferInfo.USBTxIORequestState = USB_IO_REQUEST_STATE_PENDING;
					}
					else if (remainWriteLength > 0)
					{
						USB_IO_Request_WriteControlBuffer(INDEX_ENDPOINT0_IN, (unsigned char *)USBControlTransferInfo.TXAddr + USBControlTransferInfo.TXCount,
							remainWriteLength, &(USBControlTransferInfo.TXIORequestControlData));
						USBControlTransferInfo.TXCount += remainWriteLength;
						// Go to Handshake state, let HOST send 0-length Pkt.
						USBControlTransferInfo.DCP_state = USBFSM4DCP_CONTROLREADHANDSHAKE;
					}
					else	// zero-length packet
					{	
						USB_IO_Request_WriteControlBuffer(INDEX_ENDPOINT0_IN, NULL, 
							0,&(USBControlTransferInfo.TXIORequestControlData));
						USBControlTransferInfo.DCP_state = USBFSM4DCP_CONTROLREADHANDSHAKE;
					}
					USBControlTransferInfo.TxIdle = FALSE;				
				}
			}
			else if (USBControlTransferInfo.USBTxIORequestState == USB_IO_REQUEST_STATE_PENDING)
			{
				if (USBControlTransferInfo.TXIORequestControlData.IOState == USB_IO_STATE_COMPLETE)
				{
					USBControlTransferInfo.USBTxIORequestState = USB_IO_REQUEST_STATE_IDLE;
				}
				else if (USBControlTransferInfo.RXIORequestControlData.IOState == USB_IO_STATE_ERROR)
				{
					ControlTransfer_Stall();
					USBControlTransferInfo.USBTxIORequestState = USB_IO_REQUEST_STATE_IDLE;
				}				
			}
		}
		else if (USBControlTransferInfo.DCP_state == USBFSM4DCP_REQUESTPROC)
		{		
			responseDataCount = DeviceRequest_Handler(&responseDataAddr);
			if (responseDataCount == 0)
			{
				USB_IO_Request_Write_Handshake();
				USBControlTransferInfo.DCP_state = USBFSM4DCP_CONTROLWRITEHANDSHAKE;
			}
			else if (responseDataCount > 0)
			{
				ControlTransfer_BurstTransmitEP0(responseDataAddr, responseDataCount);
				USBControlTransferInfo.TxIdle = TRUE;
				USBControlTransferInfo.DCP_state = USBFSM4DCP_DATAIN;
			}
			else //if (responseDataCount < 0)
			{
				ControlTransfer_Stall();
			}
		}
	}
	IRQ_restore(IRQ_EVT_USB, ie);
}
//----------------------------------------------------------
int DeviceRequest_Handler(unsigned char **responseDataAddr)
{
	UCHAR type, req;
	unsigned char ret;
	unsigned char *txBuffer;
	int txCount;
	int responseDataCount = -1;
	
    req = USBControlTransferInfo.DeviceRequest.bRequest;
    type = BIT8_GET(USBControlTransferInfo.DeviceRequest.bmRequestType, DEVICE_REQUEST_TYPE_TYPE);

	if ((type == DEVICE_REQUEST_TYPE_TYPE_STANDARD) && (req < MAX_STANDARD_REQUEST))
	{
		txCount = (*StandardDeviceRequest[req])(&(USBControlTransferInfo.DeviceRequest), &txBuffer);
		if (txCount >= 0)
		{
			*responseDataAddr = txBuffer;
			responseDataCount = txCount;
		}
	}
	else if ((type == DEVICE_REQUEST_TYPE_TYPE_VENDOR) && (req 	< MAX_VENDOR_REQUEST))
	{
		ret = (*VendorDeviceRequest[req])(USBControlTransferInfo.DataStageBuffer);
		if (ret == TRUE)
		{
			responseDataCount = 0;
		}
	}

	return responseDataCount;
}    
//----------------------------------------------------------
void ControlTransfer_BurstTransmitEP0(PUCHAR pData, USHORT len)
{
	if (len < USBControlTransferInfo.RequestDataLength)
	{
		USBControlTransferInfo.TXLength = len;
	}
	else
	{
		USBControlTransferInfo.TXLength = USBControlTransferInfo.RequestDataLength;
	}

	USBControlTransferInfo.TXAddr = pData;
	USBControlTransferInfo.TXCount = 0;
}
//----------------------------------------------------------
void ControlTransfer_Stall(void)
{
	USB_IO_Request_SetEndpointStatus(INDEX_ENDPOINT0_IN, EPCTLFC_STALL);
	USB_IO_Request_SetEndpointStatus(INDEX_ENDPOINT0_OUT, EPCTLFC_STALL);

	USBControlTransferInfo.DCP_state = USBFSM4DCP_STALL;
	// clear control transfer tx and rx
	USBControlTransferInfo.TxPacketDone = FALSE;
	USBControlTransferInfo.TXLength = 0;
	USBControlTransferInfo.TXCount = 0;
	USBControlTransferInfo.RxPacketDone = FALSE;
	USBControlTransferInfo.RXLength = 0;	
	USBControlTransferInfo.RXCount = 0;
	USBControlTransferInfo.RxDataExists = FALSE;
	USBControlTransferInfo.TxIdle = TRUE;

	USBControlTransferInfo.USBRxIORequestState = USB_IO_REQUEST_STATE_IDLE;
	USBControlTransferInfo.USBTxIORequestState = USB_IO_REQUEST_STATE_IDLE;
	USBControlTransferInfo.USBSetupIORequestState = USB_IO_REQUEST_STATE_IDLE;
}
//----------------------------------------------------------
