text: /*
   * Copyright (c) 2007-2015 Freescale Semiconductor, Inc.
   * Copyright 2018-2019 NXP
   *
   * SPDX-License-Identifier: BSD-3-Clause
   *
   * FreeMASTER Communication Driver - USB Example Implementation
   */
  
  #include "freemaster.h"
  #include "freemaster_serial_usb.h"
  
  #include <stdio.h>
  #include <stdlib.h>
  
  #include "usb_device_config.h"
  #include "usb.h"
  #include "usb_device.h"
  
  #include "usb_device_class.h"
  #include "usb_device_cdc_acm.h"
  #include "usb_device_ch9.h"
  
  #include "freemaster_usb_device_descriptor.h"
  #include "freemaster_usb.h"
  
  #if (defined(FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT > 0U))
  #include "fsl_sysmpu.h"
  #endif /* FSL_FEATURE_SOC_SYSMPU_COUNT */
  
  #if defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0)
  #include "usb_phy.h"
  #endif
  
  /* Currently configured line coding */
  #define LINE_CODING_SIZE (0x07)
  #define LINE_CODING_DTERATE (115200)
  #define LINE_CODING_CHARFORMAT (0x00)
  #define LINE_CODING_PARITYTYPE (0x00)
  #define LINE_CODING_DATABITS (0x08)
  
  /* Communications feature */
  #define COMM_FEATURE_DATA_SIZE (0x02)
  #define STATUS_ABSTRACT_STATE (0x0000)
  #define COUNTRY_SETTING (0x0000)
  
  /* Notification of serial state */
  #define NOTIF_PACKET_SIZE (0x08)
  #define UART_BITMAP_SIZE (0x02)
  #define NOTIF_REQUEST_TYPE (0xA1)
  
  /******************************************************************************
  * Local functions
  ******************************************************************************/
  
  static void USB_DeviceIsrEnable(void);
  void USB0_IRQHandler(void);
  
  static FMSTR_BOOL _FMSTR_UsbTxFunction(FMSTR_U8 * data, FMSTR_U32 size);
  
  static usb_status_t USB_DeviceCdcVcomCallback(class_handle_t handle, uint32_t event, void *param);
  static usb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void *param);
  /******************************************************************************
  * Type definitions
  ******************************************************************************/
  
  /* Define the information relates to abstract control model */
  typedef struct _usb_cdc_acm_info
  {
      uint8_t serialStateBuf[NOTIF_PACKET_SIZE + UART_BITMAP_SIZE]; /* Serial state buffer of the CDC device to notify the
                                                                       serial state to host. */
      bool dtePresent;          /* A flag to indicate whether DTE is present.         */
      uint16_t breakDuration;   /* Length of time in milliseconds of the break signal */
      uint8_t dteStatus;        /* Status of data terminal equipment                  */
      uint8_t currentInterface; /* Current interface index.                           */
      uint16_t uartState;       /* UART state of the CDC device.                      */
  } usb_cdc_acm_info_t;
  
  typedef struct
  {
      usb_device_handle   deviceHandle;           /* USB device handle. */
      class_handle_t      cdcAcmHandle;           /* USB CDC ACM class handle.                                                         */
      volatile uint8_t    attach;                 /* A flag to indicate whether a usb device is attached. 1: attached, 0: not attached */
      uint8_t             speed;                  /* Speed of USB device. USB_SPEED_FULL/USB_SPEED_LOW/USB_SPEED_HIGH.                 */
      uint8_t             currentConfiguration;   /* Current configuration value. */
      uint8_t             currentInterfaceAlternateSetting[USB_CDC_VCOM_INTERFACE_COUNT]; /* Current alternate setting value for each interface. */
      volatile uint8_t    startTransactions;      /* A flag to indicate whether a CDC device is ready to transmit and receive data.    */
      usb_cdc_acm_info_t s_usbCdcAcmInfo; /* CDC ACM information */
  
      /* Data buffer for receiving and sending */
      uint8_t             s_currRecvBuf[USB_DATA_BUFF_SIZE];
      uint32_t            s_recvSize;
  
  } FMSTR_EXAMPLE_USB_CTX;
  
  /******************************************************************************
  * Local variables
  ******************************************************************************/
  
  /* Line coding of cdc device */
  USB_DMA_INIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t s_lineCoding[LINE_CODING_SIZE] = {
      /* E.g. 0x00,0xC2,0x01,0x00 : 0x0001C200 is 115200 bits per second */
      (LINE_CODING_DTERATE >> 0U) & 0x000000FFU,
      (LINE_CODING_DTERATE >> 8U) & 0x000000FFU,
      (LINE_CODING_DTERATE >> 16U) & 0x000000FFU,
      (LINE_CODING_DTERATE >> 24U) & 0x000000FFU,
      LINE_CODING_CHARFORMAT,
      LINE_CODING_PARITYTYPE,
      LINE_CODING_DATABITS};
  
  /* Abstract state of cdc device */
  USB_DMA_INIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t s_abstractState[COMM_FEATURE_DATA_SIZE] = {(STATUS_ABSTRACT_STATE >> 0U) & 0x00FFU,
                                                            (STATUS_ABSTRACT_STATE >> 8U) & 0x00FFU};
  
  /* Country code of cdc device */
  USB_DMA_INIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t s_countryCode[COMM_FEATURE_DATA_SIZE] = {(COUNTRY_SETTING >> 0U) & 0x00FFU,
                                                          (COUNTRY_SETTING >> 8U) & 0x00FFU};
  
  extern usb_device_endpoint_struct_t g_UsbDeviceCdcVcomDicEndpoints[];
  extern usb_device_class_struct_t g_UsbDeviceCdcVcomConfig;
  
  /* USB device class information */
  static usb_device_class_config_struct_t s_cdcAcmConfig[1] = {{
      USB_DeviceCdcVcomCallback, 0, &g_UsbDeviceCdcVcomConfig,
  }};
  
  /* USB device class configuration information */
  static usb_device_class_config_list_struct_t s_cdcAcmConfigList = {
      s_cdcAcmConfig, USB_DeviceCallback, 1,
  };
  
  /* context data that this application needs to handle USB communication */
  static FMSTR_EXAMPLE_USB_CTX _fmstr_exampleUsbCtx;
  
  /******************************************************************************
  * API functions
  ******************************************************************************/
  
  /* Initializiation of the USB stack */
  FMSTR_BOOL FMSTR_ExampleUsbInit(void)
  {
      memset(&_fmstr_exampleUsbCtx, 0, sizeof(_fmstr_exampleUsbCtx));
  
      #if (defined(FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT > 0U))
      SYSMPU_Enable(SYSMPU, 0);
      #endif /* FSL_FEATURE_SOC_SYSMPU_COUNT */
  
      _fmstr_exampleUsbCtx.speed = USB_SPEED_FULL;
      _fmstr_exampleUsbCtx.attach = 0;
      _fmstr_exampleUsbCtx.cdcAcmHandle = (class_handle_t)NULL;
      _fmstr_exampleUsbCtx.deviceHandle = NULL;
  
      if (kStatus_USB_Success != USB_DeviceClassInit(CONTROLLER_ID, &s_cdcAcmConfigList, &_fmstr_exampleUsbCtx.deviceHandle))
      {
          return FMSTR_FALSE;
      }
      else
      {
          _fmstr_exampleUsbCtx.cdcAcmHandle = s_cdcAcmConfigList.config->classHandle;
      }
  
      USB_DeviceIsrEnable();
  
      USB_DeviceRun(_fmstr_exampleUsbCtx.deviceHandle);
  
      FMSTR_SerialUsbRegisterAppFunctions(_FMSTR_UsbTxFunction);
  
      return FMSTR_TRUE;
  }
  
  /******************************************************************************
  * Local functions
  ******************************************************************************/
  
  /* This function is called from FreeMASTER, when any data to send. */
  static FMSTR_BOOL _FMSTR_UsbTxFunction(FMSTR_U8 * data, FMSTR_U32 size)
  {
      usb_status_t error = kStatus_USB_Error;
  
      //assert(data != NULL);
  
      /* Check if USB attached */
      if(_fmstr_exampleUsbCtx.attach != 1)
          return FMSTR_FALSE;
  
      /* Send data via USB CDC */
      error = USB_DeviceCdcAcmSend(_fmstr_exampleUsbCtx.cdcAcmHandle, USB_CDC_VCOM_BULK_IN_ENDPOINT, data, size);
      if (error != kStatus_USB_Success)
          return FMSTR_FALSE;
  
      return FMSTR_TRUE;
  }
  
  static usb_status_t USB_DeviceCdcVcomCallback(class_handle_t handle, uint32_t event, void *param)
  {
      uint32_t len;
      uint8_t *uartBitmap;
      usb_device_cdc_acm_request_param_struct_t *acmReqParam;
      usb_device_endpoint_callback_message_struct_t *epCbParam;
      usb_status_t error = kStatus_USB_Error;
      usb_cdc_acm_info_t *acmInfo = &_fmstr_exampleUsbCtx.s_usbCdcAcmInfo;
      acmReqParam = (usb_device_cdc_acm_request_param_struct_t *)param;
      epCbParam = (usb_device_endpoint_callback_message_struct_t *)param;
      switch (event)
      {
          case kUSB_DeviceCdcEventSendResponse:
          {
              if ((epCbParam->length != 0) && (!(epCbParam->length % g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize)))
              {
                  /* If the last packet is the size of endpoint, then send also zero-ended packet,
                   ** meaning that we want to inform the host that we do not have any additional
                   ** data, so it can flush the output.
                   */
                  error = USB_DeviceCdcAcmSend(handle, USB_CDC_VCOM_BULK_IN_ENDPOINT, NULL, 0);
              }
              else if ((1 == _fmstr_exampleUsbCtx.attach))
              {
                  if ((epCbParam->buffer != NULL) || ((epCbParam->buffer == NULL) && (epCbParam->length == 0)))
                  {
                      /* User: add your own code for send complete event */
                      /* Schedule buffer for next receive event */
                      error = USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, _fmstr_exampleUsbCtx.s_currRecvBuf,
                                                   g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize);
                  }
              }
              else
              {
              }
              /* Call Freemaster interrupt */
              FMSTR_SerialUsbProcessEvent(FMSTR_SERIAL_USBCDC_EVENT_TYPE_SENT, NULL, 0);
          }
          break;
          case kUSB_DeviceCdcEventRecvResponse:
          {
              if ((1 == _fmstr_exampleUsbCtx.attach))
              {
                  _fmstr_exampleUsbCtx.s_recvSize = epCbParam->length;
                  /* Call Freemaster interrupt with received data */
                  FMSTR_SerialUsbProcessEvent(FMSTR_SERIAL_USBCDC_EVENT_TYPE_RECEIVED, (FMSTR_U8*)_fmstr_exampleUsbCtx.s_currRecvBuf, _fmstr_exampleUsbCtx.s_recvSize);
                  _fmstr_exampleUsbCtx.s_recvSize = 0;
                  /* Schedule buffer for next receive event */
                  error = USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, _fmstr_exampleUsbCtx.s_currRecvBuf,
                                               g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize);
              }
          }
          break;
          case kUSB_DeviceCdcEventSerialStateNotif:
              ((usb_device_cdc_acm_struct_t *)handle)->hasSentState = 0;
              error = kStatus_USB_Success;
              break;
          case kUSB_DeviceCdcEventSendEncapsulatedCommand:
              break;
          case kUSB_DeviceCdcEventGetEncapsulatedResponse:
              break;
          case kUSB_DeviceCdcEventSetCommFeature:
              if (USB_DEVICE_CDC_FEATURE_ABSTRACT_STATE == acmReqParam->setupValue)
              {
                  if (1 == acmReqParam->isSetup)
                  {
                      *(acmReqParam->buffer) = s_abstractState;
                  }
                  else
                  {
                      *(acmReqParam->length) = 0;
                  }
              }
              else if (USB_DEVICE_CDC_FEATURE_COUNTRY_SETTING == acmReqParam->setupValue)
              {
                  if (1 == acmReqParam->isSetup)
                  {
                      *(acmReqParam->buffer) = s_countryCode;
                  }
                  else
                  {
                      *(acmReqParam->length) = 0;
                  }
              }
              else
              {
              }
              error = kStatus_USB_Success;
              break;
          case kUSB_DeviceCdcEventGetCommFeature:
              if (USB_DEVICE_CDC_FEATURE_ABSTRACT_STATE == acmReqParam->setupValue)
              {
                  *(acmReqParam->buffer) = s_abstractState;
                  *(acmReqParam->length) = COMM_FEATURE_DATA_SIZE;
              }
              else if (USB_DEVICE_CDC_FEATURE_COUNTRY_SETTING == acmReqParam->setupValue)
              {
                  *(acmReqParam->buffer) = s_countryCode;
                  *(acmReqParam->length) = COMM_FEATURE_DATA_SIZE;
              }
              else
              {
              }
              error = kStatus_USB_Success;
              break;
          case kUSB_DeviceCdcEventClearCommFeature:
              break;
          case kUSB_DeviceCdcEventGetLineCoding:
              *(acmReqParam->buffer) = s_lineCoding;
              *(acmReqParam->length) = LINE_CODING_SIZE;
              error = kStatus_USB_Success;
              break;
          case kUSB_DeviceCdcEventSetLineCoding:
          {
              if (1 == acmReqParam->isSetup)
              {
                  *(acmReqParam->buffer) = s_lineCoding;
              }
              else
              {
                  *(acmReqParam->length) = 0;
              }
          }
              error = kStatus_USB_Success;
              break;
          case kUSB_DeviceCdcEventSetControlLineState:
          {
              _fmstr_exampleUsbCtx.s_usbCdcAcmInfo.dteStatus = acmReqParam->setupValue;
              /* activate/deactivate Tx carrier */
              if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_CARRIER_ACTIVATION)
              {
                  acmInfo->uartState |= USB_DEVICE_CDC_UART_STATE_TX_CARRIER;
              }
              else
              {
                  acmInfo->uartState &= (uint16_t)~USB_DEVICE_CDC_UART_STATE_TX_CARRIER;
              }
  
              /* activate carrier and DTE. Com port of terminal tool running on PC is open now */
              if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_DTE_PRESENCE)
              {
                  acmInfo->uartState |= USB_DEVICE_CDC_UART_STATE_RX_CARRIER;
              }
              /* Com port of terminal tool running on PC is closed now */
              else
              {
                  acmInfo->uartState &= (uint16_t)~USB_DEVICE_CDC_UART_STATE_RX_CARRIER;
              }
  
              /* Indicates to DCE if DTE is present or not */
              acmInfo->dtePresent = (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_DTE_PRESENCE) ? true : false;
  
              /* Initialize the serial state buffer */
              acmInfo->serialStateBuf[0] = NOTIF_REQUEST_TYPE;                /* bmRequestType */
              acmInfo->serialStateBuf[1] = USB_DEVICE_CDC_NOTIF_SERIAL_STATE; /* bNotification */
              acmInfo->serialStateBuf[2] = 0x00;                              /* wValue */
              acmInfo->serialStateBuf[3] = 0x00;
              acmInfo->serialStateBuf[4] = 0x00; /* wIndex */
              acmInfo->serialStateBuf[5] = 0x00;
              acmInfo->serialStateBuf[6] = UART_BITMAP_SIZE; /* wLength */
              acmInfo->serialStateBuf[7] = 0x00;
              /* Notify to host the line state */
              acmInfo->serialStateBuf[4] = acmReqParam->interfaceIndex;
              /* Lower byte of UART BITMAP */
              uartBitmap = (uint8_t *)&acmInfo->serialStateBuf[NOTIF_PACKET_SIZE + UART_BITMAP_SIZE - 2];
              uartBitmap[0] = acmInfo->uartState & 0xFFu;
              uartBitmap[1] = (acmInfo->uartState >> 8) & 0xFFu;
              len = (uint32_t)(NOTIF_PACKET_SIZE + UART_BITMAP_SIZE);
              if (0 == ((usb_device_cdc_acm_struct_t *)handle)->hasSentState)
              {
                  error = USB_DeviceCdcAcmSend(handle, USB_CDC_VCOM_INTERRUPT_IN_ENDPOINT, acmInfo->serialStateBuf, len);
                  if (kStatus_USB_Success != error)
                  {
                      usb_echo("kUSB_DeviceCdcEventSetControlLineState error!");
                  }
                  ((usb_device_cdc_acm_struct_t *)handle)->hasSentState = 1;
              } else
              {
                  usb_echo("kUSB_DeviceCdcEventSetControlLineState has no sent state!.\n");
                  error = kStatus_USB_Success;
              }
  
              /* Update status */
              if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_CARRIER_ACTIVATION)
              {
                  /*  To do: CARRIER_ACTIVATED */
              }
              else
              {
                  /* To do: CARRIER_DEACTIVATED */
              }
              if (acmInfo->dteStatus & USB_DEVICE_CDC_CONTROL_SIG_BITMAP_DTE_PRESENCE)
              {
                  /* DTE_ACTIVATED */
                  if (1 == _fmstr_exampleUsbCtx.attach)
                  {
                      _fmstr_exampleUsbCtx.startTransactions = 1;
  #if defined(FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED) && (FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED > 0U) && \
      defined(USB_DEVICE_CONFIG_KEEP_ALIVE_MODE) && (USB_DEVICE_CONFIG_KEEP_ALIVE_MODE > 0U) &&             \
      defined(FSL_FEATURE_USB_KHCI_USB_RAM) && (FSL_FEATURE_USB_KHCI_USB_RAM > 0U)
                      s_waitForDataReceive = 1;
                      USB0->INTEN &= ~USB_INTEN_SOFTOKEN_MASK;
                      s_comOpen = 1;
                      usb_echo("USB_APP_CDC_DTE_ACTIVATED\r\n");
  #endif
                  }
              }
              else
              {
                  /* DTE_DEACTIVATED */
                  if (1 == _fmstr_exampleUsbCtx.attach)
                  {
                      _fmstr_exampleUsbCtx.startTransactions = 0;
                  }
              }
          }
          break;
          case kUSB_DeviceCdcEventSendBreak:
              break;
          default:
              break;
      }
  
      if(error && error != kStatus_USB_Busy)
          usb_echo("USB Error: %d, event: %d.\n", error, event);
  
      return error;
  }
  
  static usb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void *param)
  {
      usb_status_t error = kStatus_USB_Error;
      uint16_t *temp16 = (uint16_t *)param;
      uint8_t *temp8 = (uint8_t *)param;
  
      switch (event)
      {
          case kUSB_DeviceEventBusReset:
          {
              _fmstr_exampleUsbCtx.attach = 0;
              _fmstr_exampleUsbCtx.currentConfiguration = 0U;
  #if (defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U)) || \
      (defined(USB_DEVICE_CONFIG_LPCIP3511HS) && (USB_DEVICE_CONFIG_LPCIP3511HS > 0U))
              /* Get USB speed to configure the device, including max packet size and interval of the endpoints. */
              if (kStatus_USB_Success == USB_DeviceClassGetSpeed(CONTROLLER_ID, &_fmstr_exampleUsbCtx.speed))
              {
                  USB_DeviceSetSpeed(handle, _fmstr_exampleUsbCtx.speed);
              }
  #endif
          }
          break;
          case kUSB_DeviceEventSetConfiguration:
              if (0U ==(*temp8))
              {
                  _fmstr_exampleUsbCtx.attach = 0;
                  _fmstr_exampleUsbCtx.currentConfiguration = 0U;
              }
              else if (USB_CDC_VCOM_CONFIGURE_INDEX == (*temp8))
              {
                  _fmstr_exampleUsbCtx.attach = 1;
                  _fmstr_exampleUsbCtx.currentConfiguration = *temp8;
                  /* Schedule buffer for receive */
                  USB_DeviceCdcAcmRecv(_fmstr_exampleUsbCtx.cdcAcmHandle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, _fmstr_exampleUsbCtx.s_currRecvBuf,
                                       g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize);
              }
              else
              {
                  error = kStatus_USB_InvalidRequest;
              }
              break;
          case kUSB_DeviceEventSetInterface:
              if (_fmstr_exampleUsbCtx.attach)
              {
                  uint8_t interface = (uint8_t)((*temp16 & 0xFF00U) >> 0x08U);
                  uint8_t alternateSetting = (uint8_t)(*temp16 & 0x00FFU);
                  if (interface < USB_CDC_VCOM_INTERFACE_COUNT)
                  {
                      _fmstr_exampleUsbCtx.currentInterfaceAlternateSetting[interface] = alternateSetting;
                  }
              }
              break;
          case kUSB_DeviceEventGetConfiguration:
              break;
          case kUSB_DeviceEventGetInterface:
              break;
          case kUSB_DeviceEventGetDeviceDescriptor:
              if (param)
              {
                  error = USB_DeviceGetDeviceDescriptor(handle, (usb_device_get_device_descriptor_struct_t *)param);
              }
              break;
          case kUSB_DeviceEventGetConfigurationDescriptor:
              if (param)
              {
                  error = USB_DeviceGetConfigurationDescriptor(handle,
                                                               (usb_device_get_configuration_descriptor_struct_t *)param);
              }
              break;
          case kUSB_DeviceEventGetStringDescriptor:
              if (param)
              {
                  /* Get device string descriptor request */
                  error = USB_DeviceGetStringDescriptor(handle, (usb_device_get_string_descriptor_struct_t *)param);
              }
              break;
          default:
              break;
      }
  
      return error;
  }
  
  void USB_MCU_INT_HANDLER(void)
  {
      USB_INTERRUPT_HANDLER(_fmstr_exampleUsbCtx.deviceHandle);
  
  #ifdef __CORE_CM4_H_GENERIC
      /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
      exception return operation might vector to incorrect interrupt */
      __DSB();
  #endif
  }
  
  static void USB_DeviceIsrEnable(void)
  {
      uint8_t irqNumber;
      uint8_t usbDeviceIrq[] = USB_IRQS_LIST;
  
      irqNumber = usbDeviceIrq[USB_CONTROLLER_IX];
  
      /* Install isr, set priority, and enable IRQ. */
      NVIC_SetPriority((IRQn_Type)irqNumber, USB_DEVICE_INTERRUPT_PRIORITY);
      EnableIRQ((IRQn_Type)irqNumber);
  
  }
  