/*
 * Copyright 2018,2020,2022 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
#include "netif/etharp.h"
#include "netif/ppp/pppoe.h"
#include "lwip/igmp.h"
#include "lwip/mld6.h"

#if USE_RTOS && defined(SDK_OS_FREE_RTOS)
#include "FreeRTOS.h"
#include "event_groups.h"
#endif

#include "usb_host_config.h"
#include "usb_host.h"
#include "usb_host_cdc_rndis.h"
#include "usb_host_cdc.h"

#include "board.h"

#include "usb_ethernetif.h"

#include "board.h"

#if ((!USB_HOST_CONFIG_KHCI) && (!USB_HOST_CONFIG_EHCI) && (!USB_HOST_CONFIG_OHCI) && (!USB_HOST_CONFIG_IP3516HS))
#error Please enable USB_HOST_CONFIG_KHCI, USB_HOST_CONFIG_EHCI, USB_HOST_CONFIG_OHCI, or USB_HOST_CONFIG_IP3516HS in file usb_host_config.
#endif

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*******************************************************************************
  * Prototypes
  ******************************************************************************/
extern void USB_HostClockInit(void);
extern void USB_HostIsrEnable(void);
extern void USB_HostTaskFn(void *param);
/*******************************************************************************
  * Variables
  ******************************************************************************/
#define IFNAME0 'N'
#define IFNAME1 'X'
/*each g_RndisInstance should have its own's buffer*/
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) uint8_t g_SendMessage[RNDIS_CONTROL_MESSAGE];
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) uint8_t g_GetMessage[RNDIS_CONTROL_MESSAGE];
/*used for send/recv data from/to device rndis, the message length should be the sum of max frame packet size and usb rndis header*/
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) uint8_t g_OutPutBuffer[RNDIS_DATA_MESSAGE];
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) uint8_t g_InPutBuffer[RNDIS_DATA_MESSAGE];

usb_host_rndis_instance_struct_t g_RndisInstance = {0};

usb_host_handle g_HostHandle;


/*******************************************************************************
 * Code
 ******************************************************************************/
 /*!
 * @brief host cdc data transfer callback.
 *
 * This function is used as callback function for bulk in transfer .
 *
 * @param param    the host cdc instance pointer.
 * @param data     data buffer pointer.
 * @param dataLength data length.
 * @status         transfer result status.
 */


void USB_HostCdcRndisDataInCallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
    usb_host_rndis_instance_struct_t * rndisInstance = (usb_host_rndis_instance_struct_t *)param;
     struct netif *netif = (struct netif *)rndisInstance->netif;
    if (status != kStatus_USB_Success)
    {
        if (status == kStatus_USB_TransferCancel)
        {
            /*usb_echo("cdc transfer cancel\r\n");*/
        }
        else
        {
            /**usb_echo("cdc in transfer retry\r\n");*/
        }
        rndisInstance->runState = kUSB_HostCdcRndisRunDataReceive;
    }
    else
    {

        struct pbuf *pbuf;
        if((dataLength > 0) && (NULL != data))
        {

            rndis_packet_msg_struct_t *temp = (rndis_packet_msg_struct_t *)data;
            pbuf= pbuf_alloc(PBUF_RAW, temp->dataLength, PBUF_POOL);
            if (pbuf)
            {

                temp->dataBuffer[temp->dataLength] = 0;
                pbuf->tot_len = temp->dataLength;
                pbuf->len = temp->dataLength;

                uint8_t *p = (uint8_t*)(&temp->dataOffset);
                memcpy(pbuf->payload, (p + temp->dataOffset), temp->dataLength);
                /*in special case, when polling out packet, in packet maybe finihsed, the in packet will be not be handled*/
                if (!rndisInstance->pollingInSending)
                {
                    netif->input(pbuf, netif);
                }
            }

        }
        rndisInstance->runState = kUSB_HostCdcRndisRunDataReceive;
    }
    return;
}

static int dial_wait_rx_done = 0;
static int ec200a_rx_index = 0;
static int ec200a_pb_done = 0;
void USB_HostCdcRndisATInCallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
    if (status != kStatus_USB_Success)
    {
        if (status == kStatus_USB_TransferCancel)
        {
            /*usb_echo("cdc transfer cancel\r\n");*/
        }
        else
        {
            /**usb_echo("cdc in transfer retry\r\n");*/
        }
        usb_echo(">>>RX: time_out \r\n", data);
    }
    else
    {
        if((dataLength > 0) && (NULL != data))
        {
            usb_echo(">>>RX: %s \r\n", data);
            usb_echo("ec200a_rx_index = %d \r\n", ec200a_rx_index);
            if(strstr(data, "PB DONE") != NULL)
            {
            	ec200a_pb_done = 1;
            	usb_echo("PB DONE detected. \r\n");
            }
            ec200a_rx_index++;
        }
    }

    dial_wait_rx_done = 0;
    return;
}
/*!
* @brief host cdc data transfer callback.
*
* This function is used as callback function for bulk out transfer .
*
* @param param    the host cdc instance pointer.
* @param data     data buffer pointer.
* @param dataLength data length.
* @status         transfer result status.
*/
void USB_HostCdcRndisDataOutCallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
    usb_host_rndis_instance_struct_t *rndisInstance = (usb_host_rndis_instance_struct_t *)param;
    if (status != kStatus_USB_Success)
    {
        if (status == kStatus_USB_TransferCancel)
        {
            usb_echo("cdc transfer cancel\r\n");
        }
        else
        {
            /*usb_echo("cdc out transfer error\r\n");*/
        }
    }
    rndisInstance->dataSend = 0U;
    return;
}

void USB_HostCdcRndisATOutCallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
    usb_host_rndis_instance_struct_t *rndisInstance = (usb_host_rndis_instance_struct_t *)param;
    if (status != kStatus_USB_Success)
    {
        if (status == kStatus_USB_TransferCancel)
        {
            usb_echo("cdc transfer cancel\r\n");
        }
        else
        {
            /*usb_echo("cdc out transfer error\r\n");*/
        }
    }

    // usb_echo("dial send done. \r\n");
	
	dial_wait_rx_done = 0;
    return;
}


err_t USB_EthernetIfOutPut(struct netif *netif, struct pbuf *p)
{
    err_t    status;
    usb_host_rndis_instance_struct_t * rndisInstance = (usb_host_rndis_instance_struct_t *)netif->state;
    status = ERR_OK;
    if (rndisInstance->attach)
    {
        if(p->tot_len == p->len)
        {
             uint32_t total;
             total = p->tot_len;
             uint32_t transferDone = 0;

             while (total)
             {
                 rndisInstance->pollingInSending = 1;
                 if(rndisInstance->dataSend)
                 {
                     /*discard current data if send flag is not cleared*/
                     return ERR_BUF;
                 }
                 rndisInstance->pollingInSending = 0;

                rndisInstance->dataSend = 1;
                if(total <= RNDIS_FRAME_MAX_FRAMELEN)
                {

                      USB_HostRndisSendDataMsg(rndisInstance->classHandle, rndisInstance->outPutBuffer, RNDIS_FRAME_MAX_FRAMELEN, 0, 0, 0, 0, 0, ((uint8_t*)p->payload + transferDone),total,
                                                  USB_HostCdcRndisDataOutCallback, rndisInstance);
                      transferDone += total;
                      total = 0U;

                }
                else
                {
                      USB_HostRndisSendDataMsg(rndisInstance->classHandle, rndisInstance->outPutBuffer, RNDIS_FRAME_MAX_FRAMELEN, 0, 0, 0, 0, 0, ((uint8_t*)p->payload + transferDone),RNDIS_FRAME_MAX_FRAMELEN,
                                                  USB_HostCdcRndisDataOutCallback, rndisInstance);
                      transferDone += RNDIS_FRAME_MAX_FRAMELEN;
                      total -=  RNDIS_FRAME_MAX_FRAMELEN;
                }
             }
        }
        else
        {
            if (p->tot_len < RNDIS_FRAME_MAX_FRAMELEN)
            {
                u16_t uCopied = pbuf_copy_partial(p, rndisInstance->outPutBuffer, p->tot_len, 0);
                LWIP_ASSERT("uCopied != p->tot_len", uCopied == p->tot_len);

                 rndisInstance->pollingInSending = 1;
                 while(rndisInstance->dataSend)
                 {

                     USB_HostTaskFn(g_HostHandle);
                 }
                 rndisInstance->pollingInSending = 0;
                  USB_HostRndisSendDataMsg(rndisInstance->classHandle, rndisInstance->outPutBuffer, RNDIS_FRAME_MAX_FRAMELEN, 0, 0, 0, 0, 0, ((uint8_t*)&g_OutPutBuffer[0]),p->tot_len,
                              USB_HostCdcRndisDataOutCallback, rndisInstance);

            }
            else
            {
                /*TO DO*/
                return ERR_BUF;
            }
        }
    }
    else
    {
        status = ERR_CONN;
        usb_echo("USB Rndis device is not attached\r\n");
    }
    return status;
}



/*!
 * @brief host callback function.
 *
 * device attach/detach callback function.
 *
 * @param deviceHandle           device handle.
 * @param configurationHandle attached device's configuration descriptor information.
 * @param event_code           callback event code, please reference to enumeration host_event_t.
 *
 * @retval kStatus_USB_Success              The host is initialized successfully.
 * @retval kStatus_USB_NotSupported         The application don't support the configuration.
 */
usb_status_t USB_HostEvent(usb_device_handle deviceHandle,
                           usb_host_configuration_handle configurationHandle,
                           uint32_t event_code)
{
    usb_status_t status;
    status = kStatus_USB_Success;

    switch (event_code)
    {
        case kUSB_HostEventAttach:
            status = USB_HostCdcRndisEvent(deviceHandle, configurationHandle, event_code);
            break;
        case kUSB_HostEventNotSupported:
            status = USB_HostCdcRndisEvent(deviceHandle, configurationHandle, event_code);
            usb_echo("device not supported.\r\n");
            break;

        case kUSB_HostEventEnumerationDone:
            status = USB_HostCdcRndisEvent(deviceHandle, configurationHandle, event_code);
            break;

        case kUSB_HostEventDetach:
            status = USB_HostCdcRndisEvent(deviceHandle, configurationHandle, event_code);
            break;

        default:
            break;
    }
    return status;
}


static void USB_HostApplicationInit(uint8_t controllerId, struct netif *netif)

{

    usb_status_t status = kStatus_USB_Success;

    USB_HostClockInit();


    status = USB_HostInit(controllerId, &g_HostHandle, USB_HostEvent);
    if (status != kStatus_USB_Success)
    {
        usb_echo("host init error\r\n");
        return;
    }
    USB_HostIsrEnable();
    usb_echo("  host init.\r\n");
    netif->state = (void *)&g_RndisInstance;
    g_RndisInstance.netif = (void*)netif;
    g_RndisInstance.hostHandle = g_HostHandle;
    g_RndisInstance.sendMessage = &g_SendMessage[0];
    g_RndisInstance.getMessage = &g_GetMessage[0];
    g_RndisInstance.outPutBuffer = &g_OutPutBuffer[0];
    g_RndisInstance.inPutBuffer = &g_InPutBuffer[0];
    while (!g_RndisInstance.attach)
    {
        USB_HostTaskFn(g_RndisInstance.hostHandle);
        USB_HosCdcRndisTask(&g_RndisInstance);
    }
}

err_t USB_EthernetIfInIt(struct netif *netif)
{
    err_t    status;
    status = ERR_IF;
    netif->name[0] = IFNAME0;
    netif->name[1] = IFNAME1;
#if LWIP_IPV4
    netif->output = etharp_output;
#endif
    netif->linkoutput = USB_EthernetIfOutPut;
    /* set MAC hardware address length */
    netif->hwaddr_len = ETH_HWADDR_LEN;
    ethernetifConfig_t *config;
    if(netif->state)
    {
        config = (ethernetifConfig_t *)netif->state;

        USB_HostApplicationInit(config->controllerId, netif);
        /*USB enet card is ready*/
        netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
        status = ERR_OK;
    }

    return status;
}


/*!
 * @brief host cdc interrupt transfer callback.
 *
 * This function is used as callback function for interrupt transfer . Interrupt transfer is used to implement
 * asynchronous notification of UART status as pstn sepc. This callback suppose the device will return SerialState
 * notification. If there is need to suppose other notification ,please refer pstn spec 6.5 and cdc spec6.3.
 * @param param    the host cdc instance pointer.
 * @param data     data buffer pointer.
 * @param dataLength data length.
 * @status         transfer result status.
 */
void USB_HostCdcRndisInterruptCallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
    /*usb_host_cdc_acm_state_struct_t *state = (usb_host_cdc_acm_state_struct_t *)data;*/
    usb_host_rndis_instance_struct_t *rndisInstance = (usb_host_rndis_instance_struct_t *)param;

    if (status != kStatus_USB_Success)
    {
        if (status == kStatus_USB_TransferCancel)
        {
            usb_echo("cdc transfer cancel\r\n");
        }
        else
        {
            usb_echo("cdc control transfer error\r\n");
        }
    }
    else
    { /*more information about SerialState ,please pstn spec 6.5.4 */
       /* usb_echo("get serial state value = %d\r\n", state->bmstate);*/
        rndisInstance->responseAvailable = 1;
        rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvDone;
    }
}

/*!
 * @brief host cdc rndis control transfer callback.
 *
 * This function is used as callback function for control transfer .
 *
 * @param param    the host cdc rndis instance pointer.
 * @param data     data buffer pointer.
 * @param dataLength data length.
 * @status         transfer result status.
 */
void USB_HostCdcRndisControlCallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
    usb_host_rndis_instance_struct_t *rndisInstance = (usb_host_rndis_instance_struct_t *)param;

    struct netif *netif = (struct netif *)rndisInstance->netif;
    if (status != kStatus_USB_Success)
    {
        usb_echo("data transfer error = %d\r\n",status);
        return;
    }

    if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitSetControlInterface)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunSetControlInterfaceDone;
    }
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitSetDataInterface)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunSetDataInterfaceDone;
    }

    // AT
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitSetATDataInterface)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunSetATDataInterfaceDone;
    }

    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitInitMsg)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunWaitInitMsgDone;
    }
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitMaxmumFrame)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunWaitMaxmumFrameDone;
    }
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitGetMACAddress)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunWaitGetMACAddressDone;
    }
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitSetMsg)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunWaitSetMsgDone;
    }
	// nxa16038
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitDial)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunDialDone;
    }
	
    else if (rndisInstance->runWaitState == kUSB_HostCdcRndisRunWaitGetEncapsulatedCommand)
    {
        rndisInstance->runState = kUSB_HostCdcRndisRunGetEncapsulatedResponseDone;
        if (rndisInstance->previousRunState == kUSB_HostCdcRndisRunWaitInitMsgDone)
        {
           rndisInstance->runState = kUSB_HostCdcRndisRunWaitMaxmumFrame;

        }
        else if (rndisInstance->previousRunState == kUSB_HostCdcRndisRunWaitMaxmumFrameDone)
        {
            rndisInstance->runState = kUSB_HostCdcRndisRunWaitGetMACAddress;

            rndis_query_cmplt_struct_t * msg = (rndis_query_cmplt_struct_t *)data;
            if(REMOTE_NDIS_QUERY_CMPLT == msg->messageType)
            {
               netif->mtu = USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(((uint8_t*)&msg->requestID + msg->informationBufferOffset));
            }
            else
            {
                /*set default value if device doesn't send query complete message*/
                netif->mtu = 1500U;
            }
        }
        else if (rndisInstance->previousRunState == kUSB_HostCdcRndisRunWaitGetMACAddressDone)
        {
            rndisInstance->runState = kUSB_HostCdcRndisRunWaitSetMsg;
            rndis_query_cmplt_struct_t * msg = (rndis_query_cmplt_struct_t *)data;

            netif->hwaddr_len = NETIF_MAX_HWADDR_LEN;

            memcpy(netif->hwaddr,(((uint8_t*)&msg->requestID + msg->informationBufferOffset)), NETIF_MAX_HWADDR_LEN);
        }
        else if (rndisInstance->previousRunState == kUSB_HostCdcRndisRunWaitSetMsgDone)
        {
            rndisInstance->runState = kUSB_HostCdcRndisRunGetState;
        }
        rndisInstance->previousRunState = kUSB_HostCdcRndisRunIdle;
    }
    else
    {
    }
}


/*!
 * @brief host rndis task function.
 *
 * This function implements the host cdc action, it is used to create task.
 *
 * @param param   the host rndis instance pointer.
 */
const char * dial_command[] =
{
	"ATE1\r",
	"ati4\r",
	"ATI0\r",
	"AT+YLC?\r",
	"AT+CGSN\r", // 5

	"AT#CIMI\r",
	"AT+CCID\r",
	"AT#BND=?\r",
	"AT+CGMI\r",
	"AT+CGMM\r", //10

	"AT#SWPKGV\r",
	"AT+CGDCONT=1,\"IP\",\"3gnet\"\r",
	"at#rndis=1,0\r" //13
};

int dial_state = 0;

#define DIAL_BUF_LEN 64
static char dial_buf[DIAL_BUF_LEN] = {0};
static void dial_update_status(void)
{
	dial_state++;
	usb_echo("dial_state == %d. \r\n", dial_state);
	dial_wait_rx_done = 1;
}
static void dial_tx(usb_host_rndis_instance_struct_t *rndisInstance, char * command)
{
	usb_echo(">>>TX: %s \r\n", command);
	USB_HostCdcDataSend(rndisInstance->classHandle_at, (uint8_t *)command, strlen(command), USB_HostCdcRndisATOutCallback, 0);
}
static void dial_rx(usb_host_rndis_instance_struct_t *rndisInstance)
{
	USB_HostCdcDataRecv(rndisInstance->classHandle_at, dial_buf, DIAL_BUF_LEN, USB_HostCdcRndisATInCallback, 0);
}

int dial_done = 0;
void lte_dial(usb_host_rndis_instance_struct_t *rndisInstance)
{
	usb_status_t status;

	if(dial_wait_rx_done)
	{
		return;
	}

	if(dial_state == 0)
	{
		usb_echo("dial...... \r\n\r\n\r\n\r\n\r\n");
		dial_update_status();
		dial_rx(rndisInstance);
		return;
	}

	if(ec200a_pb_done)
	{
		if(dial_state < 100)
		{
			dial_state = 99;
			dial_update_status();
			usb_echo("AT+qnetdevctl=1,1,1\r\n");
			dial_tx(rndisInstance, "AT+qnetdevctl=1,1,1\r\n");
		}
		else
		{
			if(dial_state == 103)
			{
				usb_echo("end do not need to rx again.\r\n");
				dial_done = 1;
				dial_state++;
				return;
			}

			if(dial_state > 103)
			{
				return;
			}

			dial_update_status();
			dial_rx(rndisInstance);
		}

		return;
	}
	else
	{
		if(dial_state < 7)
		{
			dial_update_status();
			dial_rx(rndisInstance);
			return;
		}
	}
}




int init_ec200a_done = 0;
int ec200a_wait_tx_rx_done = 0;
int ec200a_state = 0;
void USB_HostCdcRndisEC200ACallback(void *param, uint8_t *data, uint32_t dataLength, usb_status_t status)
{
	ec200a_wait_tx_rx_done = 0;

	if(status == kStatus_USB_Success)
	{
		usb_echo("ok. \r\n");
	}
	else
	{
		usb_echo("fail. \r\n");
	}

	if(ec200a_state == 12)
	{
		init_ec200a_done = 1;
		usb_echo("init_ec200a_done \r\n");
	}
}

static void update_ec200a_status(void)
{
	usb_echo("ec200a_state = %d \r\n", ec200a_state);
	ec200a_state++;
	ec200a_wait_tx_rx_done = 1;
}
static void ep0_communicate(
		usb_host_rndis_instance_struct_t *rndisInstance,
		char * setup,
        uint8_t *data)
{
	update_ec200a_status();
	USB_HostCdcControl_ex(rndisInstance->classHandle,
			setup[0], setup[1], setup[2], setup[3], setup[4], setup[5], (setup[6]<<0)|(setup[7]<<8), data,
			USB_HostCdcRndisEC200ACallback, rndisInstance);
}
static void init_ec200a(usb_host_rndis_instance_struct_t *rndisInstance)
{
	char buf_rx[32];

	if(ec200a_wait_tx_rx_done)
	{
		return;
	}

	if(ec200a_state == 0)
	{
		usb_echo("ec200a init. \r\n");

		// set
		const char command[] = {0x21,0x22,0x00,0x00,0x04,0x00,0x00,0x00};
		ep0_communicate(rndisInstance, (char *)command, NULL);
		return;
	}

	if(ec200a_state == 1)
	{
		// set
		const char command[] = {0x21,0x22,0x00,0x00,0x03,0x00,0x00,0x00};
		ep0_communicate(rndisInstance, (char *)command, NULL);
		return;
	}

	if(ec200a_state == 2)
	{
		// clear halt ep6
		const char command[] = {0x02,0x01,0x00,0x00,0x86,0x00,0x00,0x00};
		ep0_communicate(rndisInstance, (char *)command, NULL);
		return;
	}

	if(ec200a_state == 3)
	{
		// clear halt ep15
		const char command[] = {0x02,0x01,0x00,0x00,0x0f,0x00,0x00,0x00};
		ep0_communicate(rndisInstance, (char *)command, NULL);
		return;
	}

	if(ec200a_state == 4)
	{
		// get 0
		const char command[] = {0xA1,0x21,0x00,0x00,0x03,0x00,0x07,0x00};
		ep0_communicate(rndisInstance, (char *)command, buf_rx);
		return;
	}

	if(ec200a_state == 5)
	{
		// get 1
		const char command[] = {0xA1,0x21,0x00,0x00,0x03,0x00,0x07,0x00};
		ep0_communicate(rndisInstance, (char *)command, buf_rx);
		return;
	}

	if(ec200a_state == 6)
	{
		// get 2
		const char command[] = {0xA1,0x21,0x00,0x00,0x03,0x00,0x07,0x00};
		ep0_communicate(rndisInstance, (char *)command, buf_rx);
		return;
	}

	if(ec200a_state == 7)
	{
		// set
		const char command[] = {0x21,0x20,0x00,0x00,0x03,0x00,0x07,0x00};
		const char data[]    = {0x00,0xC2,0x01,0x00,0x16,0x4D,0x08};
		ep0_communicate(rndisInstance, (char *)command, (char *)data);
		return;
	}

	if(ec200a_state == 8)
	{
		// set
		const char command[] = {0x21,0x22,0x00,0x00,0x03,0x00,0x00,0x00};
		ep0_communicate(rndisInstance, (char *)command, buf_rx);
		return;
	}

	if(ec200a_state == 9)
	{
		// set
		const char command[] = {0x21,0x22,0x00,0x00,0x03,0x00,0x00,0x00};
		ep0_communicate(rndisInstance, (char *)command, buf_rx);
		return;
	}

	if(ec200a_state == 10)
	{
		// get
		const char command[] = {0xA1,0x21,0x00,0x00,0x03,0x00,0x07,0x00};
		ep0_communicate(rndisInstance, (char *)command, buf_rx);
		return;
	}

	if(ec200a_state == 11)
	{
		// set
		const char command[] = {0x21,0x20,0x00,0x00,0x03,0x00,0x07,0x00};
		const char data[]    = {0x00,0x10,0x0E,0x00,0x00,0x00,0x08};
		ep0_communicate(rndisInstance, (char *)command, (char *)data);
		return;
	}
}


void USB_HosCdcRndisTask(void *param)
{

    usb_status_t status = kStatus_USB_Success;
    usb_host_rndis_instance_struct_t *rndisInstance = (usb_host_rndis_instance_struct_t *)param;
    uint32_t filtertype;
    struct netif *netif ;
    /* device state changes */
    if (rndisInstance->deviceState != rndisInstance->previousState)
    {
        rndisInstance->previousState = rndisInstance->deviceState;
        switch (rndisInstance->deviceState)
        {
            case kStatus_DEV_Idle:
                break;
            case kStatus_DEV_Attached:
                rndisInstance->runState = kUSB_HostCdcRndisRunSetControlInterface;
                status = USB_HostCdcInit(rndisInstance->deviceHandle, &rndisInstance->classHandle);
                if(status == kStatus_USB_Success)
                {
                	usb_echo("init cdc classHandle done. \r\n");
                }
                status = USB_HostCdcInit(rndisInstance->deviceHandle, &rndisInstance->classHandle_at);
                if(status == kStatus_USB_Success)
                {
                	usb_echo("init cdc classHandle_at done. \r\n");
                }

                usb_echo("rndis device attached\r\n");
                break;
            case kStatus_DEV_Detached:
                rndisInstance->deviceState = kStatus_DEV_Idle;
                rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
                USB_HostCdcDeinit(rndisInstance->deviceHandle, rndisInstance->classHandle);
				USB_HostCdcDeinit(rndisInstance->deviceHandle, rndisInstance->classHandle_at);
				rndisInstance->classHandle = NULL;
                rndisInstance->controlInterfaceHandle = NULL;
                rndisInstance->dataInterfaceHandle = NULL;
                rndisInstance->classHandle_at = NULL;
                rndisInstance->dataInterfaceHandle_at = NULL;
                rndisInstance->deviceHandle = NULL;
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunIdle;
                usb_echo("rndis device detached\r\n");
                break;
            default:
                break;
        }
    }

    /* run state */
    switch (rndisInstance->runState)
    {
        case kUSB_HostCdcRndisRunIdle:
            break;

        case kUSB_HostCdcRndisRunSetControlInterface:
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitSetControlInterface;
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
            // rndis interface.
            if (USB_HostCdcSetControlInterface(rndisInstance->classHandle,
            								   rndisInstance->controlInterfaceHandle,
											   0,
                                               USB_HostCdcRndisControlCallback,
											   rndisInstance) != kStatus_USB_Success)
            {
                usb_echo("set control interface error\r\n");
            }
            break;

        case kUSB_HostCdcRndisRunSetControlInterfaceDone:
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitSetDataInterface;
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
            if (USB_HostCdcSetDataInterface(rndisInstance->classHandle,
            								rndisInstance->dataInterfaceHandle,
											0,
                                            USB_HostCdcRndisControlCallback,
											rndisInstance) != kStatus_USB_Success)
            {
                usb_echo("set data interface error\r\n");
            }
            break;

        case kUSB_HostCdcRndisRunSetDataInterfaceDone:
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitSetATDataInterface;
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
            if (USB_HostCdcSetDataInterface(rndisInstance->classHandle_at,
            							  rndisInstance->dataInterfaceHandle_at,
									      0,
                                          USB_HostCdcRndisControlCallback,
										  rndisInstance) != kStatus_USB_Success)
            {
                usb_echo("set at interface error\r\n");
            }
            break;

        case kUSB_HostCdcRndisRunSetATDataInterfaceDone:
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitInitMsg;
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
            if(USB_HostRndisInitMsg(rndisInstance->classHandle,rndisInstance->sendMessage, RNDIS_CONTROL_MESSAGE,
                                                        USB_HostCdcRndisControlCallback, rndisInstance))
            {
                usb_echo("Error in Init message\r\n");
            }
        	break;

        case kUSB_HostCdcRndisRunWaitInitMsgDone:

            if (rndisInstance->interruptRunState == kUSB_HostCdcRndisRunIdle)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvPrime;
                if (USB_HostCdcInterruptRecv(rndisInstance->classHandle, (uint8_t *)&rndisInstance->state,
                                             RNDIS_RESPONSE_AVAILABLE, USB_HostCdcRndisInterruptCallback,
                                             rndisInstance) != kStatus_USB_Success)
                {
                    usb_echo("Error in USB_HostCdcInterruptRecv: %x\r\n", status);
                }
            }
            else if (rndisInstance->interruptRunState == kUSB_HostCdcRndisRunInterruptRecvDone)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunIdle;
                /*remember the previous set encapsulated command*/
                rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitInitMsgDone;
                rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitGetEncapsulatedCommand;
                rndisInstance->runState = kUSB_HostCdcRndisRunIdle;

                if(USB_HostCdcGetEncapsulatedResponse(rndisInstance->classHandle,
                		                              rndisInstance->getMessage,
                                                      RNDIS_CONTROL_MESSAGE,
													  USB_HostCdcRndisControlCallback,
													  rndisInstance))
                {
                   usb_echo("Error in Init message\r\n");
                }
            }
            break;
        case kUSB_HostCdcRndisRunWaitMaxmumFrame:
            rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitMaxmumFrame;
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitMaxmumFrame;
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;

            if(USB_HostRndisQueryMsg(rndisInstance->classHandle, OID_GEN_MAXIMUM_FRAME_SIZE, rndisInstance->sendMessage,
                                                                                      RNDIS_CONTROL_MESSAGE, 0, 0, NULL, USB_HostCdcRndisControlCallback, rndisInstance))
            {
                usb_echo("Error in Init message\r\n");
            }
            break;

        case kUSB_HostCdcRndisRunWaitMaxmumFrameDone:

            if (rndisInstance->interruptRunState == kUSB_HostCdcRndisRunIdle)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvPrime;
                if (USB_HostCdcInterruptRecv(rndisInstance->classHandle, (uint8_t *)&rndisInstance->state,
                                             RNDIS_RESPONSE_AVAILABLE, USB_HostCdcRndisInterruptCallback,
                                             rndisInstance) != kStatus_USB_Success)
                {
                    usb_echo("Error in USB_HostCdcInterruptRecv: %x\r\n", status);
                }
            }
            else if (rndisInstance->interruptRunState == kUSB_HostCdcRndisRunInterruptRecvDone)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunIdle;
                rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitMaxmumFrameDone;
                rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitGetEncapsulatedCommand;
                rndisInstance->runState = kUSB_HostCdcRndisRunIdle;

                if(USB_HostCdcGetEncapsulatedResponse(rndisInstance->classHandle, rndisInstance->getMessage,
                                                                                          RNDIS_CONTROL_MESSAGE, USB_HostCdcRndisControlCallback, rndisInstance))
                {
                    usb_echo("Error in Init message\r\n");
                }
            }
            break;
        case kUSB_HostCdcRndisRunWaitGetMACAddress:
            rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitGetMACAddress;
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitGetMACAddress;
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;

            if(USB_HostRndisQueryMsg(rndisInstance->classHandle, OID_802_3_CURRENT_ADDRESS,rndisInstance->sendMessage,
                                                                                      RNDIS_CONTROL_MESSAGE, 0, 0, NULL, USB_HostCdcRndisControlCallback, rndisInstance))
            {
                usb_echo("Error in Init message\r\n");
            }
            break;

        case kUSB_HostCdcRndisRunWaitGetMACAddressDone:
            if (rndisInstance->interruptRunState  == kUSB_HostCdcRndisRunIdle)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvPrime;
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvPrime;
                if (USB_HostCdcInterruptRecv(rndisInstance->classHandle, (uint8_t *)&rndisInstance->state,
                                           RNDIS_RESPONSE_AVAILABLE, USB_HostCdcRndisInterruptCallback,
                                           rndisInstance) != kStatus_USB_Success)
                {
                    usb_echo("Error in USB_HostCdcInterruptRecv: %x\r\n", status);
                }

            }
            else if (rndisInstance->interruptRunState == kUSB_HostCdcRndisRunInterruptRecvDone)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunIdle;
                rndisInstance->responseAvailable = 0;
                rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitGetMACAddressDone;
                rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitGetEncapsulatedCommand;
                rndisInstance->runState = kUSB_HostCdcRndisRunIdle;

                if(USB_HostCdcGetEncapsulatedResponse(rndisInstance->classHandle, rndisInstance->getMessage,
                                                                                          RNDIS_CONTROL_MESSAGE, USB_HostCdcRndisControlCallback, rndisInstance))
                {
                    usb_echo("Error in Init message\r\n");
                }
            }
            break;
        case kUSB_HostCdcRndisRunWaitSetMsg:
            rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitSetMsg;
            rndisInstance->runWaitState     = kUSB_HostCdcRndisRunWaitSetMsg;
            rndisInstance->runState         = kUSB_HostCdcRndisRunIdle;
            filtertype = NDIS_PACKET_TYPE_DIRECTED;
            if(USB_HostRndisSetMsg(rndisInstance->classHandle, OID_GEN_CURRENT_PACKET_FILTER, rndisInstance->sendMessage, RNDIS_CONTROL_MESSAGE, 20U, (sizeof(filtertype)),&filtertype,
                                                                                          USB_HostCdcRndisControlCallback, rndisInstance))
            {
                usb_echo("Error in set message\r\n");
            }
            break;



        // nxa16038 ---------------------------------------------------------

        case kUSB_HostCdcRndisRunWaitSetMsgDone:
            rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitEC200AInit;
            // rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
            if(init_ec200a_done)
            {
            	rndisInstance->runState = kUSB_HostCdcRndisRunEC200AInitDone;
            }
            init_ec200a(rndisInstance);
            break;
        case kUSB_HostCdcRndisRunEC200AInitDone:
			// dial
			rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitDial;
			// rndisInstance->runState     = kUSB_HostCdcRndisRunIdle;
			lte_dial(rndisInstance);

			if(dial_done)
			{
				// dial end
				usb_echo("dial done. \r\n");
				rndisInstance->runState = kUSB_HostCdcRndisRunDialDone;
			}
			break;


        case kUSB_HostCdcRndisRunDialDone:
        	// usb_echo("kUSB_HostCdcRndisRunDialDone \r\n");
            if (rndisInstance->interruptRunState  == kUSB_HostCdcRndisRunIdle)
            {
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvPrime;
                rndisInstance->interruptRunState = kUSB_HostCdcRndisRunInterruptRecvPrime;
                if (USB_HostCdcInterruptRecv(rndisInstance->classHandle, (uint8_t *)&rndisInstance->state,
                                       RNDIS_RESPONSE_AVAILABLE, USB_HostCdcRndisInterruptCallback,
                                       rndisInstance) != kStatus_USB_Success)
                {
                    usb_echo("Error in USB_HostCdcInterruptRecv: %x\r\n", status);
                }
            }
            else if (rndisInstance->interruptRunState == kUSB_HostCdcRndisRunInterruptRecvDone)
            {
                rndisInstance->responseAvailable = 0;
                rndisInstance->previousRunState = kUSB_HostCdcRndisRunWaitSetMsgDone;
                rndisInstance->runWaitState = kUSB_HostCdcRndisRunWaitGetEncapsulatedCommand;
                rndisInstance->runState = kUSB_HostCdcRndisRunIdle;

                if(USB_HostCdcGetEncapsulatedResponse(rndisInstance->classHandle, rndisInstance->getMessage,
                                                                  RNDIS_CONTROL_MESSAGE, USB_HostCdcRndisControlCallback, rndisInstance))
                {
                    usb_echo("Error in Init message\r\n");
                }
            }
          break;

        case kUSB_HostCdcRndisRunGetState:
        	//usb_echo("kUSB_HostCdcRndisRunGetState \r\n");
            rndisInstance->attach = 1;
            rndisInstance->dataSend = 0;
            netif = (struct netif *)rndisInstance->netif;
            netif_set_link_up(netif);

        case kUSB_HostCdcRndisRunDataReceive:
        	//usb_echo("kUSB_HostCdcRndisRunDataReceive \r\n");
            rndisInstance->runState = kUSB_HostCdcRndisRunIdle;
            USB_HostRndisRecvDataMsg(rndisInstance->classHandle, rndisInstance->inPutBuffer, RNDIS_DATA_MESSAGE,
                                                            USB_HostCdcRndisDataInCallback, rndisInstance);
            break;
        default:
            break;
    }
}

usb_status_t USB_HostCdcRndisEvent(usb_device_handle deviceHandle,
                              usb_host_configuration_handle configurationHandle,
                              uint32_t event_code)
{
    usb_status_t status;
    uint8_t id;
    usb_host_configuration_t *configuration;
    uint8_t interface_index;
    usb_host_interface_t *hostInterface;
    uint32_t info_value;
    struct netif *netif;
    status = kStatus_USB_Success;

    switch (event_code)
    {
        case kUSB_HostEventAttach:
            /* judge whether is configurationHandle supported */
            configuration = (usb_host_configuration_t *)configurationHandle;

            // rndis interface
            for (interface_index = 0; interface_index < configuration->interfaceCount; ++interface_index)
            {
                hostInterface = &configuration->interfaceList[interface_index];
                id = hostInterface->interfaceDesc->bInterfaceClass;
                if (id != USB_HOST_CDC_RNDIS_CLASS_CODE)
                {
                    continue;
                }
                id = hostInterface->interfaceDesc->bInterfaceSubClass;
                if (id != USB_HOST_CDC_RNDIS_SUBCLASS_CODE)
                {
                    continue;
                }
                id = hostInterface->interfaceDesc->bInterfaceProtocol;
                if (id != USB_HOST_CDC_RNDIS_PROTOCOL_CODE)
                {
                    continue;
                }
                else
                {
                    /* the interface is supported by the application */
                	usb_echo("- 0001 a\r\n");
                    g_RndisInstance.controlInterfaceHandle = hostInterface;
                }
            }

            // data interface, for enet comunitation.
            for (interface_index = 0; interface_index < configuration->interfaceCount; ++interface_index)
            {
                hostInterface = &configuration->interfaceList[interface_index];
                id = hostInterface->interfaceDesc->bInterfaceClass;

                if (id != USB_HOST_CDC_DATA_CLASS_CODE)
                {
                    continue;
                }
                id = hostInterface->interfaceDesc->bInterfaceSubClass;
                if (id != USB_HOST_CDC_DATA_SUBCLASS_CODE)
                {
                    continue;
                }
                id = hostInterface->interfaceDesc->bInterfaceProtocol;
                if (id != USB_HOST_CDC_DATA_PROTOCOL_CODE)
                {
                    continue;
                }
                else
                {
                	usb_echo("- 0001 b\r\n");
                    g_RndisInstance.dataInterfaceHandle = hostInterface;
                }
            }

            // at interface.
            hostInterface = &configuration->interfaceList[3];
            g_RndisInstance.dataInterfaceHandle_at = hostInterface;

            g_RndisInstance.deviceHandle = deviceHandle;
            if ((NULL != g_RndisInstance.dataInterfaceHandle)    &&
                (NULL != g_RndisInstance.controlInterfaceHandle) &&
				(NULL != g_RndisInstance.dataInterfaceHandle_at)
            )
            {
                status = kStatus_USB_Success;
            }
            else
            {
            	usb_echo("- 0001 \r\n");
                status = kStatus_USB_NotSupported;
            }
            break;

        case kUSB_HostEventNotSupported:
            usb_echo("  the usb tethering featue is not enabled, please turn on usb tethering in mobile phone\r\n ");
            break;

        case kUSB_HostEventEnumerationDone:
            if (g_RndisInstance.deviceState == kStatus_DEV_Idle)
            {
                if ((g_RndisInstance.deviceHandle != NULL) && (g_RndisInstance.dataInterfaceHandle != NULL) && (g_RndisInstance.controlInterfaceHandle != NULL))
                {
                    g_RndisInstance.deviceState = kStatus_DEV_Attached;

                    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDevicePID, &info_value);
                    usb_echo("device cdc attached:\r\npid=0x%x", info_value);
                    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDeviceVID, &info_value);
                    usb_echo("vid=0x%x ", info_value);
                    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDeviceAddress, &info_value);
                    usb_echo("address=%d\r\n", info_value);
                }
            }
            else
            {
                usb_echo("not idle rndis instance\r\n");
            }
            break;

        case kUSB_HostEventDetach:
            if (g_RndisInstance.deviceState != kStatus_DEV_Idle)
            {

                g_RndisInstance.attach = 0;
                g_RndisInstance.dataSend = 0;
                netif = (struct netif *)g_RndisInstance.netif;
                netif_set_link_down(netif);
                g_RndisInstance.deviceState = kStatus_DEV_Detached;
            }
            break;

        default:
            break;
    }
    return status;
}

void usb_ethernetif_input(struct netif *netif)
{
    usb_host_rndis_instance_struct_t * rndisInstance = (usb_host_rndis_instance_struct_t *)netif->state;
    USB_HostTaskFn(rndisInstance->hostHandle);
    USB_HosCdcRndisTask(rndisInstance);
}
