Editar
Puede que no haya hecho las preguntas correctas aquí. No intente responder antes de editar nuevamente para eliminar este mensaje.
Resumen
Estoy desarrollando firmware para un dispositivo USB, y no puedo hacerlo a través del proceso de enumeración. Mi intención es configurar los descriptores del dispositivo para que sean detectado automáticamente por un Windows 7/8 / 10 host como un dispositivo USB 2.0 WinUSB de velocidad completa con una canalización en cada dirección.
Durante la enumeración, esto es lo que parece estar sucediendo en el lado del dispositivo para las solicitudes de descriptores, cuando se conecta a un host de Windows 10:
- Obtener descriptor de dispositivo (USB estándar)
- Obtenga el descriptor de configuración (estándar USB)
- Obtenga Descriptor de cadena de Microsoft OS
- Obtenga el Descriptor de Calificador de Dispositivo (USB Estándar - solicitud de información de velocidad completa) - > el dispositivo se detiene
Preguntas
- ¿Por qué el host de Windows está enviando la solicitud de Device_Qualifier?
- ¿Hay alguna forma de configurar los descriptores anteriores para evitar que el host de Windows envíe esa solicitud?
- En caso negativo, ¿qué debo enviar en respuesta a la solicitud?
- ¿Y cómo puedo agregar código de usuario para hacerlo? (Consulte los detalles a continuación para comprender por qué esto no está claro).
- Como se trata de dispositivos USB de bajo nivel, pensé que EE era mi mejor apuesta. ¿O debería mover esto a StackOverflow?
Detalles de implementación
Actualmente estoy usando una placa STM32F072 Discovery . Usé STM32CubeMX para generar el código de v1.7.0 de STM32CubeF0 biblioteca de firmware.
Inicialmente, configuré el proyecto en Cube como un dispositivo USB CDC (puerto COM virtual). Luego moví los archivos de la biblioteca de clases (CDC) a mi código de aplicación, los cambié de nombre y cambié mi archivo de diseño de Cube para generar solo un código para un dispositivo USB sin clase, así que puedo modificar los archivos de clase CDC para convertirlo en un dispositivo WinUSB, y que Cube no perturbe esos archivos.
El controlador para solicitudes de descriptor está en código de biblioteca. Maneja las solicitudes estándar, pero pasa las solicitudes no estándar al código de usuario. Con respecto a la pregunta 4 anterior, parece que no tengo forma de manejar la solicitud del descriptor Device_Qualifier sin modificar el código de la biblioteca.
A continuación se muestra un extracto del controlador, que se encuentra en Middlewares\ST\STM32_USB_Device_Library\Core\Src\usbd_ctlreq.c
. De interés en este momento es el caso USB_DESC_TYPE_DEVICE_QUALIFIER
en la instrucción de cambio, pero se proporciona la función completa para el contexto.
/**
* @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);
}
}
Desde pdev->dev_speed == USBD_SPEED_FULL
, llama a USBD_CtlError()
, por lo tanto, detiene el bus y aborta la enumeración.
/**
* @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);
}
Descriptores USB
Aquí están los descriptores USB estándar anteriores.
Dispositivo
/* USB Standard Device Descriptor */
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
0x12, /* bLength */
USB_DESC_TYPE_DEVICE, /* bDescriptorType*/
0x00, /* bcdUSB */
0x02,
0xFF, /* bDeviceClass - vendor specific */
0xFF, /* bDeviceSubClass - vendor specific */
0xFF, /* bDeviceProtocol - vendor specific */
USB_MAX_EP0_SIZE, /* bMaxPacketSize */
LOBYTE(USBD_VID ), /* idVendor */
HIBYTE(USBD_VID ), /* idVendor */
LOBYTE(USBD_PID_FS), /* idProduct */
HIBYTE(USBD_PID_FS), /* idProduct */
0x01, /* bcdDevice rel. 0.01 */
0x00,
0x00, /* Index of manufacturer string - 0x00 means NOT USED */
0x00, /* Index of product string - 0x00 means NOT USED */
0x00, /* Index of serial number string - 0x00 means NOT USED */
USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations*/
};
/* USB_DeviceDescriptor */
Config
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0x80, /* bmAttributes: USB-powered */
0x96, /* MaxPower 0x96 = 150 = 300 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: One endpoints used */
0xFF, /* bInterfaceClass: Communication Interface Class - vendor-specific */
0x01, /* bInterfaceSubClass: arbitrary (vendor's choice b/c bInterfaceClass is vendor-specific) */
0xFF, /* bInterfaceProtocol: vendor-specific */
0x00, /* iInterface: not used */
// /*Header Functional Descriptor*/
// 0x05, /* bLength: Endpoint Descriptor size */
// 0x24, /* bDescriptorType: CS_INTERFACE */
// 0x00, /* bDescriptorSubtype: Header Func Desc */
// 0x10, /* bcdCDC: spec release number */
// 0x01,
//
// /*Call Management Functional Descriptor*/
// 0x05, /* bFunctionLength */
// 0x24, /* bDescriptorType: CS_INTERFACE */
// 0x01, /* bDescriptorSubtype: Call Management Func Desc */
// 0x00, /* bmCapabilities: D0+D1 */
// 0x01, /* bDataInterface: 1 */
//
// /*ACM Functional Descriptor*/
// 0x04, /* bFunctionLength */
// 0x24, /* bDescriptorType: CS_INTERFACE */
// 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
// 0x02, /* bmCapabilities */
//
// /*Union Functional Descriptor*/
// 0x05, /* bFunctionLength */
// 0x24, /* bDescriptorType: CS_INTERFACE */
// 0x06, /* bDescriptorSubtype: Union func desc */
// 0x00, /* bMasterInterface: Communication class interface */
// 0x01, /* bSlaveInterface0: Data Class Interface */
//
// /*Endpoint 2 Descriptor*/
// 0x07, /* bLength: Endpoint Descriptor size */
// USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
// CDC_CMD_EP, /* bEndpointAddress */
// 0x03, /* bmAttributes: Interrupt */
// LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
// HIBYTE(CDC_CMD_PACKET_SIZE),
// 0x10, /* bInterval: */
/*---------------------------------------------------------------------------*/
// /*Data class interface descriptor*/
// 0x09, /* bLength: Endpoint Descriptor size */
// USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
// 0x01, /* bInterfaceNumber: Number of Interface */
// 0x00, /* bAlternateSetting: Alternate setting */
// 0x02, /* bNumEndpoints: Two endpoints used */
// 0x0A, /* bInterfaceClass: CDC */
// 0x00, /* bInterfaceSubClass: */
// 0x00, /* bInterfaceProtocol: */
// 0x00, /* iInterface: */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
USBD_EP_TYPE_BULK, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
USBD_EP_TYPE_BULK, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer */
};