Page 1 of 1

USB Host Control Request to Start Android Device in Accessory Mode

Posted: Thu Jun 16, 2022 12:30 pm
by raymondshi
Hi there,

I am trying to use the USB Host peripheral on the ESP32-S3 to communicate with an Android device.

I am using the usb_host_lib example and managed to get the Android device descriptor info displayed on the terminal. I've got basic communication working.

In order to request the Android device to work in the accessory mode as per Android Open Accessory specification (https://source.android.com/devices/accessories/aoa), it would need to send some control request e.g.:

[Codebox]
requestType: USB_DIR_OUT | USB_TYPE_VENDOR
request: 53
value: 0
index: 0
data: none
[/Codebox]

as well as the following,
[Codebox]
requestType: USB_DIR_OUT | USB_TYPE_VENDOR
request: 52
value: 0
index: string ID
data zero terminated UTF8 string sent from accessory to device
[/Codebox]

I tried to implement it with a call to usb_host_transfer_submit_control() as follows, which didn't work. It looks like the usb_transfer_t for the control request data doesn't get constructed properly.

Can someone please shed lights on how the control transfer data should get built for the above AOA control request?

Thanks in advance
Ray

[Codebox]
/*
* 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"

#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

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

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

static void transfer_desc_cb(usb_transfer_t *transfer)
{
ESP_LOGI("transfer","got 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);
}

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(1024, 0, &transfer_desc);

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

//usb_host_interface_claim(driver_obj.client_hdl, driver_obj.dev_hdl, 1, 0);

transfer_desc->num_bytes = 72;
transfer_desc->data_buffer[0]=0x40; //request type
transfer_desc->data_buffer[1]=53; //request accessory mode
transfer_desc->data_buffer[2]=0x00; //value 0
transfer_desc->data_buffer[3]=0x00; //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_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));
}


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

[/Codebox]