/*! \file usbh_std.c \brief USB 2.0 standard function definition \version 2014-12-26, V1.0.0, firmware for GD32F10x \version 2017-06-20, V2.0.0, firmware for GD32F10x \version 2018-07-31, V2.1.0, firmware for GD32F10x */ /* Copyright (c) 2018, GigaDevice Semiconductor Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "usbh_core.h" #include "usbh_usr.h" #include "usbh_std.h" #include "usbh_ctrl.h" uint8_t local_buffer[64U]; uint8_t usbh_cfg_desc[512U]; 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 (0U == 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 + 0U); dev_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1U); dev_desc->bcdUSB = SWAPBYTE(buf + 2U); dev_desc->bDeviceClass = *(uint8_t *)(buf + 4U); dev_desc->bDeviceSubClass = *(uint8_t *)(buf + 5U); dev_desc->bDeviceProtocol = *(uint8_t *)(buf + 6U); dev_desc->bMaxPacketSize0 = *(uint8_t *)(buf + 7U); if (len > 8U){ /* for 1st time after device connection, host may issue only 8 bytes for device descriptor length */ dev_desc->idVendor = SWAPBYTE(buf + 8U); dev_desc->idProduct = SWAPBYTE(buf + 10U); dev_desc->bcdDevice = SWAPBYTE(buf + 12U); dev_desc->iManufacturer = *(uint8_t *)(buf + 14U); dev_desc->iProduct = *(uint8_t *)(buf + 15U); dev_desc->iSerialNumber = *(uint8_t *)(buf + 16U); dev_desc->bNumberConfigurations = *(uint8_t *)(buf + 17U); } } /*! \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 + 0U); cfg_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1U); cfg_desc->wTotalLength = SWAPBYTE(buf + 2U); cfg_desc->bNumInterfaces = *(uint8_t *)(buf + 4U); cfg_desc->bConfigurationValue = *(uint8_t *)(buf + 5U); cfg_desc->iConfiguration = *(uint8_t *)(buf + 6U); cfg_desc->bmAttributes = *(uint8_t *)(buf + 7U); cfg_desc->bMaxPower = *(uint8_t *)(buf + 8U); if (len > USB_CFGDESC_SIZE) { ptr = USB_CFG_DESC_LEN; if (cfg_desc->bNumInterfaces <= USBH_MAX_INTERFACES_NUM) { pitf = (usb_descriptor_interface_struct *)0U; 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 + 0U); itf_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1U); itf_desc->bInterfaceNumber = *(uint8_t *)(buf + 2U); itf_desc->bAlternateSetting = *(uint8_t *)(buf + 3U); itf_desc->bNumEndpoints = *(uint8_t *)(buf + 4U); itf_desc->bInterfaceClass = *(uint8_t *)(buf + 5U); itf_desc->bInterfaceSubClass = *(uint8_t *)(buf + 6U); itf_desc->bInterfaceProtocol = *(uint8_t *)(buf + 7U); itf_desc->iInterface = *(uint8_t *)(buf + 8U); } /*! \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 + 0U); ep_desc->Header.bDescriptorType = *(uint8_t *)(buf + 1U); ep_desc->bEndpointAddress = *(uint8_t *)(buf + 2U); ep_desc->bmAttributes = *(uint8_t *)(buf + 3U); ep_desc->wMaxPacketSize = SWAPBYTE(buf + 4U); ep_desc->bInterval = *(uint8_t *)(buf + 6U); } /*! \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); }