/**
******************************************************************************
* @file usbh_ctlreq.c
* @author MCD Application Team
* @brief This file implements the control requests for device enumeration
******************************************************************************
* @attention
*
*
© Copyright (c) 2015 STMicroelectronics.
* All rights reserved.
*
* 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 "usbh_ctlreq.h"
/** @addtogroup USBH_LIB
* @{
*/
/** @addtogroup USBH_LIB_CORE
* @{
*/
/** @defgroup USBH_CTLREQ
* @brief This file implements the standard requests for device enumeration
* @{
*/
/** @defgroup USBH_CTLREQ_Private_Defines
* @{
*/
/**
* @}
*/
/** @defgroup USBH_CTLREQ_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @defgroup USBH_CTLREQ_Private_Macros
* @{
*/
/**
* @}
*/
/** @defgroup USBH_CTLREQ_Private_Variables
* @{
*/
/**
* @}
*/
/** @defgroup USBH_CTLREQ_Private_FunctionPrototypes
* @{
*/
static USBH_StatusTypeDef USBH_HandleControl(USBH_HandleTypeDef *phost);
static void USBH_ParseDevDesc(USBH_DevDescTypeDef *dev_desc,
uint8_t *buf, uint16_t length);
static void USBH_ParseCfgDesc(USBH_CfgDescTypeDef *cfg_desc,
uint8_t *buf, uint16_t length);
static void USBH_ParseEPDesc(USBH_EpDescTypeDef *ep_descriptor, uint8_t *buf);
static void USBH_ParseStringDesc(uint8_t *psrc, uint8_t *pdest, uint16_t length);
static void USBH_ParseInterfaceDesc(USBH_InterfaceDescTypeDef *if_descriptor, uint8_t *buf);
/**
* @}
*/
/** @defgroup USBH_CTLREQ_Private_Functions
* @{
*/
/**
* @brief USBH_Get_DevDesc
* Issue Get Device Descriptor command to the device. Once the response
* received, it parses the device descriptor and updates the status.
* @param phost: Host Handle
* @param length: Length of the descriptor
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_Get_DevDesc(USBH_HandleTypeDef *phost, uint8_t length)
{
USBH_StatusTypeDef status;
if ((status = USBH_GetDescriptor(phost,
USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD,
USB_DESC_DEVICE, phost->device.Data,
(uint16_t)length)) == USBH_OK)
{
/* Commands successfully sent and Response Received */
USBH_ParseDevDesc(&phost->device.DevDesc, phost->device.Data,
(uint16_t)length);
}
return status;
}
/**
* @brief USBH_Get_CfgDesc
* Issues Configuration Descriptor to the device. Once the response
* received, it parses the configuration descriptor and updates the
* status.
* @param phost: Host Handle
* @param length: Length of the descriptor
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_Get_CfgDesc(USBH_HandleTypeDef *phost,
uint16_t length)
{
USBH_StatusTypeDef status;
uint8_t *pData = phost->device.CfgDesc_Raw;;
if ((status = USBH_GetDescriptor(phost, (USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD),
USB_DESC_CONFIGURATION, pData, length)) == USBH_OK)
{
/* Commands successfully sent and Response Received */
USBH_ParseCfgDesc(&phost->device.CfgDesc, pData, length);
}
return status;
}
/**
* @brief USBH_Get_StringDesc
* Issues string Descriptor command to the device. Once the response
* received, it parses the string descriptor and updates the status.
* @param phost: Host Handle
* @param string_index: String index for the descriptor
* @param buff: Buffer address for the descriptor
* @param length: Length of the descriptor
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_Get_StringDesc(USBH_HandleTypeDef *phost,
uint8_t string_index, uint8_t *buff,
uint16_t length)
{
USBH_StatusTypeDef status;
if ((status = USBH_GetDescriptor(phost,
USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD,
USB_DESC_STRING | string_index,
phost->device.Data, length)) == USBH_OK)
{
/* Commands successfully sent and Response Received */
USBH_ParseStringDesc(phost->device.Data, buff, length);
}
return status;
}
/**
* @brief USBH_GetDescriptor
* Issues Descriptor command to the device. Once the response received,
* it parses the descriptor and updates the status.
* @param phost: Host Handle
* @param req_type: Descriptor type
* @param value_idx: Value for the GetDescriptr request
* @param buff: Buffer to store the descriptor
* @param length: Length of the descriptor
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_GetDescriptor(USBH_HandleTypeDef *phost,
uint8_t req_type,
uint16_t value_idx,
uint8_t *buff,
uint16_t length)
{
if (phost->RequestState == CMD_SEND)
{
phost->Control.setup.b.bmRequestType = USB_D2H | req_type;
phost->Control.setup.b.bRequest = USB_REQ_GET_DESCRIPTOR;
phost->Control.setup.b.wValue.w = value_idx;
if ((value_idx & 0xff00U) == USB_DESC_STRING)
{
phost->Control.setup.b.wIndex.w = 0x0409U;
}
else
{
phost->Control.setup.b.wIndex.w = 0U;
}
phost->Control.setup.b.wLength.w = length;
}
return USBH_CtlReq(phost, buff, length);
}
/**
* @brief USBH_SetAddress
* This command sets the address to the connected device
* @param phost: Host Handle
* @param DeviceAddress: Device address to assign
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_SetAddress(USBH_HandleTypeDef *phost,
uint8_t DeviceAddress)
{
if (phost->RequestState == CMD_SEND)
{
phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_DEVICE | \
USB_REQ_TYPE_STANDARD;
phost->Control.setup.b.bRequest = USB_REQ_SET_ADDRESS;
phost->Control.setup.b.wValue.w = (uint16_t)DeviceAddress;
phost->Control.setup.b.wIndex.w = 0U;
phost->Control.setup.b.wLength.w = 0U;
}
return USBH_CtlReq(phost, 0U, 0U);
}
/**
* @brief USBH_SetCfg
* The command sets the configuration value to the connected device
* @param phost: Host Handle
* @param cfg_idx: Configuration value
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_SetCfg(USBH_HandleTypeDef *phost, uint16_t cfg_idx)
{
if (phost->RequestState == CMD_SEND)
{
phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_DEVICE
| USB_REQ_TYPE_STANDARD;
phost->Control.setup.b.bRequest = USB_REQ_SET_CONFIGURATION;
phost->Control.setup.b.wValue.w = cfg_idx;
phost->Control.setup.b.wIndex.w = 0U;
phost->Control.setup.b.wLength.w = 0U;
}
return USBH_CtlReq(phost, 0U, 0U);
}
/**
* @brief USBH_SetInterface
* The command sets the Interface value to the connected device
* @param phost: Host Handle
* @param altSetting: Interface value
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_SetInterface(USBH_HandleTypeDef *phost, uint8_t ep_num,
uint8_t altSetting)
{
if (phost->RequestState == CMD_SEND)
{
phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_INTERFACE
| USB_REQ_TYPE_STANDARD;
phost->Control.setup.b.bRequest = USB_REQ_SET_INTERFACE;
phost->Control.setup.b.wValue.w = altSetting;
phost->Control.setup.b.wIndex.w = ep_num;
phost->Control.setup.b.wLength.w = 0U;
}
return USBH_CtlReq(phost, 0U, 0U);
}
/**
* @brief USBH_SetFeature
* The command sets the device features (remote wakeup feature,..)
* @param pdev: Selected device
* @param itf_idx
* @retval Status
*/
USBH_StatusTypeDef USBH_SetFeature(USBH_HandleTypeDef *phost, uint8_t wValue)
{
if (phost->RequestState == CMD_SEND)
{
phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_DEVICE
| USB_REQ_TYPE_STANDARD;
phost->Control.setup.b.bRequest = USB_REQ_SET_FEATURE;
phost->Control.setup.b.wValue.w = wValue;
phost->Control.setup.b.wIndex.w = 0U;
phost->Control.setup.b.wLength.w = 0U;
}
return USBH_CtlReq(phost, 0U, 0U);
}
/**
* @brief USBH_ClrFeature
* This request is used to clear or disable a specific feature.
* @param phost: Host Handle
* @param ep_num: endpoint number
* @param hc_num: Host channel number
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_ClrFeature(USBH_HandleTypeDef *phost, uint8_t ep_num)
{
if (phost->RequestState == CMD_SEND)
{
phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_ENDPOINT
| USB_REQ_TYPE_STANDARD;
phost->Control.setup.b.bRequest = USB_REQ_CLEAR_FEATURE;
phost->Control.setup.b.wValue.w = FEATURE_SELECTOR_ENDPOINT;
phost->Control.setup.b.wIndex.w = ep_num;
phost->Control.setup.b.wLength.w = 0U;
}
return USBH_CtlReq(phost, 0U, 0U);
}
/**
* @brief USBH_ParseDevDesc
* This function Parses the device descriptor
* @param dev_desc: device_descriptor destination address
* @param buf: Buffer where the source descriptor is available
* @param length: Length of the descriptor
* @retval None
*/
static void USBH_ParseDevDesc(USBH_DevDescTypeDef *dev_desc, uint8_t *buf,
uint16_t length)
{
dev_desc->bLength = *(uint8_t *)(buf + 0);
dev_desc->bDescriptorType = *(uint8_t *)(buf + 1);
dev_desc->bcdUSB = LE16(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->bMaxPacketSize = *(uint8_t *)(buf + 7);
if (length > 8U)
{
/* For 1st time after device connection, Host may issue only 8 bytes for
Device Descriptor Length */
dev_desc->idVendor = LE16(buf + 8);
dev_desc->idProduct = LE16(buf + 10);
dev_desc->bcdDevice = LE16(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->bNumConfigurations = *(uint8_t *)(buf + 17);
}
}
/**
* @brief USBH_ParseCfgDesc
* This function Parses the configuration descriptor
* @param cfg_desc: Configuration Descriptor address
* @param buf: Buffer where the source descriptor is available
* @param length: Length of the descriptor
* @retval None
*/
static void USBH_ParseCfgDesc(USBH_CfgDescTypeDef *cfg_desc, uint8_t *buf,
uint16_t length)
{
USBH_InterfaceDescTypeDef *pif ;
USBH_EpDescTypeDef *pep;
USBH_DescHeader_t *pdesc = (USBH_DescHeader_t *)(void *)buf;
uint16_t ptr;
uint8_t if_ix = 0U;
uint8_t ep_ix = 0U;
pdesc = (USBH_DescHeader_t *)(void *)buf;
/* Parse configuration descriptor */
cfg_desc->bLength = *(uint8_t *)(buf + 0);
cfg_desc->bDescriptorType = *(uint8_t *)(buf + 1);
cfg_desc->wTotalLength = LE16(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 (length > USB_CONFIGURATION_DESC_SIZE)
{
ptr = USB_LEN_CFG_DESC;
pif = (USBH_InterfaceDescTypeDef *)0;
while ((if_ix < USBH_MAX_NUM_INTERFACES) && (ptr < cfg_desc->wTotalLength))
{
pdesc = USBH_GetNextDesc((uint8_t *)(void *)pdesc, &ptr);
if (pdesc->bDescriptorType == USB_DESC_TYPE_INTERFACE)
{
pif = &cfg_desc->Itf_Desc[if_ix];
USBH_ParseInterfaceDesc(pif, (uint8_t *)(void *)pdesc);
ep_ix = 0U;
pep = (USBH_EpDescTypeDef *)0;
while ((ep_ix < pif->bNumEndpoints) && (ptr < cfg_desc->wTotalLength))
{
pdesc = USBH_GetNextDesc((uint8_t *)(void *)pdesc, &ptr);
if (pdesc->bDescriptorType == USB_DESC_TYPE_ENDPOINT)
{
pep = &cfg_desc->Itf_Desc[if_ix].Ep_Desc[ep_ix];
USBH_ParseEPDesc(pep, (uint8_t *)(void *)pdesc);
ep_ix++;
}
}
if_ix++;
}
}
}
}
/**
* @brief USBH_ParseInterfaceDesc
* This function Parses the interface descriptor
* @param if_descriptor : Interface descriptor destination
* @param buf: Buffer where the descriptor data is available
* @retval None
*/
static void USBH_ParseInterfaceDesc(USBH_InterfaceDescTypeDef *if_descriptor,
uint8_t *buf)
{
if_descriptor->bLength = *(uint8_t *)(buf + 0);
if_descriptor->bDescriptorType = *(uint8_t *)(buf + 1);
if_descriptor->bInterfaceNumber = *(uint8_t *)(buf + 2);
if_descriptor->bAlternateSetting = *(uint8_t *)(buf + 3);
if_descriptor->bNumEndpoints = *(uint8_t *)(buf + 4);
if_descriptor->bInterfaceClass = *(uint8_t *)(buf + 5);
if_descriptor->bInterfaceSubClass = *(uint8_t *)(buf + 6);
if_descriptor->bInterfaceProtocol = *(uint8_t *)(buf + 7);
if_descriptor->iInterface = *(uint8_t *)(buf + 8);
}
/**
* @brief USBH_ParseEPDesc
* This function Parses the endpoint descriptor
* @param ep_descriptor: Endpoint descriptor destination address
* @param buf: Buffer where the parsed descriptor stored
* @retval None
*/
static void USBH_ParseEPDesc(USBH_EpDescTypeDef *ep_descriptor,
uint8_t *buf)
{
ep_descriptor->bLength = *(uint8_t *)(buf + 0);
ep_descriptor->bDescriptorType = *(uint8_t *)(buf + 1);
ep_descriptor->bEndpointAddress = *(uint8_t *)(buf + 2);
ep_descriptor->bmAttributes = *(uint8_t *)(buf + 3);
ep_descriptor->wMaxPacketSize = LE16(buf + 4);
ep_descriptor->bInterval = *(uint8_t *)(buf + 6);
}
/**
* @brief USBH_ParseStringDesc
* This function Parses the string descriptor
* @param psrc: Source pointer containing the descriptor data
* @param pdest: Destination address pointer
* @param length: Length of the descriptor
* @retval None
*/
static void USBH_ParseStringDesc(uint8_t *psrc, uint8_t *pdest, uint16_t length)
{
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 (psrc[1] == USB_DESC_TYPE_STRING)
{
/* 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) <= length) ? ((uint16_t)psrc[0] - 2U) : length);
/* Adjust the offset ignoring the String Len and Descriptor type */
psrc += 2U;
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 USBH_GetNextDesc
* This function return the next descriptor header
* @param buf: Buffer where the cfg descriptor is available
* @param ptr: data pointer inside the cfg descriptor
* @retval next header
*/
USBH_DescHeader_t *USBH_GetNextDesc(uint8_t *pbuf, uint16_t *ptr)
{
USBH_DescHeader_t *pnext;
*ptr += ((USBH_DescHeader_t *)(void *)pbuf)->bLength;
pnext = (USBH_DescHeader_t *)(void *)((uint8_t *)(void *)pbuf + \
((USBH_DescHeader_t *)(void *)pbuf)->bLength);
return (pnext);
}
/**
* @brief USBH_CtlReq
* USBH_CtlReq sends a control request and provide the status after
* completion of the request
* @param phost: Host Handle
* @param req: Setup Request Structure
* @param buff: data buffer address to store the response
* @param length: length of the response
* @retval USBH Status
*/
USBH_StatusTypeDef USBH_CtlReq(USBH_HandleTypeDef *phost, uint8_t *buff,
uint16_t length)
{
USBH_StatusTypeDef status;
status = USBH_BUSY;
switch (phost->RequestState)
{
case CMD_SEND:
/* Start a SETUP transfer */
phost->Control.buff = buff;
phost->Control.length = length;
phost->Control.state = CTRL_SETUP;
phost->RequestState = CMD_WAIT;
status = USBH_BUSY;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
break;
case CMD_WAIT:
status = USBH_HandleControl(phost);
if ((status == USBH_OK) || (status == USBH_NOT_SUPPORTED))
{
/* Transaction completed, move control state to idle */
phost->RequestState = CMD_SEND;
phost->Control.state = CTRL_IDLE;
}
else if (status == USBH_FAIL)
{
/* Failure Mode */
phost->RequestState = CMD_SEND;
}
else
{
/* .. */
}
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
break;
default:
break;
}
return status;
}
/**
* @brief USBH_HandleControl
* Handles the USB control transfer state machine
* @param phost: Host Handle
* @retval USBH Status
*/
static USBH_StatusTypeDef USBH_HandleControl(USBH_HandleTypeDef *phost)
{
uint8_t direction;
USBH_StatusTypeDef status = USBH_BUSY;
USBH_URBStateTypeDef URB_Status = USBH_URB_IDLE;
switch (phost->Control.state)
{
case CTRL_SETUP:
/* send a SETUP packet */
USBH_CtlSendSetup(phost, (uint8_t *)(void *)phost->Control.setup.d8,
phost->Control.pipe_out);
phost->Control.state = CTRL_SETUP_WAIT;
break;
case CTRL_SETUP_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out);
/* case SETUP packet sent successfully */
if (URB_Status == USBH_URB_DONE)
{
direction = (phost->Control.setup.b.bmRequestType & USB_REQ_DIR_MASK);
/* check if there is a data stage */
if (phost->Control.setup.b.wLength.w != 0U)
{
if (direction == USB_D2H)
{
/* Data Direction is IN */
phost->Control.state = CTRL_DATA_IN;
}
else
{
/* Data Direction is OUT */
phost->Control.state = CTRL_DATA_OUT;
}
}
/* No DATA stage */
else
{
/* If there is No Data Transfer Stage */
if (direction == USB_D2H)
{
/* Data Direction is IN */
phost->Control.state = CTRL_STATUS_OUT;
}
else
{
/* Data Direction is OUT */
phost->Control.state = CTRL_STATUS_IN;
}
}
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else
{
if ((URB_Status == USBH_URB_ERROR) || (URB_Status == USBH_URB_NOTREADY))
{
phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
}
break;
case CTRL_DATA_IN:
/* Issue an IN token */
phost->Control.timer = (uint16_t)phost->Timer;
USBH_CtlReceiveData(phost, phost->Control.buff, phost->Control.length,
phost->Control.pipe_in);
phost->Control.state = CTRL_DATA_IN_WAIT;
break;
case CTRL_DATA_IN_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_in);
/* check is DATA packet transferred successfully */
if (URB_Status == USBH_URB_DONE)
{
phost->Control.state = CTRL_STATUS_OUT;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
/* manage error cases*/
if (URB_Status == USBH_URB_STALL)
{
/* In stall case, return to previous machine state*/
status = USBH_NOT_SUPPORTED;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else
{
if (URB_Status == USBH_URB_ERROR)
{
/* Device error */
phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
}
break;
case CTRL_DATA_OUT:
USBH_CtlSendData(phost, phost->Control.buff, phost->Control.length,
phost->Control.pipe_out, 1U);
phost->Control.timer = (uint16_t)phost->Timer;
phost->Control.state = CTRL_DATA_OUT_WAIT;
break;
case CTRL_DATA_OUT_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out);
if (URB_Status == USBH_URB_DONE)
{
/* If the Setup Pkt is sent successful, then change the state */
phost->Control.state = CTRL_STATUS_IN;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
/* handle error cases */
else if (URB_Status == USBH_URB_STALL)
{
/* In stall case, return to previous machine state*/
phost->Control.state = CTRL_STALLED;
status = USBH_NOT_SUPPORTED;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else if (URB_Status == USBH_URB_NOTREADY)
{
/* Nack received from device */
phost->Control.state = CTRL_DATA_OUT;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else
{
if (URB_Status == USBH_URB_ERROR)
{
/* device error */
phost->Control.state = CTRL_ERROR;
status = USBH_FAIL;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
}
break;
case CTRL_STATUS_IN:
/* Send 0 bytes out packet */
USBH_CtlReceiveData(phost, 0U, 0U, phost->Control.pipe_in);
phost->Control.timer = (uint16_t)phost->Timer;
phost->Control.state = CTRL_STATUS_IN_WAIT;
break;
case CTRL_STATUS_IN_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_in);
if (URB_Status == USBH_URB_DONE)
{
/* Control transfers completed, Exit the State Machine */
phost->Control.state = CTRL_COMPLETE;
status = USBH_OK;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else if (URB_Status == USBH_URB_ERROR)
{
phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else
{
if (URB_Status == USBH_URB_STALL)
{
/* Control transfers completed, Exit the State Machine */
status = USBH_NOT_SUPPORTED;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
}
break;
case CTRL_STATUS_OUT:
USBH_CtlSendData(phost, 0U, 0U, phost->Control.pipe_out, 1U);
phost->Control.timer = (uint16_t)phost->Timer;
phost->Control.state = CTRL_STATUS_OUT_WAIT;
break;
case CTRL_STATUS_OUT_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out);
if (URB_Status == USBH_URB_DONE)
{
status = USBH_OK;
phost->Control.state = CTRL_COMPLETE;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else if (URB_Status == USBH_URB_NOTREADY)
{
phost->Control.state = CTRL_STATUS_OUT;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
else
{
if (URB_Status == USBH_URB_ERROR)
{
phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1U)
phost->os_msg = (uint32_t)USBH_CONTROL_EVENT;
#if (osCMSIS < 0x20000U)
(void)osMessagePut(phost->os_event, phost->os_msg, 0U);
#else
(void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL);
#endif
#endif
}
}
break;
case CTRL_ERROR:
/*
After a halt condition is encountered or an error is detected by the
host, a control endpoint is allowed to recover by accepting the next Setup
PID; i.e., recovery actions via some other pipe are not required for control
endpoints. For the Default Control Pipe, a device reset will ultimately be
required to clear the halt or error condition if the next Setup PID is not
accepted.
*/
if (++phost->Control.errorcount <= USBH_MAX_ERROR_COUNT)
{
/* Do the transmission again, starting from SETUP Packet */
phost->Control.state = CTRL_SETUP;
phost->RequestState = CMD_SEND;
}
else
{
phost->pUser(phost, HOST_USER_UNRECOVERED_ERROR);
phost->Control.errorcount = 0U;
USBH_ErrLog("Control error: Device not responding");
/* Free control pipes */
USBH_FreePipe(phost, phost->Control.pipe_out);
USBH_FreePipe(phost, phost->Control.pipe_in);
phost->gState = HOST_IDLE;
status = USBH_FAIL;
}
break;
default:
break;
}
return status;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/