/** ****************************************************************************** * @file usbd_req.c * @author MCD Application Team * @brief This file provides the standard USB requests following chapter 9. ****************************************************************************** * @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 "usbd_ctlreq.h" #include "usbd_ioreq.h" /** @addtogroup STM32_USBD_STATE_DEVICE_LIBRARY * @{ */ /** @defgroup USBD_REQ * @brief USB standard requests module * @{ */ /** @defgroup USBD_REQ_Private_TypesDefinitions * @{ */ /** * @} */ /** @defgroup USBD_REQ_Private_Defines * @{ */ /** * @} */ /** @defgroup USBD_REQ_Private_Macros * @{ */ /** * @} */ /** @defgroup USBD_REQ_Private_Variables * @{ */ /** * @} */ /** @defgroup USBD_REQ_Private_FunctionPrototypes * @{ */ static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static uint8_t USBD_GetLen(uint8_t *buf); /** * @} */ /** @defgroup USBD_REQ_Private_Functions * @{ */ /** * @brief USBD_StdDevReq * Handle standard usb device requests * @param pdev: device instance * @param req: usb request * @retval status */ USBD_StatusTypeDef USBD_StdDevReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_CLASS: case USB_REQ_TYPE_VENDOR: ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); break; case USB_REQ_TYPE_STANDARD: switch (req->bRequest) { case USB_REQ_GET_DESCRIPTOR: USBD_GetDescriptor(pdev, req); break; case USB_REQ_SET_ADDRESS: USBD_SetAddress(pdev, req); break; case USB_REQ_SET_CONFIGURATION: ret = USBD_SetConfig(pdev, req); break; case USB_REQ_GET_CONFIGURATION: USBD_GetConfig(pdev, req); break; case USB_REQ_GET_STATUS: USBD_GetStatus(pdev, req); break; case USB_REQ_SET_FEATURE: USBD_SetFeature(pdev, req); break; case USB_REQ_CLEAR_FEATURE: USBD_ClrFeature(pdev, req); break; default: USBD_CtlError(pdev, req); break; } break; default: USBD_CtlError(pdev, req); break; } return ret; } /** * @brief USBD_StdItfReq * Handle standard usb interface requests * @param pdev: device instance * @param req: usb request * @retval status */ USBD_StatusTypeDef USBD_StdItfReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_CLASS: case USB_REQ_TYPE_VENDOR: case USB_REQ_TYPE_STANDARD: switch (pdev->dev_state) { case USBD_STATE_DEFAULT: case USBD_STATE_ADDRESSED: case USBD_STATE_CONFIGURED: if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) { ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); if ((req->wLength == 0U) && (ret == USBD_OK)) { (void)USBD_CtlSendStatus(pdev); } } else { USBD_CtlError(pdev, req); } break; default: USBD_CtlError(pdev, req); break; } break; default: USBD_CtlError(pdev, req); break; } return ret; } /** * @brief USBD_StdEPReq * Handle standard usb endpoint requests * @param pdev: device instance * @param req: usb request * @retval status */ USBD_StatusTypeDef USBD_StdEPReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_EndpointTypeDef *pep; uint8_t ep_addr; USBD_StatusTypeDef ret = USBD_OK; ep_addr = LOBYTE(req->wIndex); switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_CLASS: case USB_REQ_TYPE_VENDOR: ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); break; case USB_REQ_TYPE_STANDARD: switch (req->bRequest) { case USB_REQ_SET_FEATURE: switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) { (void)USBD_LL_StallEP(pdev, ep_addr); (void)USBD_LL_StallEP(pdev, 0x80U); } else { USBD_CtlError(pdev, req); } break; case USBD_STATE_CONFIGURED: if (req->wValue == USB_FEATURE_EP_HALT) { if ((ep_addr != 0x00U) && (ep_addr != 0x80U) && (req->wLength == 0x00U)) { (void)USBD_LL_StallEP(pdev, ep_addr); } } (void)USBD_CtlSendStatus(pdev); break; default: USBD_CtlError(pdev, req); break; } break; case USB_REQ_CLEAR_FEATURE: switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) { (void)USBD_LL_StallEP(pdev, ep_addr); (void)USBD_LL_StallEP(pdev, 0x80U); } else { USBD_CtlError(pdev, req); } break; case USBD_STATE_CONFIGURED: if (req->wValue == USB_FEATURE_EP_HALT) { if ((ep_addr & 0x7FU) != 0x00U) { (void)USBD_LL_ClearStallEP(pdev, ep_addr); } (void)USBD_CtlSendStatus(pdev); (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); } break; default: USBD_CtlError(pdev, req); break; } break; case USB_REQ_GET_STATUS: switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) { USBD_CtlError(pdev, req); break; } pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU] : \ &pdev->ep_out[ep_addr & 0x7FU]; pep->status = 0x0000U; (void)USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2U); break; case USBD_STATE_CONFIGURED: if ((ep_addr & 0x80U) == 0x80U) { if (pdev->ep_in[ep_addr & 0xFU].is_used == 0U) { USBD_CtlError(pdev, req); break; } } else { if (pdev->ep_out[ep_addr & 0xFU].is_used == 0U) { USBD_CtlError(pdev, req); break; } } pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU] : \ &pdev->ep_out[ep_addr & 0x7FU]; if ((ep_addr == 0x00U) || (ep_addr == 0x80U)) { pep->status = 0x0000U; } else if (USBD_LL_IsStallEP(pdev, ep_addr) != 0U) { pep->status = 0x0001U; } else { pep->status = 0x0000U; } (void)USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2U); break; default: USBD_CtlError(pdev, req); break; } break; default: USBD_CtlError(pdev, req); break; } break; default: USBD_CtlError(pdev, req); break; } return ret; } /** * @brief USBD_GetDescriptor * Handle Get Descriptor requests * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint16_t len = 0U; uint8_t *pbuf = NULL; uint8_t err = 0U; switch (req->wValue >> 8) { #if ((USBD_LPM_ENABLED == 1U) || (USBD_CLASS_BOS_ENABLED == 1U)) case USB_DESC_TYPE_BOS: if (pdev->pDesc->GetBOSDescriptor != NULL) { pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; #endif case USB_DESC_TYPE_DEVICE: pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len); break; case USB_DESC_TYPE_CONFIGURATION: if (pdev->dev_speed == USBD_SPEED_HIGH) { pbuf = pdev->pClass->GetHSConfigDescriptor(&len); pbuf[1] = USB_DESC_TYPE_CONFIGURATION; } else { pbuf = pdev->pClass->GetFSConfigDescriptor(&len); pbuf[1] = USB_DESC_TYPE_CONFIGURATION; } break; case USB_DESC_TYPE_STRING: switch ((uint8_t)(req->wValue)) { case USBD_IDX_LANGID_STR: if (pdev->pDesc->GetLangIDStrDescriptor != NULL) { pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; case USBD_IDX_MFC_STR: if (pdev->pDesc->GetManufacturerStrDescriptor != NULL) { pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; case USBD_IDX_PRODUCT_STR: if (pdev->pDesc->GetProductStrDescriptor != NULL) { pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; case USBD_IDX_SERIAL_STR: if (pdev->pDesc->GetSerialStrDescriptor != NULL) { pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; case USBD_IDX_CONFIG_STR: if (pdev->pDesc->GetConfigurationStrDescriptor != NULL) { pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; case USBD_IDX_INTERFACE_STR: if (pdev->pDesc->GetInterfaceStrDescriptor != NULL) { pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len); } else { USBD_CtlError(pdev, req); err++; } break; default: #if (USBD_SUPPORT_USER_STRING_DESC == 1U) if (pdev->pClass->GetUsrStrDescriptor != NULL) { pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue), &len); } else { USBD_CtlError(pdev, req); err++; } #elif (USBD_CLASS_USER_STRING_DESC == 1U) if (pdev->pDesc->GetUserStrDescriptor != NULL) { pbuf = pdev->pDesc->GetUserStrDescriptor(pdev->dev_speed, (req->wValue), &len); } else { USBD_CtlError(pdev, req); err++; } #else USBD_CtlError(pdev, req); err++; #endif break; } break; case USB_DESC_TYPE_DEVICE_QUALIFIER: if (pdev->dev_speed == USBD_SPEED_HIGH) { pbuf = pdev->pClass->GetDeviceQualifierDescriptor(&len); } else { USBD_CtlError(pdev, req); err++; } break; case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: if (pdev->dev_speed == USBD_SPEED_HIGH) { pbuf = pdev->pClass->GetOtherSpeedConfigDescriptor(&len); pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; } else { USBD_CtlError(pdev, req); err++; } break; default: USBD_CtlError(pdev, req); err++; break; } if (err != 0U) { return; } else { if (req->wLength != 0U) { if (len != 0U) { len = MIN(len, req->wLength); (void)USBD_CtlSendData(pdev, pbuf, len); } else { USBD_CtlError(pdev, req); } } else { (void)USBD_CtlSendStatus(pdev); } } } /** * @brief USBD_SetAddress * Set device address * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint8_t dev_addr; if ((req->wIndex == 0U) && (req->wLength == 0U) && (req->wValue < 128U)) { dev_addr = (uint8_t)(req->wValue) & 0x7FU; if (pdev->dev_state == USBD_STATE_CONFIGURED) { USBD_CtlError(pdev, req); } else { pdev->dev_address = dev_addr; (void)USBD_LL_SetUSBAddress(pdev, dev_addr); (void)USBD_CtlSendStatus(pdev); if (dev_addr != 0U) { pdev->dev_state = USBD_STATE_ADDRESSED; } else { pdev->dev_state = USBD_STATE_DEFAULT; } } } else { USBD_CtlError(pdev, req); } } /** * @brief USBD_SetConfig * Handle Set device configuration request * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; static uint8_t cfgidx; cfgidx = (uint8_t)(req->wValue); if (cfgidx > USBD_MAX_NUM_CONFIGURATION) { USBD_CtlError(pdev, req); return USBD_FAIL; } switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: if (cfgidx != 0U) { pdev->dev_config = cfgidx; ret = USBD_SetClassConfig(pdev, cfgidx); if (ret != USBD_OK) { USBD_CtlError(pdev, req); } else { (void)USBD_CtlSendStatus(pdev); pdev->dev_state = USBD_STATE_CONFIGURED; } } else { (void)USBD_CtlSendStatus(pdev); } break; case USBD_STATE_CONFIGURED: if (cfgidx == 0U) { pdev->dev_state = USBD_STATE_ADDRESSED; pdev->dev_config = cfgidx; (void)USBD_ClrClassConfig(pdev, cfgidx); (void)USBD_CtlSendStatus(pdev); } else if (cfgidx != pdev->dev_config) { /* Clear old configuration */ (void)USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); /* set new configuration */ pdev->dev_config = cfgidx; ret = USBD_SetClassConfig(pdev, cfgidx); if (ret != USBD_OK) { USBD_CtlError(pdev, req); (void)USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); pdev->dev_state = USBD_STATE_ADDRESSED; } else { (void)USBD_CtlSendStatus(pdev); } } else { (void)USBD_CtlSendStatus(pdev); } break; default: USBD_CtlError(pdev, req); (void)USBD_ClrClassConfig(pdev, cfgidx); ret = USBD_FAIL; break; } return ret; } /** * @brief USBD_GetConfig * Handle Get device configuration request * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { if (req->wLength != 1U) { USBD_CtlError(pdev, req); } else { switch (pdev->dev_state) { case USBD_STATE_DEFAULT: case USBD_STATE_ADDRESSED: pdev->dev_default_config = 0U; (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_default_config, 1U); break; case USBD_STATE_CONFIGURED: (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config, 1U); break; default: USBD_CtlError(pdev, req); break; } } } /** * @brief USBD_GetStatus * Handle Get Status request * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { switch (pdev->dev_state) { case USBD_STATE_DEFAULT: case USBD_STATE_ADDRESSED: case USBD_STATE_CONFIGURED: if (req->wLength != 0x2U) { USBD_CtlError(pdev, req); break; } #if (USBD_SELF_POWERED == 1U) pdev->dev_config_status = USB_CONFIG_SELF_POWERED; #else pdev->dev_config_status = 0U; #endif if (pdev->dev_remote_wakeup != 0U) { pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; } (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config_status, 2U); break; default: USBD_CtlError(pdev, req); break; } } /** * @brief USBD_SetFeature * Handle Set device feature request * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) { pdev->dev_remote_wakeup = 1U; (void)USBD_CtlSendStatus(pdev); } } /** * @brief USBD_ClrFeature * Handle clear device feature request * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { switch (pdev->dev_state) { case USBD_STATE_DEFAULT: case USBD_STATE_ADDRESSED: case USBD_STATE_CONFIGURED: if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) { pdev->dev_remote_wakeup = 0U; (void)USBD_CtlSendStatus(pdev); } break; default: USBD_CtlError(pdev, req); break; } } /** * @brief USBD_ParseSetupRequest * Copy buffer into setup structure * @param pdev: device instance * @param req: usb request * @retval None */ void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata) { uint8_t *pbuff = pdata; req->bmRequest = *(uint8_t *)(pbuff); pbuff++; req->bRequest = *(uint8_t *)(pbuff); pbuff++; req->wValue = SWAPBYTE(pbuff); pbuff++; pbuff++; req->wIndex = SWAPBYTE(pbuff); pbuff++; pbuff++; req->wLength = SWAPBYTE(pbuff); } /** * @brief USBD_CtlError * Handle USB low level Error * @param pdev: device instance * @param req: usb request * @retval None */ void USBD_CtlError(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { UNUSED(req); (void)USBD_LL_StallEP(pdev, 0x80U); (void)USBD_LL_StallEP(pdev, 0U); } /** * @brief USBD_GetString * Convert Ascii string into unicode one * @param desc : descriptor buffer * @param unicode : Formatted string buffer (unicode) * @param len : descriptor length * @retval None */ void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len) { uint8_t idx = 0U; uint8_t *pdesc; if (desc == NULL) { return; } pdesc = desc; *len = ((uint16_t)USBD_GetLen(pdesc) * 2U) + 2U; unicode[idx] = *(uint8_t *)len; idx++; unicode[idx] = USB_DESC_TYPE_STRING; idx++; while (*pdesc != (uint8_t)'\0') { unicode[idx] = *pdesc; pdesc++; idx++; unicode[idx] = 0U; idx++; } } /** * @brief USBD_GetLen * return the string length * @param buf : pointer to the ascii string buffer * @retval string length */ static uint8_t USBD_GetLen(uint8_t *buf) { uint8_t len = 0U; uint8_t *pbuff = buf; while (*pbuff != (uint8_t)'\0') { len++; pbuff++; } return len; } /** * @} */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/