text: /**************************************************************************//**
   * @file     hid_transfer_and_msc.c
   * @version  V3.00
   * @brief    HSUSBD HID transfer and mass storage sample file.
   *
   * @copyright SPDX-License-Identifier: Apache-2.0
   * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
   ******************************************************************************/
  
  /*!<Includes */
  #include <string.h>
  #include "NuMicro.h"
  #include "hid_transfer_and_msc.h"
  #include "massstorage.h"
  
  uint8_t volatile g_u8EPAReady = 0;
  uint32_t g_u32EpAMaxPacketSize;
  uint32_t g_u32EpBMaxPacketSize;
  
  /*--------------------------------------------------------------------------*/
  /* Global variables for Control Pipe */
  int32_t g_TotalSectors = 0;
  
  /* USB flow control variables */
  uint8_t g_u8BulkState = BULK_NORMAL;
  uint8_t g_u8Prevent = 0;
  uint8_t volatile g_u8MscStart = 0;
  uint8_t g_au8SenseKey[4];
  
  uint32_t g_u32MSCMaxLun = 0;
  uint32_t g_u32LbaAddress;
  uint32_t g_u32MassBase, g_u32StorageBase;
  
  uint32_t g_u32EpMaxPacketSize;
  
  /* CBW/CSW variables */
  struct CBW g_sCBW;
  struct CSW g_sCSW;
  
  
  /*--------------------------------------------------------------------------*/
  uint8_t g_au8InquiryID[36] =
  {
      0x00,                   /* Peripheral Device Type */
      0x80,                   /* RMB */
      0x00,                   /* ISO/ECMA, ANSI Version */
      0x00,                   /* Response Data Format */
      0x1F, 0x00, 0x00, 0x00, /* Additional Length */
  
      /* Vendor Identification */
      'N', 'u', 'v', 'o', 't', 'o', 'n', ' ',
  
      /* Product Identification */
      'U', 'S', 'B', ' ', 'M', 'a', 's', 's', ' ', 'S', 't', 'o', 'r', 'a', 'g', 'e',
  
      /* Product Revision */
      '1', '.', '0', '0'
  };
  
  // code = 5Ah, Mode Sense
  static uint8_t g_au8ModePage_01[12] =
  {
      0x01, 0x0A, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
      0x03, 0x00, 0x00, 0x00
  };
  
  static uint8_t g_au8ModePage_05[32] =
  {
      0x05, 0x1E, 0x13, 0x88, 0x08, 0x20, 0x02, 0x00,
      0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x05, 0x1E, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00
  };
  
  static uint8_t g_au8ModePage_1B[12] =
  {
      0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00
  };
  
  static uint8_t g_au8ModePage_1C[8] =
  {
      0x1C, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00
  };
  
  static uint8_t g_au8ModePage[24] =
  {
      0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x02, 0x00, 0x1C, 0x0A, 0x80, 0x03,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  };
  
  
  /*--------------------------------------------------------------------------*/
  void USBD20_IRQHandler(void)
  {
      __IO uint32_t IrqStL, IrqSt;
  
      IrqStL = HSUSBD->GINTSTS & HSUSBD->GINTEN;    /* get interrupt status */
  
      if(!IrqStL)    return;
  
      /* USB interrupt */
      if(IrqStL & HSUSBD_GINTSTS_USBIF_Msk)
      {
          IrqSt = HSUSBD->BUSINTSTS & HSUSBD->BUSINTEN;
  
          if(IrqSt & HSUSBD_BUSINTSTS_SOFIF_Msk)
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_SOFIF_Msk);
  
          if(IrqSt & HSUSBD_BUSINTSTS_RSTIF_Msk)
          {
              HSUSBD_SwReset();
              g_u8MscStart = 0;
              g_u8BulkState = BULK_NORMAL;
  
              HSUSBD_ResetDMA();
              HSUSBD->EP[EPA].EPRSPCTL = HSUSBD_EPRSPCTL_FLUSH_Msk;
              HSUSBD->EP[EPB].EPRSPCTL = HSUSBD_EPRSPCTL_FLUSH_Msk;
              HSUSBD->EP[EPC].EPRSPCTL = HSUSBD_EPRSPCTL_FLUSH_Msk;
              HSUSBD->EP[EPD].EPRSPCTL = HSUSBD_EPRSPCTL_FLUSH_Msk;
  
              if(HSUSBD->OPER & 0x04)   /* high speed */
                  HID_InitForHighSpeed();
              else                    /* full speed */
                  HID_InitForFullSpeed();
              HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk);
              HSUSBD_SET_ADDR(0);
              HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_RESUMEIEN_Msk | HSUSBD_BUSINTEN_SUSPENDIEN_Msk);
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_RSTIF_Msk);
              HSUSBD_CLR_CEP_INT_FLAG(0x1ffc);
          }
  
          if(IrqSt & HSUSBD_BUSINTSTS_RESUMEIF_Msk)
          {
              HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_SUSPENDIEN_Msk);
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_RESUMEIF_Msk);
          }
  
          if(IrqSt & HSUSBD_BUSINTSTS_SUSPENDIF_Msk)
          {
              HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_RESUMEIEN_Msk);
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_SUSPENDIF_Msk);
          }
  
          if(IrqSt & HSUSBD_BUSINTSTS_HISPDIF_Msk)
          {
              HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk);
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_HISPDIF_Msk);
          }
  
          if(IrqSt & HSUSBD_BUSINTSTS_DMADONEIF_Msk)
          {
              g_hsusbd_DmaDone = 1;
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_DMADONEIF_Msk);
  
              if(!(HSUSBD->DMACTL & HSUSBD_DMACTL_DMARD_Msk))
              {
                  if(g_u8BulkState == BULK_OUT)
                      g_u8BulkState = BULK_CBW;
                  HSUSBD_ENABLE_EP_INT(EPD, HSUSBD_EPINTEN_RXPKIEN_Msk);
              }
  
              if(HSUSBD->DMACTL & HSUSBD_DMACTL_DMARD_Msk)
              {
                  if(g_hsusbd_ShortPacket == 1)
                  {
                      HSUSBD->EP[EPC].EPRSPCTL = (HSUSBD->EP[EPC].EPRSPCTL & 0x10) | HSUSBD_EP_RSPCTL_SHORTTXEN;    // packet end
                      g_hsusbd_ShortPacket = 0;
                  }
              }
          }
  
          if(IrqSt & HSUSBD_BUSINTSTS_PHYCLKVLDIF_Msk)
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_PHYCLKVLDIF_Msk);
  
          if(IrqSt & HSUSBD_BUSINTSTS_VBUSDETIF_Msk)
          {
              if(HSUSBD_IS_ATTACHED())
              {
                  /* USB Plug In */
                  HSUSBD_ENABLE_USB();
              }
              else
              {
                  /* USB Un-plug */
                  HSUSBD_DISABLE_USB();
              }
              HSUSBD_CLR_BUS_INT_FLAG(HSUSBD_BUSINTSTS_VBUSDETIF_Msk);
          }
      }
  
      if(IrqStL & HSUSBD_GINTSTS_CEPIF_Msk)
      {
          IrqSt = HSUSBD->CEPINTSTS & HSUSBD->CEPINTEN;
  
          if(IrqSt & HSUSBD_CEPINTSTS_SETUPTKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_SETUPTKIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_SETUPPKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_SETUPPKIF_Msk);
              HSUSBD_ProcessSetupPacket();
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_OUTTKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_OUTTKIF_Msk);
              HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_STSDONEIEN_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_INTKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_INTKIF_Msk);
              if(!(IrqSt & HSUSBD_CEPINTSTS_STSDONEIF_Msk))
              {
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_TXPKIF_Msk);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_TXPKIEN_Msk);
                  HSUSBD_CtrlIn();
              }
              else
              {
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_TXPKIF_Msk);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_TXPKIEN_Msk | HSUSBD_CEPINTEN_STSDONEIEN_Msk);
              }
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_PINGIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_PINGIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_TXPKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
              HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
              if(g_hsusbd_CtrlInSize)
              {
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_INTKIF_Msk);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_INTKIEN_Msk);
              }
              else
              {
                  if(g_hsusbd_CtrlZero == 1)
                      HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_ZEROLEN);
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk | HSUSBD_CEPINTEN_STSDONEIEN_Msk);
              }
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_TXPKIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_RXPKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_RXPKIF_Msk);
              HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
              HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk | HSUSBD_CEPINTEN_STSDONEIEN_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_NAKIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_NAKIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_STALLIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STALLIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_ERRIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_ERRIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_STSDONEIF_Msk)
          {
              HSUSBD_UpdateDeviceState();
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
              HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_BUFFULLIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_BUFFULLIF_Msk);
              return;
          }
  
          if(IrqSt & HSUSBD_CEPINTSTS_BUFEMPTYIF_Msk)
          {
              HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_BUFEMPTYIF_Msk);
              return;
          }
      }
  
      /* interrupt in */
      if(IrqStL & HSUSBD_GINTSTS_EPAIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPA].EPINTSTS & HSUSBD->EP[EPA].EPINTEN;
          if(HSUSBD->EP[EPA].EPINTSTS & 0x02)
              EPA_Handler();
          HSUSBD_CLR_EP_INT_FLAG(EPA, IrqSt);
      }
      /* interrupt out */
      if(IrqStL & HSUSBD_GINTSTS_EPBIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPB].EPINTSTS & HSUSBD->EP[EPB].EPINTEN;
          if(HSUSBD->EP[EPB].EPINTSTS & 0x01)
              EPB_Handler();
          HSUSBD_CLR_EP_INT_FLAG(EPB, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPCIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPC].EPINTSTS & HSUSBD->EP[EPC].EPINTEN;
          HSUSBD_ENABLE_EP_INT(EPC, 0);
          HSUSBD_CLR_EP_INT_FLAG(EPC, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPDIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPD].EPINTSTS & HSUSBD->EP[EPD].EPINTEN;
          HSUSBD_ENABLE_EP_INT(EPD, 0);
          HSUSBD_CLR_EP_INT_FLAG(EPD, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPEIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPE].EPINTSTS & HSUSBD->EP[EPE].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPE, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPFIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPF].EPINTSTS & HSUSBD->EP[EPF].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPF, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPGIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPG].EPINTSTS & HSUSBD->EP[EPG].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPG, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPHIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPH].EPINTSTS & HSUSBD->EP[EPH].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPH, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPIIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPI].EPINTSTS & HSUSBD->EP[EPI].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPI, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPJIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPJ].EPINTSTS & HSUSBD->EP[EPJ].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPJ, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPKIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPK].EPINTSTS & HSUSBD->EP[EPK].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPK, IrqSt);
      }
  
      if(IrqStL & HSUSBD_GINTSTS_EPLIF_Msk)
      {
          IrqSt = HSUSBD->EP[EPL].EPINTSTS & HSUSBD->EP[EPL].EPINTEN;
          HSUSBD_CLR_EP_INT_FLAG(EPL, IrqSt);
      }
  }
  
  void EPA_Handler(void)  /* Interrupt IN handler */
  {
      HID_SetInReport();
  }
  
  void EPB_Handler(void)  /* Interrupt OUT handler */
  {
      uint32_t len, i;
      len = HSUSBD->EP[EPB].EPDATCNT & 0xffff;
      for(i = 0; i < len; i++)
          g_u8OutBuff[i] = HSUSBD->EP[EPB].EPDAT_BYTE;
      HID_GetOutReport(g_u8OutBuff, len);
  }
  
  
  /*--------------------------------------------------------------------------*/
  /**
    * @brief  USBD Endpoint Config.
    * @param  None.
    * @retval None.
    */
  void HID_InitForHighSpeed(void)
  {
      /*****************************************************/
      /* EPA ==> Interrupt IN endpoint, address 1 */
      HSUSBD_SetEpBufAddr(EPA, EPA_BUF_BASE, EPA_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPA, EPA_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPA, INT_IN_EP_NUM, HSUSBD_EP_CFG_TYPE_INT, HSUSBD_EP_CFG_DIR_IN);
      //HSUSBD_ENABLE_EP_INT(EPA, HSUSBD_EPINTEN_TXPKIEN_Msk);
      g_u32EpAMaxPacketSize = EPA_MAX_PKT_SIZE;
  
      /* EPB ==> Interrupt OUT endpoint, address 2 */
      HSUSBD_SetEpBufAddr(EPB, EPB_BUF_BASE, EPB_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPB, EPB_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPB, INT_OUT_EP_NUM, HSUSBD_EP_CFG_TYPE_INT, HSUSBD_EP_CFG_DIR_OUT);
      HSUSBD_ENABLE_EP_INT(EPB, HSUSBD_EPINTEN_RXPKIEN_Msk | HSUSBD_EPINTEN_BUFFULLIEN_Msk);
      g_u32EpBMaxPacketSize = EPB_MAX_PKT_SIZE;
  
      /*****************************************************/
      /* EPC ==> Bulk IN endpoint, address 3 */
      HSUSBD_SetEpBufAddr(EPC, EPC_BUF_BASE, EPC_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPC, EPC_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPC, BULK_IN_EP_NUM, HSUSBD_EP_CFG_TYPE_BULK, HSUSBD_EP_CFG_DIR_IN);
  
      /* EPD ==> Bulk OUT endpoint, address 4 */
      HSUSBD_SetEpBufAddr(EPD, EPD_BUF_BASE, EPD_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPD, EPD_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPD, BULK_OUT_EP_NUM, HSUSBD_EP_CFG_TYPE_BULK, HSUSBD_EP_CFG_DIR_OUT);
      HSUSBD_ENABLE_EP_INT(EPD, HSUSBD_EPINTEN_RXPKIEN_Msk);
  
      g_u32EpMaxPacketSize = EPC_MAX_PKT_SIZE;
  }
  
  void HID_InitForFullSpeed(void)
  {
      /*****************************************************/
      /* EPA ==> Interrupt IN endpoint, address 1 */
      HSUSBD_SetEpBufAddr(EPA, EPA_BUF_BASE, EPA_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPA, EPA_OTHER_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPA, INT_IN_EP_NUM, HSUSBD_EP_CFG_TYPE_INT, HSUSBD_EP_CFG_DIR_IN);
      //HSUSBD_ENABLE_EP_INT(EPA, HSUSBD_EPINTEN_TXPKIEN_Msk);
      g_u32EpAMaxPacketSize = EPA_OTHER_MAX_PKT_SIZE;
  
      /* EPB ==> Interrupt OUT endpoint, address 2 */
      HSUSBD_SetEpBufAddr(EPB, EPB_BUF_BASE, EPB_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPB, EPB_OTHER_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPB, INT_OUT_EP_NUM, HSUSBD_EP_CFG_TYPE_INT, HSUSBD_EP_CFG_DIR_OUT);
      HSUSBD_ENABLE_EP_INT(EPB, HSUSBD_EPINTEN_RXPKIEN_Msk | HSUSBD_EPINTEN_BUFFULLIEN_Msk);
      g_u32EpBMaxPacketSize = EPB_OTHER_MAX_PKT_SIZE;
  
      /*****************************************************/
      /* EPC ==> Bulk IN endpoint, address 3 */
      HSUSBD_SetEpBufAddr(EPC, EPC_BUF_BASE, EPC_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPC, EPC_OTHER_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPC, BULK_IN_EP_NUM, HSUSBD_EP_CFG_TYPE_BULK, HSUSBD_EP_CFG_DIR_IN);
  
      /* EPD ==> Bulk OUT endpoint, address 4 */
      HSUSBD_SetEpBufAddr(EPD, EPD_BUF_BASE, EPD_BUF_LEN);
      HSUSBD_SET_MAX_PAYLOAD(EPD, EPD_OTHER_MAX_PKT_SIZE);
      HSUSBD_ConfigEp(EPD, BULK_OUT_EP_NUM, HSUSBD_EP_CFG_TYPE_BULK, HSUSBD_EP_CFG_DIR_OUT);
      HSUSBD_ENABLE_EP_INT(EPD, HSUSBD_EPINTEN_RXPKIEN_Msk);
  
      g_u32EpMaxPacketSize = EPC_OTHER_MAX_PKT_SIZE;
  }
  
  void HID_Init(void)
  {
      //HSUSBD->OPER = 0;
      /* Configure USB controller */
      /* Enable USB BUS, CEP and EPA ~ EPD global interrupt */
      HSUSBD_ENABLE_USB_INT(HSUSBD_GINTEN_USBIEN_Msk | HSUSBD_GINTEN_CEPIEN_Msk | HSUSBD_GINTEN_EPAIEN_Msk | HSUSBD_GINTEN_EPBIEN_Msk | HSUSBD_GINTEN_EPCIEN_Msk | HSUSBD_GINTEN_EPDIEN_Msk);
      /* Enable BUS interrupt */
      HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_DMADONEIEN_Msk | HSUSBD_BUSINTEN_RESUMEIEN_Msk | HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_VBUSDETIEN_Msk);
      /* Reset Address to 0 */
      HSUSBD_SET_ADDR(0);
  
      /*****************************************************/
      /* Control endpoint */
      HSUSBD_SetEpBufAddr(CEP, CEP_BUF_BASE, CEP_BUF_LEN);
      HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_SETUPPKIEN_Msk | HSUSBD_CEPINTEN_STSDONEIEN_Msk);
  
      HID_InitForHighSpeed();
  
      g_sCSW.dCSWSignature = CSW_SIGNATURE;
      g_TotalSectors = 60;
      g_u32MassBase = 0x20001000;
      g_u32StorageBase = 0x20002000;
  }
  
  void HID_ClassRequest(void)
  {
      if(gUsbCmd.bmRequestType & 0x80)    /* request data transfer direction */
      {
          // Device to host
          switch(gUsbCmd.bRequest)
          {
              case GET_MAX_LUN:
              {
                  g_u8MscStart = 1;
                  // Return current configuration setting
                  HSUSBD_PrepareCtrlIn((uint8_t *)&g_u32MSCMaxLun, 1);
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_INTKIF_Msk);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_INTKIEN_Msk);
                  break;
              }
              case GET_REPORT:
              case GET_IDLE:
              case GET_PROTOCOL:
              default:
              {
                  /* Setup error, stall the device */
                  HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_STALLEN_Msk);
                  break;
              }
          }
      }
      else
      {
          // Host to device
          switch(gUsbCmd.bRequest)
          {
              case BULK_ONLY_MASS_STORAGE_RESET:
              {
                  /* Status stage */
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
                  HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_STSDONEIEN_Msk);
                  break;
              }
              case SET_REPORT:
              {
                  if(((gUsbCmd.wValue >> 8) & 0xff) == 3)
                  {
                      /* Request Type = Feature */
                      HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
                      HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
                      HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_STSDONEIEN_Msk);
                  }
                  break;
              }
              case SET_IDLE:
              {
                  /* Status stage */
                  HSUSBD_CLR_CEP_INT_FLAG(HSUSBD_CEPINTSTS_STSDONEIF_Msk);
                  HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_NAKCLR);
                  HSUSBD_ENABLE_CEP_INT(HSUSBD_CEPINTEN_STSDONEIEN_Msk);
                  break;
              }
              case SET_PROTOCOL:
              default:
              {
                  // Stall
                  /* Setup error, stall the device */
                  HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_STALLEN_Msk);
                  break;
              }
          }
      }
  }
  
  /*----------------------------------------------------------------------------*/
  void MSC_RequestSense(void)
  {
      memset((uint8_t *)(g_u32MassBase), 0, 18);
      if(g_u8Prevent)
      {
          g_u8Prevent = 0;
          *(uint8_t *)(g_u32MassBase) = 0x70;
      }
      else
          *(uint8_t *)(g_u32MassBase) = 0xf0;
  
      *(uint8_t *)(g_u32MassBase + 2) = g_au8SenseKey[0];
      *(uint8_t *)(g_u32MassBase + 7) = 0x0a;
      *(uint8_t *)(g_u32MassBase + 12) = g_au8SenseKey[1];
      *(uint8_t *)(g_u32MassBase + 13) = g_au8SenseKey[2];
      MSC_BulkIn(g_u32MassBase, g_sCBW.dCBWDataTransferLength);
  
      g_au8SenseKey[0] = 0;
      g_au8SenseKey[1] = 0;
      g_au8SenseKey[2] = 0;
  }
  
  void MSC_ReadFormatCapacity(void)
  {
      memset((uint8_t *)g_u32MassBase, 0, 36);
  
      *((uint8_t *)(g_u32MassBase + 3)) = 0x10;
      *((uint8_t *)(g_u32MassBase + 4)) = *((uint8_t *)&g_TotalSectors + 3);
      *((uint8_t *)(g_u32MassBase + 5)) = *((uint8_t *)&g_TotalSectors + 2);
      *((uint8_t *)(g_u32MassBase + 6)) = *((uint8_t *)&g_TotalSectors + 1);
      *((uint8_t *)(g_u32MassBase + 7)) = *((uint8_t *)&g_TotalSectors + 0);
      *((uint8_t *)(g_u32MassBase + 8)) = 0x02;
      *((uint8_t *)(g_u32MassBase + 10)) = 0x02;
      *((uint8_t *)(g_u32MassBase + 12)) = *((uint8_t *)&g_TotalSectors + 3);
      *((uint8_t *)(g_u32MassBase + 13)) = *((uint8_t *)&g_TotalSectors + 2);
      *((uint8_t *)(g_u32MassBase + 14)) = *((uint8_t *)&g_TotalSectors + 1);
      *((uint8_t *)(g_u32MassBase + 15)) = *((uint8_t *)&g_TotalSectors + 0);
      *((uint8_t *)(g_u32MassBase + 18)) = 0x02;
  
      MSC_BulkIn(g_u32MassBase, g_sCBW.dCBWDataTransferLength);
  }
  
  void MSC_ReadCapacity(void)
  {
      uint32_t tmp;
  
      memset((uint8_t *)g_u32MassBase, 0, 36);
  
      tmp = g_TotalSectors - 1;
      *((uint8_t *)(g_u32MassBase + 0)) = *((uint8_t *)&tmp + 3);
      *((uint8_t *)(g_u32MassBase + 1)) = *((uint8_t *)&tmp + 2);
      *((uint8_t *)(g_u32MassBase + 2)) = *((uint8_t *)&tmp + 1);
      *((uint8_t *)(g_u32MassBase + 3)) = *((uint8_t *)&tmp + 0);
      *((uint8_t *)(g_u32MassBase + 6)) = 0x02;
  
      MSC_BulkIn(g_u32MassBase, g_sCBW.dCBWDataTransferLength);
  }
  
  void MSC_ModeSense10(void)
  {
      uint8_t i, j;
      uint8_t NumHead, NumSector;
      uint16_t NumCyl = 0;
  
      /* Clear the command buffer */
      *((uint32_t *)g_u32MassBase) = 0;
      *((uint32_t *)g_u32MassBase + 1) = 0;
  
      switch(g_sCBW.au8Data[0])
      {
          case 0x01:
              *((uint8_t *)g_u32MassBase) = 19;
              i = 8;
              for(j = 0; j < 12; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_01[j];
              break;
  
          case 0x05:
              *((uint8_t *)g_u32MassBase) = 39;
              i = 8;
              for(j = 0; j < 32; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_05[j];
  
              NumHead = 2;
              NumSector = 64;
              NumCyl = g_TotalSectors / 128;
  
              *((uint8_t *)(g_u32MassBase + 12)) = NumHead;
              *((uint8_t *)(g_u32MassBase + 13)) = NumSector;
              *((uint8_t *)(g_u32MassBase + 16)) = (uint8_t)(NumCyl >> 8);
              *((uint8_t *)(g_u32MassBase + 17)) = (uint8_t)(NumCyl & 0x00ff);
              break;
  
          case 0x1B:
              *((uint8_t *)g_u32MassBase) = 19;
              i = 8;
              for(j = 0; j < 12; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_1B[j];
              break;
  
          case 0x1C:
              *((uint8_t *)g_u32MassBase) = 15;
              i = 8;
              for(j = 0; j < 8; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_1C[j];
              break;
  
          case 0x3F:
              *((uint8_t *)g_u32MassBase) = 0x47;
              i = 8;
              for(j = 0; j < 12; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_01[j];
              for(j = 0; j < 32; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_05[j];
              for(j = 0; j < 12; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_1B[j];
              for(j = 0; j < 8; j++, i++)
                  *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage_1C[j];
  
              NumHead = 2;
              NumSector = 64;
              NumCyl = g_TotalSectors / 128;
  
              *((uint8_t *)(g_u32MassBase + 24)) = NumHead;
              *((uint8_t *)(g_u32MassBase + 25)) = NumSector;
              *((uint8_t *)(g_u32MassBase + 28)) = (uint8_t)(NumCyl >> 8);
              *((uint8_t *)(g_u32MassBase + 29)) = (uint8_t)(NumCyl & 0x00ff);
              break;
  
          default:
              g_au8SenseKey[0] = 0x05;
              g_au8SenseKey[1] = 0x24;
              g_au8SenseKey[2] = 0x00;
      }
      MSC_BulkIn(g_u32MassBase, g_sCBW.dCBWDataTransferLength);
  }
  
  void MSC_ModeSense6(void)
  {
      uint8_t i;
  
      for(i = 0; i < 4; i++)
          *((uint8_t *)(g_u32MassBase + i)) = g_au8ModePage[i];
  
      MSC_BulkIn(g_u32MassBase, g_sCBW.dCBWDataTransferLength);
  }
  
  void MSC_BulkOut(uint32_t u32Addr, uint32_t u32Len)
  {
      uint32_t u32Loop;
      uint32_t i;
  
      /* bulk out, dma write, epnum = 4 */
      HSUSBD_SET_DMA_WRITE(BULK_OUT_EP_NUM);
      g_hsusbd_ShortPacket = 0;
  
      u32Loop = u32Len / USBD_MAX_DMA_LEN;
      for(i = 0; i < u32Loop; i++)
      {
          MSC_ActiveDMA(u32Addr + i * USBD_MAX_DMA_LEN, USBD_MAX_DMA_LEN);
      }
  
      u32Loop = u32Len % USBD_MAX_DMA_LEN;
      if(u32Loop)
      {
          MSC_ActiveDMA(u32Addr + i * USBD_MAX_DMA_LEN, u32Loop);
      }
  }
  
  void MSC_BulkIn(uint32_t u32Addr, uint32_t u32Len)
  {
      uint32_t u32Loop;
      uint32_t i, addr, count;
  
      /* bulk in, dma read, epnum = 3 */
      HSUSBD_SET_DMA_READ(BULK_IN_EP_NUM);
  
      u32Loop = u32Len / USBD_MAX_DMA_LEN;
      for(i = 0; i < u32Loop; i++)
      {
          HSUSBD_ENABLE_EP_INT(EPC, HSUSBD_EPINTEN_TXPKIEN_Msk);
          g_hsusbd_ShortPacket = 0;
          while(1)
          {
              if(HSUSBD_GET_EP_INT_FLAG(EPC) & HSUSBD_EPINTSTS_BUFEMPTYIF_Msk)
              {
                  MSC_ActiveDMA(u32Addr + i * USBD_MAX_DMA_LEN, USBD_MAX_DMA_LEN);
                  break;
              }
          }
      }
  
      addr = u32Addr + i * USBD_MAX_DMA_LEN;
      u32Loop = u32Len % USBD_MAX_DMA_LEN;
      if(u32Loop)
      {
          count = u32Loop / g_u32EpMaxPacketSize;
          if(count)
          {
              HSUSBD_ENABLE_EP_INT(EPC, HSUSBD_EPINTEN_TXPKIEN_Msk);
              g_hsusbd_ShortPacket = 0;
              while(1)
              {
                  if(HSUSBD_GET_EP_INT_FLAG(EPC) & HSUSBD_EPINTSTS_BUFEMPTYIF_Msk)
                  {
                      MSC_ActiveDMA(addr, count * g_u32EpMaxPacketSize);
                      break;
                  }
              }
              addr += (count * g_u32EpMaxPacketSize);
          }
          count = u32Loop % g_u32EpMaxPacketSize;
          if(count)
          {
              HSUSBD_ENABLE_EP_INT(EPC, HSUSBD_EPINTEN_TXPKIEN_Msk);
              g_hsusbd_ShortPacket = 1;
              while(1)
              {
                  if(HSUSBD_GET_EP_INT_FLAG(EPC) & HSUSBD_EPINTSTS_BUFEMPTYIF_Msk)
                  {
                      MSC_ActiveDMA(addr, count);
                      break;
                  }
              }
          }
      }
  }
  
  
  void MSC_ReceiveCBW(uint32_t u32Buf)
  {
      /* bulk out, dma write, epnum = 4 */
      HSUSBD_SET_DMA_WRITE(BULK_OUT_EP_NUM);
  
      /* Enable BUS interrupt */
      HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_DMADONEIEN_Msk | HSUSBD_BUSINTEN_SUSPENDIEN_Msk | HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_VBUSDETIEN_Msk);
  
      HSUSBD_SET_DMA_ADDR(u32Buf);
      HSUSBD_SET_DMA_LEN(31);
  
      g_hsusbd_DmaDone = 0;
      HSUSBD_ENABLE_DMA();
  }
  
  void MSC_ProcessCmd(void)
  {
      uint32_t i;
  
      if(g_u8BulkState == BULK_NORMAL)
      {
          g_u8BulkState = BULK_OUT;
          MSC_ReceiveCBW(g_u32MassBase);
      }
  
      if(g_u8BulkState == BULK_CBW)
      {
          /* Check Signature of CBW */
          if((*(uint32_t *)(g_u32MassBase) != CBW_SIGNATURE))
          {
              g_u8BulkState = BULK_NORMAL;
              return;
          }
  
          /* Get the CBW */
          for(i = 0; i < 31; i++)
              *((uint8_t *)(&g_sCBW.dCBWSignature) + i) = *(uint8_t *)(g_u32MassBase + i);
  
          /* Prepare to echo the tag from CBW to CSW */
          g_sCSW.dCSWTag = g_sCBW.dCBWTag;
  
          /* Parse Op-Code of CBW */
          switch(g_sCBW.u8OPCode)
          {
              case UFI_READ_10:
              {
                  /* Get LBA address */
                  g_u32LbaAddress = get_be32(&g_sCBW.au8Data[0]) * USBD_SECTOR_SIZE;
                  //MSC_ReadMedia(g_u32LbaAddress, g_sCBW.dCBWDataTransferLength, (uint8_t *)g_u32StorageBase);
  
                  MSC_BulkIn(g_u32StorageBase + g_u32LbaAddress, g_sCBW.dCBWDataTransferLength);
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_WRITE_10:
              {
                  g_u32LbaAddress = get_be32(&g_sCBW.au8Data[0]) * USBD_SECTOR_SIZE;
                  MSC_BulkOut(g_u32StorageBase + g_u32LbaAddress, g_sCBW.dCBWDataTransferLength);
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_PREVENT_ALLOW_MEDIUM_REMOVAL:
              {
                  if(g_sCBW.au8Data[2] & 0x01)
                  {
                      g_au8SenseKey[0] = 0x05;  //INVALID COMMAND
                      g_au8SenseKey[1] = 0x24;
                      g_au8SenseKey[2] = 0;
                      g_u8Prevent = 1;
                  }
                  else
                      g_u8Prevent = 0;
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_VERIFY_10:
              case UFI_START_STOP:
              case UFI_TEST_UNIT_READY:
              {
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_REQUEST_SENSE:
              {
                  MSC_RequestSense();
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_READ_FORMAT_CAPACITY:
              {
                  MSC_ReadFormatCapacity();
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_READ_CAPACITY:
              {
                  MSC_ReadCapacity();
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_MODE_SELECT_10:
              {
                  MSC_BulkOut(g_u32StorageBase, g_sCBW.dCBWDataTransferLength);
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_MODE_SENSE_10:
              {
                  MSC_ModeSense10();
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_MODE_SENSE_6:
              {
                  MSC_ModeSense6();
                  MSC_AckCmd(0);
                  break;
              }
              case UFI_INQUIRY:
              {
                  /* Bulk IN buffer */
                  HSUSBD_MemCopy((uint8_t *)(g_u32MassBase), (uint8_t *)g_au8InquiryID, 36);
                  MSC_BulkIn(g_u32MassBase, g_sCBW.dCBWDataTransferLength);
                  MSC_AckCmd(0);
                  break;
              }
              default:
              {
                  /* Unsupported command */
                  g_au8SenseKey[0] = 0x05;
                  g_au8SenseKey[1] = 0x20;
                  g_au8SenseKey[2] = 0x00;
  
                  /* If CBW request for data phase, just return zero packet to end data phase */
                  if(g_sCBW.dCBWDataTransferLength > 0)
                      MSC_AckCmd(g_sCBW.dCBWDataTransferLength);
                  else
                      MSC_AckCmd(0);
              }
          }
      }
  }
  
  void MSC_ActiveDMA(uint32_t u32Addr, uint32_t u32Len)
  {
      /* Enable BUS interrupt */
      HSUSBD_ENABLE_BUS_INT(HSUSBD_BUSINTEN_DMADONEIEN_Msk | HSUSBD_BUSINTEN_SUSPENDIEN_Msk | HSUSBD_BUSINTEN_RSTIEN_Msk | HSUSBD_BUSINTEN_VBUSDETIEN_Msk);
  
      HSUSBD_SET_DMA_ADDR(u32Addr);
      HSUSBD_SET_DMA_LEN(u32Len);
      g_hsusbd_DmaDone = 0;
      HSUSBD_ENABLE_DMA();
      while(g_u8MscStart)
      {
          if(g_hsusbd_DmaDone)
              break;
  
          if(!HSUSBD_IS_ATTACHED())
              break;
      }
  }
  
  void MSC_AckCmd(uint32_t u32Residue)
  {
      g_sCSW.dCSWDataResidue = u32Residue;
      g_sCSW.bCSWStatus = g_u8Prevent;
      HSUSBD_MemCopy((uint8_t *)g_u32MassBase, (uint8_t *)&g_sCSW.dCSWSignature, 16);
      MSC_BulkIn(g_u32MassBase, 13);
      g_u8BulkState = BULK_NORMAL;
  }
  
  void MSC_ReadMedia(uint32_t addr, uint32_t size, uint8_t *buffer)
  {
  }
  
  void MSC_WriteMedia(uint32_t addr, uint32_t size, uint8_t *buffer)
  {
  }
  
  /*----------------------------------------------------------------------------*/
  void HID_VendorRequest(void)
  {
      if(gUsbCmd.bmRequestType & 0x80)    /* request data transfer direction */
      {
          // Device to host
          switch(gUsbCmd.bRequest)
          {
              default:
              {
                  /* Setup error, stall the device */
                  HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_STALLEN_Msk);
                  break;
              }
          }
      }
      else
      {
          // Host to device
          switch(gUsbCmd.bRequest)
          {
              default:
              {
                  // Stall
                  /* Setup error, stall the device */
                  HSUSBD_SET_CEP_STATE(HSUSBD_CEPCTL_STALLEN_Msk);
                  break;
              }
          }
      }
  }
  
  /***************************************************************/
  #define HID_CMD_SIGNATURE   0x43444948
  
  /* HID Transfer Commands */
  #define HID_CMD_NONE     0x00
  #define HID_CMD_ERASE    0x71
  #define HID_CMD_READ     0xD2
  #define HID_CMD_WRITE    0xC3
  #define HID_CMD_TEST     0xB4
  
  #define PAGE_SIZE        512
  
  #ifdef __ICCARM__
  typedef __packed struct
  {
      uint8_t u8Cmd;
      uint8_t u8Size;
      uint32_t u32Arg1;
      uint32_t u32Arg2;
      uint32_t u32Signature;
      uint32_t u32Checksum;
  } CMD_T;
  
  #else
  typedef struct __attribute__((__packed__))
  {
      uint8_t u8Cmd;
      uint8_t u8Size;
      uint32_t u32Arg1;
      uint32_t u32Arg2;
      uint32_t u32Signature;
      uint32_t u32Checksum;
  }
  CMD_T;
  #endif
  
  CMD_T gCmd;
  
  #ifdef __ICCARM__
  #pragma data_alignment=4
  static uint8_t  g_u8PageBuff[PAGE_SIZE] = {0};    /* Page buffer to upload/download through HID report */
  static uint32_t g_u32BytesInPageBuf = 0;          /* The bytes of data in g_u8PageBuff */
  uint8_t  g_u8OutBuff[EPB_MAX_PKT_SIZE] = {0};
  #else
  static uint8_t  g_u8PageBuff[PAGE_SIZE] __attribute__((aligned(4))) = {0};    /* Page buffer to upload/download through HID report */
  static uint32_t g_u32BytesInPageBuf __attribute__((aligned(4))) = 0;          /* The bytes of data in g_u8PageBuff */
  uint8_t  g_u8OutBuff[EPB_MAX_PKT_SIZE] __attribute__((aligned(4))) = {0};
  #endif
  
  
  int32_t HID_CmdEraseSectors(CMD_T *pCmd)
  {
      uint32_t u32StartSector;
      uint32_t u32Sectors;
      uint32_t i;
  
      u32StartSector = pCmd->u32Arg1;
      u32Sectors = pCmd->u32Arg2;
  
      printf("Erase command - Sector: %d   Sector Cnt: %d\n", u32StartSector, u32Sectors);
  
      for(i = 0; i < u32Sectors; i++)
      {
          //printf("Erase sector - %d\n",u32StartSector + i);
          /* TODO: To erase the sector of storage */
  
      }
      /* To note the command has been done */
      pCmd->u8Cmd = HID_CMD_NONE;
  
      return 0;
  }
  
  
  int32_t HID_CmdReadPages(CMD_T *pCmd)
  {
      uint32_t u32StartPage;
      uint32_t u32Pages;
      uint32_t len;
      int32_t i;
  
      u32StartPage = pCmd->u32Arg1;
      u32Pages     = pCmd->u32Arg2;
  
      printf("Read command - Start page: %d    Pages Numbers: %d\n", u32StartPage, u32Pages);
  
      if(u32Pages)
      {
          /* Update data to page buffer to upload */
          /* TODO: We need to update the page data if got a page read command. (0xFF is used in this sample code) */
          for(i = 0; i < PAGE_SIZE; i++)
              g_u8PageBuff[i] = 0xFF;
          g_u32BytesInPageBuf = PAGE_SIZE;
  
          /* The signature word is used as page counter */
          pCmd->u32Signature = 1;
  
          /* Trigger HID IN */
          len = Minimum(g_u32EpAMaxPacketSize, g_u32BytesInPageBuf);
          for(i = 0; i < len; i++)
              HSUSBD->EP[EPA].EPDAT_BYTE = g_u8PageBuff[i];
          HSUSBD->EP[EPA].EPTXCNT = len;
          HSUSBD_ENABLE_EP_INT(EPA, HSUSBD_EPINTEN_INTKIEN_Msk);
          g_u32BytesInPageBuf -= len;
      }
  
      return 0;
  }
  
  
  int32_t HID_CmdWritePages(CMD_T *pCmd)
  {
      uint32_t u32StartPage;
      uint32_t u32Pages;
  
      u32StartPage = pCmd->u32Arg1;
      u32Pages     = pCmd->u32Arg2;
  
      printf("Write command - Start page: %d    Pages Numbers: %d\n", u32StartPage, u32Pages);
      g_u32BytesInPageBuf = 0;
  
      /* The signature is used to page counter */
      pCmd->u32Signature = 0;
      return 0;
  }
  
  
  int32_t gi32CmdTestCnt = 0;
  int32_t HID_CmdTest(CMD_T *pCmd)
  {
      int32_t i;
      uint8_t *pu8;
  
      pu8 = (uint8_t *)pCmd;
      printf("Get test command #%d (%d bytes)\n", gi32CmdTestCnt++, pCmd->u8Size);
      for(i = 0; i < pCmd->u8Size; i++)
      {
          if((i & 0xF) == 0)
          {
              printf("\n");
          }
          printf(" %02x", pu8[i]);
      }
  
      printf("\n");
  
  
      /* To note the command has been done */
      pCmd->u8Cmd = HID_CMD_NONE;
  
      return 0;
  }
  
  
  uint32_t CalCheckSum(uint8_t *buf, uint32_t size)
  {
      uint32_t sum;
      int32_t i;
  
      i = 0;
      sum = 0;
      while(size--)
      {
          sum += buf[i++];
      }
  
      return sum;
  
  }
  
  
  int32_t ProcessCommand(uint8_t *pu8Buffer, uint32_t u32BufferLen)
  {
      uint32_t u32sum;
  
  
      HSUSBD_MemCopy((uint8_t *)&gCmd, pu8Buffer, u32BufferLen);
  
      /* Check size */
      if((gCmd.u8Size > sizeof(gCmd)) || (gCmd.u8Size > u32BufferLen))
          return -1;
  
      /* Check signature */
      if(gCmd.u32Signature != HID_CMD_SIGNATURE)
          return -1;
  
      /* Calculate checksum & check it*/
      u32sum = CalCheckSum((uint8_t *)&gCmd, gCmd.u8Size);
      if(u32sum != gCmd.u32Checksum)
          return -1;
  
      switch(gCmd.u8Cmd)
      {
          case HID_CMD_ERASE:
          {
              HID_CmdEraseSectors(&gCmd);
              break;
          }
          case HID_CMD_READ:
          {
              HID_CmdReadPages(&gCmd);
              break;
          }
          case HID_CMD_WRITE:
          {
              HID_CmdWritePages(&gCmd);
              break;
          }
          case HID_CMD_TEST:
          {
              HID_CmdTest(&gCmd);
              break;
          }
          default:
              return -1;
      }
  
      return 0;
  }
  
  
  void HID_GetOutReport(uint8_t *pu8EpBuf, uint32_t u32Size)
  {
      uint32_t volatile i;
      uint8_t  u8Cmd;
      uint32_t u32StartPage;
      uint32_t u32Pages;
      uint32_t u32PageCnt;
  
      /* Get command information */
      u8Cmd        = gCmd.u8Cmd;
      u32StartPage = gCmd.u32Arg1;
      u32Pages     = gCmd.u32Arg2;
      u32PageCnt   = gCmd.u32Signature; /* The signature word is used to count pages */
  
      /* Check if it is in the data phase of write command */
      if((u8Cmd == HID_CMD_WRITE) && (u32PageCnt < u32Pages))
      {
          /* Process the data phase of write command */
  
          /* Get data from HID OUT */
          HSUSBD_MemCopy(&g_u8PageBuff[g_u32BytesInPageBuf], pu8EpBuf, u32Size);
          g_u32BytesInPageBuf += u32Size;
  
          /* The HOST must make sure the data is PAGE_SIZE alignment */
          if(g_u32BytesInPageBuf >= PAGE_SIZE)
          {
              printf("Writing page %d\n", u32StartPage + u32PageCnt);
              /* TODO: We should program received data to storage here */
              u32PageCnt++;
  
              /* Write command complete! */
              if(u32PageCnt >= u32Pages)
              {
                  u8Cmd = HID_CMD_NONE;
  
                  printf("Write command complete.\n");
              }
  
              g_u32BytesInPageBuf = 0;
  
          }
  
          /* Update command status */
          gCmd.u8Cmd        = u8Cmd;
          gCmd.u32Signature = u32PageCnt;
      }
      else
      {
          /* Check and process the command packet */
          if(ProcessCommand(pu8EpBuf, u32Size))
          {
              printf("Unknown HID command!\n");
          }
      }
  }
  
  void HID_SetInReport(void)
  {
      uint32_t u32StartPage;
      uint32_t u32TotalPages;
      uint32_t u32PageCnt;
      uint32_t len;
      int32_t i;
      uint8_t u8Cmd;
  
      u8Cmd        = gCmd.u8Cmd;
      u32StartPage = gCmd.u32Arg1;
      u32TotalPages = gCmd.u32Arg2;
      u32PageCnt   = gCmd.u32Signature;
  
      /* Check if it is in data phase of read command */
      if(u8Cmd == HID_CMD_READ)
      {
          /* Process the data phase of read command */
          if((u32PageCnt >= u32TotalPages) && (g_u32BytesInPageBuf == 0))
          {
              /* The data transfer is complete. */
              u8Cmd = HID_CMD_NONE;
              printf("Read command complete!\n");
          }
          else
          {
              if(g_u32BytesInPageBuf == 0)
              {
                  /* The previous page has sent out. Read new page to page buffer */
                  /* TODO: We should update new page data here. (0xFF is used in this sample code) */
                  printf("Reading page %d\n", u32StartPage + u32PageCnt);
                  for(i = 0; i < PAGE_SIZE; i++)
                      g_u8PageBuff[i] = 0xFF;
  
                  g_u32BytesInPageBuf = PAGE_SIZE;
  
                  /* Update the page counter */
                  u32PageCnt++;
              }
  
              /* Prepare the data for next HID IN transfer */
              len = Minimum(g_u32EpAMaxPacketSize, g_u32BytesInPageBuf);
              for(i = 0; i < len; i++)
                  HSUSBD->EP[EPA].EPDAT_BYTE = g_u8PageBuff[PAGE_SIZE - g_u32BytesInPageBuf];
              HSUSBD->EP[EPA].EPTXCNT = len;
              HSUSBD_ENABLE_EP_INT(EPA, HSUSBD_EPINTEN_INTKIEN_Msk);
              g_u32BytesInPageBuf -= len;
          }
      }
  
      gCmd.u8Cmd        = u8Cmd;
      gCmd.u32Signature = u32PageCnt;
  
  }
  