text: /**
    **************************************************************************
    * @file     usbd_core.c
    * @version  v2.0.4
    * @date     2021-12-31
    * @brief    usb device driver
    **************************************************************************
    *                       Copyright notice & Disclaimer
    *
    * The software Board Support Package (BSP) that is made available to 
    * download from Artery official website is the copyrighted work of Artery. 
    * Artery authorizes customers to use, copy, and distribute the BSP 
    * software and its related documentation for the purpose of design and 
    * development in conjunction with Artery microcontrollers. Use of the 
    * software is governed by this copyright notice and the following disclaimer.
    *
    * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
    * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
    * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
    * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
    * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
    *
    **************************************************************************
    */
   
  #include "usb_core.h"
  #include "usbd_core.h"
  #include "usbd_sdr.h"
  
  /** @addtogroup AT32F435_437_middlewares_usbd_drivers
    * @{
    */
    
  /** @defgroup USBD_drivers_core
    * @brief usb device drivers core
    * @{
    */  
  
  /** @defgroup USBD_core_private_functions
    * @{
    */
  
  /**
    * @brief  usb core in transfer complete handler
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval none
    */
  void usbd_core_in_handler(usbd_core_type *udev, uint8_t ept_addr)
  {
    /* get endpoint info*/
    usb_ept_info *ept_info = &udev->ept_in[ept_addr & 0x7F];
    
    if(ept_addr == 0)
    {
      if(udev->ept0_sts == USB_EPT0_DATA_IN)
      {
        if(ept_info->ept0_slen > ept_info->maxpacket)
        {
          ept_info->ept0_slen -= ept_info->maxpacket;
          usbd_ept_send(udev, 0, ept_info->trans_buf, 
                        MIN(ept_info->ept0_slen, ept_info->maxpacket));
        }
        /* endpoint 0 */
        else if(ept_info->last_len == ept_info->maxpacket
          && ept_info->ept0_slen <= udev->ept0_wlength)
        {
          ept_info->last_len = 0;
          usbd_ept_send(udev, 0, 0, 0);
          usbd_ept_recv(udev, ept_addr, 0, 0);
        }
        else
        {
          if(udev->class_handler->ept0_tx_handler != 0 && 
              udev->conn_state == USB_CONN_STATE_CONFIGURED)
            {
              udev->class_handler->ept0_tx_handler(udev);
            }
            usbd_ctrl_recv_status(udev);
        }
      }
    }
    else if(udev->class_handler->in_handler != 0 && 
            udev->conn_state == USB_CONN_STATE_CONFIGURED)
    {
      /* other user define endpoint */
      udev->class_handler->in_handler(udev, ept_addr);
    }
  }
  
  /**
    * @brief  usb core out transfer complete handler
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval none
    */
  void usbd_core_out_handler(usbd_core_type *udev, uint8_t ept_addr)
  {
     /* get endpoint info*/
    usb_ept_info *ept_info = &udev->ept_out[ept_addr & 0x7F];
    
    if(ept_addr == 0)
    {
      /* endpoint 0 */
      if(udev->ept0_sts == USB_EPT0_DATA_OUT)
      {
        if(ept_info->ept0_slen > ept_info->maxpacket)
        {
          ept_info->ept0_slen -= ept_info->maxpacket;
          usbd_ept_recv(udev, ept_addr, ept_info->trans_buf, 
                        MIN(ept_info->ept0_slen, ept_info->maxpacket));
        }
        else
        {
            if(udev->class_handler->ept0_rx_handler != 0)
            {
              udev->class_handler->ept0_rx_handler(udev);
            }
            usbd_ctrl_send_status(udev);
        }
      }
    }
    else if(udev->class_handler->out_handler != 0 && 
            udev->conn_state == USB_CONN_STATE_CONFIGURED)
    {
      /* other user define endpoint */
      udev->class_handler->out_handler(udev, ept_addr);
    }
  }
  
  /**
    * @brief  usb core setup transfer complete handler
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval none
    */
  void usbd_core_setup_handler(usbd_core_type *udev, uint8_t ept_num)
  {
    /* setup parse */
    usbd_setup_request_parse(&udev->setup, udev->setup_buffer);
    
    /* set ept0 status */
    udev->ept0_sts = USB_EPT0_SETUP;
    udev->ept0_wlength = udev->setup.wLength;
    
    switch(udev->setup.bmRequestType & USB_REQ_RECIPIENT_MASK)
    {
      case USB_REQ_RECIPIENT_DEVICE:
        /* recipient device request */
        usbd_device_request(udev);
        break;
      case USB_REQ_RECIPIENT_INTERFACE:
        /* recipient interface request */
        usbd_interface_request(udev);
        break;
      case USB_REQ_RECIPIENT_ENDPOINT:
        /* recipient endpoint request */
        usbd_endpoint_request(udev);
        break;
      default:
        break;
    }
  }
  
  /**
    * @brief  usb control endpoint send data
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @param  buffer: send data buffer
    * @param  len: send data length
    * @retval none
    */
  void usbd_ctrl_send(usbd_core_type *udev, uint8_t *buffer, uint16_t len)
  {
    usb_ept_info *ept_info = &udev->ept_in[0];
    
    ept_info->ept0_slen = len;
    udev->ept0_sts = USB_EPT0_DATA_IN;
    
    usbd_ept_send(udev, 0, buffer, len);
  }
  
  /**
    * @brief  usb control endpoint recv data
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @param  buffer: recv data buffer
    * @param  len: recv data length
    * @retval none
    */
  void usbd_ctrl_recv(usbd_core_type *udev, uint8_t *buffer, uint16_t len)
  {
    usb_ept_info *ept_info = &udev->ept_out[0];
  
    ept_info->ept0_slen = len;
    udev->ept0_sts = USB_EPT0_DATA_OUT;
    
    usbd_ept_recv(udev, 0, buffer, len);
  }
  
  /**
    * @brief  usb control endpoint send in status
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_ctrl_send_status(usbd_core_type *udev)
  {
    udev->ept0_sts = USB_EPT0_STATUS_IN;
    
    usbd_ept_send(udev, 0, 0, 0);
  }
  
  /**
    * @brief  usb control endpoint send out status
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_ctrl_recv_status(usbd_core_type *udev)
  {
    udev->ept0_sts = USB_EPT0_STATUS_OUT;
    
    usbd_ept_recv(udev, 0, 0, 0);
  }
  
  /**
    * @brief  clear endpoint stall
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval none
    */
  void usbd_clear_stall(usbd_core_type *udev, uint8_t ept_addr)
  {
    usb_ept_info *ept_info;
    usb_reg_type *usbx = udev->usb_reg;
    
    if(ept_addr & 0x80) 
    {
      /* in endpoint */
      ept_info = &udev->ept_in[ept_addr & 0x7F];
    }
    else
    {
      /* out endpoint */
      ept_info = &udev->ept_out[ept_addr & 0x7F];
    }
    usb_ept_clear_stall(usbx, ept_info);
    ept_info->stall = 0;
  }
  
  /**
    * @brief  usb set endpoint to stall status
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval none
    */
  void usbd_set_stall(usbd_core_type *udev, uint8_t ept_addr)
  {
    usb_ept_info *ept_info;
    usb_reg_type *usbx = udev->usb_reg;
  
    if(ept_addr & 0x80)
    {
      /* in endpoint */
      ept_info = &udev->ept_in[ept_addr & 0x7F];
    }
    else
    {
      /* out endpoint */
      ept_info = &udev->ept_out[ept_addr & 0x7F];
    }
    usb_ept_stall(usbx, ept_info);
    
    ept_info->stall = 1;
  }
  
  /**
    * @brief  un-support device request
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_ctrl_unsupport(usbd_core_type *udev)
  {
    /* return stall status */
    usbd_set_stall(udev, 0x00);
    usbd_set_stall(udev, 0x80);
  }
  
  /**
    * @brief  get endpoint receive data length
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval data receive len
    */
  uint32_t usbd_get_recv_len(usbd_core_type *udev, uint8_t ept_addr)
  {
    usb_ept_info *ept = &udev->ept_out[ept_addr & 0x7F];
    return ept->trans_len;
  }
  
  /**
    * @brief  usb open endpoint
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @param  ept_type: endpoint type
    * @param  maxpacket: endpoint support max buffer size
    * @retval none
    */
  void usbd_ept_open(usbd_core_type *udev, uint8_t ept_addr, uint8_t ept_type, uint16_t maxpacket)
  {
    usb_reg_type *usbx = udev->usb_reg;
    usb_ept_info *ept_info;
    
    if((ept_addr & 0x80) == 0)
    {
      /* out endpoint info */
      ept_info = &udev->ept_out[ept_addr & 0x7F];
      ept_info->inout = EPT_DIR_OUT;
    }
    else
    {
      /* in endpoint info */
      ept_info = &udev->ept_in[ept_addr & 0x7F];
      ept_info->inout = EPT_DIR_IN;
    }
    
    /* set endpoint maxpacket and type */
    ept_info->maxpacket = maxpacket;
    ept_info->trans_type = ept_type;
    
    /* open endpoint */
    usb_ept_open(usbx, ept_info);
  }
  
  /**
    * @brief  usb close endpoint
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @retval none
    */
  void usbd_ept_close(usbd_core_type *udev, uint8_t ept_addr)
  {
    usb_ept_info *ept_info; 
    if(ept_addr & 0x80)
    {
      /* in endpoint */
      ept_info = &udev->ept_in[ept_addr & 0x7F];
    }
    else
    {
      /* out endpoint */
      ept_info = &udev->ept_out[ept_addr & 0x7F];
    }
    
    /* close endpoint */
    usb_ept_close(udev->usb_reg, ept_info);
  }
  
  /**
    * @brief  usb device connect to host
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_connect(usbd_core_type *udev)
  {
    usb_connect(udev->usb_reg);
  }
  
  /**
    * @brief  usb device disconnect to host
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_disconnect(usbd_core_type *udev)
  {
    usb_disconnect(udev->usb_reg);
  }
  
  /**
    * @brief  usb device set device address.
    * @param  udev: to the structure of usbd_core_type
    * @param  address: host assignment address
    * @retval none
    */
  void usbd_set_device_addr(usbd_core_type *udev, uint8_t address)
  {
    usb_set_address(udev->usb_reg, address);
  }
  
  /**
    * @brief  usb endpoint structure initialization
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usb_ept_default_init(usbd_core_type *udev)
  {
    uint8_t i_index = 0;
    /* init in endpoint info structure */
    for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
    {
      udev->ept_in[i_index].eptn        = i_index;
      udev->ept_in[i_index].ept_address = i_index;
      udev->ept_in[i_index].inout         = EPT_DIR_IN;
      udev->ept_in[i_index].maxpacket   = 0;
      udev->ept_in[i_index].trans_buf   = 0;
      udev->ept_in[i_index].total_len   = 0;
    }
    
    /* init out endpoint info structure */
    for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
    {
      udev->ept_out[i_index].eptn        = i_index;
      udev->ept_out[i_index].ept_address = i_index;
      udev->ept_out[i_index].inout       = EPT_DIR_OUT;
      udev->ept_out[i_index].maxpacket   = 0;
      udev->ept_out[i_index].trans_buf   = 0;
      udev->ept_out[i_index].total_len   = 0;
    }
  }
  
  /**
    * @brief  endpoint send data
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @param  buffer: send data buffer
    * @param  len: send data length
    * @retval none
    */
  void usbd_ept_send(usbd_core_type *udev, uint8_t ept_addr, uint8_t *buffer, uint16_t len)
  {
    /* get endpoint info struct and register */
    usb_reg_type *usbx = udev->usb_reg;
    usb_ept_info *ept_info = &udev->ept_in[ept_addr & 0x7F];
    otg_eptin_type *ept_in = USB_INEPT(usbx, ept_info->eptn);
    otg_device_type *dev = OTG_DEVICE(usbx);
    uint32_t pktcnt;
    
    /* set send data buffer and length */
    ept_info->trans_buf = buffer;
    ept_info->total_len = len;
    ept_info->trans_len = 0;
    
    /* transfer data len is zero */
    if(ept_info->total_len == 0)
    {
      ept_in->dieptsiz_bit.pktcnt = 1;
      ept_in->dieptsiz_bit.xfersize = 0;
    }
    else
    {
      if((ept_addr & 0x7F) == 0) // endpoint 0
      {
        /* endpoint 0 */
        if(ept_info->total_len > ept_info->maxpacket)
        {
          ept_info->total_len = ept_info->maxpacket;
        }
        
        /* set transfer size  */
        ept_in->dieptsiz_bit.xfersize = ept_info->total_len;
        
        /* set packet count */
        ept_in->dieptsiz_bit.pktcnt = 1;
        
        ept_info->last_len = ept_info->total_len;
      }
      else
      { 
        /* other endpoint */
        
        /* packet count */
        pktcnt = (ept_info->total_len + ept_info->maxpacket - 1) / ept_info->maxpacket;
        
        /* set transfer size  */
        ept_in->dieptsiz_bit.xfersize = ept_info->total_len;
        
        /* set packet count */
        ept_in->dieptsiz_bit.pktcnt = pktcnt;
        
        if(ept_info->trans_type == EPT_ISO_TYPE)
        {
          ept_in->dieptsiz_bit.mc = 1;
        }
      }
    }
    
    if(ept_info->trans_type != EPT_ISO_TYPE)
    {
      if(ept_info->total_len > 0)
      {
        /* set in endpoint tx fifo empty interrupt mask */
        dev->diepempmsk |= 1 << ept_info->eptn;
      }
    }
    
    if(ept_info->trans_type == EPT_ISO_TYPE)
    {
      if((dev->dsts_bit.soffn & 0x1) == 0)
      {
        ept_in->diepctl_bit.setd1pid = TRUE;
      }
      else
      {
        ept_in->diepctl_bit.setd0pid = TRUE;
      }
    }
    
    /* clear endpoint nak */
    ept_in->diepctl_bit.cnak = TRUE;
    
    /* endpoint enable */
    ept_in->diepctl_bit.eptena = TRUE;
    
    if(ept_info->trans_type == EPT_ISO_TYPE)
    {
      /* write data to fifo */
      usb_write_packet(usbx, ept_info->trans_buf, ept_info->eptn, ept_info->total_len); 
    }   
  }
  
  /**
    * @brief  endpoint receive data
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_addr: endpoint number
    * @param  buffer: receive data buffer
    * @param  len: receive data length
    * @retval none
    */
  void usbd_ept_recv(usbd_core_type *udev, uint8_t ept_addr, uint8_t *buffer, uint16_t len)
  {
    /* get endpoint info struct and register */
    usb_reg_type *usbx = udev->usb_reg;
    usb_ept_info *ept_info = &udev->ept_out[ept_addr & 0x7F];
    otg_eptout_type *ept_out = USB_OUTEPT(usbx, ept_info->eptn);
    otg_device_type *dev = OTG_DEVICE(usbx);
    uint32_t pktcnt;
    
     /* set receive data buffer and length */
    ept_info->trans_buf = buffer;
    ept_info->total_len = len;
    ept_info->trans_len = 0;
    
    if((ept_addr & 0x7F) == 0)
    {
      /* endpoint 0 */
      ept_info->total_len = ept_info->maxpacket;
    }
    
    if(ept_info->total_len == 0 || ((ept_addr & 0x7F) == 0))
    {
      /* set transfer size  */
      ept_out->doeptsiz_bit.xfersize = ept_info->maxpacket;
      
      /* set packet count */
      ept_out->doeptsiz_bit.pktcnt = 1;
    }
    else
    {
      pktcnt = (ept_info->total_len + ept_info->maxpacket - 1) / ept_info->maxpacket;
      
      /* set transfer size  */
      ept_out->doeptsiz_bit.xfersize = ept_info->maxpacket * pktcnt;
      
      /* set packet count */
      ept_out->doeptsiz_bit.pktcnt = pktcnt;
    }
    
    if(ept_info->trans_type == EPT_ISO_TYPE)
    {
     if((dev->dsts_bit.soffn & 0x01) == 0)
     {
       ept_out->doepctl_bit.setd1pid = TRUE;
     }
     else
     {
       ept_out->doepctl_bit.setd0pid = TRUE;
     }
    }
    
    /* clear endpoint nak */
    ept_out->doepctl_bit.cnak = TRUE;
    
    /* endpoint enable */
    ept_out->doepctl_bit.eptena = TRUE;
  }
  
  /**
    * @brief  get usb connect state
    * @param  udev: to the structure of usbd_core_type
    * @retval usb connect state
    */
  usbd_conn_state usbd_connect_state_get(usbd_core_type *udev)
  {
    return udev->conn_state;
  }
  
  /**
    * @brief  usb device remote wakeup
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_remote_wakeup(usbd_core_type *udev)
  {
    /* check device is in suspend mode */
    if(usb_suspend_status_get(udev->usb_reg) == 1)
    {    
      /* set connect state */
      udev->conn_state = udev->old_conn_state;
      
      /* open phy clock */
      usb_open_phy_clk(udev->usb_reg);
      
      /* set remote wakeup */
      usb_remote_wkup_set(udev->usb_reg);
      
      /* delay 10 ms */
      usb_delay_ms(10);
      
      /* clear remote wakup */
      usb_remote_wkup_clear(udev->usb_reg);
    }
  }
  
  /**
    * @brief  usb device enter suspend mode
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_enter_suspend(usbd_core_type *udev)
  {
    /* check device is in suspend mode */
    if(usb_suspend_status_get(udev->usb_reg) == 1)
    {
      /* stop phy clk */
      usb_stop_phy_clk(udev->usb_reg);
    }
  }
  
  /**
    * @brief  usb device flush in endpoint fifo
    * @param  udev: to the structure of usbd_core_type
    * @param  ept_num: endpoint number
    * @retval none
    */
  void usbd_flush_tx_fifo(usbd_core_type *udev, uint8_t ept_num)
  {
    /* flush endpoint tx fifo */
    usb_flush_tx_fifo(udev->usb_reg, ept_num & 0x1F);
  }
  
  /**
    * @brief  usb device endpoint fifo alloc
    * @param  udev: to the structure of usbd_core_type
    * @retval none
    */
  void usbd_fifo_alloc(usbd_core_type *udev)
  {
    usb_reg_type *usbx = udev->usb_reg;
    
    if(usbx == OTG1_GLOBAL)
    {
      /* set receive fifo size */
      usb_set_rx_fifo(usbx, USBD_RX_SIZE);
      
      /* set endpoint0 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT0, USBD_EP0_TX_SIZE);
      
      /* set endpoint1 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT1, USBD_EP1_TX_SIZE);
      
      /* set endpoint2 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT2, USBD_EP2_TX_SIZE);
      
      /* set endpoint3 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT3, USBD_EP3_TX_SIZE);
      
      if(USB_EPT_MAX_NUM == 8)
      {
        /* set endpoint4 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT4, USBD_EP4_TX_SIZE);
        
        /* set endpoint5 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT5, USBD_EP5_TX_SIZE);
        
        /* set endpoint6 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT6, USBD_EP6_TX_SIZE);
        
        /* set endpoint7 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT7, USBD_EP7_TX_SIZE);
      }
    }
  #ifdef OTG2_GLOBAL
    if(usbx == OTG2_GLOBAL)
    {
      /* set receive fifo size */
      usb_set_rx_fifo(usbx, USBD2_RX_SIZE);
      
      /* set endpoint0 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT0, USBD2_EP0_TX_SIZE);
      
      /* set endpoint1 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT1, USBD2_EP1_TX_SIZE);
      
      /* set endpoint2 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT2, USBD2_EP2_TX_SIZE);
      
      /* set endpoint3 tx fifo size */
      usb_set_tx_fifo(usbx, USB_EPT3, USBD2_EP3_TX_SIZE);
      
      if(USB_EPT_MAX_NUM == 8)
      {
        /* set endpoint4 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT4, USBD2_EP4_TX_SIZE);
        
        /* set endpoint5 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT5, USBD2_EP5_TX_SIZE);
        
        /* set endpoint6 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT6, USBD2_EP6_TX_SIZE);
        
        /* set endpoint7 tx fifo size */
        usb_set_tx_fifo(usbx, USB_EPT7, USBD2_EP7_TX_SIZE);
      }
    }
  #endif
  }
  
  /**
    * @brief  usb device core initialization
    * @param  udev: to the structure of usbd_core_type
    * @param  usb_reg: usb otgfs peripheral global register
    *         this parameter can be one of the following values:
    *         OTG1_GLOBAL , OTG2_GLOBAL
    * @param  class_handler: usb class handler
    * @param  desc_handler: device config handler
    * @param  core_id: usb core id number
    * @retval usb_sts_type
    */
  usb_sts_type usbd_core_init(usbd_core_type *udev, 
                              usb_reg_type *usb_reg,
                              usbd_class_handler *class_handler,
                              usbd_desc_handler *desc_handler, 
                              uint8_t core_id)
  {
    usb_reg_type *usbx;
    otg_device_type *dev;
    otg_eptin_type *ept_in;
    otg_eptout_type *ept_out;
    uint32_t i_index;
    
    udev->usb_reg = usb_reg;
    usbx = usb_reg;
    dev = OTG_DEVICE(usbx);
    
    /* set connect state */
    udev->conn_state = USB_CONN_STATE_DEFAULT;
    
    /* device class config */
    udev->device_addr = 0;
    udev->class_handler = class_handler;
    udev->desc_handler = desc_handler;
    /* set device disconnect */
    usbd_disconnect(udev);
    
    /* set endpoint to default status */
    usb_ept_default_init(udev);
     
    /* disable usb global interrupt */
    usb_interrupt_disable(usbx);
    
    /* init global register */
    usb_global_init(usbx);
    
    /* set device mode */
    usb_global_set_mode(usbx, OTG_DEVICE_MODE);
    
    /* open phy clock */
    usb_open_phy_clk(udev->usb_reg);
     
    /* set periodic frame interval */
    dev->dcfg_bit.perfrint = DCFG_PERFRINT_80;
    
    /* set device speed to full-speed */
    dev->dcfg_bit.devspd = USB_DCFG_FULL_SPEED;
    
    /* flush all tx fifo */
    usb_flush_tx_fifo(usbx, 16);
    
    /* flush share rx fifo */
    usb_flush_rx_fifo(usbx);
    
    /* clear all endpoint interrupt flag and mask */
    dev->daint = 0xFFFFFFFF;
    dev->daintmsk = 0;
    dev->diepmsk = 0;
    dev->doepmsk = 0;
    
    for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
    {
      usbx->dieptxfn[i_index] = 0;
    }
    
    /* endpoint fifo alloc */
    usbd_fifo_alloc(udev);
    
    /* disable all in endpoint */
    for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
    {
      ept_in = USB_INEPT(usbx, i_index);
      if(ept_in->diepctl_bit.eptena)
      {
        ept_in->diepctl = 0;
        ept_in->diepctl_bit.eptdis = TRUE;
        ept_in->diepctl_bit.snak = TRUE;
      }
      else
      {
        ept_in->diepctl = 0;
      }
      ept_in->dieptsiz = 0;
      ept_in->diepint = 0xFF;
    }
    
    /* disable all out endpoint */
    for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
    {
      ept_out = USB_OUTEPT(usbx, i_index);
      if(ept_out->doepctl_bit.eptena)
      {
        ept_out->doepctl = 0;
        ept_out->doepctl_bit.eptdis = TRUE;
        ept_out->doepctl_bit.snak = TRUE;
      }
      else
      {
        ept_out->doepctl = 0;
      }
      ept_out->doeptsiz = 0;
      ept_out->doepint = 0xFF;
    }
    dev->diepmsk_bit.txfifoudrmsk = TRUE;
    
    /* clear global interrupt and mask */
    usbx->gintmsk = 0;
    usbx->gintsts = 0xBFFFFFFF;
    
    /* enable global interrupt mask */
    usbx->gintmsk = USB_OTG_SOF_INT | USB_OTG_RXFLVL_INT | 
                    USB_OTG_USBSUSP_INT | USB_OTG_USBRST_INT | 
                    USB_OTG_ENUMDONE_INT | USB_OTG_IEPT_INT | 
                    USB_OTG_OEPT_INT | USB_OTG_INCOMISOIN_INT | 
                    USB_OTG_INCOMPIP_INCOMPISOOUT_INT | USB_OTG_WKUP_INT |
                    USB_OTG_OTGINT_INT;
    
    /* usb connect */
    usbd_connect(udev);  
    
    /* enable global interrupt */
    usb_interrupt_enable(usbx);
    
    return USB_OK;
    
  }
  
  /**
    * @}
    */ 
  
  /**
    * @}
    */
  
  /**
    * @}
    */
  
  