ESP32-S3 problem with usb_host_transfer_submit() - ESP_ERR_INVALID_STATE

raymondshi
Posts: 6
Joined: Fri Jul 09, 2021 2:46 am

ESP32-S3 problem with usb_host_transfer_submit() - ESP_ERR_INVALID_STATE

Postby raymondshi » Sun Aug 13, 2023 10:23 am

Hi there,

I have a ESP32-S3-USB-OTG dev board working as USB host for an Android phone. I followed the simple client task example in the ESP-IDF version 5.1 USB host API reference guide, and my USB host application is able to open the device, find the host interface and its endpoints, call usb_host_interface_claim(), and submit the control transfers to device. However it always returns error 0x103 ESP_ERR_INVALID_STATE when trying to kick off the BULK-IN transfer using usb_host_transfer_submit() in the following start_transfers().

Does anyone have an idea why this error can happen? I need to have the ESP32-S3-USB-OTG dev board communicate with Android phone but can't figure out this ESP_ERR_INVALID_STATE error in the following start_transfers() function.

Code: Select all

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;

// connected android if any
android_dev_t android_dev = {0};

extern void handle_rx(uint8_t *data, size_t data_len, void *arg);

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 esp_err_t find_intf_and_ep_desc(android_dev_t* android_dev, const usb_ep_desc_t **in_ep, const usb_ep_desc_t **out_ep)
{
    const usb_config_desc_t *config_desc;

    ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(android_dev->dev_hdl, &config_desc));

    bool interface_found = false;
    int desc_offset = 0;
    const usb_standard_desc_t *this_desc = (const usb_standard_desc_t *)config_desc;

    do {
        this_desc = usb_parse_next_descriptor_of_type(this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE, &desc_offset);

        if (this_desc == NULL)
            break; // Reached end of configuration descriptor

        const usb_intf_desc_t *intf_desc = (const usb_intf_desc_t *)this_desc;
        assert(intf_desc);

        bool foundEp = false;
        int temp_offset = desc_offset;
        for (int i = 0; i < 2; i++) {
            const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, i, config_desc->wTotalLength, &desc_offset);
            assert(this_ep);
            uint8_t ep_type = USB_BM_ATTRIBUTES_XFERTYPE_MASK & this_ep->bmAttributes;
            if (ep_type == USB_BM_ATTRIBUTES_XFER_BULK) {  // found bulk EP
            	foundEp = true;
            	if (USB_EP_DESC_GET_EP_DIR(this_ep) == 0) {  // found OUT EP
            	    *out_ep = this_ep;
            	    ESP_LOGI(TAG, "found usb interface BULK-OUT ep address %d", this_ep->bEndpointAddress);
            	} else {  // found IN EP
            	    *in_ep = this_ep;
            	    ESP_LOGI(TAG, "found usb interface BULK-IN ep address %d", this_ep->bEndpointAddress);
            	}
            }
            desc_offset = temp_offset;
        }

    	if (foundEp) {
        	android_dev->data.intf_desc = intf_desc;
        	interface_found = true;
    	}
    } while (!interface_found);

    if (interface_found) {
        return ESP_OK;
    }
    return ESP_ERR_NOT_FOUND;
}

/**
 * @brief Check finished transfer status
 *
 * Return to on transfer completed OK.
 * Cancel the transfer and issue user's callback in case of an error.
 *
 * @param[in] transfer Transfer to be checked
 * @return true Transfer completed
 * @return false Transfer NOT completed
 */
static bool is_transfer_completed(usb_transfer_t *transfer)
{
    bool completed = false;

    if (transfer->status == USB_TRANSFER_STATUS_COMPLETED) {
        completed = true;
    }

    return completed;
}

static void in_xfer_cb(usb_transfer_t *transfer)
{
    //ESP_LOGI(TAG, "Starting BULK IN transfer callback");
    android_dev_t *android_dev = (android_dev_t *)transfer->context;

    if (is_transfer_completed(transfer)) {
        if (android_dev->data.in_cb) {
            android_dev->data.in_cb(transfer->data_buffer, transfer->actual_num_bytes, android_dev->cb_arg);
        }
        //ESP_LOGI(TAG, "Submitting poll for BULK IN transfer");

        usb_host_transfer_submit(android_dev->data.in_xfer);
    }
}

static void out_xfer_cb(usb_transfer_t *transfer)
{
    //ESP_LOGI(TAG, "out_xfer: BULK OUT transfer completed");

    assert(transfer->context);
    xSemaphoreGive((SemaphoreHandle_t)transfer->context);
}

static void free_transfers(android_dev_t *android_dev)
{
    assert(android_dev);

    usb_host_transfer_free(android_dev->data.in_xfer);

    if (android_dev->data.out_xfer != NULL) {
        usb_host_transfer_free(android_dev->data.out_xfer);
    }
}

static esp_err_t allocate_transfers(android_dev_t *android_dev, const usb_ep_desc_t *in_ep_desc, const usb_ep_desc_t *out_ep_desc, size_t out_buf_len)
{
    esp_err_t ret;

    // Setup IN data transfer
    ESP_GOTO_ON_ERROR(
        usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(in_ep_desc), 0, &android_dev->data.in_xfer),
        err, TAG,
    );
    assert(android_dev->data.in_xfer);
    android_dev->data.in_xfer->callback = in_xfer_cb;
    android_dev->data.in_xfer->num_bytes = USB_EP_DESC_GET_MPS(in_ep_desc);
    android_dev->data.in_xfer->bEndpointAddress = in_ep_desc->bEndpointAddress;
    android_dev->data.in_xfer->device_handle = android_dev->dev_hdl;
    android_dev->data.in_xfer->context = android_dev;

    ESP_LOGI(TAG, "allocate BULK IN endpoint address #%d", android_dev->data.in_xfer->bEndpointAddress);

    // Setup OUT bulk transfer (if it is required (out_buf_len > 0))
    if (out_buf_len != 0) {
        ESP_GOTO_ON_ERROR(
            usb_host_transfer_alloc(out_buf_len, 0, &android_dev->data.out_xfer),
            err, TAG,
        );
        assert(android_dev->data.out_xfer);
        android_dev->data.out_xfer->callback = out_xfer_cb;
        android_dev->data.out_xfer->device_handle = android_dev->dev_hdl;
        // set the OUT transfer semaphore
        android_dev->data.out_xfer->context = sending_sem;
        android_dev->data.out_xfer->bEndpointAddress = out_ep_desc->bEndpointAddress;

        ESP_LOGI(TAG, "allocate BULK OUT endpoint address #%d", android_dev->data.out_xfer->bEndpointAddress);
    }
    return ESP_OK;

err:
    free_transfers(android_dev);
    return ret;
}

static esp_err_t start_transfers(class_driver_t *driver_obj, android_dev_t *android_dev, android_data_callback_t in_cb)
{
    esp_err_t ret = ESP_OK;
    assert(android_dev);

	android_dev->data.in_cb = in_cb;
	android_dev->cb_arg = NULL;

	ESP_LOGI(TAG, "Kick off BULK IN transfer");
	ESP_ERROR_CHECK(usb_host_transfer_submit(android_dev->data.in_xfer));

	// kick off OUT transfer
	ESP_LOGI(TAG, "Kick off BULK OUT transfer");
	xSemaphoreGive((SemaphoreHandle_t)android_dev->data.out_xfer->context);

	// Nothing to do next
	driver_obj->actions &= ~ACTION_START_ANDROID_TRANSFER;

    return ret;
}

esp_err_t host_data_tx_blocking(android_dev_t *android_dev, const uint8_t *data, size_t data_len, uint32_t timeout_ms)
{
    assert(android_dev);
    USB_HOST_CHECK(data && (data_len > 0), ESP_ERR_INVALID_ARG);
    USB_HOST_CHECK(android_dev->data.out_xfer, ESP_ERR_NOT_SUPPORTED); // Device was opened as read-only.
    USB_HOST_CHECK(data_len <= android_dev->data.out_xfer->data_buffer_size, ESP_ERR_INVALID_SIZE);

    ESP_LOGD(TAG, "submitting BULK OUT transfer");

    memcpy(android_dev->data.out_xfer->data_buffer, data, data_len);
    android_dev->data.out_xfer->num_bytes = data_len;
    android_dev->data.out_xfer->timeout_ms = timeout_ms;
    esp_err_t ret = usb_host_transfer_submit(android_dev->data.out_xfer);
    return ret;
}

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

    //Allocate a USB transfer
    usb_transfer_t *transfer_desc;
    usb_host_transfer_alloc(CONFIG_USB_TRANSFER_OUT_BUF_SIZE, 0, &transfer_desc);

    while (1) {
    	//CHECK_TWDT_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);

        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_START_ANDROID_ACCESSORY) {
            	android_dev.dev_hdl = driver_obj.dev_hdl;

                ESP_ERROR_CHECK(find_intf_and_ep_desc(&android_dev, &in_ep_usb, &out_ep_usb));

                ESP_ERROR_CHECK(usb_host_interface_claim(driver_obj.client_hdl, android_dev.dev_hdl, android_dev.data.intf_desc->bInterfaceNumber, 0));

            	current_control_transfer_index = 0;

                action_start_android_accessory(transfer_desc, &driver_obj);
            }
            if (driver_obj.actions & ACTION_START_ANDROID_TRANSFER) {
            	ESP_ERROR_CHECK(allocate_transfers(&android_dev, in_ep_usb, out_ep_usb, CONFIG_USB_TRANSFER_OUT_BUF_SIZE));
                ESP_ERROR_CHECK(start_transfers(&driver_obj, &android_dev, handle_rx));
            }
            // disconnected
            if (driver_obj.actions & ACTION_CLOSE_DEV) {
                aciton_close_dev(&driver_obj, &android_dev);
            }
            if (driver_obj.actions & ACTION_EXIT) {
                break;
            }
        }  // else
    }  // while

    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);
}
And the output is as follows,

Code: Select all

I (782) DAEMON: Installing USB Host Library
I (822) TAM-CLASS: Registering Client
...
I (1212) main_task: Returned from app_main()
D (1652) HUB: Root port reset
D (1652) HUB: Stage done: START
D (1652) HUB: Stage done: GET_SHORT_DEV_DESC
D (1652) HUB: Stage done: CHECK_SHORT_DEV_DESC
D (1712) HUB: Stage done: SECOND_RESET
D (1712) HUB: Stage done: SET_ADDR
D (1712) HUB: Stage done: CHECK_ADDR
D (1722) HUB: Stage done: SET_ADDR_RECOVERY
D (1722) HUB: Stage done: GET_FULL_DEV_DESC
D (1722) HUB: Stage done: CHECK_FULL_DEV_DESC
D (1722) HUB: Stage done: GET_SHORT_CONFIG_DESC
D (1722) HUB: Stage done: CHECK_SHORT_CONFIG_DESC
D (1732) HUB: Stage done: GET_FULL_CONFIG_DESC
D (1732) HUB: Stage done: CHECK_FULL_CONFIG_DESC
D (1742) HUB: Stage done: SET_CONFIG
D (1742) HUB: Stage done: CHECK_CONFIG
D (1742) HUB: Stage done: GET_SHORT_LANGID_TABLE
D (1752) HUB: Stage done: CHECK_SHORT_LANGID_TABLE
D (1752) HUB: Stage done: GET_FULL_LANGID_TABLE
D (1762) HUB: Stage done: CHECK_FULL_LANGID_TABLE
D (1762) HUB: Stage done: GET_SHORT_MANU_STR_DESC
D (1772) HUB: Stage done: CHECK_SHORT_MANU_STR_DESC
D (1772) HUB: Stage done: GET_FULL_MANU_STR_DESC
D (1782) HUB: Stage done: CHECK_FULL_MANU_STR_DESC
D (1782) HUB: Stage done: GET_SHORT_PROD_STR_DESC
D (1792) HUB: Stage done: CHECK_SHORT_PROD_STR_DESC
D (1792) HUB: Stage done: GET_FULL_PROD_STR_DESC
D (1802) HUB: Stage done: CHECK_FULL_PROD_STR_DESC
D (1802) HUB: Stage done: GET_SHORT_SER_STR_DESC
D (1812) HUB: Stage done: CHECK_SHORT_SER_STR_DESC
D (1812) HUB: Stage done: GET_FULL_SER_STR_DESC
D (1822) HUB: Stage done: CHECK_FULL_SER_STR_DESC
D (1822) HUB: Stage done: CLEANUP
D (1822) USBH: Processing actions 0x100
D (1832) USBH: New device 1
D (1832) TAM-CLASS: Opening device at address 1
I (1842) TAM-CLASS: found usb interface BULK-OUT ep address 1
I (1842) TAM-CLASS: found usb interface BULK-IN ep address 129
I (1852) TAM-CLASS: found usb interface bInterfaceNumber 0
I (1852) TAM-CLASS: found usb interface bNumEndpoints 2
I (1862) TAM-CLASS: about to claim usb host interface #0
D (1872) transfer: attempting control ESP_OK
D (1872) USBH: Processing actions 0x4
D (1872) USBH: Default pipe device 1
D (1882) transfer: got desc 0, actual number of bytes transferred 15
D (1882) transfer: attempting control ESP_OK
D (1892) transfer: got desc 0, actual number of bytes transferred 17
D (1902) transfer: attempting control ESP_OK
D (1902) transfer: got desc 0, actual number of bytes transferred 17
D (1912) transfer: attempting control ESP_OK
D (1912) transfer: got desc 0, actual number of bytes transferred 11
D (1922) transfer: attempting control ESP_OK
D (1922) transfer: got desc 0, actual number of bytes transferred 29
D (1932) transfer: attempting control ESP_OK
D (1932) transfer: got desc 0, actual number of bytes transferred 12
D (1942) transfer: attempting control ESP_OK
D (1942) transfer: got desc 0, actual number of bytes transferred 8
I (1952) TAM-CLASS: allocate BULK IN endpoint address #129
I (1952) TAM-CLASS: allocate BULK OUT endpoint address #1
I (1962) TAM-CLASS: Kick off BULK IN transfer
ESP_ERROR_CHECK failed: esp_err_t 0x103 (ESP_ERR_INVALID_STATE) at 0x4200dfd8
0x4200dfd8: start_transfers(class_driver_t*, android_dev_s*, void (*)(unsigned char*, unsigned int, void*)) at X:/projects/jiesoft/esp/tam-controller/usb/class_driver.cc:644 (discriminator 1)

file: "./usb/class_driver.cc" line 644
func: esp_err_t start_transfers(class_driver_t*, android_dev_t*, android_data_callback_t)
expression: usb_host_transfer_submit(android_dev->data.in_xfer)

abort() was called at PC 0x4037b7b7 on core 0
0x4037b7b7: _esp_error_check_failed at X:/software/espressif/frameworks/esp-idf-v5.1/components/esp_system/esp_err.c:50
Last edited by raymondshi on Sat Aug 19, 2023 7:10 am, edited 1 time in total.

bidrohini
Posts: 202
Joined: Thu Oct 27, 2022 12:55 pm

Re: ESP32-S3 problem with usb_host_transfer_submit() - ESP_ERR_INVALID_STATE

Postby bidrohini » Sun Aug 13, 2023 2:59 pm

Here is a thread about the error message: https://esp32.com/viewtopic.php?t=33553

raymondshi
Posts: 6
Joined: Fri Jul 09, 2021 2:46 am

Re: ESP32-S3 problem with usb_host_transfer_submit() - ESP_ERR_INVALID_STATE

Postby raymondshi » Sun Aug 13, 2023 11:58 pm

Further debug shows that the ESP_ERR_INVALID_STATE error comes from below lines

Code: Select all

    ...
    //Check that pipe and port are in the correct state to receive URBs
    HCD_CHECK_FROM_CRIT(pipe->port->state == HCD_PORT_STATE_ENABLED         //The pipe's port must be in the correct state
                        && pipe->state == HCD_PIPE_STATE_ACTIVE             //The pipe must be in the correct state
                        && !pipe->cs_flags.pipe_cmd_processing              //Pipe cannot currently be processing a pipe command
                        && !pipe->cs_flags.reset_lock,                      //Pipe cannot be persisting through a port reset
                        ESP_ERR_INVALID_STATE);
    ...
inside esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb), where pipe->port->state is HCD_PORT_STATE_RECOVERY

So it looks like the bulk-in ep is not in the correct state when the bulk-in transfer is requested. But I am not sure why this happens and how to get around it.

I would really appreciate if some one could shed some lights here.

Who is online

Users browsing this forum: benrank, Jorgen and 99 guests