/**
  ******************************************************************************
  * @file    usbd_billboard.c
  * @author  MCD Application Team
  * @brief   This file provides the high layer firmware functions to manage the
  *          following functionalities of the USB BillBoard Class:
  *           - Initialization and Configuration of high and low layer
  *           - Enumeration as BillBoard Device
  *           - Error management
  * @verbatim
  *
  *          ===================================================================
  *                                BillBoard Class Description
  *          ===================================================================
  *           This module manages the BillBoard class V1.2.1 following the "Device Class Definition
  *           for BillBoard Devices (BB) Version R1.2.1 Sept 08, 2016".
  *           This driver implements the following aspects of the specification:
  *             - Device descriptor management
  *             - Configuration descriptor management
  *             - Enumeration as an USB BillBoard device
  *             - Enumeration & management of BillBoard device supported alternate modes
  *
  *  @endverbatim
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "usbd_billboard.h"
#include "usbd_ctlreq.h"


/** @addtogroup STM32_USB_DEVICE_LIBRARY
  * @{
  */


/** @defgroup USBD_BB
  * @brief usbd core module
  * @{
  */

/** @defgroup USBD_BB_Private_TypesDefinitions
  * @{
  */
/**
  * @}
  */


/** @defgroup USBD_BB_Private_Defines
  * @{
  */

/**
  * @}
  */


/** @defgroup USBD_BB_Private_Macros
  * @{
  */
/**
  * @}
  */


/** @defgroup USBD_BB_Private_FunctionPrototypes
  * @{
  */

static uint8_t USBD_BB_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t USBD_BB_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t USBD_BB_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
static uint8_t USBD_BB_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t USBD_BB_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t USBD_BB_EP0_RxReady(USBD_HandleTypeDef *pdev);

static uint8_t *USBD_BB_GetCfgDesc(uint16_t *length);
static uint8_t *USBD_BB_GetDeviceQualifierDesc(uint16_t *length);
static uint8_t *USBD_BB_GetOtherSpeedCfgDesc(uint16_t *length);

#if (USBD_CLASS_BOS_ENABLED == 1)
USBD_BB_DescHeader_t *USBD_BB_GetNextDesc(uint8_t *pbuf, uint16_t *ptr);
#endif



/**
  * @}
  */

/** @defgroup USBD_BB_Private_Variables
  * @{
  */
USBD_ClassTypeDef  USBD_BB =
{
  USBD_BB_Init,             /* Init */
  USBD_BB_DeInit,           /* DeInit */
  USBD_BB_Setup,            /* Setup */
  NULL,                     /* EP0_TxSent */
  USBD_BB_EP0_RxReady,      /* EP0_RxReady */
  USBD_BB_DataIn,           /* DataIn */
  USBD_BB_DataOut,          /* DataOut */
  NULL,                     /* SOF */
  NULL,
  NULL,
  USBD_BB_GetCfgDesc,
  USBD_BB_GetCfgDesc,
  USBD_BB_GetOtherSpeedCfgDesc,
  USBD_BB_GetDeviceQualifierDesc,
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
  NULL,
#endif
};

/* USB Standard Device Qualifier Descriptor */
__ALIGN_BEGIN static uint8_t USBD_BB_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC]  __ALIGN_END =
{
  USB_LEN_DEV_QUALIFIER_DESC,     /* bLength */
  USB_DESC_TYPE_DEVICE_QUALIFIER, /* bDescriptorType */
  0x01,                           /* bcdUSB */
  0x20,
  0x11,                           /* bDeviceClass */
  0x00,                           /* bDeviceSubClass */
  0x00,                           /* bDeviceProtocol */
  0x40,                           /* bMaxPacketSize0 */
  0x01,                           /* bNumConfigurations */
  0x00,                           /* bReserved */
};

/* USB device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_BB_CfgDesc[USB_BB_CONFIG_DESC_SIZ]  __ALIGN_END =
{
  0x09,                        /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
  USB_BB_CONFIG_DESC_SIZ,      /* wTotalLength: Bytes returned */
  0x00,
  0x01,                        /* bNumInterfaces: 1 interface */
  0x01,                        /* bConfigurationValue: Configuration value */
  USBD_IDX_CONFIG_STR,         /* iConfiguration: Index of string descriptor describing the configuration */
#if (USBD_SELF_POWERED == 1U)
  0xC0,                        /* bmAttributes: Bus Powered according to user configuration */
#else
  0x80,                        /* bmAttributes: Bus Powered according to user configuration */
#endif
  USBD_MAX_POWER,              /* MaxPower 100 mA: this current is used for detecting Vbus */
  /* 09 */

  /************** Descriptor of BillBoard interface ****************/
  /* 09 */
  0x09,                        /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,     /* bDescriptorType: Interface descriptor type */
  0x00,                        /* bInterfaceNumber: Number of Interface */
  0x00,                        /* bAlternateSetting: Alternate setting */
  0x00,                        /* bNumEndpoints */
  0x11,                        /* bInterfaceClass: billboard */
  0x00,                        /* bInterfaceSubClass */
  0x00,                        /* nInterfaceProtocol */
  USBD_BB_IF_STRING_INDEX,     /* iInterface: Index of string descriptor */
};

/* USB device Other Speed Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_BB_OtherSpeedCfgDesc[USB_BB_CONFIG_DESC_SIZ]   __ALIGN_END  =
{
  0x09,                        /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION,
  USB_BB_CONFIG_DESC_SIZ,
  0x00,
  0x01,                        /* bNumInterfaces: 1 interface */
  0x01,                        /* bConfigurationValue: */
  USBD_IDX_CONFIG_STR,         /* iConfiguration: */
#if (USBD_SELF_POWERED == 1U)
  0xC0,                        /* bmAttributes: Bus Powered according to user configuration */
#else
  0x80,                        /* bmAttributes: Bus Powered according to user configuration */
#endif
  USBD_MAX_POWER,              /* MaxPower 100 mA */

  /************** Descriptor of BillBoard interface ****************/
  /* 09 */
  0x09,                        /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,     /* bDescriptorType: Interface descriptor type */
  0x00,                        /* bInterfaceNumber: Number of Interface */
  0x00,                        /* bAlternateSetting: Alternate setting */
  0x00,                        /* bNumEndpoints*/
  0x11,                        /* bInterfaceClass: billboard */
  0x00,                        /* bInterfaceSubClass */
  0x00,                        /* nInterfaceProtocol */
  USBD_BB_IF_STRING_INDEX,     /* iInterface: Index of string descriptor */
} ;

/**
  * @}
  */

/** @defgroup USBD_BB_Private_Functions
  * @{
  */

/**
  * @brief  USBD_BB_Init
  *         Initialize the BB interface
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
static uint8_t USBD_BB_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);
  UNUSED(cfgidx);

  return (uint8_t)USBD_OK;
}

/**
  * @brief  USBD_BB_Init
  *         DeInitialize the BB layer
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
static uint8_t USBD_BB_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);
  UNUSED(cfgidx);

  return (uint8_t)USBD_OK;
}

/**
  * @brief  USBD_BB_Setup
  *         Handle the BB specific requests
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
static uint8_t USBD_BB_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
  USBD_StatusTypeDef ret = USBD_OK;
  uint16_t status_info = 0U;
  uint16_t AltSetting = 0U;

  switch (req->bmRequest & USB_REQ_TYPE_MASK)
  {
    case USB_REQ_TYPE_CLASS:
      break;
    case USB_REQ_TYPE_STANDARD:
      switch (req->bRequest)
      {
        case USB_REQ_GET_STATUS:
          if (pdev->dev_state == USBD_STATE_CONFIGURED)
          {
            (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U);
          }
          else
          {
            USBD_CtlError(pdev, req);
            ret = USBD_FAIL;
          }
          break;

        case USB_REQ_GET_INTERFACE:
          if (pdev->dev_state == USBD_STATE_CONFIGURED)
          {
            (void)USBD_CtlSendData(pdev, (uint8_t *)&AltSetting, 1U);
          }
          else
          {
            USBD_CtlError(pdev, req);
            ret = USBD_FAIL;
          }
          break;

        case USB_REQ_SET_INTERFACE:
        case USB_REQ_CLEAR_FEATURE:
          break;

        default:
          USBD_CtlError(pdev, req);
          ret = USBD_FAIL;
          break;
      }
      break;

    default:
      USBD_CtlError(pdev, req);
      ret = USBD_FAIL;
      break;
  }

  return (uint8_t)ret;
}

/**
  * @brief  USBD_BB_DataIn
  *         Data sent on non-control IN endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
static uint8_t USBD_BB_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);
  UNUSED(epnum);

  return (uint8_t)USBD_OK;
}

/**
  * @brief  USBD_BB_DataOut
  *         Data received on non-control Out endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
static uint8_t USBD_BB_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);
  UNUSED(epnum);

  return (uint8_t)USBD_OK;
}

/**
  * @brief  USBD_BB_EP0_RxReady
  *         Handle EP0 Rx Ready event
  * @param  pdev: device instance
  * @retval status
  */
static uint8_t USBD_BB_EP0_RxReady(USBD_HandleTypeDef *pdev)
{
  /* Prevent unused argument compilation warning */
  UNUSED(pdev);

  return (uint8_t)USBD_OK;
}

/**
  * @brief  USBD_BB_GetCfgDesc
  *         return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t *USBD_BB_GetCfgDesc(uint16_t *length)
{
  *length = (uint16_t)sizeof(USBD_BB_CfgDesc);
  return USBD_BB_CfgDesc;
}

/**
  * @brief  USBD_BB_GetOtherSpeedCfgDesc
  *         return other speed configuration descriptor
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
uint8_t *USBD_BB_GetOtherSpeedCfgDesc(uint16_t *length)
{
  *length = (uint16_t)sizeof(USBD_BB_OtherSpeedCfgDesc);
  return USBD_BB_OtherSpeedCfgDesc;
}

/**
  * @brief  DeviceQualifierDescriptor
  *         return Device Qualifier descriptor
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t *USBD_BB_GetDeviceQualifierDesc(uint16_t *length)
{
  *length = (uint16_t)sizeof(USBD_BB_DeviceQualifierDesc);
  return USBD_BB_DeviceQualifierDesc;
}


#if (USBD_CLASS_BOS_ENABLED == 1U)
/**
  * @brief  USBD_BB_GetNextDesc
  *         This function return the next descriptor header
  * @param  buf: Buffer where the descriptor is available
  * @param  ptr: data pointer inside the descriptor
  * @retval next header
  */
USBD_BB_DescHeader_t *USBD_BB_GetNextDesc(uint8_t *pbuf, uint16_t *ptr)
{
  USBD_BB_DescHeader_t *pnext = (USBD_BB_DescHeader_t *)(void *)pbuf;

  *ptr += pnext->bLength;
  pnext = (USBD_BB_DescHeader_t *)(void *)(pbuf + pnext->bLength);

  return (pnext);
}


/**
  * @brief  USBD_BB_GetCapDesc
  *         This function return the Billboard Capability descriptor
  * @param  pdev: device instance
  * @param  pBosDesc:  pointer to Bos descriptor
  * @retval pointer to Billboard Capability descriptor
  */
void *USBD_BB_GetCapDesc(USBD_HandleTypeDef *pdev, uint8_t *pBosDesc)
{
  UNUSED(pdev);

  USBD_BB_DescHeader_t *pdesc = (USBD_BB_DescHeader_t *)(void *)pBosDesc;
  USBD_BosDescTypedef *desc = (USBD_BosDescTypedef *)(void *)pBosDesc;
  USBD_BosBBCapDescTypedef *pCapDesc = NULL;
  uint16_t ptr;

  if (desc->wTotalLength > desc->bLength)
  {
    ptr = desc->bLength;

    while (ptr < desc->wTotalLength)
    {
      pdesc = USBD_BB_GetNextDesc((uint8_t *)pdesc, &ptr);

      if (pdesc->bDevCapabilityType == USBD_BILLBOARD_CAPABILITY)
      {
        pCapDesc = (USBD_BosBBCapDescTypedef *)(void *)pdesc;
        break;
      }
    }
  }
  return (void *)pCapDesc;
}


/**
  * @brief  USBD_BB_GetAltModeDesc
  *         This function return the Billboard Alternate Mode descriptor
  * @param  pdev: device instance
  * @param  pBosDesc:  pointer to Bos descriptor
  * @param  idx:  Index of requested Alternate Mode descriptor
  * @retval pointer to Alternate Mode descriptor
  */
void *USBD_BB_GetAltModeDesc(USBD_HandleTypeDef *pdev, uint8_t *pBosDesc, uint8_t idx)
{
  UNUSED(pdev);

  USBD_BB_DescHeader_t *pdesc = (USBD_BB_DescHeader_t *)(void *)pBosDesc;
  USBD_BosDescTypedef *desc = (USBD_BosDescTypedef *)(void *)pBosDesc;
  USBD_BB_AltModeCapDescTypeDef *pAltModDesc = NULL;
  uint8_t cnt = 0U;
  uint16_t ptr;

  if (desc->wTotalLength > desc->bLength)
  {
    ptr = desc->bLength;

    while (ptr < desc->wTotalLength)
    {
      pdesc = USBD_BB_GetNextDesc((uint8_t *)pdesc, &ptr);

      if (pdesc->bDevCapabilityType == USBD_BILLBOARD_ALTMODE_CAPABILITY)
      {
        if (cnt == idx)
        {
          pAltModDesc = (USBD_BB_AltModeCapDescTypeDef *)(void *)pdesc;
          break;
        }
        else
        {
          cnt++;
        }
      }
    }
  }
  return (void *)pAltModDesc;
}
#endif

/**
  * @}
  */


/**
  * @}
  */


/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
