rt-thread/bsp/gd32303e-eval/Libraries/GD32F30x_usbfs_driver/Source/usbh_std.c

808 lines
32 KiB
C
Raw Normal View History

2018-05-21 11:13:46 +08:00
/*!
\file usbh_std.c
\brief USB 2.0 standard function definition
*/
/*
Copyright (C) 2017 GigaDevice
2017-02-10, V1.0.0, firmware for GD32F30x
*/
#include "usbh_core.h"
#include "usbh_usr.h"
#include "usbh_std.h"
#include "usbh_ctrl.h"
uint8_t local_buffer[64];
uint8_t usbh_cfg_desc[512];
uint8_t enum_polling_handle_flag = 0U;
static void enum_idle_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_get_full_dev_desc_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_set_addr_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_get_cfg_desc_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_get_full_cfg_desc_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_get_mfc_string_desc_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_get_product_string_desc_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_get_serialnum_string_desc_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_set_configuration_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
static void enum_dev_configured_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
/* the enumeration state handle function array */
void (*enum_state_handle[]) (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate) =
{
enum_idle_handle,
enum_set_addr_handle,
enum_get_full_dev_desc_handle,
enum_get_cfg_desc_handle,
enum_get_full_cfg_desc_handle,
enum_get_mfc_string_desc_handle,
enum_get_product_string_desc_handle,
enum_get_serialnum_string_desc_handle,
enum_set_configuration_handle,
enum_dev_configured_handle,
};
/* the enumeration state handle table */
state_table_struct enum_handle_table[ENUM_HANDLE_TABLE_SIZE] =
{
/* the current state the current event the next state the event function */
{ENUM_IDLE, ENUM_EVENT_SET_ADDR, ENUM_SET_ADDR, only_state_move },
{ENUM_SET_ADDR, ENUN_EVENT_GET_FULL_DEV_DESC, ENUM_GET_FULL_DEV_DESC, only_state_move },
{ENUM_GET_FULL_DEV_DESC, ENUN_EVENT_GET_CFG_DESC, ENUM_GET_CFG_DESC, only_state_move },
{ENUM_GET_CFG_DESC, ENUN_EVENT_GET_FULL_CFG_DESC, ENUM_GET_FULL_CFG_DESC, only_state_move },
{ENUM_GET_FULL_CFG_DESC, ENUN_EVENT_GET_MFC_STRING_DESC, ENUM_GET_MFC_STRING_DESC, only_state_move },
{ENUM_GET_MFC_STRING_DESC, ENUN_EVENT_GET_PRODUCT_STRING_DESC, ENUM_GET_PRODUCT_STRING_DESC, only_state_move },
{ENUM_GET_PRODUCT_STRING_DESC, ENUN_EVENT_GET_SERIALNUM_STRING_DESC, ENUM_GET_SERIALNUM_STRING_DESC, only_state_move },
{ENUM_GET_SERIALNUM_STRING_DESC, ENUN_EVENT_SET_CONFIGURATION, ENUM_SET_CONFIGURATION, only_state_move },
{ENUM_SET_CONFIGURATION, ENUN_EVENT_DEV_CONFIGURED, ENUM_DEV_CONFIGURED, only_state_move },
{ENUM_DEV_CONFIGURED, GO_TO_UP_STATE_EVENT, UP_STATE, goto_up_state_fun },
};
/*!
\brief the polling function of enumeration state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval usb host status
*/
usbh_status_enum enum_state_polling_fun (usb_core_handle_struct *pudev, usbh_host_struct *puhost, void *pustate)
{
usbh_status_enum exe_state = USBH_BUSY;
usbh_state_handle_struct *p_state;
p_state = (usbh_state_handle_struct *)pustate;
if (0U == enum_polling_handle_flag) {
enum_polling_handle_flag = 1U;
scd_table_push(p_state);
scd_state_move(p_state, ENUM_IDLE);
}
/* start the enumeration state handle */
scd_begin(p_state,ENUM_FSM_ID);
if (0 == p_state->usbh_current_state_stack_top) {
enum_state_handle[p_state->usbh_current_state](pudev, puhost, p_state);
} else {
enum_state_handle[p_state->stack[1].state](pudev, puhost, p_state);
}
/* determine the enumeration whether to complete */
if (ENUM_DEV_CONFIGURED == puhost->usbh_backup_state.enum_backup_state) {
puhost->usbh_backup_state.enum_backup_state = ENUM_IDLE;
enum_polling_handle_flag = 0U;
exe_state = USBH_OK;
}
return exe_state;
}
/*!
\brief the handle function of ENUM_IDLE state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_idle_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_IDLE;
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get(pudev,
puhost,
pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_DEVDESC,
8U);
if ((void *)0 != pudev->mdelay) {
pudev->mdelay(100U);
}
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
usbh_device_desc_parse(&puhost->device.dev_desc, pudev->host.rx_buffer, 8U);
puhost->control.ep0_size = puhost->device.dev_desc.bMaxPacketSize0;
/* issue reset */
usb_port_reset(pudev);
/* modify control channels configuration for maxpacket size */
usbh_channel_modify (pudev,
puhost->control.hc_out_num,
0U,
0U,
0U,
(uint16_t)puhost->control.ep0_size);
usbh_channel_modify (pudev,
puhost->control.hc_in_num,
0U,
0U,
0U,
(uint16_t)puhost->control.ep0_size);
scd_event_handle(pudev,
puhost,
pustate,
ENUM_EVENT_SET_ADDR,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_GET_FULL_DEV_DESC state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_get_full_dev_desc_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_GET_FULL_DEV_DESC;
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get(pudev,
puhost,
pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_DEVDESC,
USB_DEVDESC_SIZE);
}
if(USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)){
usbh_device_desc_parse(&puhost->device.dev_desc, pudev->host.rx_buffer, USB_DEVDESC_SIZE);
puhost->usr_cb->device_desc_available(&puhost->device.dev_desc);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_CFG_DESC,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_SET_ADDR state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_set_addr_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_SET_ADDR;
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_addr_set(pudev, puhost,USBH_DEVICE_ADDRESS);
if ((void *)0 != pudev->mdelay) {
pudev->mdelay(100U);
}
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
if ((void *)0 != pudev->mdelay) {
pudev->mdelay(2U);
}
puhost->device.address = USBH_DEVICE_ADDRESS;
/* user callback for device address assigned */
puhost->usr_cb->device_address_set();
/* modify control channels to update device address */
usbh_channel_modify (pudev,
puhost->control.hc_in_num,
puhost->device.address,
0U,
0U,
0U);
usbh_channel_modify (pudev,
puhost->control.hc_out_num,
puhost->device.address,
0U,
0U,
0U);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_FULL_DEV_DESC,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_GET_CFG_DESC state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_get_cfg_desc_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
uint16_t index = 0U;
puhost->usbh_backup_state.enum_backup_state = ENUM_GET_CFG_DESC;
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get(pudev,
puhost,
pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_CFGDESC,
USB_CFGDESC_SIZE);
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
/* save configuration descriptor for class parsing usage */
for (; index < USB_CFGDESC_SIZE; index ++) {
usbh_cfg_desc[index] = pudev->host.rx_buffer[index];
}
/* commands successfully sent and response received */
usbh_cfg_desc_parse (&puhost->device.cfg_desc,
puhost->device.itf_desc,
puhost->device.ep_desc,
pudev->host.rx_buffer,
USB_CFGDESC_SIZE);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_FULL_CFG_DESC,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_GET_FULL_CFG_DESC state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_get_full_cfg_desc_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
uint16_t index = 0U;
puhost->usbh_backup_state.enum_backup_state = ENUM_GET_FULL_CFG_DESC;
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get (pudev, puhost, pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_CFGDESC, puhost->device.cfg_desc.wTotalLength);
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
/* save configuration descriptor for class parsing usage */
for (; index < puhost->device.cfg_desc.wTotalLength; index ++) {
usbh_cfg_desc[index] = pudev->host.rx_buffer[index];
}
/* commands successfully sent and response received */
usbh_cfg_desc_parse (&puhost->device.cfg_desc,
puhost->device.itf_desc,
puhost->device.ep_desc,
pudev->host.rx_buffer,
puhost->device.cfg_desc.wTotalLength);
/* User callback for configuration descriptors available */
puhost->usr_cb->configuration_desc_available(&puhost->device.cfg_desc,
puhost->device.itf_desc,
puhost->device.ep_desc[0]);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_MFC_STRING_DESC,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_GET_MFC_STRING_DESC state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_get_mfc_string_desc_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_GET_MFC_STRING_DESC;
if (0U != puhost->device.dev_desc.iManufacturer) {
if(CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get(pudev,
puhost,
pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_STRDESC | puhost->device.dev_desc.iManufacturer,
0xffU);
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
usbh_string_desc_parse(pudev->host.rx_buffer, local_buffer, 0xffU);
puhost->usr_cb->manufacturer_string(local_buffer);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_PRODUCT_STRING_DESC,
pustate->usbh_current_state);
}
} else {
puhost->usr_cb->manufacturer_string("N/A");
scd_state_move((usbh_state_handle_struct *)pustate, ENUM_GET_PRODUCT_STRING_DESC);
}
}
/*!
\brief the handle function of ENUM_GET_PRODUCT_STRING_DESC state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_get_product_string_desc_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_GET_PRODUCT_STRING_DESC;
if (0U != puhost->device.dev_desc.iProduct) {
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get(pudev,
puhost,
pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_STRDESC | puhost->device.dev_desc.iProduct,
0xffU);
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
usbh_string_desc_parse(pudev->host.rx_buffer, local_buffer, 0xffU);
/* user callback for product string */
puhost->usr_cb->product_string(local_buffer);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_SERIALNUM_STRING_DESC,
pustate->usbh_current_state);
}
} else {
puhost->usr_cb->product_string("N/A");
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_GET_SERIALNUM_STRING_DESC,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_GET_SERIALNUM_STRING_DESC state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_get_serialnum_string_desc_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_GET_SERIALNUM_STRING_DESC;
if (0U != puhost->device.dev_desc.iSerialNumber) {
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state) {
usbh_enum_desc_get(pudev,
puhost,
pudev->host.rx_buffer,
USB_REQTYPE_DEVICE | USB_STANDARD_REQ,
USB_STRDESC | puhost->device.dev_desc.iSerialNumber,
0xffU);
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)){
usbh_string_desc_parse(pudev->host.rx_buffer, local_buffer, 0xffU);
/* user callback for product string */
puhost->usr_cb->serial_num_string(local_buffer);
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_SET_CONFIGURATION,
pustate->usbh_current_state);
}
} else {
puhost->usr_cb->serial_num_string("N/A");
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_SET_CONFIGURATION,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_SET_CONFIGURATION state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_set_configuration_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_SET_CONFIGURATION;
if (CTRL_IDLE == puhost->usbh_backup_state.ctrl_backup_state ) {
usbh_enum_cfg_set(pudev, puhost, (uint16_t)puhost->device.cfg_desc.bConfigurationValue);
}
if (USBH_OK == ctrl_state_polling_fun(pudev, puhost, pustate)) {
scd_event_handle(pudev,
puhost,
pustate,
ENUN_EVENT_DEV_CONFIGURED,
pustate->usbh_current_state);
}
}
/*!
\brief the handle function of ENUM_DEV_CONFIGURED state
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] pustate: pointer to USB state driver
\param[out] none
\retval none
*/
static void enum_dev_configured_handle (usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
usbh_state_handle_struct *pustate)
{
puhost->usbh_backup_state.enum_backup_state = ENUM_DEV_CONFIGURED;
scd_event_handle(pudev, puhost, pustate, GO_TO_UP_STATE_EVENT, pustate->usbh_current_state);
}
/*!
\brief get descriptor in usb host enumeration stage
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] buf: buffer to store the descriptor
\param[in] ReqType: descriptor type
\param[in] ValueIdx: wValue for the GetDescriptr request
\param[in] Len: length of the descriptor
\param[out] none
\retval none
*/
void usbh_enum_desc_get(usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
uint8_t *buf,
uint8_t req_type,
uint16_t value_idx,
uint16_t len)
{
usb_setup_union *pSetup = &(puhost->control.setup);
pSetup->b.bmRequestType = USB_DIR_IN | req_type;
pSetup->b.bRequest = USBREQ_GET_DESCRIPTOR;
pSetup->b.wValue = value_idx;
if (USB_STRDESC == (value_idx & 0xff00U)){
pSetup->b.wIndex = 0x0409U;
} else {
pSetup->b.wIndex = 0U;
}
pSetup->b.wLength = len;
puhost->control.buff = buf;
puhost->control.length = len;
}
/*!
\brief set address in usb host enumeration stage
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] device_address: the device address
\param[out] none
\retval none
*/
void usbh_enum_addr_set(usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
uint8_t device_address)
{
usb_setup_union *p_setup = &(puhost->control.setup);
p_setup->b.bmRequestType = USB_DIR_OUT | USB_REQTYPE_DEVICE | USB_STANDARD_REQ;
p_setup->b.bRequest = USBREQ_SET_ADDRESS;
p_setup->b.wValue = (uint16_t)device_address;
p_setup->b.wIndex = 0U;
p_setup->b.wLength = 0U;
puhost->control.buff = 0U;
puhost->control.length = 0U;
}
/*!
\brief set configuration in usb host enumeration stage
\param[in] pudev: pointer to USB device
\param[in] puhost: pointer to USB host
\param[in] cfg_idx: the index of the configuration
\param[out] none
\retval none
*/
void usbh_enum_cfg_set(usb_core_handle_struct *pudev,
usbh_host_struct *puhost,
uint16_t cfg_idx)
{
usb_setup_union *p_setup = &(puhost->control.setup);
p_setup->b.bmRequestType = USB_DIR_OUT | USB_REQTYPE_DEVICE | USB_STANDARD_REQ;
p_setup->b.bRequest = USBREQ_SET_CONFIGURATION;
p_setup->b.wValue = cfg_idx;
p_setup->b.wIndex = 0U;
p_setup->b.wLength = 0U;
puhost->control.buff = 0;
puhost->control.length = 0U;
}
/*!
\brief parse the device descriptor
\param[in] dev_desc: device_descriptor destinaton address
\param[in] buf: buffer where the source descriptor is available
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
void usbh_device_desc_parse (usb_descriptor_device_struct *dev_desc, uint8_t *buf, uint16_t len)
{
dev_desc->Header.bLength = *(uint8_t *)(buf + 0);
dev_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1);
dev_desc->bcdUSB = SWAPBYTE(buf + 2);
dev_desc->bDeviceClass = *(uint8_t *)(buf + 4);
dev_desc->bDeviceSubClass = *(uint8_t *)(buf + 5);
dev_desc->bDeviceProtocol = *(uint8_t *)(buf + 6);
dev_desc->bMaxPacketSize0 = *(uint8_t *)(buf + 7);
if (len > 8U){
/* for 1st time after device connection, host may issue only 8 bytes for device descriptor length */
dev_desc->idVendor = SWAPBYTE(buf + 8);
dev_desc->idProduct = SWAPBYTE(buf + 10);
dev_desc->bcdDevice = SWAPBYTE(buf + 12);
dev_desc->iManufacturer = *(uint8_t *)(buf + 14);
dev_desc->iProduct = *(uint8_t *)(buf + 15);
dev_desc->iSerialNumber = *(uint8_t *)(buf + 16);
dev_desc->bNumberConfigurations = *(uint8_t *)(buf + 17);
}
}
/*!
\brief parse the configuration descriptor
\param[in] cfg_desc: configuration descriptor address
\param[in] itf_desc: interface descriptor address
\param[in] ep_desc: endpoint descriptor address
\param[in] buf: buffer where the source descriptor is available
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
void usbh_cfg_desc_parse (usb_descriptor_configuration_struct *cfg_desc,
usb_descriptor_interface_struct *itf_desc,
usb_descriptor_endpoint_struct ep_desc[][USBH_MAX_EP_NUM],
uint8_t *buf,
uint16_t len)
{
usb_descriptor_interface_struct *pitf = NULL;
usb_descriptor_interface_struct temp_pitf;
usb_descriptor_endpoint_struct *pep = NULL;
usb_descriptor_header_struct *pdesc = (usb_descriptor_header_struct *)buf;
uint8_t itf_ix = 0U;
uint8_t ep_ix = 0U;
uint16_t ptr = 0U;
static uint8_t prev_itf = 0U;
static uint16_t prev_ep_size = 0U;
/* parse configuration descriptor */
cfg_desc->Header.bLength = *(uint8_t *)(buf + 0);
cfg_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1);
cfg_desc->wTotalLength = SWAPBYTE(buf + 2);
cfg_desc->bNumInterfaces = *(uint8_t *)(buf + 4);
cfg_desc->bConfigurationValue = *(uint8_t *)(buf + 5);
cfg_desc->iConfiguration = *(uint8_t *)(buf + 6);
cfg_desc->bmAttributes = *(uint8_t *)(buf + 7);
cfg_desc->bMaxPower = *(uint8_t *)(buf + 8);
if (len > USB_CFGDESC_SIZE) {
ptr = USB_CFG_DESC_LEN;
if (cfg_desc->bNumInterfaces <= USBH_MAX_INTERFACES_NUM) {
pitf = (usb_descriptor_interface_struct *)0;
for (; ptr < cfg_desc->wTotalLength; ) {
pdesc = usbh_next_desc_get((uint8_t *)pdesc, &ptr);
if (USB_DESCTYPE_INTERFACE == pdesc->bDescriptorType) {
itf_ix = *((uint8_t *)pdesc + 2U);
pitf = &itf_desc[itf_ix];
if (*((uint8_t *)pdesc + 3U) < 3U) {
usbh_interface_desc_parse (&temp_pitf, (uint8_t *)pdesc);
/* parse endpoint descriptors relative to the current interface */
if (temp_pitf.bNumEndpoints <= USBH_MAX_EP_NUM) {
for (ep_ix = 0U; ep_ix < temp_pitf.bNumEndpoints;) {
pdesc = usbh_next_desc_get((void* )pdesc, &ptr);
if (USB_DESCTYPE_ENDPOINT == pdesc->bDescriptorType) {
pep = &ep_desc[itf_ix][ep_ix];
if (prev_itf != itf_ix) {
prev_itf = itf_ix;
usbh_interface_desc_parse (pitf, (uint8_t *)&temp_pitf);
} else {
if (prev_ep_size > SWAPBYTE((uint8_t *)pdesc + 4)) {
break;
} else {
usbh_interface_desc_parse (pitf, (uint8_t *)&temp_pitf);
}
}
usbh_endpoint_desc_parse (pep, (uint8_t *)pdesc);
prev_ep_size = SWAPBYTE((uint8_t *)pdesc + 4);
ep_ix++;
}
}
}
}
}
}
}
prev_ep_size = 0U;
prev_itf = 0U;
}
}
/*!
\brief parse the interface descriptor
\param[in] itf_desc: interface descriptor destination
\param[in] buf: buffer where the descriptor data is available
\param[out] none
\retval none
*/
void usbh_interface_desc_parse (usb_descriptor_interface_struct *itf_desc, uint8_t *buf)
{
itf_desc->Header.bLength = *(uint8_t *)(buf + 0);
itf_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1);
itf_desc->bInterfaceNumber = *(uint8_t *)(buf + 2);
itf_desc->bAlternateSetting = *(uint8_t *)(buf + 3);
itf_desc->bNumEndpoints = *(uint8_t *)(buf + 4);
itf_desc->bInterfaceClass = *(uint8_t *)(buf + 5);
itf_desc->bInterfaceSubClass = *(uint8_t *)(buf + 6);
itf_desc->bInterfaceProtocol = *(uint8_t *)(buf + 7);
itf_desc->iInterface = *(uint8_t *)(buf + 8);
}
/*!
\brief parse the endpoint descriptor
\param[in] ep_desc: endpoint descriptor destination address
\param[in] buf: buffer where the parsed descriptor stored
\param[out] none
\retval none
*/
void usbh_endpoint_desc_parse (usb_descriptor_endpoint_struct *ep_desc, uint8_t *buf)
{
ep_desc->Header.bLength = *(uint8_t *)(buf + 0);
ep_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1);
ep_desc->bEndpointAddress = *(uint8_t *)(buf + 2);
ep_desc->bmAttributes = *(uint8_t *)(buf + 3);
ep_desc->wMaxPacketSize = SWAPBYTE(buf + 4);
ep_desc->bInterval = *(uint8_t *)(buf + 6);
}
/*!
\brief parse the string descriptor
\param[in] psrc: source pointer containing the descriptor data
\param[in] pdest: destination address pointer
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
void usbh_string_desc_parse (uint8_t* psrc, uint8_t* pdest, uint16_t len)
{
uint16_t strlength;
uint16_t idx;
/* the unicode string descriptor is not null-terminated. the string length is
computed by substracting two from the value of the first byte of the descriptor.
*/
/* check which is lower size, the size of string or the length of bytes read from the device */
if (USB_DESCTYPE_STRING == psrc[1]){
/* make sure the descriptor is string type */
/* psrc[0] contains size of descriptor, subtract 2 to get the length of string */
strlength = ((((uint16_t)psrc[0] - 2U) <= len) ? ((uint16_t)psrc[0] - 2U) : len);
psrc += 2; /* adjust the offset ignoring the string len and descriptor type */
for (idx = 0U; idx < strlength; idx += 2U) {
/* copy only the string and ignore the unicode id, hence add the src */
*pdest = psrc[idx];
pdest++;
}
*pdest = 0U; /* mark end of string */
}
}
/*!
\brief get the next descriptor header
\param[in] pbuf: pointer to buffer where the cfg descriptor is available
\param[in] ptr: data popinter inside the configuration descriptor
\param[out] none
\retval next descriptor header
*/
usb_descriptor_header_struct *usbh_next_desc_get (uint8_t *pbuf, uint16_t *ptr)
{
uint8_t len = ((usb_descriptor_header_struct *)pbuf)->bLength;
usb_descriptor_header_struct *pnext;
*ptr += len;
pnext = (usb_descriptor_header_struct *)((uint8_t *)pbuf + len);
return(pnext);
}