USB host EP 0 STALL
Posted: Wed May 11, 2022 3:33 am
I'm attempting to use the usb host peripheral on the ESP32 to communicate with a gamepad. Using the usb_host_lib example I've got basic communication working. I can see the descriptors and read values from the gamepad by communicating on the "IN" endpoint.
In order to make things more robust I want to be able to parse the HID descriptors and reports to automatically figure out how a given gamepad will represent it data. To do this I believe I need to use control transfers, however when I attempt to do so I get "USBH: Dev 1 EP 0 STALL". Below is the output of my program:
The regular transfers work as expected, but not the control transfer. What am I overlooking?
In order to make things more robust I want to be able to parse the HID descriptors and reports to automatically figure out how a given gamepad will represent it data. To do this I believe I need to use control transfers, however when I attempt to do so I get "USBH: Dev 1 EP 0 STALL". Below is the output of my program:
And here is the code I'd attempting to use to read the HID descriptor. See around line 224.I (0) cpu_start: Starting scheduler on APP CPU.
I (344) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (344) gpio: GPIO[12]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (354) gpio: GPIO[17]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (364) DAEMON: Installing USB Host Library
I (404) CLASS: Registering Client
E (2754) HUB: Root port reset failed
I (3204) CLASS: Opening device at address 1
I (3204) CLASS: Getting device information
I (3204) CLASS: Full speed
I (3204) CLASS: bConfigurationValue 1
I (3204) CLASS: Getting device descriptor
*** Device descriptor ***
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0xff
bDeviceSubClass 0xff
bDeviceProtocol 0xff
bMaxPacketSize0 64
idVendor 0x45e
idProduct 0x28e
bcdDevice 1.10
iManufacturer 1
iProduct 2
iSerialNumber 0
bNumConfigurations 1
I (3234) CLASS: Getting config descriptor
*** Configuration descriptor ***
bLength 9
bDescriptorType 2
wTotalLength 49
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
bMaxPower 100mA
*** Interface descriptor ***
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 0x1
iInterface 0
*** Endpoint descriptor ***
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 0x3 INT
wMaxPacketSize 32
bInterval 1
*** Endpoint descriptor ***
bLength 7
bDescriptorType 5
bEndpointAddress 0x5 EP 5 OUT
bmAttributes 0x3 INT
wMaxPacketSize 32
bInterval 1
I (3294) CLASS: Getting Manufacturer string descriptor
ZhiXu
I (3304) CLASS: Getting Product string descriptor
Controller
I (3314) CLASS: Getting Serial Number string descriptor
I (3314) claim: attempting ESP_OK
E (3324) USBH: Dev 1 EP 0 STALL
I (3324) transfer: attempting control ESP_OK
I (3334) transfer: got HID desc 4, actual number of bytes transferred 0
E (4334) USBH: Dev 1 EP 0 STALL
I (4334) transfer: attempting control ESP_OK
I (4334) transfer: got HID desc 4, ac
The regular transfers work as expected, but not the control transfer. What am I overlooking?
Code: Select all
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "usb/usb_host.h"
#include <string.h>
#define CLIENT_NUM_EVENT_MSG 5
#define ACTION_OPEN_DEV 0x01
#define ACTION_GET_DEV_INFO 0x02
#define ACTION_GET_DEV_DESC 0x04
#define ACTION_GET_CONFIG_DESC 0x08
#define ACTION_GET_STR_DESC 0x10
#define ACTION_CLOSE_DEV 0x20
#define ACTION_EXIT 0x40
#define ACTION_TRANSFER 0x80
#define ACTION_HID_DESC 0x100
typedef struct {
usb_host_client_handle_t client_hdl;
uint8_t dev_addr;
usb_device_handle_t dev_hdl;
uint32_t actions;
} class_driver_t;
static const char *TAG = "CLASS";
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
{
class_driver_t *driver_obj = (class_driver_t *)arg;
switch (event_msg->event) {
case USB_HOST_CLIENT_EVENT_NEW_DEV:
if (driver_obj->dev_addr == 0) {
driver_obj->dev_addr = event_msg->new_dev.address;
//Open the device next
driver_obj->actions |= ACTION_OPEN_DEV;
}
break;
case USB_HOST_CLIENT_EVENT_DEV_GONE:
if (driver_obj->dev_hdl != NULL) {
//Cancel any other actions and close the device next
driver_obj->actions = ACTION_CLOSE_DEV;
}
break;
default:
//Should never occur
abort();
}
}
static void action_open_dev(class_driver_t *driver_obj)
{
assert(driver_obj->dev_addr != 0);
ESP_LOGI(TAG, "Opening device at address %d", driver_obj->dev_addr);
ESP_ERROR_CHECK(usb_host_device_open(driver_obj->client_hdl, driver_obj->dev_addr, &driver_obj->dev_hdl));
//Get the device's information next
driver_obj->actions &= ~ACTION_OPEN_DEV;
driver_obj->actions |= ACTION_GET_DEV_INFO;
}
static void action_get_info(class_driver_t *driver_obj)
{
assert(driver_obj->dev_hdl != NULL);
ESP_LOGI(TAG, "Getting device information");
usb_device_info_t dev_info;
ESP_ERROR_CHECK(usb_host_device_info(driver_obj->dev_hdl, &dev_info));
ESP_LOGI(TAG, "\t%s speed", (dev_info.speed == USB_SPEED_LOW) ? "Low" : "Full");
ESP_LOGI(TAG, "\tbConfigurationValue %d", dev_info.bConfigurationValue);
//Todo: Print string descriptors
//Get the device descriptor next
driver_obj->actions &= ~ACTION_GET_DEV_INFO;
driver_obj->actions |= ACTION_GET_DEV_DESC;
}
static void action_get_dev_desc(class_driver_t *driver_obj)
{
assert(driver_obj->dev_hdl != NULL);
ESP_LOGI(TAG, "Getting device descriptor");
const usb_device_desc_t *dev_desc;
ESP_ERROR_CHECK(usb_host_get_device_descriptor(driver_obj->dev_hdl, &dev_desc));
usb_print_device_descriptor(dev_desc);
//Get the device's config descriptor next
driver_obj->actions &= ~ACTION_GET_DEV_DESC;
driver_obj->actions |= ACTION_GET_CONFIG_DESC;
}
static void action_get_config_desc(class_driver_t *driver_obj)
{
assert(driver_obj->dev_hdl != NULL);
ESP_LOGI(TAG, "Getting config descriptor");
const usb_config_desc_t *config_desc;
ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(driver_obj->dev_hdl, &config_desc));
usb_print_config_descriptor(config_desc, NULL);
//Get the device's string descriptors next
driver_obj->actions &= ~ACTION_GET_CONFIG_DESC;
driver_obj->actions |= ACTION_GET_STR_DESC;
}
static void action_get_str_desc(class_driver_t *driver_obj)
{
assert(driver_obj->dev_hdl != NULL);
usb_device_info_t dev_info;
ESP_ERROR_CHECK(usb_host_device_info(driver_obj->dev_hdl, &dev_info));
if (dev_info.str_desc_manufacturer) {
ESP_LOGI(TAG, "Getting Manufacturer string descriptor");
usb_print_string_descriptor(dev_info.str_desc_manufacturer);
}
if (dev_info.str_desc_product) {
ESP_LOGI(TAG, "Getting Product string descriptor");
usb_print_string_descriptor(dev_info.str_desc_product);
}
if (dev_info.str_desc_serial_num) {
ESP_LOGI(TAG, "Getting Serial Number string descriptor");
usb_print_string_descriptor(dev_info.str_desc_serial_num);
}
//Nothing to do until the device disconnects
driver_obj->actions &= ~ACTION_GET_STR_DESC;
driver_obj->actions |=ACTION_HID_DESC;
esp_err_t result=usb_host_interface_claim(driver_obj->client_hdl, driver_obj->dev_hdl, 0, 0);
ESP_LOGI("claim","attempting %s", esp_err_to_name(result));
}
static void aciton_close_dev(class_driver_t *driver_obj)
{
ESP_ERROR_CHECK(usb_host_device_close(driver_obj->client_hdl, driver_obj->dev_hdl));
driver_obj->dev_hdl = NULL;
driver_obj->dev_addr = 0;
//We need to exit the event handler loop
driver_obj->actions &= ~ACTION_CLOSE_DEV;
driver_obj->actions &= ~ACTION_TRANSFER;
driver_obj->actions |= ACTION_EXIT;
}
int PrintDivider=0;
static void transfer_cb(usb_transfer_t *transfer)
{
//This is function is called from within usb_host_client_handle_events(). Don't block and try to keep it short
//struct class_driver_control *class_driver_obj = (struct class_driver_control *)transfer->context;
if((PrintDivider++%10)==0)
{
ESP_LOGI("transfer","status %d, actual number of bytes transferred %d\n", transfer->status, transfer->actual_num_bytes);
ESP_LOGI("transfer","first 8 bytes %X %X %X %X %X %X %X %X\n",transfer->data_buffer[0],transfer->data_buffer[1],transfer->data_buffer[2],transfer->data_buffer[3],
transfer->data_buffer[4],transfer->data_buffer[5],transfer->data_buffer[6],transfer->data_buffer[7]);
}
//class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV;
}
static void transfer_HID_desc_cb(usb_transfer_t *transfer)
{
ESP_LOGI("transfer","got HID desc %d, actual number of bytes transferred %d", transfer->status, transfer->actual_num_bytes);
for(int i=0;i<transfer->actual_num_bytes;i++)
printf("%02X ", transfer->data_buffer[i]);
//class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV;
}
void class_driver_task(void *arg)
{
SemaphoreHandle_t signaling_sem = (SemaphoreHandle_t)arg;
class_driver_t driver_obj = {0};
//Wait until daemon task has installed USB Host Library
xSemaphoreTake(signaling_sem, portMAX_DELAY);
ESP_LOGI(TAG, "Registering Client");
usb_host_client_config_t client_config = {
.is_synchronous = false, //Synchronous clients currently not supported. Set this to false
.max_num_event_msg = CLIENT_NUM_EVENT_MSG,
.async = {
.client_event_callback = client_event_cb,
.callback_arg = (void *)&driver_obj,
},
};
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &driver_obj.client_hdl));
usb_transfer_t *transfer;
usb_host_transfer_alloc(32, 0, &transfer);
usb_transfer_t *transfer_desc;
usb_host_transfer_alloc(72, 0, &transfer_desc);
int32_t LastSendTime=0;
uint8_t HID_desc_req=0;
while (1) {
if (driver_obj.actions == 0) {
usb_host_client_handle_events(driver_obj.client_hdl, portMAX_DELAY);
} else {
if (driver_obj.actions & ACTION_OPEN_DEV) {
action_open_dev(&driver_obj);
}
if (driver_obj.actions & ACTION_GET_DEV_INFO) {
action_get_info(&driver_obj);
}
if (driver_obj.actions & ACTION_GET_DEV_DESC) {
action_get_dev_desc(&driver_obj);
}
if (driver_obj.actions & ACTION_GET_CONFIG_DESC) {
action_get_config_desc(&driver_obj);
}
if (driver_obj.actions & ACTION_GET_STR_DESC) {
action_get_str_desc(&driver_obj);
}
if(driver_obj.actions & ACTION_HID_DESC)
{
if(HID_desc_req==0 && (xTaskGetTickCount()-LastSendTime)>100)
{
LastSendTime=xTaskGetTickCount();
transfer_desc->num_bytes = 72;
transfer_desc->data_buffer[0]=0x81; //request type
transfer_desc->data_buffer[1]=0x06; //GET_DESCRIPTOR
transfer_desc->data_buffer[2]=0x22; //HID Descriptor
transfer_desc->data_buffer[3]=0; //descriptor index 0
transfer_desc->data_buffer[4]=0; //interface 0
transfer_desc->data_buffer[5]=0; //interface 0
transfer_desc->data_buffer[6]=64; //length 64
transfer_desc->data_buffer[7]=0; //length 64
transfer_desc->device_handle = driver_obj.dev_hdl;
transfer_desc->bEndpointAddress = 0x00;
transfer_desc->callback = transfer_HID_desc_cb;
transfer_desc->context = (void *)&driver_obj;
esp_err_t result=usb_host_transfer_submit_control(driver_obj.client_hdl,transfer_desc);
//if(result != ESP_OK)
ESP_LOGI("transfer","attempting control %s", esp_err_to_name(result));
}
usb_host_client_handle_events(driver_obj.client_hdl, 10);
}
if(driver_obj.actions & ACTION_TRANSFER)
{
LastSendTime=xTaskGetTickCount();
memset(transfer->data_buffer, 0xAA, 32);
transfer->num_bytes = 32;
transfer->device_handle = driver_obj.dev_hdl;
transfer->bEndpointAddress = 0x81;
transfer->callback = transfer_cb;
transfer->context = (void *)&driver_obj;
esp_err_t result=usb_host_transfer_submit(transfer);
if(result != ESP_OK)
ESP_LOGI("transfer","attempting %s", esp_err_to_name(result));
usb_host_client_handle_events(driver_obj.client_hdl, 10);
vTaskDelay(1);
}
if (driver_obj.actions & ACTION_CLOSE_DEV) {
aciton_close_dev(&driver_obj);
}
if (driver_obj.actions & ACTION_EXIT) {
break;
}
}
}
ESP_LOGI(TAG, "Deregistering Client");
ESP_ERROR_CHECK(usb_host_client_deregister(driver_obj.client_hdl));
//Wait to be deleted
xSemaphoreGive(signaling_sem);
vTaskSuspend(NULL);
}