STM32 USB学习笔记8

主机环境:Windows 7 SP1

开发环境:MDK5.14

目标板:STM32F103C8T6

开发库:STM32F1Cube库和STM32_USB_Device_Library

现在分析USB器件库核心文件的最后一个文件usbd_ctlreq,该文件提供了标准请求的处理,跟USB2.0协议的第九章节紧密关联。在Setup阶段根据bmRequest字段的内容分为:设备请求、接口请求、端点请求。并根据不同的请求调用不同的函数体,USB2.0协议中定义的标清请求有以下几种

通过第一个竖栏可以看出同一个请求代码可以对应多个接收者,例如CLEAR_FEATURE请求可以是设备请求也可以是接口请求,同样也可以是端点请求。对应的各个请求代码,如下

在usbd_def.h文件中可以找到与之对应的定义,如下:

#define  USB_REQ_GET_STATUS                             0x00
#define  USB_REQ_CLEAR_FEATURE                          0x01
#define  USB_REQ_SET_FEATURE                            0x03
#define  USB_REQ_SET_ADDRESS                            0x05
#define  USB_REQ_GET_DESCRIPTOR                         0x06
#define  USB_REQ_SET_DESCRIPTOR                         0x07
#define  USB_REQ_GET_CONFIGURATION                      0x08
#define  USB_REQ_SET_CONFIGURATION                      0x09
#define  USB_REQ_GET_INTERFACE                          0x0A
#define  USB_REQ_SET_INTERFACE                          0x0B
#define  USB_REQ_SYNCH_FRAME                            0x0C
首先看一下标准设备请求的处理,
/**
* @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->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;
}
符合标准的设备请求通过Table 9-3可以看出一共有8种,这里只处理了7种,SET_DESCRIPTOR是没有处理的,当请求不是这7种请求的任何一种时通过USBD_CtlError()函数回应给USB主机一个STALL表明请求错误发生。首先看第一个请求获取描述符请求的处理,如下:

/**
* @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;
  uint8_t *pbuf;
  
    
  switch (req->wValue >> 8)
  { 
#if (USBD_LPM_ENABLED == 1)
  case USB_DESC_TYPE_BOS:
    pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len);
    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   = (uint8_t *)pdev->pClass->GetHSConfigDescriptor(&len);
      pbuf[1] = USB_DESC_TYPE_CONFIGURATION;
    }
    else
    {
      pbuf   = (uint8_t *)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:
     pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len);        
      break;
      
    case USBD_IDX_MFC_STR:
      pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len);
      break;
      
    case USBD_IDX_PRODUCT_STR:
      pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len);
      break;
      
    case USBD_IDX_SERIAL_STR:
      pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len);
      break;
      
    case USBD_IDX_CONFIG_STR:
      pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len);
      break;
      
    case USBD_IDX_INTERFACE_STR:
      pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len);
      break;
      
    default:
#if (USBD_SUPPORT_USER_STRING == 1)
      pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue) , &len);
      break;
#else      
       USBD_CtlError(pdev , req);
      return;
#endif   
    }
    break;
  case USB_DESC_TYPE_DEVICE_QUALIFIER:                   

    if(pdev->dev_speed == USBD_SPEED_HIGH  )   
    {
      pbuf   = (uint8_t *)pdev->pClass->GetDeviceQualifierDescriptor(&len);
      break;
    }
    else
    {
      USBD_CtlError(pdev , req);
      return;
    } 

  case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION:
    if(pdev->dev_speed == USBD_SPEED_HIGH  )   
    {
      pbuf   = (uint8_t *)pdev->pClass->GetOtherSpeedConfigDescriptor(&len);
      pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION;
      break; 
    }
    else
    {
      USBD_CtlError(pdev , req);
      return;
    }

  default: 
     USBD_CtlError(pdev , req);
    return;
  }
  
  if((len != 0)&& (req->wLength != 0))
  {
    
    len = MIN(len , req->wLength);
    
    USBD_CtlSendData (pdev, 
                      pbuf,
                      len);
  }
  
}
也是一大串switch-case语句,这样的代码虽然多但容易分析,在GET_DESCRIPTOR请求中,根据Table 9-3可知wValue字段的含义是描述符类型和描述符索引,在USB2.0协议的9.4.3章节中可以看到wValue的高字节标记了描述符的类型,wValue的低字节标记了描述符的索引。在USBD_GetDescriptor()函数中首先是检测描述符类型,第一个分支是设备描述符,通过pdev句柄的pDesc指针下的GetDeviceDescriptor指针调用所需要的函数即我们之前分析过的usbd_desc.c文件中的对应函数。第二个分支是获取配置描述符,这个是通过设备类指针获取的,放在后面分析,且这里分出了高速和全速模式。接着一个大分支是获取字符串描述符,这里就需要用到wValue的低字节描述符索引来细分各个字符串描述符,获取字符串描述符的处理跟获取设备描述符类似,不再赘述,索引号错误返回请求错误状态。DEVICE_QUALIFIER和OTHER_SPEED_CONFIGURATION描述符的获取都是在高速模式下才会有,由于STM32F103C8T6只支持全速模式,因此这里不再分析,直接返回请求错误。在最后发送这些数据到USB主机时有判断len和wLenth的大小,且返回的数据长度是两者的最小值,USB2.0协议的9.3.5章节中提到在一个输入请求中设备返回的数据不可以大于wLength字段指示的值,在一个输出请求中wLength字段的值一定是USB主机发出的数据长度值。如下:

至此,描述符的获取分析完毕,返回到标准设备请求函数USBD_StdDevReq()中查看第二个请求设置地址,如下:

/**
* @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 == 0) && (req->wLength == 0)) 
  {
    dev_addr = (uint8_t)(req->wValue) & 0x7F;     
    
    if (pdev->dev_state == USBD_STATE_CONFIGURED) 
    {
      USBD_CtlError(pdev , req);
    } 
    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 
  {
     USBD_CtlError(pdev , req);                        
  } 
}
一个USB总线上可以支持128个地址,地址0作为默认地址,且每个设备地址是唯一的,当USB主机为USB设备分配完地址后,设备即进入地址状态,等待配置完成,由Table 9-3可以看出SET_ADDRESS请求中wIndex和wLength字段值为0,wValue字段存储USB设备地址值,当设备处于配置状态时表明其已经分配了设备地址,因此返回请求错误,其他状态下通过USBD_LL_SetUSBAddress()函数使能该地址,并发送状态信息给USB主机(这里是没有数据阶段,只有状态阶段),当地址不为0时,标记设备进入地址状态。返回到USBD_StdDevReq()函数中查看SET_CONFIGURATION请求,如下:

/**
* @brief  USBD_SetConfig
*         Handle Set device configuration request
* @param  pdev: device instance
* @param  req: usb request
* @retval status
*/
static void USBD_SetConfig(USBD_HandleTypeDef *pdev , 
                           USBD_SetupReqTypedef *req)
{
  
  static uint8_t  cfgidx;
  
  cfgidx = (uint8_t)(req->wValue);                 
  
  if (cfgidx > USBD_MAX_NUM_CONFIGURATION ) 
  {            
     USBD_CtlError(pdev , req);                              
  } 
  else 
  {
    switch (pdev->dev_state) 
    {
    case USBD_STATE_ADDRESSED:
      if (cfgidx) 
      {                                			   							   							   				
        pdev->dev_config = cfgidx;
        pdev->dev_state = USBD_STATE_CONFIGURED;
        if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL)
        {
          USBD_CtlError(pdev , req);  
          return;
        }
        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;
        if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL)
        {
          USBD_CtlError(pdev , req);  
          return;
        }
        USBD_CtlSendStatus(pdev);
      }
      else
      {
        USBD_CtlSendStatus(pdev);
      }
      break;
      
    default:					
       USBD_CtlError(pdev , req);                     
      break;
    }
  }
}
代码稍微长了一些,该请求下wIndex和wLength字段的值同样为0,wValue字段值为配置ID,USB2.0协议中有关该请求的描述如下:

本例在usbd_conf.h文件中限制了最大配置数为1,该段代码中没有对wIndex和wLength进行限制,不科学那。。。当设备处于地址状态时可以对设备进行配置,记录此时的配置id号,切换设备状态,调用usbd_core.c文件中的USBD_SetClassConfig()函数执行真正的配置工作,该函数会链接到设备类的处理函数中。配置成功后发送状态信息到USB主机。如果设备处于配置状态时,发送0号配置ID设备进入地址状态,发送非0号配置ID且跟当前配置ID不同时重新加载该配置并发送状态信息到USB主机。设置配置流程比较简单,最重要的是它设备类中配置的实现。返回到标准设备请求函数中查看GET_CONFIGURATION请求,该请求返回设备的配置值,其中wValue、wIndex均为0,wLength为1,获取配置的实现如下:

/**
* @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 != 1) 
  {                   
     USBD_CtlError(pdev , req);
  }
  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:
       USBD_CtlError(pdev , req);
      break;
    }
  }
}
这里同样没有对wIndex和wIndex进行检测,只对wLength进行了检测,配置ID只占用了一个字节,因此返回的数据长度为1,设备处于地址状态时说明没有配置生效,因此返回默认的配置号0,而处于配置状态时返回当前设备的配置ID,该函数也比较简单。查看下一个标准设备请求GET_STATUS,如下:

/**
* @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_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, 
                      (uint8_t *)& pdev->dev_config_status,
                      2);
    break;
    
  default :
    USBD_CtlError(pdev , req);                        
    break;
  }
}
GET_STATUS请求的结构如下:

wValue和wLength分别为0和2,该请求支持三个接收者,这里我们只看对设备的请求,其返回的信息如下:

设备返回的状态只有两个有效位:是否支持自供电,是否支持远程唤醒,其中Self Powered特性是不能够通过SetFeature()或ClearFeature()请求修改的。在本例中设备支持自供电模式,因此该字段为1,如果支持远程唤醒则相应字段也为1,最后把设备状态信息发送到USB主机上。

接着查看SET_FEATURE请求的处理,如下:

/**
* @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 = 1;  
    pdev->pClass->Setup (pdev, req);   
    USBD_CtlSendStatus(pdev);
  }

}
该请求结构如下:

同样支持三个接收者,其中特性选择器的取值如下:

对于设备请求来说,有1和2可选,其中TEST_MODE中还有更多测试可选,如下:

但本例是不支持测试模式的,因此这里只关心远程唤醒特性,该段代码没有对参数进行检测也没有对设备状态进行检测,感觉不够严谨了,当使能远程唤醒时调用设备类的相应函数进行处理,并发送状态信息到USB主机。最后看一下CLEAR_FEATURE请求的处理,

/**
* @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_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);
    }
    break;
    
  default :
     USBD_CtlError(pdev , req);
    break;
  }
}
CLEAR_FEATURE请求跟SET_FEATURE请求相反,处理流程相同,但这里比之SET_FEATURE请求处理要严谨多了,对设备状态进行了检测。至此,标准设备请求的处理就分析完毕了,接着产看标准接口请求的处理,

/**
* @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) 
    {
      pdev->pClass->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 USBD_OK;
}
标准接口请求一共有5个,这里处理比较简单,只对调用设备类的处理函数来处理某些请求,其他请求均返回请求错误状态。现在只剩下标准端点请求的处理了,也是本文件的最后一个需要分析的函数,如下:

/**
* @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 class request */
  if ((req->bmRequest & 0x60) == 0x20)
  {
    pdev->pClass->Setup (pdev, req);
    
    return USBD_OK;
  }
  
  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);
          
        }
      }
      pdev->pClass->Setup (pdev, req);   
      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 != 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) 
        {        
          USBD_LL_ClearStallEP(pdev , ep_addr);
          pdev->pClass->Setup (pdev, req);
        }
        USBD_CtlSendStatus(pdev);
      }
      break;
      
    default:                         
      USBD_CtlError(pdev , req);
      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:                         
      USBD_CtlError(pdev , req);
      break;
    }
    break;
    
  default:
    break;
  }
  return ret;
}
代码比较长,标准端点请求一共有4个:CLEAR_FEATURE、GET_STATUS、SET_FEATURE、SYNCH_FRAME。这几个请求中wIndex字段的值为端点号,因此首先通过该字段获取端点号,根据bmRequest来检测是否是类请求,如果是类请求则调用设备类的相应函数进行处理,如果不是类请求则正常处理,这里只处理了前三个标准请求,首先看SET_FEATURE请求,在之前的特性选择器中端点对应的只有EP_HALT,在地址状态下如果指定的端点号非0,则设备回应请求错误,在配置状态下该请求有效,并调用设备类的处理函数实现特性的设置,CLEAR_FEATURE请求的处理跟SET_FEATURE类似,而GET_STATUS请求返回的信息如下:

有一个有效位,检测所请求端点的状态发送给USB主机,至此,usbd_ctlreq文件分析完毕,该文件需对照USB2.0协议的第9章节来分析,现在就只剩下USB的设备类文件没有分析了,下次分析usbd_cdc文件。



©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值