/*
 * (C) Copyright 2007-2013
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 * Jerry Wang <wangflord@allwinnertech.com>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include "usb_base.h"
#include <scsi.h>
#ifdef CONFIG_ARM
#include <asm/arch/gic.h>
#elif CONFIG_RISCV
#include <asm/arch/plic.h>
#endif
#include <asm/arch/timer.h>
//#include <sys_partition.h>
#include <sunxi_board.h>
#include <console.h>
#include "usb_dma_control.h"

#define  SUNXI_USB_EP0_BUFFER_SIZE   (512)

#define  HIGH_SPEED_EP_MAX_PACKET_SIZE   (512)
#define  FULL_SPEED_EP_MAX_PACKET_SIZE	 (64)

#define  BULK_FIFOSIZE                   (512)

#define  SUNXI_USB_CTRL_EP_INDEX		0
#define  SUNXI_USB_BULK_IN_EP_INDEX		1	/* tx */
#define  SUNXI_USB_BULK_OUT_EP_INDEX	2	/* rx */

enum usb_device_speed {
        USB_SPEED_UNKNOWN = 0,                  /* enumerating */
        USB_SPEED_LOW, USB_SPEED_FULL,          /* usb 1.1 */
        USB_SPEED_HIGH,                         /* usb 2.0 */
        USB_SPEED_WIRELESS,                     /* wireless (usb 2.5) */
        USB_SPEED_SUPER,                        /* usb 3.0 */
        USB_SPEED_RESERVED
};

static uchar sunxi_usb_ep0_buffer[SUNXI_USB_EP0_BUFFER_SIZE];

sunxi_udc_t  			  sunxi_udc_source;
static sunxi_ubuf_t	sunxi_ubuf __aligned(CONFIG_SYS_CACHELINE_SIZE) = { 0 };
sunxi_usb_setup_req_t     *sunxi_udev_active;

static uint usb_dma_trans_unaliged_bytes;
static uchar *usb_dma_trans_unaligned_buf;

static int  __usb_read_ep0_data(void *buffer, uint data_type);
//static int  __usb_read_fifo(void *buffer, unsigned int buffer_size);
static int  __usb_write_fifo(uchar *buffer, unsigned int buffer_size);
static void __usb_bulk_ep_reset (void);
static void __usb_clear_all_irq(void);

static void __usb_writecomplete(__hdle hUSB, u32 ep_type, u32 complete);
static void __usb_readcomplete(__hdle hUSB, u32 ep_type, u32 complete);

static void __usb_recv_by_dma_isr(void *p_arg);
static void __usb_send_by_dma_isr(void *p_arg);
static int __sunxi_udc_set_address(uchar address);


static int eptx_send_op(void);
static int eprx_recv_op(void);
static int ep0_recv_op(void);

extern int fastboot_data_flag;
extern volatile int sunxi_usb_burn_from_boot_init;
#ifdef CONFIG_SUNXI_USB_DETECT
extern volatile int sunxi_usb_detect_flag;
#endif

/* for fastboot */
__weak int get_fastboot_data_flag(void)
{
	return 0;
}

/* for usb burn */
__weak void set_usb_burn_boot_init_flag(int flag )
{

}

/* for usb config */
__weak void otg_phy_config(void)
{
	printf("weak:otg_phy_config\n");
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
void sunxi_usb_irq(void *data)
{
	u8 misc_irq = 0;
	u16 tx_irq = 0;
	u16 rx_irq = 0;
	u32 dma_irq = 0;
    u32 old_ep_idx  = 0;

    /* Save index */
	old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);

    /* Read status registers */
	misc_irq = USBC_INT_MiscPending(sunxi_udc_source.usbc_hd);
	tx_irq  = USBC_INT_EpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX);
	rx_irq  = USBC_INT_EpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
	dma_irq = usb_dma_int_query();
    /* RESET */
	if(misc_irq & USBC_INTUSB_RESET)
	{
	    sunxi_usb_dbg("IRQ: reset\n");

	    USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_RESET);
        __usb_clear_all_irq();

        USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, 0);
		USBC_Dev_SetAddress_default(sunxi_udc_source.usbc_hd);

		sunxi_udc_source.address = 0;					//default value
		sunxi_udc_source.speed = USB_SPEED_HIGH;		//default value

		usb_dma_stop(sunxi_udc_source.dma_recv_channal);
		usb_dma_stop(sunxi_udc_source.dma_send_channal);

		usb_dma_set_pktlen(sunxi_udc_source.dma_send_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE);
		usb_dma_set_pktlen(sunxi_udc_source.dma_recv_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE);

		sunxi_ubuf.rx_ready_for_data = 0;
		sunxi_udev_active->state_reset();

		return ;
    }
	/* RESUME 暂时不处理，仅仅清理中断*/
	if (misc_irq & USBC_INTUSB_RESUME)
	{
		sunxi_usb_dbg("IRQ: resume\n");
		/* clear interrupt */
		USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_RESUME);
	}
	/* SUSPEND */
	if (misc_irq & USBC_INTUSB_SUSPEND)
	{
		sunxi_usb_dbg("IRQ: suspend\n");
		/* clear interrupt */
		USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_SUSPEND);
	}
    /* DISCONNECT */
    if(misc_irq & USBC_INTUSB_DISCONNECT)
    {
        sunxi_usb_dbg("IRQ: disconnect\n");

		USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_DISCONNECT);

        return ;
	}
#if 1
	/* SOF */
	if(misc_irq & USBC_INTUSB_SOF)
	{
	    sunxi_usb_dbg("IRQ: SOF\n");

#ifdef CONFIG_SUNXI_USB_DETECT
		sunxi_usb_detect_flag = 1;
#endif
		set_usb_burn_boot_init_flag(1);
		USBC_INT_DisableUsbMiscUint(sunxi_udc_source.usbc_hd, USBC_INTUSB_SOF);

		USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_SOF);
	}
#endif
	/* ep0 */
	if (tx_irq & (1 << SUNXI_USB_CTRL_EP_INDEX) )
	{
		sunxi_usb_dbg("IRQ: EP0\n");

		USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX);
		//中断内完成ep0处理
		ep0_recv_op();
    }
	/* tx endpoint data transfers */
	if (tx_irq & (1 << SUNXI_USB_BULK_IN_EP_INDEX))
	{
		sunxi_usb_dbg("tx irq occur\n");
		/* Clear the interrupt bit by setting it to 1 */
		USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_BULK_IN_EP_INDEX);
		eptx_send_op();
	}

	/* rx endpoint data transfers */
	if (rx_irq & (1 << SUNXI_USB_BULK_OUT_EP_INDEX))
	{
		sunxi_usb_dbg("rx irq occur\n");
		/* Clear the interrupt bit by setting it to 1 */
		USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, SUNXI_USB_BULK_OUT_EP_INDEX);
		eprx_recv_op();
	}

	if(dma_irq & (1 << SUNXI_USB_BULK_IN_EP_INDEX))
	{
		sunxi_usb_dbg("tx dma\n");
		__usb_send_by_dma_isr(NULL);
	}

	if(dma_irq & (1 << SUNXI_USB_BULK_OUT_EP_INDEX))
	{
		sunxi_usb_dbg("rx dma\n");
		__usb_recv_by_dma_isr(NULL);
	}
	usb_dma_int_clear();

    USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx);

    return ;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :   usb初始化动作，完成后，即可开启中断后，使用中断处理程序
*
*
************************************************************************************************************
*/
int sunxi_usb_init(int delaytime)
{
	uint reg_val = 0;

	if(sunxi_udev_active->state_init())
	{
		printf("sunxi usb err: fail to init usb device\n");

		return -1;
	}

	//预先关闭usb中断
	irq_disable(AW_IRQ_USB_OTG);
	//初始化 sunxi_udc用到的资源
	memset(&sunxi_udc_source, 0, sizeof(sunxi_udc_t));
	//获取控制器地址资源
	sunxi_udc_source.usbc_hd = USBC_open_otg(0);
	if(sunxi_udc_source.usbc_hd == 0)
	{
		printf("sunxi usb err : USBC_open_otg failed\n");

		return -1;
	}
	usb_dma_init(sunxi_udc_source.usbc_hd);
	//断开usb
	USBC_Dev_ConectSwitch(sunxi_udc_source.usbc_hd, USBC_DEVICE_SWITCH_OFF);
	//预先关闭usb时钟
	usb_close_clock();
	//延时 delaytime  ms
	printf("delay time %d\n", delaytime);
	__msdelay(delaytime);
	//申请DMA资源
	sunxi_udc_source.dma_send_channal = usb_dma_request();
	if(!sunxi_udc_source.dma_send_channal)
	{
		printf("sunxi usb err : unable to request dma for usb send data\n");

		goto __sunxi_usb_init_fail;
	}
	sunxi_usb_dbg("dma send ch %d\n", sunxi_udc_source.dma_send_channal);
	sunxi_udc_source.dma_recv_channal = usb_dma_request();
	if(!sunxi_udc_source.dma_recv_channal)
	{
		printf("sunxi usb err : unable to request dma for usb receive data\n");

		goto __sunxi_usb_init_fail;
	}
	sunxi_usb_dbg("dma recv ch %d\n", sunxi_udc_source.dma_recv_channal);

	sunxi_udc_source.address = 0;
	sunxi_udc_source.speed = USB_SPEED_HIGH;
	sunxi_udc_source.bulk_ep_max = HIGH_SPEED_EP_MAX_PACKET_SIZE;
	sunxi_udc_source.fifo_size    = BULK_FIFOSIZE;
	sunxi_udc_source.bulk_in_addr = 100;
	sunxi_udc_source.bulk_out_addr = sunxi_udc_source.bulk_in_addr + sunxi_udc_source.fifo_size * 2;
	//内存资源
	memset(&sunxi_ubuf, 0, sizeof(sunxi_ubuf_t));

	sunxi_ubuf.rx_base_buffer = (uchar *)memalign(CONFIG_SYS_CACHELINE_SIZE, 1024);
	if(!sunxi_ubuf.rx_base_buffer)
	{
		printf("%s:alloc memory  fail\n", __func__);

		goto __sunxi_usb_init_fail;
	}
	sunxi_ubuf.rx_req_buffer = sunxi_ubuf.rx_base_buffer;

	usb_open_clock();
	//设置为device模式
	USBC_ForceId(sunxi_udc_source.usbc_hd, USBC_ID_TYPE_DEVICE);
	//设置VBUS为高
	USBC_ForceVbusValid(sunxi_udc_source.usbc_hd, USBC_VBUS_TYPE_HIGH);

	USBC_Dev_ConectSwitch(sunxi_udc_source.usbc_hd, USBC_DEVICE_SWITCH_OFF);
	//soft connect
	USBC_EnableDpDmPullUp(sunxi_udc_source.usbc_hd);
	USBC_EnableIdPullUp(sunxi_udc_source.usbc_hd);
	//选择使用PIO模式搬移数据
	USBC_SelectBus(sunxi_udc_source.usbc_hd, USBC_IO_TYPE_PIO, 0, 0);
	//映射SRAM buffer
	USBC_ConfigFIFO_Base(sunxi_udc_source.usbc_hd, 0, 0);
	//
	USBC_EnhanceSignal(sunxi_udc_source.usbc_hd);
	//默认采用高速模式传输
#ifdef	CONFIG_USB_1_1_DEVICE
	USBC_Dev_ConfigTransferMode(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_TS_MODE_FS);
#else
	USBC_Dev_ConfigTransferMode(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_TS_MODE_HS);
#endif
	//配置发送端dma资源
	usb_dma_setting(sunxi_udc_source.dma_send_channal, USB_DMA_FROM_DRAM_TO_HOST, SUNXI_USB_BULK_IN_EP_INDEX);
	usb_dma_set_pktlen(sunxi_udc_source.dma_send_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE);
	//配置接收端dma资源
	usb_dma_setting(sunxi_udc_source.dma_recv_channal, USB_DMA_FROM_HOST_TO_DRAM, SUNXI_USB_BULK_OUT_EP_INDEX);
	usb_dma_set_pktlen(sunxi_udc_source.dma_recv_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE);
	/* disable all interrupt */
	USBC_INT_DisableUsbMiscAll(sunxi_udc_source.usbc_hd);
	USBC_INT_DisableEpAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
	USBC_INT_DisableEpAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX);

	/* 开启 reset、resume、suspend中断 */
	USBC_INT_EnableUsbMiscUint(sunxi_udc_source.usbc_hd, USBC_INTUSB_SUSPEND | USBC_INTUSB_RESUME	\
													   | USBC_INTUSB_RESET   | USBC_INTUSB_SOF);

	/* enbale ep0_tx_irq */
	USBC_INT_EnableEp(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX);

	__usb_bulk_ep_reset();

	USBC_Dev_ConectSwitch(sunxi_udc_source.usbc_hd, USBC_DEVICE_SWITCH_ON);

	irq_install_handler(AW_IRQ_USB_OTG, sunxi_usb_irq, NULL);
	irq_enable(AW_IRQ_USB_OTG);
	/* sun8iw10p1 spec default value is not correct, bit 1 should be  0 */
	reg_val = readl((const volatile void __iomem *)(SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL));
	reg_val &= ~(0x01<<1);
	writel(reg_val, (volatile void __iomem *)(SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL));

#if defined(CONFIG_SUNXI_NCAT) || defined(CONFIG_SUNXI_NCAT_V2)
	reg_val = readl((const volatile void __iomem *)(SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL));
	reg_val &= ~(0x01<<USBC_PHY_CTL_SIDDQ);
	reg_val |= 0x01<<USBC_PHY_CTL_VBUSVLDEXT;
	writel(reg_val, (volatile void __iomem *)(SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL));
#endif
	otg_phy_config();

	return 0;

__sunxi_usb_init_fail:
	if(sunxi_udc_source.dma_send_channal)
	{
		usb_dma_release(sunxi_udc_source.dma_send_channal);
	}
	if(sunxi_udc_source.dma_recv_channal)
	{
		usb_dma_release(sunxi_udc_source.dma_recv_channal);
	}
	if(sunxi_udc_source.usbc_hd)
	{
		USBC_close_otg(sunxi_udc_source.usbc_hd);
	}
	if(sunxi_ubuf.rx_base_buffer)
	{
		free(sunxi_ubuf.rx_base_buffer);
	}

	return -1;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_usb_exit(void)
{
	irq_disable(AW_IRQ_USB_OTG);
	irq_free_handler(AW_IRQ_USB_OTG);
	if(sunxi_udc_source.dma_send_channal)
	{
		usb_dma_stop(sunxi_udc_source.dma_send_channal);
		usb_dma_release(sunxi_udc_source.dma_send_channal);
	}
	if(sunxi_udc_source.dma_recv_channal)
	{
		usb_dma_stop(sunxi_udc_source.dma_recv_channal);
		usb_dma_release(sunxi_udc_source.dma_recv_channal);
	}
	if(sunxi_ubuf.rx_base_buffer)
	{
		free(sunxi_ubuf.rx_base_buffer);
	}
	USBC_close_otg(sunxi_udc_source.usbc_hd);
	usb_close_clock();
	sunxi_udev_active->state_exit();
	memset(&sunxi_ubuf, 0, sizeof(sunxi_ubuf_t));

	return 0;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
static void __usb_send_by_dma_isr(void *p_arg)
{
	sunxi_udev_active->dma_tx_isr(p_arg);
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
static void __usb_recv_by_dma_isr(void *p_arg)
{
	u32 old_ep_idx;

	old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);			//选择RXEP

	//选择使用IO方式搬运数据
	sunxi_usb_dbg("select io mode to transfer data\n");
	USBC_Dev_ClearEpDma(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);

	if(usb_dma_trans_unaliged_bytes)
	{
		uint fifo, this_len;

		this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
		fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);
		USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, usb_dma_trans_unaligned_buf);
		__usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1);		//返回状态
		usb_dma_trans_unaliged_bytes = 0;
	}
	//如果当前dma传输的不是完整包，则需要手动清除中断
	if(sunxi_ubuf.request_size % sunxi_udc_source.bulk_ep_max)
	{
		USBC_Dev_ReadDataStatus(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1);
		//printf("clear rx pending manually\n");
	}

	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx);

	sunxi_udev_active->dma_rx_isr(p_arg);
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
static void __usb_clear_all_irq(void)
{
	USBC_INT_ClearEpPendingAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX);
    USBC_INT_ClearEpPendingAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
    USBC_INT_ClearMiscPendingAll(sunxi_udc_source.usbc_hd);
}
/*
*******************************************************************************
*                     __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;
}
/*
*******************************************************************************
*                     __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;
}
/*
*******************************************************************************
*                     __usb_bulk_ep_reset
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static void __usb_bulk_ep_reset (void)
{
	u8 old_ep_index = 0;

	old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	/* tx */
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_IN_EP_INDEX);
	USBC_Dev_ConfigEp(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_EP_TYPE_TX, 1, sunxi_udc_source.bulk_ep_max & 0x7ff);
	USBC_ConfigFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, 1, sunxi_udc_source.fifo_size, (uint)sunxi_udc_source.bulk_out_addr);
	USBC_INT_EnableEp(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_BULK_IN_EP_INDEX);
	/* rx */
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);
    USBC_Dev_ConfigEp(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_EP_TYPE_RX, 1, sunxi_udc_source.bulk_ep_max & 0x7ff);
	USBC_ConfigFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1, sunxi_udc_source.fifo_size, (uint)sunxi_udc_source.bulk_in_addr);
	USBC_INT_EnableEp(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, SUNXI_USB_BULK_OUT_EP_INDEX);

	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index);

	return;
}
/*
*******************************************************************************
*                     __usb_read_ep0_data
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static int __usb_read_ep0_data(void *buffer, uint data_type)
{
	u32 fifo_count  = 0;
	u32 fifo        = 0;
	int ret = 0;
	u32 old_ep_index  = 0;

	old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_CTRL_EP_INDEX);
	fifo_count = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0);
	if(!data_type)
	{
		if(fifo_count != 8 )
		{
			printf("err: ep0 fifo_count %d is not 8\n", fifo_count);
			return -1;
		}
	}
	USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, fifo_count, (void *)buffer);
	__usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1);
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index);

	return ret;
}
/*
*******************************************************************************
*                     __sunxi_udc_set_address
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static int __sunxi_udc_set_address(uchar address)
{
	USBC_Dev_SetAddress(sunxi_udc_source.usbc_hd, address);
	if(USBC_Dev_QueryTransferMode(sunxi_udc_source.usbc_hd) == USBC_TS_MODE_HS)
	{
		sunxi_udc_source.speed = USB_SPEED_HIGH;
		sunxi_udc_source.fifo_size = HIGH_SPEED_EP_MAX_PACKET_SIZE;
		sunxi_usb_dbg("usb speed: HIGH\n");
	}
	else
	{
		sunxi_udc_source.speed = USB_SPEED_FULL;
		sunxi_udc_source.fifo_size = FULL_SPEED_EP_MAX_PACKET_SIZE;
		sunxi_usb_dbg("usb speed: FULL\n");
	}

	return SUNXI_USB_REQ_SUCCESSED;
}

/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
#if 0
static int __usb_read_fifo(void *buffer, unsigned int buffer_size)
{
    u32 old_ep_idx = 0;
    u32 fifo = 0;
	u32 transfered = 0;
    u32 left = 0;
    u32 this_len;

	old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);			//选择当前EP

	fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);		//选择fifo

	left = buffer_size;

	if(left)
	{
		while(left)
		{
			if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX))
			{
				this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
				this_len = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, buffer + transfered);

				transfered += this_len;
	        	left -= this_len;

	        	__usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1);		//返回状态
			}
		}
		USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, SUNXI_USB_BULK_OUT_EP_INDEX);
	}
	else
	{
		if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX))
		{
			this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
			this_len = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, buffer);

			transfered = this_len;
			__usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1);		//返回状态
		}
		else
		{
			sunxi_usb_dbg("sunxi usb rxdata not ready\n");
		}
	}

	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx);

	sunxi_usb_dbg("read bytes 0x%x\n", transfered);

	return transfered;
}
#endif
/*
*******************************************************************************
*                     fastboot_tx_status
*
* Description:
*    void
*
* Parameters:
*    void
*
* Return value:
*    void
*
* note:
*    void
*
*******************************************************************************
*/
static int __usb_write_fifo(uchar *buffer, unsigned int buffer_size)
{
	u32 old_ep_idx = 0;
	u32 fifo = 0;
	u32 transfered = 0;
	u32 left = 0;
	u32 this_len;

    /* Save index */
	old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_IN_EP_INDEX);

	left = buffer_size;
	fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_IN_EP_INDEX);

	while(left)
	{
		this_len = MIN(sunxi_udc_source.fifo_size, left);
		this_len = USBC_WritePacket(sunxi_udc_source.usbc_hd, fifo, this_len, buffer + transfered);

		transfered += this_len;
		left -= this_len;

		__usb_writecomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, 1);
	}

	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx);

	return 0;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
void sunxi_udc_ep_reset(void)
{
	__usb_bulk_ep_reset();
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_start_recv_by_dma(void* mem_base, uint length)
{
	uint old_ep_idx;
	ulong mem_buf = (ulong)mem_base;
	usb_dma_trans_unaliged_bytes = length & (sizeof(int) - 1);
	length &= ~(sizeof(int) - 1);
	usb_dma_trans_unaligned_buf = (uchar *)mem_buf + length;

	old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);			//选择当前EP
	//usb控制器选择dma传输方式
	USBC_Dev_ConfigEpDma(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);

	//刷掉cache
	flush_cache(mem_buf, ALIGN(length, 64));
	//使能dma传输
	sunxi_ubuf.request_size = length;
	sunxi_usb_dbg("dma start 0x%lx, length 0x%x\n", mem_buf, length);
	usb_dma_start(sunxi_udc_source.dma_recv_channal, mem_buf, length);
	//恢复EP
	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx);			//恢复原有EP

	return 0;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
void sunxi_udc_send_setup(uint bLength, void *buffer)
{
	u32 fifo = 0;

	if(!bLength)
	{
		/* sent zero packet */
		__usb_writecomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1);
	}
	else
	{
		fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_CTRL_EP_INDEX);
		USBC_WritePacket(sunxi_udc_source.usbc_hd, fifo, bLength, (void *)buffer);
		__usb_writecomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1);
	}
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_set_configuration(int config_param)
{
	return 0;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_set_address(uchar address)
{
	sunxi_udc_source.address = address;
	USBC_Dev_ReadDataStatus(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1);
	return SUNXI_USB_REQ_SUCCESSED;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_send_data(void *buffer, unsigned int buffer_size)
{
	sunxi_ubuf.rx_ready_for_data = 0;

	return __usb_write_fifo((uchar *)buffer, buffer_size);
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_get_ep_max(void)
{
	return  sunxi_udc_source.bulk_ep_max;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_get_ep_in_type(void)
{
	return  (0x80 | SUNXI_USB_BULK_IN_EP_INDEX);
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_udc_get_ep_out_type(void)
{
	return  SUNXI_USB_BULK_OUT_EP_INDEX;
}
/*
************************************************************************************************************
*
*                                             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");
		if(sunxi_udc_source.address)
		{
			__sunxi_udc_set_address(sunxi_udc_source.address&0xff);
			printf("set address 0x%x ok\n", sunxi_udc_source.address);
			sunxi_udc_source.address = 0;
		}
		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;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
static int eprx_recv_op(void)
{
	uint old_ep_index;
	uint this_len;
	uint fifo;

	old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd);

	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);

    if (USBC_Dev_IsEpStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX))
    {
        USBC_Dev_EpClearStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);

		printf("sunxi ubs read error: usb rx ep is busy already\n");
	}
	else
    {
    	if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX))
		{
			this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX);
			if(get_fastboot_data_flag() == 1)
			{
				fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);

				sunxi_ubuf.rx_req_length = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, sunxi_ubuf.rx_req_buffer);
				sunxi_ubuf.rx_req_buffer += this_len;

				sunxi_usb_dbg("special read ep bytes 0x%x\n", sunxi_ubuf.rx_req_length);
				__usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1);		//返回状态
			}
			else if(!sunxi_ubuf.rx_ready_for_data)
			{
				fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX);

				memset(sunxi_ubuf.rx_req_buffer, 0, 64);
				sunxi_ubuf.rx_req_length = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, sunxi_ubuf.rx_req_buffer);
				sunxi_ubuf.rx_ready_for_data = 1;

				sunxi_usb_dbg("read ep bytes 0x%x\n", sunxi_ubuf.rx_req_length);
				__usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1);		//返回状态
			}
			else
			{
				sunxi_usb_dbg("eprx do nothing and left it to dma\n");
			}
		}
		else
		{
			sunxi_usb_dbg("sunxi usb rxdata not ready\n");
		}
    }

	USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index);

	return 0;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
static int eptx_send_op(void)
{
	return 0;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
void sunxi_usb_main_loop(int delaytime)
{
	int ret;

	if(sunxi_usb_init(delaytime))
	{
		printf("usb init fail\n");

		sunxi_usb_exit();

		return ;
	}
	printf("usb init ok\n");

	while(1)
	{
		ret = sunxi_udev_active->state_loop(&sunxi_ubuf);
		if(ret)
		{
			break;
		}

		if(ctrlc())
		{
			break;
		}
	}

	printf("exit usb\n");
	sunxi_usb_exit();
	sunxi_update_subsequent_processing(ret);

	return ;
}
/*
************************************************************************************************************
*
*                                             function
*
*    name          :
*
*    parmeters     :
*
*    return        :
*
*    note          :
*
*
************************************************************************************************************
*/
int sunxi_usb_extern_loop(void)
{
	return sunxi_udev_active->state_loop(&sunxi_ubuf);
}
