/** ****************************************************************************** * @file usbd_req.c * @author MCD Application Team * @version V1.1.0 * @date 19-March-2012 * @brief This file provides the standard USB requests following chapter 9. ****************************************************************************** * @attention * *

© COPYRIGHT 2012 STMicroelectronics

* * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.st.com/software_license_agreement_liberty_v2 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "usbd_req.h" #include "usbd_ioreq.h" #include "usbd_desc.h" /** @addtogroup STM32_USB_OTG_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 * @{ */ extern __IO USB_OTG_DCTL_TypeDef SET_TEST_MODE; #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ __ALIGN_BEGIN uint32_t USBD_ep_status __ALIGN_END = 0; #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ __ALIGN_BEGIN uint32_t USBD_default_cfg __ALIGN_END = 0; #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ __ALIGN_BEGIN uint32_t USBD_cfg_status __ALIGN_END = 0; #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ __ALIGN_BEGIN uint8_t USBD_StrDesc[USB_MAX_STR_DESC_SIZ] __ALIGN_END ; /** * @} */ /** @defgroup USBD_REQ_Private_FunctionPrototypes * @{ */ static void USBD_GetDescriptor(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req); static void USBD_SetAddress(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req); static void USBD_SetConfig(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req); static void USBD_GetConfig(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req); static void USBD_GetStatus(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req); static void USBD_SetFeature(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req); static void USBD_ClrFeature(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *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_Status USBD_StdDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { USBD_Status ret = USBD_OK; 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: 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; } return ret; } /** * @brief USBD_StdItfReq * Handle standard usb interface requests * @param pdev: USB OTG device instance * @param req: usb request * @retval status */ USBD_Status USBD_StdItfReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { USBD_Status ret = USBD_OK; switch (pdev->dev.device_status) { case USB_OTG_CONFIGURED: if (LOBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) { pdev->dev.class_cb->Setup (pdev, req); if((req->wLength == 0)&& (ret == USBD_OK)) { USBD_CtlSendStatus(pdev); } } else { USBD_CtlError(pdev , req); } break; default: USBD_CtlError(pdev , req); break; } return ret; } /** * @brief USBD_StdEPReq * Handle standard usb endpoint requests * @param pdev: USB OTG device instance * @param req: usb request * @retval status */ USBD_Status USBD_StdEPReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { uint8_t ep_addr; USBD_Status ret = USBD_OK; ep_addr = LOBYTE(req->wIndex); switch (req->bRequest) { case USB_REQ_SET_FEATURE : switch (pdev->dev.device_status) { case USB_OTG_ADDRESSED: if ((ep_addr != 0x00) && (ep_addr != 0x80)) { DCD_EP_Stall(pdev , ep_addr); } break; case USB_OTG_CONFIGURED: if (req->wValue == USB_FEATURE_EP_HALT) { if ((ep_addr != 0x00) && (ep_addr != 0x80)) { DCD_EP_Stall(pdev , ep_addr); } } pdev->dev.class_cb->Setup (pdev, req); USBD_CtlSendStatus(pdev); break; default: USBD_CtlError(pdev , req); break; } break; case USB_REQ_CLEAR_FEATURE : switch (pdev->dev.device_status) { case USB_OTG_ADDRESSED: if ((ep_addr != 0x00) && (ep_addr != 0x80)) { DCD_EP_Stall(pdev , ep_addr); } break; case USB_OTG_CONFIGURED: if (req->wValue == USB_FEATURE_EP_HALT) { if ((ep_addr != 0x00) && (ep_addr != 0x80)) { DCD_EP_ClrStall(pdev , ep_addr); pdev->dev.class_cb->Setup (pdev, req); } USBD_CtlSendStatus(pdev); } break; default: USBD_CtlError(pdev , req); break; } break; case USB_REQ_GET_STATUS: switch (pdev->dev.device_status) { case USB_OTG_ADDRESSED: if ((ep_addr != 0x00) && (ep_addr != 0x80)) { DCD_EP_Stall(pdev , ep_addr); } break; case USB_OTG_CONFIGURED: if ((ep_addr & 0x80)== 0x80) { if(pdev->dev.in_ep[ep_addr & 0x7F].is_stall) { USBD_ep_status = 0x0001; } else { USBD_ep_status = 0x0000; } } else if ((ep_addr & 0x80)== 0x00) { if(pdev->dev.out_ep[ep_addr].is_stall) { USBD_ep_status = 0x0001; } else { USBD_ep_status = 0x0000; } } USBD_CtlSendData (pdev, (uint8_t *)&USBD_ep_status, 2); break; default: USBD_CtlError(pdev , req); break; } break; default: break; } return ret; } /** * @brief USBD_GetDescriptor * Handle Get Descriptor requests * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_GetDescriptor(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { uint16_t len; uint8_t *pbuf; switch (req->wValue >> 8) { case USB_DESC_TYPE_DEVICE: pbuf = pdev->dev.usr_device->GetDeviceDescriptor(pdev->cfg.speed, &len); if ((req->wLength == 64) ||( pdev->dev.device_status == USB_OTG_DEFAULT)) { len = 8; } break; case USB_DESC_TYPE_CONFIGURATION: pbuf = (uint8_t *)pdev->dev.class_cb->GetConfigDescriptor(pdev->cfg.speed, &len); #ifdef USB_OTG_HS_CORE if((pdev->cfg.speed == USB_OTG_SPEED_FULL )&& (pdev->cfg.phy_itface == USB_OTG_ULPI_PHY)) { pbuf = (uint8_t *)pdev->dev.class_cb->GetOtherConfigDescriptor(pdev->cfg.speed, &len); } #endif pbuf[1] = USB_DESC_TYPE_CONFIGURATION; pdev->dev.pConfig_descriptor = pbuf; break; case USB_DESC_TYPE_STRING: switch ((uint8_t)(req->wValue)) { case USBD_IDX_LANGID_STR: pbuf = pdev->dev.usr_device->GetLangIDStrDescriptor(pdev->cfg.speed, &len); break; case USBD_IDX_MFC_STR: pbuf = pdev->dev.usr_device->GetManufacturerStrDescriptor(pdev->cfg.speed, &len); break; case USBD_IDX_PRODUCT_STR: pbuf = pdev->dev.usr_device->GetProductStrDescriptor(pdev->cfg.speed, &len); break; case USBD_IDX_SERIAL_STR: pbuf = pdev->dev.usr_device->GetSerialStrDescriptor(pdev->cfg.speed, &len); break; case USBD_IDX_CONFIG_STR: pbuf = pdev->dev.usr_device->GetConfigurationStrDescriptor(pdev->cfg.speed, &len); break; case USBD_IDX_INTERFACE_STR: pbuf = pdev->dev.usr_device->GetInterfaceStrDescriptor(pdev->cfg.speed, &len); break; default: #ifdef USB_SUPPORT_USER_STRING_DESC pbuf = pdev->dev.class_cb->GetUsrStrDescriptor(pdev->cfg.speed, (req->wValue) , &len); break; #else USBD_CtlError(pdev , req); return; #endif /* USBD_CtlError(pdev , req); */ } break; case USB_DESC_TYPE_DEVICE_QUALIFIER: #ifdef USB_OTG_HS_CORE if(pdev->cfg.speed == USB_OTG_SPEED_HIGH ) { pbuf = (uint8_t *)pdev->dev.class_cb->GetConfigDescriptor(pdev->cfg.speed, &len); USBD_DeviceQualifierDesc[4]= pbuf[14]; USBD_DeviceQualifierDesc[5]= pbuf[15]; USBD_DeviceQualifierDesc[6]= pbuf[16]; pbuf = USBD_DeviceQualifierDesc; len = USB_LEN_DEV_QUALIFIER_DESC; break; } else { USBD_CtlError(pdev , req); return; } #else USBD_CtlError(pdev , req); return; #endif case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: #ifdef USB_OTG_HS_CORE if(pdev->cfg.speed == USB_OTG_SPEED_HIGH ) { pbuf = (uint8_t *)pdev->dev.class_cb->GetOtherConfigDescriptor(pdev->cfg.speed, &len); pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; break; } else { USBD_CtlError(pdev , req); return; } #else USBD_CtlError(pdev , req); return; #endif default: USBD_CtlError(pdev , req); return; } if((len != 0)&& (req->wLength != 0)) { len = MIN(len , req->wLength); USBD_CtlSendData (pdev, pbuf, len); } } /** * @brief USBD_SetAddress * Set device address * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_SetAddress(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { uint8_t dev_addr; if ((req->wIndex == 0) && (req->wLength == 0)) { dev_addr = (uint8_t)(req->wValue) & 0x7F; if (pdev->dev.device_status == USB_OTG_CONFIGURED) { USBD_CtlError(pdev , req); } else { pdev->dev.device_address = dev_addr; DCD_EP_SetAddress(pdev, dev_addr); USBD_CtlSendStatus(pdev); if (dev_addr != 0) { pdev->dev.device_status = USB_OTG_ADDRESSED; } else { pdev->dev.device_status = USB_OTG_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 void USBD_SetConfig(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { static uint8_t cfgidx; cfgidx = (uint8_t)(req->wValue); if (cfgidx > USBD_CFG_MAX_NUM ) { USBD_CtlError(pdev , req); } else { switch (pdev->dev.device_status) { case USB_OTG_ADDRESSED: if (cfgidx) { pdev->dev.device_config = cfgidx; pdev->dev.device_status = USB_OTG_CONFIGURED; USBD_SetCfg(pdev , cfgidx); USBD_CtlSendStatus(pdev); } else { USBD_CtlSendStatus(pdev); } break; case USB_OTG_CONFIGURED: if (cfgidx == 0) { pdev->dev.device_status = USB_OTG_ADDRESSED; pdev->dev.device_config = cfgidx; USBD_ClrCfg(pdev , cfgidx); USBD_CtlSendStatus(pdev); } else if (cfgidx != pdev->dev.device_config) { /* Clear old configuration */ USBD_ClrCfg(pdev , pdev->dev.device_config); /* set new configuration */ pdev->dev.device_config = cfgidx; USBD_SetCfg(pdev , cfgidx); USBD_CtlSendStatus(pdev); } else { USBD_CtlSendStatus(pdev); } break; default: USBD_CtlError(pdev , req); break; } } } /** * @brief USBD_GetConfig * Handle Get device configuration request * @param pdev: device instance * @param req: usb request * @retval status */ static void USBD_GetConfig(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { if (req->wLength != 1) { USBD_CtlError(pdev , req); } else { switch (pdev->dev.device_status ) { case USB_OTG_ADDRESSED: USBD_CtlSendData (pdev, (uint8_t *)&USBD_default_cfg, 1); break; case USB_OTG_CONFIGURED: USBD_CtlSendData (pdev, &pdev->dev.device_config, 1); 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(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { switch (pdev->dev.device_status) { case USB_OTG_ADDRESSED: case USB_OTG_CONFIGURED: #ifdef USBD_SELF_POWERED USBD_cfg_status = USB_CONFIG_SELF_POWERED; #else USBD_cfg_status = 0x00; #endif if (pdev->dev.DevRemoteWakeup) { USBD_cfg_status |= USB_CONFIG_REMOTE_WAKEUP; } USBD_CtlSendData (pdev, (uint8_t *)&USBD_cfg_status, 2); 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(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { USB_OTG_DCTL_TypeDef dctl; uint8_t test_mode = 0; if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) { pdev->dev.DevRemoteWakeup = 1; pdev->dev.class_cb->Setup (pdev, req); USBD_CtlSendStatus(pdev); } else if ((req->wValue == USB_FEATURE_TEST_MODE) && ((req->wIndex & 0xFF) == 0)) { dctl.d32 = USB_OTG_READ_REG32(&pdev->regs.DREGS->DCTL); test_mode = req->wIndex >> 8; switch (test_mode) { case 1: // TEST_J dctl.b.tstctl = 1; break; case 2: // TEST_K dctl.b.tstctl = 2; break; case 3: // TEST_SE0_NAK dctl.b.tstctl = 3; break; case 4: // TEST_PACKET dctl.b.tstctl = 4; break; case 5: // TEST_FORCE_ENABLE dctl.b.tstctl = 5; break; } SET_TEST_MODE = dctl; pdev->dev.test_mode = 1; 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(USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { switch (pdev->dev.device_status) { case USB_OTG_ADDRESSED: case USB_OTG_CONFIGURED: if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) { pdev->dev.DevRemoteWakeup = 0; pdev->dev.class_cb->Setup (pdev, req); 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( USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { req->bmRequest = *(uint8_t *) (pdev->dev.setup_packet); req->bRequest = *(uint8_t *) (pdev->dev.setup_packet + 1); req->wValue = SWAPBYTE (pdev->dev.setup_packet + 2); req->wIndex = SWAPBYTE (pdev->dev.setup_packet + 4); req->wLength = SWAPBYTE (pdev->dev.setup_packet + 6); pdev->dev.in_ep[0].ctl_data_len = req->wLength ; pdev->dev.device_state = USB_OTG_EP0_SETUP; } /** * @brief USBD_CtlError * Handle USB low level Error * @param pdev: device instance * @param req: usb request * @retval None */ void USBD_CtlError( USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req) { DCD_EP_Stall(pdev , 0x80); DCD_EP_Stall(pdev , 0); USB_OTG_EP0_OutStart(pdev); } /** * @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 = 0; if (desc != NULL) { *len = USBD_GetLen(desc) * 2 + 2; unicode[idx++] = *len; unicode[idx++] = USB_DESC_TYPE_STRING; while (*desc != NULL) { unicode[idx++] = *desc++; unicode[idx++] = 0x00; } } } /** * @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 = 0; while (*buf != NULL) { len++; buf++; } return len; } /** * @} */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/