/** ****************************************************************************** * @file usbd_req.c * @author MCD Application Team * @version V2.4.2 * @date 11-December-2015 * @brief This file provides the standard USB requests following chapter 9. ****************************************************************************** * @attention * *

© COPYRIGHT 2015 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_ctlreq.h" #include "usbd_ioreq.h" static USBD_StatusTypeDef USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static USBD_StatusTypeDef USBD_GetMicrosoftDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static uint8_t USBD_GetLen(uint8_t *buf); /** * @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; if(req->bRequest == USB_REQ_VENDOR_FEATURE) // Debug Добавить ответ только во время работы с вендор протоколом { ret = USBD_GetMicrosoftDescriptor(pdev, req); return ret; } // ~~~~~~~~~~~~~ Legacy support ~~~~~~~~~~~~~~~~~~~~~~~ switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_VENDOR: { ret = USBD_StdEPReq( pdev, req ); return ret; } break; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ switch (req->bRequest) { case USB_REQ_GET_DESCRIPTOR: ret = USBD_GetDescriptor(pdev, req); break; case USB_REQ_SET_ADDRESS: ret = USBD_SetAddress(pdev, req); break; case USB_REQ_SET_CONFIGURATION: ret = USBD_SetConfig(pdev, req); break; case USB_REQ_GET_CONFIGURATION: ret = USBD_GetConfig(pdev, req); break; case USB_REQ_GET_INTERFACE: ret = USBD_FAIL; break; case USB_REQ_GET_STATUS: ret = USBD_GetStatus(pdev, req); break; case USB_REQ_SET_FEATURE: ret = USBD_SetFeature(pdev, req); break; case USB_REQ_CLEAR_FEATURE: ret = USBD_ClrFeature(pdev, req); break; default: ret = USBD_FAIL; 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 (pdev->dev_state) { case USBD_STATE_CONFIGURED: { if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) { ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); if( USBD_OK == ret ) { if( req->wLength == 0 ) { USBD_CtlSendStatus(pdev); } } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } break; default: ret = USBD_FAIL; 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) { uint8_t ep_addr; USBD_StatusTypeDef ret = USBD_OK; USBD_EndpointTypeDef *pep; ep_addr = LOBYTE(req->wIndex); /* Check if it is a USB-class request or a USB-vendor request */ switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_CLASS: case USB_REQ_TYPE_VENDOR: { // pass the request to the class handler ret = (USBD_StatusTypeDef)pdev->pClass->Setup (pdev, req); return ret; } break; } switch (req->bRequest) { case USB_REQ_SET_FEATURE : { switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: { if ((ep_addr != 0x00) && (ep_addr != 0x80)) { USBD_LL_StallEP(pdev, ep_addr); } } break; case USBD_STATE_CONFIGURED: { if (req->wValue == USB_FEATURE_EP_HALT) { if ((ep_addr != 0x00) && (ep_addr != 0x80)) { USBD_LL_StallEP(pdev, ep_addr); } } ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); if( USBD_OK == ret ) { USBD_CtlSendStatus(pdev); } } break; default: ret = USBD_FAIL; break; } } break; case USB_REQ_CLEAR_FEATURE: { switch( pdev->dev_state ) { case USBD_STATE_ADDRESSED: { if ((ep_addr != 0x00) && (ep_addr != 0x80)) { USBD_LL_StallEP(pdev, ep_addr); } } break; case USBD_STATE_CONFIGURED: { if (req->wValue == USB_FEATURE_EP_HALT) { if ((ep_addr & 0x7F) != 0x00) { // First: clear EP status by default (VALID STATE) USBD_LL_ClearStallEP(pdev, ep_addr); // then: call user handler (user can override EP status now) ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); } if( USBD_OK == ret ) { USBD_CtlSendStatus(pdev); } } } break; default: ret = USBD_FAIL; break; } } break; case USB_REQ_GET_STATUS: { switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: { if ((ep_addr & 0x7F) != 0x00) { USBD_LL_StallEP(pdev, ep_addr); } } break; case USBD_STATE_CONFIGURED: { pep = ((ep_addr & 0x80) == 0x80) ? &pdev->ep_in[ep_addr & 0x7F]:\ &pdev->ep_out[ep_addr & 0x7F]; if(USBD_LL_IsStallEP(pdev, ep_addr)) { pep->status = 0x0001; } else { pep->status = 0x0000; } USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2); } break; default: ret = USBD_FAIL; break; } } break; default: ret = USBD_FAIL; break; } return ret; } /** * @brief USBD_GetMicrosoftDescriptor * Handle Get Microsoft Descriptor requests * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_GetMicrosoftDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint16_t len; const uint8_t *pbuf = NULL; USBD_StatusTypeDef ret = USBD_OK; switch(req->wIndex) { case USB_REQ_EXT_COMPAT_ID_OS_FEATURE_DESCRIPTOR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_ExtCompatIdOsFeat, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetExtCompatIdOsFeatureDescriptor ); #endif } break; case USB_REQ_EXT_PROPERTIES_OS_FEATURE_DESCRIPTOR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_ExtPropertiesOsFeat, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetExtPropertiesOsFeatureDescriptor ); #endif } break; } if( USBD_FAIL != ret ) { if( (len != 0) && (req->wLength != 0) ) { len = MIN(len , req->wLength); USBD_CtlSendData(pdev, pbuf, len); } else { ret = USBD_FAIL; } } return ret; } /** * @brief USBD_GetDescriptor * Handle Get Descriptor requests * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint16_t len; const uint8_t *pbuf = NULL; USBD_StatusTypeDef ret = USBD_OK; switch (req->wValue >> 8) { case USB_DESC_TYPE_DEVICE: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_Device, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetDeviceDescriptor ); #endif } break; case USB_DESC_TYPE_CONFIGURATION: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_Config, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetConfigDescriptor ); #endif } break; case USB_DESC_TYPE_STRING: { switch ((uint8_t)(req->wValue)) { case USBD_IDX_LANGID_STR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_LangIdStr, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetLangIDStrDescriptor ); #endif } break; case USBD_IDX_MFC_STR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_ManufacturerStr, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetManufacturerStrDescriptor ); #endif } break; case USBD_IDX_PRODUCT_STR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_ProductStr, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetProductStrDescriptor ); #endif } break; #if USBD_IDX_SERIAL_STR != 0 case USBD_IDX_SERIAL_STR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_SerialStr, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetSerialStrDescriptor ); #endif } break; #endif case USBD_IDX_CONFIG_STR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_ConfigurationStr, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetConfigurationStrDescriptor ); #endif } break; case USBD_IDX_INTERFACE_STR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_InterfaceStr, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetInterfaceStrDescriptor ); #endif } break; case USB_REQ_MS_OS_STRING_DESCRIPTOR: { #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_MsOsString, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pbuf, len, GetMsOsStringDescriptor ); #endif } break; default: { #if (USBD_SUPPORT_USER_STRING == 1) #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 if( NULL != pdev->pDesc && pdev->pDesc->GetUsrStrDescriptor ) pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue), &len); else if( NULL != pdev->pClass->pDesc && pdev->pClass->pDesc->GetUsrStrDescriptor ) pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue), &len); else ret = USBD_FAIL; #else if( NULL != pdev->pClass->pDesc && pdev->pClass->pDesc->GetUsrStrDescriptor ) pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue), &len); else ret = USBD_FAIL; #endif #else ret = USBD_FAIL; #endif } break; } } break; default: ret = USBD_FAIL; break; } if( USBD_FAIL != ret ) { if( (len != 0) && (req->wLength != 0) ) { len = MIN(len , req->wLength); USBD_CtlSendData(pdev, pbuf, len); } else { ret = USBD_FAIL; } } return ret; } /** * @brief USBD_SetAddress * Set device address * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint8_t dev_addr; USBD_StatusTypeDef ret = USBD_OK; if ((req->wIndex == 0) && (req->wLength == 0)) { dev_addr = (uint8_t)(req->wValue) & 0x7F; if (pdev->dev_state == USBD_STATE_CONFIGURED) { ret = USBD_FAIL; } else { pdev->dev_address = dev_addr; USBD_LL_SetUSBAddress(pdev, dev_addr); USBD_CtlSendStatus(pdev); if (dev_addr != 0) { pdev->dev_state = USBD_STATE_ADDRESSED; } else { pdev->dev_state = USBD_STATE_DEFAULT; } } } else { ret = USBD_FAIL; } return ret; } /** * @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) { static uint8_t cfgidx; USBD_StatusTypeDef ret = USBD_OK; cfgidx = (uint8_t)(req->wValue); if (cfgidx > USBD_MAX_NUM_CONFIGURATION ) { ret = USBD_FAIL; } else { switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: { if(cfgidx > 0) { pdev->dev_config = cfgidx; pdev->dev_state = USBD_STATE_CONFIGURED; ret = USBD_SetClassConfig(pdev, cfgidx); if( USBD_OK == ret ) { USBD_CtlSendStatus(pdev); } } else { USBD_CtlSendStatus(pdev); } } break; case USBD_STATE_CONFIGURED: { if (cfgidx == 0) { pdev->dev_state = USBD_STATE_ADDRESSED; pdev->dev_config = cfgidx; USBD_ClrClassConfig(pdev, cfgidx); USBD_CtlSendStatus(pdev); } else if (cfgidx != pdev->dev_config) { /* Clear old configuration */ USBD_ClrClassConfig(pdev, pdev->dev_config); /* set new configuration */ pdev->dev_config = cfgidx; ret = USBD_SetClassConfig(pdev, cfgidx); if( USBD_OK == ret ) { USBD_CtlSendStatus(pdev); } } else { USBD_CtlSendStatus(pdev); } } break; default: 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 USBD_StatusTypeDef USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; if (req->wLength != 1) { ret = USBD_FAIL; } else { switch (pdev->dev_state ) { case USBD_STATE_ADDRESSED: { pdev->dev_default_config = 0; USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_default_config, 1); } break; case USBD_STATE_CONFIGURED: { USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config, 1); } break; default: ret = USBD_FAIL; break; } } return ret; } /** * @brief USBD_GetStatus * Handle Get Status request * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: case USBD_STATE_CONFIGURED: { #if ( USBD_SELF_POWERED == 1) pdev->dev_config_status = USB_CONFIG_SELF_POWERED; #else pdev->dev_config_status = 0; #endif if (pdev->dev_remote_wakeup) { pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; } USBD_CtlSendData(pdev, (const uint8_t *)& pdev->dev_config_status, 2); } break; default : ret = USBD_FAIL; break; } return ret; } /** * @brief USBD_SetFeature * Handle Set device feature request * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) { pdev->dev_remote_wakeup = 1; pdev->pClass->Setup(pdev, req); USBD_CtlSendStatus(pdev); } else { ret = USBD_FAIL; } return ret; } /** * @brief USBD_ClrFeature * Handle clear device feature request * @param pdev: device instance * @param req: usb request * @retval status */ static USBD_StatusTypeDef USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_StatusTypeDef ret = USBD_OK; switch (pdev->dev_state) { case USBD_STATE_ADDRESSED: case USBD_STATE_CONFIGURED: { if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) { pdev->dev_remote_wakeup = 0; pdev->pClass->Setup(pdev, req); USBD_CtlSendStatus(pdev); } else { ret = USBD_FAIL; } } break; default : ret = USBD_FAIL; break; } return ret; } /** * @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) { req->bmRequest = *(uint8_t *) (pdata); req->bRequest = *(uint8_t *) (pdata + 1); req->wValue = SWAPBYTE (pdata + 2); req->wIndex = SWAPBYTE (pdata + 4); req->wLength = SWAPBYTE (pdata + 6); } /** * @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) { USBD_LL_StallEP(pdev, 0x80); USBD_LL_StallEP(pdev, 0); } /** * @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 != '\0') { 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 != '\0') { len++; buf++; } return len; }