/*!
 * @file        usbd_descriptor.c
 *
 * @brief       usb device descriptor configuration
 *
 * @version     V1.0.0
 *
 * @date        2025-02-15
 *
 * @attention
 *
 *  Copyright (C) 2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes ***************************************************************/
#include "usbd_descriptor.h"

/* Private includes *******************************************************/
#include "usbd_customhid.h"
#include <stdio.h>
#include <string.h>

/* Private macro **********************************************************/
#define USBD_GEEHY_VID              12619
#define USBD_HS_PID                 0x107
#define USBD_LANGID_STR             0x0409
#define USBD_MANUFACTURER_STR       "Geehy"
#define USBD_PRODUCT_STR            "APM32 HID"
#define USBD_CONFIGURATION_STR      "HID Config"
#define USBD_INTERFACE_STR          "HID Interface"

/* Private function prototypes ********************************************/
static USBD_DESC_INFO_T USBD_HS_DeviceDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_ConfigDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_ConfigStrDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_InterfaceStrDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_LangIdStrDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_ManufacturerStrDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_ProductStrDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_HS_SerialStrDescHandler(uint8_t usbSpeed);
#if USBD_SUP_LPM
static USBD_DESC_INFO_T USBD_HS_BosDescHandler(uint8_t usbSpeed);
#endif
static USBD_DESC_INFO_T USBD_OtherSpeedConfigDescHandler(uint8_t usbSpeed);
static USBD_DESC_INFO_T USBD_DevQualifierDescHandler(uint8_t usbSpeed);

/* Private typedef ********************************************************/

/* USB device descripotr handler */
USBD_DESC_T USBD_DESC_HS =
{
    "Custom HID Descriptor",
    USBD_HS_DeviceDescHandler,
    USBD_HS_ConfigDescHandler,
    USBD_HS_ConfigStrDescHandler,
    USBD_HS_InterfaceStrDescHandler,
    USBD_HS_LangIdStrDescHandler,
    USBD_HS_ManufacturerStrDescHandler,
    USBD_HS_ProductStrDescHandler,
    USBD_HS_SerialStrDescHandler,
#if USBD_SUP_LPM
    USBD_HS_BosDescHandler,
#endif
    NULL,
    USBD_OtherSpeedConfigDescHandler,
    USBD_DevQualifierDescHandler,
};

/* Private variables ******************************************************/

/*!
 * @brief   Device descriptor
 */
uint8_t USBD_DeviceDesc[USBD_DEVICE_DESCRIPTOR_SIZE] =
{
    /* bLength */
    0x12,
    /* bDescriptorType */
    USBD_DESC_DEVICE,
    /* bcdUSB */
#if USBD_SUP_LPM
    0x01,            /*<! For resume test of USBCV3.0. Only support LPM USB device */
#else
    0x00,
#endif
    0x02,
    /* bDeviceClass */
    0x00,
    /* bDeviceSubClass */
    0x00,
    /* bDeviceProtocol */
    0x00,
    /* bMaxPacketSize */
    USBD_EP0_PACKET_MAX_SIZE,
    /* idVendor */
    USBD_GEEHY_VID & 0xFF, USBD_GEEHY_VID >> 8,
    /* idProduct */
    USBD_HS_PID & 0xFF, USBD_HS_PID >> 8,
    /* bcdDevice = 2.00 */
    0x00, 0x02,
    /* Index of string descriptor describing manufacturer */
    USBD_DESC_STR_MFC,
    /* Index of string descriptor describing product */
    USBD_DESC_STR_PRODUCT,
    /* Index of string descriptor describing the device serial number */
    USBD_DESC_STR_SERIAL,
    /* bNumConfigurations */
    USBD_SUP_CONFIGURATION_MAX_NUM,
};

/*!
 * @brief   Configuration descriptor
 */
uint8_t USBD_ConfigDesc[USBD_CONFIG_DESCRIPTOR_SIZE] =
{
    /* bLength */
    0x09,
    /* bDescriptorType */
    USBD_DESC_CONFIGURATION,
    /* wTotalLength */
    USBD_CONFIG_DESCRIPTOR_SIZE & 0xFF,
    USBD_CONFIG_DESCRIPTOR_SIZE >> 8,

    /* bNumInterfaces */
    0x01,
    /* bConfigurationValue */
    0x01,
    /* iConfiguration */
    0x00,
    /* bmAttributes */
#if USBD_SUP_SELF_PWR
    0xE0,
#else
    0xA0,
#endif
    /* MaxPower */
    0x32,

    /* HID Interface */
    /* bLength */
    0x09,
    /* bDescriptorType */
    USBD_DESC_INTERFACE,
    /* bInterfaceNumber */
    0x00,
    /* bAlternateSetting */
    0x00,
    /* bNumEndpoints */
    0x02,
    /* bInterfaceClass */
    USBD_HID_ITF_CLASS_ID,
    /* bInterfaceSubClass */
    USBD_HID_SUB_CLASS_BOOT,
    /* bInterfaceProtocol */
    USBD_HID_ITF_PORTOCOL_KEYBOARD,
    /* iInterface */
    0x00,

    /* HID descriptor */
    /* bLength */
    0x09,
    /* bDescriptorType: HID */
    USBD_DESC_HID,
    /* bcdHID */
    0x11, 0x01,
    /* bCountryCode */
    0x00,
    /* bNumDescriptors */
    0x01,
    /* bDescriptorType */
    USBD_DESC_HID_REPORT,
    /* wItemLength */
    USBD_CUSTOM_HID_REPORT_DESC_SIZE & 0xFF, USBD_CUSTOM_HID_REPORT_DESC_SIZE >> 8,

    /* CUSTOM HID Endpoint */
    /* bLength */
    0x07,
    /* bDescriptorType: Endpoint */
    USBD_DESC_ENDPOINT,
    /* bEndpointAddress */
    USBD_CUSTOM_HID_IN_EP_ADDR,
    /* bmAttributes */
    0x03,
    /* wMaxPacketSize: */
    USBD_CUSTOM_HID_IN_EP_SIZE & 0xFF,
    USBD_CUSTOM_HID_IN_EP_SIZE >> 8,
    /* bInterval: */
    USBD_CUSTOM_HID_HS_INTERVAL,

    /* bLength */
    0x07,
    /* bDescriptorType: Endpoint */
    USBD_DESC_ENDPOINT,
    /* bEndpointAddress */
    USBD_CUSTOM_HID_OUT_EP_ADDR,
    /* bmAttributes */
    0x03,
    /* wMaxPacketSize: */
    USBD_CUSTOM_HID_OUT_EP_SIZE & 0xFF,
    USBD_CUSTOM_HID_OUT_EP_SIZE >> 8,
    /* bInterval: */
    USBD_CUSTOM_HID_HS_INTERVAL,
};

/*!
 * @brief   Other speed configuration descriptor
 */
uint8_t USBD_OtherSpeedCfgDesc[USBD_CONFIG_DESCRIPTOR_SIZE] =
{
    /* bLength */
    0x09,
    /* bDescriptorType */
    USBD_DESC_OTHER_SPEED,
    /* wTotalLength */
    USBD_CONFIG_DESCRIPTOR_SIZE & 0xFF,
    USBD_CONFIG_DESCRIPTOR_SIZE >> 8,

    /* bNumInterfaces */
    0x01,
    /* bConfigurationValue */
    0x01,
    /* iConfiguration */
    0x00,
    /* bmAttributes */
#if USBD_SUP_SELF_PWR
    0xE0,
#else
    0xA0,
#endif
    /* MaxPower */
    0x32,

    /* HID Interface */
    /* bLength */
    0x09,
    /* bDescriptorType */
    USBD_DESC_INTERFACE,
    /* bInterfaceNumber */
    0x00,
    /* bAlternateSetting */
    0x00,
    /* bNumEndpoints */
    0x02,
    /* bInterfaceClass */
    USBD_HID_ITF_CLASS_ID,
    /* bInterfaceSubClass */
    USBD_HID_SUB_CLASS_BOOT,
    /* bInterfaceProtocol */
    USBD_HID_ITF_PORTOCOL_KEYBOARD,
    /* iInterface */
    0x00,

    /* HID descriptor */
    /* bLength */
    0x09,
    /* bDescriptorType: HID */
    USBD_DESC_HID,
    /* bcdHID */
    0x11, 0x01,
    /* bCountryCode */
    0x00,
    /* bNumDescriptors */
    0x01,
    /* bDescriptorType */
    USBD_DESC_HID_REPORT,
    /* wItemLength */
    USBD_CUSTOM_HID_REPORT_DESC_SIZE & 0xFF, USBD_CUSTOM_HID_REPORT_DESC_SIZE >> 8,

    /* CUSTOM HID Endpoint */
    /* bLength */
    0x07,
    /* bDescriptorType: Endpoint */
    USBD_DESC_ENDPOINT,
    /* bEndpointAddress */
    USBD_CUSTOM_HID_IN_EP_ADDR,
    /* bmAttributes */
    0x03,
    /* wMaxPacketSize: */
    USBD_CUSTOM_HID_IN_EP_SIZE & 0xFF,
    USBD_CUSTOM_HID_IN_EP_SIZE >> 8,
    /* bInterval: */
    USBD_CUSTOM_HID_FS_INTERVAL,

    /* bLength */
    0x07,
    /* bDescriptorType: Endpoint */
    USBD_DESC_ENDPOINT,
    /* bEndpointAddress */
    USBD_CUSTOM_HID_OUT_EP_ADDR,
    /* bmAttributes */
    0x03,
    /* wMaxPacketSize: */
    USBD_CUSTOM_HID_OUT_EP_SIZE & 0xFF,
    USBD_CUSTOM_HID_OUT_EP_SIZE >> 8,
    /* bInterval: */
    USBD_CUSTOM_HID_FS_INTERVAL,
};

#if USBD_SUP_LPM
/*!
 * @brief   BOS descriptor
 */
uint8_t USBD_BosDesc[USBD_BOS_DESCRIPTOR_SIZE] =
{
    /* bLength */
    0x05,
    /* bDescriptorType */
    USBD_DESC_BOS,
    /* wtotalLength */
    0x0C, 0x00,
    /* bNumDeviceCaps */
    0x01,

    /* Device Capability */
    /* bLength */
    0x07,
    /* bDescriptorType */
    USBD_DEVICE_CAPABILITY_TYPE,
    /* bDevCapabilityType */
    USBD_20_EXTENSION_TYPE,
    /* bmAttributes */
    0x02, 0x00, 0x00, 0x00,
};
#endif

/*!
 * @brief   Serial string descriptor
 */
uint8_t USBD_SerialStrDesc[USBD_SERIAL_STRING_SIZE] =
{
    USBD_SERIAL_STRING_SIZE,
    USBD_DESC_STRING,

    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '0', 0,
    '1', 0
};

/*!
 * @brief   Language ID string descriptor
 */
uint8_t USBD_LandIDStrDesc[USBD_LANGID_STRING_SIZE] =
{
    /* Size */
    USBD_LANGID_STRING_SIZE,
    /* bDescriptorType */
    USBD_DESC_STRING,
    USBD_LANGID_STR & 0xFF, USBD_LANGID_STR >> 8
};

/*!
 * @brief   Device qualifier descriptor
 */
uint8_t USBD_DevQualifierDesc[USBD_DEVICE_QUALIFIER_DESCRIPTOR_SIZE] =
{
    /* Size */
    USBD_DEVICE_QUALIFIER_DESCRIPTOR_SIZE,
    /* bDescriptorType */
    USBD_DESC_DEVICE_QUALIFIER,
    0x00,
    0x02,
    0x00,
    0x00,
    0x00,
    0x40,
    0x01,
    0x00,
};

/* Private functions ******************************************************/

/*!
 * @brief     USB device convert ascii string descriptor to unicode format
 *
 * @param     desc : descriptor string
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_DESC_Ascii2Unicode(uint8_t* desc)
{
    USBD_DESC_INFO_T descInfo;
    uint8_t* buffer;
    uint8_t str[USBD_SUP_STR_DESC_MAX_NUM];

    uint8_t* unicode = str;
    uint16_t length;
    __IO uint8_t index = 0;

    if (desc == NULL)
    {
        descInfo.desc = NULL;
        descInfo.size = 0;
    }
    else
    {
        buffer = desc;
        length = (strlen((char*)buffer) * 2) + 2;
        /* Get unicode descriptor */
        unicode[index] = length;

        index++;
        unicode[index] = USBD_DESC_STRING;
        index++;

        while (*buffer != '\0')
        {
            unicode[index] = *buffer;
            buffer++;
            index++;

            unicode[index] = 0x00;
            index++;
        }
    }

    descInfo.desc = unicode;
    descInfo.size = length;

    return descInfo;
}

/*!
 * @brief     USB device HS device descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_DeviceDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo.desc = USBD_DeviceDesc;
    descInfo.size = sizeof(USBD_DeviceDesc);

    return descInfo;
}

/*!
 * @brief     USB device HS configuration descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_ConfigDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;
    USBD_EP_DESC_T *hidEpOutDesc = USBD_GetEpDesc(USBD_ConfigDesc, USBD_CUSTOM_HID_OUT_EP_ADDR);
    USBD_EP_DESC_T *hidEpInDesc = USBD_GetEpDesc(USBD_ConfigDesc, USBD_CUSTOM_HID_IN_EP_ADDR);

    if (usbSpeed == USBD_SPEED_FS)
    {
        if (hidEpInDesc != NULL)
        {
            hidEpInDesc->wMaxPacketSize = USBD_CUSTOM_HID_IN_EP_SIZE;
            hidEpInDesc->bInterval = USBD_CUSTOM_HID_FS_INTERVAL;
        }

        if (hidEpOutDesc != NULL)
        {
            hidEpOutDesc->wMaxPacketSize = USBD_CUSTOM_HID_OUT_EP_SIZE;
            hidEpOutDesc->bInterval = USBD_CUSTOM_HID_FS_INTERVAL;
        }
    }
    else
    {
        if (hidEpInDesc != NULL)
        {
            hidEpInDesc->wMaxPacketSize = USBD_CUSTOM_HID_IN_EP_SIZE;
            hidEpInDesc->bInterval = USBD_CUSTOM_HID_HS_INTERVAL;
        }

        if (hidEpOutDesc != NULL)
        {
            hidEpOutDesc->wMaxPacketSize = USBD_CUSTOM_HID_OUT_EP_SIZE;
            hidEpOutDesc->bInterval = USBD_CUSTOM_HID_HS_INTERVAL;
        }
    }

    descInfo.desc = USBD_ConfigDesc;
    descInfo.size = sizeof(USBD_ConfigDesc);

    return descInfo;
}

#if USBD_SUP_LPM
/*!
 * @brief     USB device HS BOS descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_BosDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo.desc = USBD_BosDesc;
    descInfo.size = sizeof(USBD_BosDesc);

    return descInfo;
}
#endif

/*!
 * @brief     USB device HS configuration string descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_ConfigStrDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo = USBD_DESC_Ascii2Unicode((uint8_t*)USBD_CONFIGURATION_STR);

    return descInfo;
}

/*!
 * @brief     USB device HS interface string descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_InterfaceStrDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo = USBD_DESC_Ascii2Unicode((uint8_t*)USBD_INTERFACE_STR);

    return descInfo;
}

/*!
 * @brief     USB device HS LANG ID string descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_LangIdStrDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo.desc = USBD_LandIDStrDesc;
    descInfo.size = sizeof(USBD_LandIDStrDesc);

    return descInfo;
}

/*!
 * @brief     USB device HS manufacturer string descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_ManufacturerStrDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo = USBD_DESC_Ascii2Unicode((uint8_t*)USBD_MANUFACTURER_STR);

    return descInfo;
}

/*!
 * @brief     USB device HS product string descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_ProductStrDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo = USBD_DESC_Ascii2Unicode((uint8_t*)USBD_PRODUCT_STR);

    return descInfo;
}

/*!
 * @brief     USB device HS serial string descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_HS_SerialStrDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    descInfo.desc = USBD_SerialStrDesc;
    descInfo.size = sizeof(USBD_SerialStrDesc);

    return descInfo;
}

/*!
 * @brief     USB device other speed configuration descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_OtherSpeedConfigDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    /* Use FS configuration */
    descInfo.desc = USBD_OtherSpeedCfgDesc;
    descInfo.size = sizeof(USBD_OtherSpeedCfgDesc);

    return descInfo;
}

/*!
 * @brief     USB device device qualifier descriptor
 *
 * @param     usbSpeed : usb speed
 *
 * @retval    usb descriptor information
 */
static USBD_DESC_INFO_T USBD_DevQualifierDescHandler(uint8_t usbSpeed)
{
    USBD_DESC_INFO_T descInfo;

    UNUSED(usbSpeed);

    /* Use FS configuration */
    descInfo.desc = USBD_DevQualifierDesc;
    descInfo.size = sizeof(USBD_DevQualifierDesc);

    return descInfo;
}
