/*
*******************************************************************************
*                     ReadDataStatusComplete
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static void ReadDataStatusComplete(__hdle hUSB, u32 ep_type, u32 complete)
{
	USBC_Dev_ReadDataStatus(hUSB, ep_type, complete);

    udelay(2);

    if(ep_type == USBC_EP_TYPE_EP0){
        /* clear data end */
        if(complete){
            USBC_Dev_Ctrl_ClearSetupEnd(hUSB);
        }

        /* clear irq */
        USBC_INT_ClearEpPending(hUSB, USBC_EP_TYPE_TX, 0);
    }

	return;
}
/*
*******************************************************************************
*                     __usb_readcomplete
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static void __usb_readcomplete(__hdle hUSB, u32 ep_type, u32 complete)
{
	USBC_Dev_ReadDataStatus(hUSB, ep_type, complete);

    if(ep_type == USBC_EP_TYPE_EP0)
    {
        /* clear data end */
        if(complete)
        {
            USBC_Dev_Ctrl_ClearSetupEnd(hUSB);
        }

        /* clear irq */
        USBC_INT_ClearEpPending(hUSB, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX);
    }

	return;
}
/*
*******************************************************************************
*                     WriteDataStatusComplete
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static void WriteDataStatusComplete(__hdle hUSB, u32 ep_type, u32 complete)
{
	USBC_Dev_WriteDataStatus(hUSB, ep_type, complete);

    /* wait for tx packet sent out */
	while(USBC_Dev_IsWriteDataReady(hUSB, ep_type)){
        udelay(1);
    }

    if(ep_type == USBC_EP_TYPE_EP0){
        /* clear data end */
        if(complete){
            USBC_Dev_Ctrl_ClearSetupEnd(hUSB);
        }

        /* clear irq */
        USBC_INT_ClearEpPending(hUSB, USBC_EP_TYPE_TX, 0);
    }

	return;
}
/*
*******************************************************************************
*                     __usb_writecomplete
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static void __usb_writecomplete(__hdle hUSB, u32 ep_type, u32 complete)
{
	USBC_Dev_WriteDataStatus(hUSB, ep_type, complete);

	/* wait for tx packet sent out */
	while(USBC_Dev_IsWriteDataReady(hUSB, ep_type));

	if(ep_type == USBC_EP_TYPE_EP0)
    {
        /* clear data end */
        if(complete)
        {
            USBC_Dev_Ctrl_ClearSetupEnd(hUSB);
        }

        /* clear irq */
        USBC_INT_ClearEpPending(hUSB, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX);
    }

	return;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
static int ep0_recv_op(void)
{
   	u32 old_ep_index  = 0;
   	int ret = 0;
   	static uint ep0_stage = 0;

	if(!ep0_stage)
	{
		memset(&sunxi_udc_source.standard_reg, 0, sizeof(struct usb_device_request));
	}

	old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_CTRL_EP_INDEX);
    //clear stall status
    if(USBC_Dev_IsEpStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0))
    {
       	printf("ERR: handle_ep0: ep0 stall\n");

		USBC_Dev_EpClearStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0);
        ret = -1;

        goto __ep0_recv_op_err;
    }

    //clear setup end
	if (USBC_Dev_Ctrl_IsSetupEnd(sunxi_udc_source.usbc_hd))
	{
   	    USBC_Dev_Ctrl_ClearSetupEnd(sunxi_udc_source.usbc_hd);
	}
	//检查读ep0数据是否完成
	if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0))
	{
		uint status;

		if(!ep0_stage)
		{
			status = __usb_read_ep0_data(&sunxi_udc_source.standard_reg, ep0_stage);
		}
		else
		{
			status = __usb_read_ep0_data(sunxi_usb_ep0_buffer, ep0_stage);
		}
		if(status!= 0)
		{
			printf("sunxi usb err: read_request failed\n");
			ret = -1;

			goto __ep0_recv_op_err;
		}
	}
	else		//此情况通常由于ep0发送空包引起,可以不处理
	{
		sunxi_usb_dbg("sunxi usb msg: ep0 rx data is not ready\n");

		goto __ep0_recv_op_err;
	}
	/* Check data */
	if(USB_REQ_TYPE_STANDARD == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_TYPE_MASK))
	{
		ret = SUNXI_USB_REQ_UNMATCHED_COMMAND;

		/* standard */
		switch(sunxi_udc_source.standard_reg.bRequest)
		{
			case USB_REQ_GET_STATUS:		//   0x00
			{
				/* device-to-host */
				if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_STATUS, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer);
				}

				break;
			}
			case USB_REQ_CLEAR_FEATURE:		//   0x01
			{
				/* host-to-device */
				if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					ret = sunxi_udev_active->standard_req_op(USB_REQ_CLEAR_FEATURE, &sunxi_udc_source.standard_reg, NULL);
				}

				break;
			}
			case USB_REQ_SET_FEATURE:		//   0x03
			{
				/* host-to-device */
				if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_FEATURE, &sunxi_udc_source.standard_reg, NULL);
				}

				break;
			}
			case USB_REQ_SET_ADDRESS:		//   0x05
			{
				/* host-to-device */
				if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						/* receiver is device */
						ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_ADDRESS, &sunxi_udc_source.standard_reg, NULL);
					}
				}

				break;
			}
			case USB_REQ_GET_DESCRIPTOR:		//   0x06
			{
				/* device-to-host */
				if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_DESCRIPTOR, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer);
					}
				}

				break;
			}
			case USB_REQ_SET_DESCRIPTOR:		//   0x07
			{
				/* host-to-device */
				if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						//there is some problem
						ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_DESCRIPTOR, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer);
					}
				}

				break;
			}
			case USB_REQ_GET_CONFIGURATION:		//   0x08
			{
				/* device-to-host */
				if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_CONFIGURATION, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer);
					}
				}

				break;
			}
			case USB_REQ_SET_CONFIGURATION:		//   0x09
			{
				/* host-to-device */
				if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_CONFIGURATION, &sunxi_udc_source.standard_reg, NULL);
					}
				}

				break;
			}
			case USB_REQ_GET_INTERFACE:		//   0x0a
			{
				/* device-to-host */
				if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_INTERFACE, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer);
					}
				}

				break;
			}
			case USB_REQ_SET_INTERFACE:		//   0x0b
			{
				/* host-to-device */
				if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_INTERFACE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_INTERFACE, &sunxi_udc_source.standard_reg, NULL);
					}
				}

				break;
			}
			case USB_REQ_SYNCH_FRAME:		//   0x0b
			{
				/* device-to-host */
				if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK))
				{
					if(USB_RECIP_INTERFACE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK))
					{
						//there is some problem
						if(!ep0_stage)
						{
							ep0_stage = 1;
						}
						else
						{
							ret = sunxi_udev_active->standard_req_op(USB_REQ_SYNCH_FRAME, &sunxi_udc_source.standard_reg, NULL);
							ep0_stage = 0;
						}
					}
				}

				break;
			}
			default:
			{
				printf("sunxi usb err: unknown usb out request to device\n");

				USBC_Dev_EpSendStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0);
				ret = SUNXI_USB_REQ_DEVICE_NOT_SUPPORTED;
				ep0_stage = 0;

				break;
			}
		}
	}
	else
	{
		/* Non-Standard Req */
		printf("non standard req\n");
		ret = sunxi_udev_active->nonstandard_req_op(USB_REQ_GET_STATUS, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer, ep0_stage);
		if(ret == SUNXI_USB_REQ_DATA_HUNGRY)
		{
			ep0_stage = 1;
		}
		else if(ret == SUNXI_USB_REQ_SUCCESSED)
		{
			ep0_stage = 0;
		}
		else if(ret < 0)
		{
			ep0_stage = 0;
			printf("err: unkown bmRequestType(%d)\n", sunxi_udc_source.standard_reg.bmRequestType);
			USBC_Dev_EpSendStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0);
		}
	}

__ep0_recv_op_err:
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index);

	return ret;
}