text: /*
   * Copyright (c) 2015, Freescale Semiconductor, Inc.
   * Copyright 2016 NXP
   * All rights reserved.
   *
   * SPDX-License-Identifier: BSD-3-Clause
   */
  
  #include <stdbool.h>
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  #include "usb_device_config.h"
  #include "usb.h"
  #include "usb_device.h"
  
  #include "usb_device_class.h"
  #include "usb_device_phdc.h"
  
  #include "usb_device_ch9.h"
  #include "usb_device_descriptor.h"
  #include "ieee11073_types.h"
  #include "ieee11073_timer.h"
  #include "ieee11073_agent.h"
  #include "usb_shim_agent.h"
  
  /*******************************************************************************
   * Definitions
   ******************************************************************************/
  
  /*******************************************************************************
   * Prototypes
   ******************************************************************************/
  
  /*******************************************************************************
   * Variables
   ******************************************************************************/
  
  #if META_DATA_MESSAGE_PREAMBLE_IMPLEMENTED
  /*! @brief the string used to give preamble verifiability */
  static char metaDataMsgPreambleSignature[16U] = "PhdcQoSSignature";
  #endif
  extern usb_shim_agent_struct_t g_shimAgent;
  
  /*******************************************************************************
   * Code
   ******************************************************************************/
  
  /*!
   * @brief USB shim agent receive data callback function.
   *
   * This function implements a queue to support receiving the PHDC data with length is more than
   * maximum of endpoint packet size. Then notify to upper layer when receiving is all completed.
   *
   * @param handle                The device handle.
   * @param param                 The param of receive callback function.
   *
   * @return A USB error code or kStatus_USB_Success.
   */
  usb_status_t USB_ShimAgentRecvComplete(void *handle, void *param)
  {
      usb_device_endpoint_callback_message_struct_t *message = (usb_device_endpoint_callback_message_struct_t *)param;
  
      /* endpoint callback length is USB_CANCELLED_TRANSFER_LENGTH (0xFFFFFFFFU) when transfer is canceled */
      if ((!message->length) || (USB_CANCELLED_TRANSFER_LENGTH == message->length))
      {
          /* Prepare for the next receiving */
          USB_DevicePhdcRecv(g_shimAgent.classHandle, g_shimAgent.bulkOutData.epNumber, g_shimAgent.recvDataBuffer,
                             g_shimAgent.bulkOutData.epMaxPacketSize);
          return kStatus_USB_Success;
      }
  #if META_DATA_MESSAGE_PREAMBLE_IMPLEMENTED
      if (1U == g_shimAgent.isMetaDataMessagePreambleEnabled)
      {
          /* The meta-data message preamble feature is enabled, then all data transfers or sets
          of data transfers shall be preceded by a meta-data message preamble transfer. The
          numberTransferBulkOut is initialized as zero for receiving this message preamble data,
          then it is updated to the value of bNumTransfers field of message preamble data */
          if (g_shimAgent.numberTransferBulkOut)
          {
              /* When numberTransferBulkOut reduces to 0, a new meta-data message preamble shall
              be transferred */
              g_shimAgent.numberTransferBulkOut--;
              AGENT_Callback(handle, SHIM_AGENT_EVENT_RECV_OPAQUE_DATA, (uint8_t *)(message->buffer), message->length);
              return kStatus_USB_Success;
          }
          else
          {
              uint8_t preambleSignatureChecking = 1U;
              /* The received packet is meta-data message preamble */
              usb_shim_metadata_preamble_t *metaDataMsgPreamble = (usb_shim_metadata_preamble_t *)message->buffer;
              /* Meta-data message preamble signature checking */
              for (uint8_t i = 0U; i < METADATA_PREAMBLE_SIGNATURE; i++)
              {
                  if ((metaDataMsgPreamble->aSignature[i]) != metaDataMsgPreambleSignature[i])
                  {
                      preambleSignatureChecking = 0U;
                      break;
                  }
              }
              if (preambleSignatureChecking)
              {
                  /* Checks if the meta-data message preamble contains an invalid bmLatencyReliability value
                  or bNumTransfers value */
                  if ((!(metaDataMsgPreamble->bNumberTransfers)) ||            /* bNumTransfers shall never equal zero */
                      (metaDataMsgPreamble->bQosEncodingVersion != 0x01U) ||   /* Encoding version should be 0x01 */
                      ((metaDataMsgPreamble->bmLatencyReliability != 0x02U) && /* Medium.Good latency, reliability bin */
                       (metaDataMsgPreamble->bmLatencyReliability !=
                        0x04U) && /* Medium.Better latency, reliability bin */
                       (metaDataMsgPreamble->bmLatencyReliability != 0x08U) && /* Medium.Best latency, reliability bin */
                       (metaDataMsgPreamble->bmLatencyReliability != 0x10U) && /* High.Best latency, reliability bin */
                       (metaDataMsgPreamble->bmLatencyReliability != 0x20U) /* VeryHigh.Best latency, reliability bin */))
                  {
                      /* The device shall stall subsequent transaction to the receiving endpoint */
                      return kStatus_USB_InvalidRequest;
                  }
                  else
                  {
                      /* The meta-data message preamble data is correct, update the phdc status and numberTransferBulkOut
                       * value */
                      g_shimAgent.numberTransferBulkOut = metaDataMsgPreamble->bNumberTransfers;
                      AGENT_Callback(handle, SHIM_AGENT_EVENT_RECV_MESSAGE_PREAMBLE, (uint8_t *)(message->buffer),
                                     message->length);
                      return kStatus_USB_Success;
                  }
              }
              else
              {
                  /* The device shall stall subsequent transaction to the receiving endpoint */
                  return kStatus_USB_InvalidRequest;
              }
          }
      }
  #endif
      if (NULL == g_shimAgent.bulkOutData.recvData.buffer)
      {
          /* Save the length of the first received data block */
          g_shimAgent.bulkOutData.transferCount = message->length;
          g_shimAgent.bulkOutData.recvData.transferSize =
              (uint32_t)(USB_SHORT_FROM_BIG_ENDIAN(*((uint16_t *)message->buffer + 1U)) + 4U /* APDU header */);
          g_shimAgent.bulkOutData.recvData.buffer = &g_shimAgent.recvDataBuffer[0];
          /* Save the received data */
          memcpy(g_shimAgent.bulkOutData.recvData.buffer, message->buffer, message->length);
      }
      else
      {
          /* update the transferred data length */
          g_shimAgent.bulkOutData.transferCount += message->length;
      }
      if (g_shimAgent.bulkOutData.transferCount == g_shimAgent.bulkOutData.recvData.transferSize)
      {
          /* Receive all the data */
          AGENT_Callback(handle, SHIM_AGENT_EVENT_RECV_COMPLETE, g_shimAgent.bulkOutData.recvData.buffer,
                         g_shimAgent.bulkOutData.recvData.transferSize);
          g_shimAgent.bulkOutData.transferCount         = 0;
          g_shimAgent.bulkOutData.recvData.transferSize = 0;
          g_shimAgent.bulkOutData.recvData.buffer       = NULL;
          /* Prepare for the next receiving */
          USB_DevicePhdcRecv(g_shimAgent.classHandle, g_shimAgent.bulkOutData.epNumber, g_shimAgent.recvDataBuffer,
                             g_shimAgent.bulkOutData.epMaxPacketSize);
      }
      else
      {
          /* The data is still pending for receiving */
          (void)USB_DevicePhdcRecv(
              handle, g_shimAgent.bulkOutData.epNumber,
              (uint8_t *)(g_shimAgent.bulkOutData.recvData.buffer + g_shimAgent.bulkOutData.transferCount),
              g_shimAgent.bulkOutData.epMaxPacketSize);
      }
      return kStatus_USB_Success;
  }
  
  /*!
   * @brief USB shim agent send data callback function.
   *
   * This function is the callback function of USB shim agent send function, it is implemented
   * to continue sending the data in sending queue if the queue is not empty.
   *
   * @param handle                he device handle.
   * @param param                 The param of receive callback function.
   *
   * @return A USB error code or kStatus_USB_Success.
   */
  usb_status_t USB_ShimAgentSendComplete(void *handle, uint32_t event, void *param)
  {
      usb_device_endpoint_callback_message_struct_t *message = (usb_device_endpoint_callback_message_struct_t *)param;
      usb_shim_tx_data_struct_t *sentData                    = NULL;
      if (kUSB_DevicePhdcEventInterruptInSendComplete == event)
      {
          sentData = &g_shimAgent.interruptInData;
      }
      else
      {
          sentData = &g_shimAgent.bulkInData;
      }
      g_shimAgent.endpointsHaveData &= (uint16_t)(~(uint16_t)(1U << sentData->epNumber));
      /* notify the application of the send complete */
      AGENT_Callback(handle, SHIM_AGENT_EVENT_SENT_COMPLETE, (uint8_t *)message->buffer, message->length);
      /* de-queue the queue */
      sentData->buyer += 1U;
      if (sentData->buyer != sentData->seller)
      {
          /* Set bit map for the corresponding endpoint*/
          g_shimAgent.endpointsHaveData |= (uint16_t)(1U << sentData->epNumber);
          USB_DevicePhdcSend(handle, sentData->epNumber,
                             sentData->sendData[sentData->buyer % SHIM_AGENT_MAX_QUEUE_NUMBER].buffer,
                             sentData->sendData[sentData->buyer % SHIM_AGENT_MAX_QUEUE_NUMBER].transferSize);
      }
      return kStatus_USB_Success;
  }
  
  /*!
   * @brief USB shim agent send data function.
   *
   * This function implements a queue to support sending multiple transfer request.
   *
   * @param handle                The device handle.
   * @param metaData              The flag to check the data to send is meta data or not.
   * @param numberTransfer        The number of transfer following the meta data message preamble
   *                              when sending data is meta data.
   * @param qos                   The current QoS of the transfer.
   * @param appBuffer             The data buffer.
   * @param size                  The length of the transfer.
   *
   * @return A USB error code or kStatus_USB_Success.
   */
  usb_status_t USB_ShimAgentSendData(void *handle, uint8_t qos, uint8_t *appBuffer, uint32_t size)
  {
      usb_status_t status                   = kStatus_USB_Success;
      usb_shim_tx_data_struct_t *dataToSend = NULL;
      if (qos != 0x01U /* low latency/good reliability */)
      {
  #if META_DATA_MESSAGE_PREAMBLE_IMPLEMENTED
          if (1U == g_shimAgent.isMetaDataMessagePreambleEnabled)
          {
              /* The meta-data message preamble feature is enabled, then all data transfers or sets
              of data transfers shall be preceded by a meta-data message preamble transfer. The
              numberTransferBulkIn is initialized as zero for sending this message preamble data,
              then it is updated to the value of bNumTransfers field of message preamble data */
              if (g_shimAgent.numberTransferBulkOut)
              {
                  /* When numberTransferBulkIn reduces to 0, a new meta-data message preamble shall
                  be transferred */
                  g_shimAgent.numberTransferBulkIn--;
              }
              {
                  uint8_t latencyReliability = ((usb_shim_metadata_preamble_t *)appBuffer)->bmLatencyReliability;
                  /* Latency reliability validity checking */
                  if ((latencyReliability != 0x02U) && /* Medium.Good latency, reliability bin */
                      (latencyReliability != 0x04U) && /* Medium.Better latency, reliability bin */
                      (latencyReliability != 0x08U) && /* Medium.Best latency, reliability bin */
                      (latencyReliability != 0x10U) && /* High.Best latency, reliability bin */
                      (latencyReliability != 0x20U) /* VeryHigh.Best latency, reliability bin */)
                  {
                      status = kStatus_USB_InvalidRequest;
                      usb_echo("USB_ShimAgentSendData: Error invalid LatencyReliability");
                  }
                  /* LatencyReliablity checking */
                  if (0U == (USB_PHDC_WEIGHT_SCALE_BULK_IN_QOS & ~latencyReliability))
                  {
                      status = kStatus_USB_Error;
                      usb_echo(
                          "USB_ShimAgentSendData: Error the latency reliability is not supported by Bulk IN endpoint");
                  }
                  if (0U == ((usb_shim_metadata_preamble_t *)appBuffer)->bNumberTransfers)
                  {
                      status = kStatus_USB_Error;
                      usb_echo("USB_ShimAgentSendData: Error the numTransfer should never zero");
                  }
                  /* Update the number of bulk in transfer */
                  g_shimAgent.numberTransferBulkIn = ((usb_shim_metadata_preamble_t *)appBuffer)->bNumberTransfers;
              }
          }
  #endif
          /* Initialize the data to send */
          dataToSend = &g_shimAgent.bulkInData;
      }
      else
      {
          /* Initialize the data to send */
          dataToSend = &g_shimAgent.interruptInData;
      }
      if (kStatus_USB_Success == status)
      {
          /* Set bit map for the corresponding endpoint*/
          g_shimAgent.endpointsHaveData |= (uint16_t)(1U << dataToSend->epNumber);
          /* Add data to send to sending queue */
          if ((uint8_t)(dataToSend->seller - dataToSend->buyer) < (uint8_t)(SHIM_AGENT_MAX_QUEUE_NUMBER))
          {
              dataToSend->sendData[dataToSend->seller % SHIM_AGENT_MAX_QUEUE_NUMBER].transferSize = size;
              dataToSend->sendData[dataToSend->seller % SHIM_AGENT_MAX_QUEUE_NUMBER].buffer       = appBuffer;
              /* increase queue number by 1 */
              dataToSend->seller += 1U;
              /* send the first entry of queue */
              if (1U == (uint32_t)(dataToSend->seller - dataToSend->buyer))
              {
                  status = USB_DevicePhdcSend(handle, dataToSend->epNumber, appBuffer, size);
              }
          }
          else
          {
              /* sending queue is full */
              status = kStatus_USB_Busy;
          }
      }
      return status;
  }
  