USB host EP 0 STALL

__FLR__
Posts: 1
Joined: Wed May 11, 2022 3:20 am

USB host EP 0 STALL

Postby __FLR__ » 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:
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
And here is the code I'd attempting to use to read the HID descriptor. See around line 224.

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);
}

ESP_Dazz
Posts: 308
Joined: Fri Jun 02, 2017 6:50 am

Re: USB host EP 0 STALL

Postby ESP_Dazz » Thu May 19, 2022 3:05 am

A stall on the control endpoint (EP0) generally indicates that a particular request is not supported. Double check the setup packet of the control transfer that triggered the halt on EP0.

chegewara
Posts: 2174
Joined: Wed Jun 14, 2017 9:00 pm

Re: USB host EP 0 STALL

Postby chegewara » Thu May 19, 2022 4:10 am

__FLR__ wrote: 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
I think you have swapped byte 2 and 3:

Code: Select all

0x81,        // bmRequestType: Dir: D2H, Type: Standard, Recipient: Interface
0x06,        // bRequest (Get Descriptor)
0x00,        // wValue[0:7]  Desc Index: 0
0x22,        // wValue[8:15] Desc Type: (HID Report)
0x00, 0x00,  // wIndex Language ID: 0x00
0x64, 0x00,  // wLength = 100
http://eleccelerator.com/usbdescreqparser/

gdsports
Posts: 15
Joined: Wed Aug 02, 2017 12:17 am

Re: USB host EP 0 STALL

Postby gdsports » Thu May 19, 2022 4:37 pm

The Xbox 360 controller is generally a human interface device but is not a USB HID. bInterfaceClass must be equal to 3 for a USB HID.
My Xbox 360 cheap clone is a vendor specific device. I suspect authentic Xbox 360 controllers are also vendor specific devices.

Who is online

Users browsing this forum: Bing [Bot] and 69 guests