espressif_idf-extra-components/usb/usb_host_uvc/src/libusb_adapter.c

822 lines
26 KiB
C

/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "libusb.h"
#include <stdio.h>
#include <wchar.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/queue.h>
#include "esp_pthread.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_check.h"
#include "usb/usb_host.h"
#include "usb/usb_types_ch9.h"
#include "usb/usb_types_stack.h"
#include "usb/usb_helpers.h"
#include "descriptor.h"
#include "sdkconfig.h"
#include "libuvc/libuvc.h"
#include "libuvc/libuvc_internal.h"
#include "libuvc_adapter.h"
#define TAG "libusb adapter"
#define GOTO_ON_FALSE(exp) ESP_GOTO_ON_FALSE(exp, ESP_ERR_NO_MEM, fail, TAG, "")
#define RETURN_ON_ERROR(exp) ESP_RETURN_ON_ERROR(exp, TAG, "err: %s", esp_err_to_name(err_rc_))
#define GOTO_ON_ERROR(exp) ESP_GOTO_ON_ERROR(exp, fail, TAG, "")
#define RETURN_ON_ERROR_LIBUSB(exp) do { \
esp_err_t _err_ = (exp); \
if(_err_ != ESP_OK) { \
return esp_to_libusb_error(_err_); \
} \
} while(0)
#define UVC_ENTER_CRITICAL() portENTER_CRITICAL(&s_uvc_lock)
#define UVC_EXIT_CRITICAL() portEXIT_CRITICAL(&s_uvc_lock)
#define COUNT_OF(array) (sizeof(array) / sizeof(array[0]))
typedef struct {
usb_transfer_t *xfer;
struct libusb_transfer libusb_xfer;
} uvc_transfer_t;
typedef struct opened_camera {
uint8_t address;
uint8_t open_count;
uint16_t endpoint_mps; // interrupt endpoint
uint8_t active_alt_setting;
usb_device_handle_t handle;
usb_transfer_t *control_xfer;
SemaphoreHandle_t transfer_done;
usb_transfer_status_t transfer_status;
STAILQ_ENTRY(opened_camera) tailq_entry;
} uvc_camera_t;
typedef struct {
usb_host_client_handle_t client;
volatile bool delete_client_task;
SemaphoreHandle_t client_task_deleted;
STAILQ_HEAD(opened_devs, opened_camera) opened_devices_tailq;
} uvc_driver_t;
static portMUX_TYPE s_uvc_lock = portMUX_INITIALIZER_UNLOCKED;
static uvc_driver_t *s_uvc_driver;
static libuvc_adapter_config_t s_config = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 4096,
.callback = NULL,
};
static const usb_standard_desc_t *next_interface_desc(const usb_standard_desc_t *desc, size_t len, int *offset)
{
return usb_parse_next_descriptor_of_type(desc, len, USB_W_VALUE_DT_INTERFACE, offset);
}
static const usb_standard_desc_t *next_endpoint_desc(const usb_standard_desc_t *desc, size_t len, int *offset)
{
return usb_parse_next_descriptor_of_type(desc, len, USB_B_DESCRIPTOR_TYPE_ENDPOINT, (int *)offset);
}
// Find endpoint number under specified interface.
static esp_err_t find_endpoint_of_interface(const usb_config_desc_t *config_desc, uint8_t interface, uint8_t *endpoint)
{
int offset = 0;
size_t total_length = config_desc->wTotalLength;
const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *)config_desc;
next_desc = next_interface_desc(next_desc, total_length, &offset);
while ( next_desc ) {
const usb_intf_desc_t *ifc_desc = (const usb_intf_desc_t *)next_desc;
if ( ifc_desc->bInterfaceNumber == interface && ifc_desc->bNumEndpoints != 0) {
next_desc = next_endpoint_desc(next_desc, total_length, &offset);
if (next_desc == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
*endpoint = ((const usb_ep_desc_t *)next_desc)->bEndpointAddress;
return ESP_OK;
}
next_desc = next_interface_desc(next_desc, total_length, &offset);
};
return ESP_ERR_NOT_SUPPORTED;
}
static uint16_t get_interupt_endpoint_mps(const usb_config_desc_t *config_desc)
{
int offset = 0;
size_t total_length = config_desc->wTotalLength;
const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *)config_desc;
while ( (next_desc = next_endpoint_desc(next_desc, total_length, &offset)) ) {
const usb_ep_desc_t *ep_desc = (const usb_ep_desc_t *)next_desc;
if (USB_EP_DESC_GET_XFERTYPE(ep_desc) == USB_BM_ATTRIBUTES_XFER_INT) {
return ep_desc->wMaxPacketSize;
}
};
return 32;
}
void libuvc_adapter_set_config(libuvc_adapter_config_t *config)
{
if (config == NULL) {
return;
}
s_config = *config;
}
static void print_str_desc(const usb_str_desc_t *desc, const char *name)
{
wchar_t str[32];
size_t str_len = MIN((desc->bLength - USB_STANDARD_DESC_SIZE) / 2, COUNT_OF(str) - 1);
// Copy utf-16 to wchar_t array
for (size_t i = 0; i < str_len; i++) {
str[i] = desc->wData[i];
}
str[str_len] = '\0';
wprintf(L"%s: %S \n", name, str);
}
static void print_string_descriptors(usb_device_info_t *dev_info)
{
printf("*** String Descriptors ***\n");
if (dev_info->str_desc_product) {
print_str_desc(dev_info->str_desc_product, "iProduct");
}
if (dev_info->str_desc_manufacturer) {
print_str_desc(dev_info->str_desc_manufacturer, "iManufacturer");
}
if (dev_info->str_desc_serial_num) {
print_str_desc(dev_info->str_desc_serial_num, "iSerialNumber");
}
}
esp_err_t libuvc_adapter_print_descriptors(uvc_device_handle_t *device)
{
uvc_camera_t *camera = (uvc_camera_t *)(device->usb_devh);
const usb_config_desc_t *config_desc;
const usb_device_desc_t *device_desc;
usb_device_info_t dev_info;
RETURN_ON_ERROR( usb_host_get_device_descriptor(camera->handle, &device_desc) );
RETURN_ON_ERROR( usb_host_get_active_config_descriptor(camera->handle, &config_desc) );
RETURN_ON_ERROR( usb_host_device_info(camera->handle, &dev_info) );
usb_print_device_descriptor(device_desc);
usb_print_config_descriptor(config_desc, print_usb_class_descriptors);
print_string_descriptors(&dev_info);
return ESP_OK;
}
esp_err_t libuvc_adapter_handle_events(uint32_t timeout_ms)
{
if (s_uvc_driver == NULL) {
return ESP_ERR_INVALID_STATE;
}
return usb_host_client_handle_events(s_uvc_driver->client, pdMS_TO_TICKS(timeout_ms));
}
static int esp_to_libusb_error(esp_err_t err)
{
switch (err) {
case ESP_ERR_TIMEOUT: return LIBUSB_ERROR_TIMEOUT;
case ESP_ERR_NO_MEM: return LIBUSB_ERROR_NO_MEM;
case ESP_FAIL: return LIBUSB_ERROR_PIPE;
case ESP_OK: return LIBUSB_SUCCESS;
default: return LIBUSB_ERROR_OTHER;
}
}
static enum libusb_transfer_status eps_to_libusb_status(usb_transfer_status_t esp_status)
{
switch (esp_status) {
case USB_TRANSFER_STATUS_COMPLETED: return LIBUSB_TRANSFER_COMPLETED;
case USB_TRANSFER_STATUS_TIMED_OUT: return LIBUSB_TRANSFER_TIMED_OUT;
case USB_TRANSFER_STATUS_CANCELED: return LIBUSB_TRANSFER_CANCELLED;
case USB_TRANSFER_STATUS_NO_DEVICE: return LIBUSB_TRANSFER_NO_DEVICE;
case USB_TRANSFER_STATUS_OVERFLOW: return LIBUSB_TRANSFER_OVERFLOW;
case USB_TRANSFER_STATUS_STALL: return LIBUSB_TRANSFER_STALL;
default: return LIBUSB_TRANSFER_ERROR;
}
}
static void usb_client_event_handler(void *arg)
{
ulTaskNotifyTake(false, pdMS_TO_TICKS(1000));
do {
usb_host_client_handle_events(s_uvc_driver->client, pdMS_TO_TICKS(50));
} while (!s_uvc_driver->delete_client_task);
xSemaphoreGive(s_uvc_driver->client_task_deleted);
vTaskDelete(NULL);
}
static void client_event_cb(const usb_host_client_event_msg_t *event, void *arg)
{
if (s_config.callback) {
switch (event->event) {
case USB_HOST_CLIENT_EVENT_NEW_DEV:
ESP_LOGD(TAG, "USB device connected");
s_config.callback(UVC_DEVICE_CONNECTED);
break;
case USB_HOST_CLIENT_EVENT_DEV_GONE:
ESP_LOGD(TAG, "USB device disconnected");
s_config.callback(UVC_DEVICE_DISCONNECTED);
break;
default:
break;
}
}
}
int libusb_init(struct libusb_context **ctx)
{
uvc_driver_t *driver = NULL;
TaskHandle_t client_task_handle = NULL;
esp_err_t ret = ESP_ERR_NO_MEM;
usb_host_client_config_t client_config = {
.async.client_event_callback = client_event_cb,
.async.callback_arg = NULL,
.max_num_event_msg = 5,
};
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
esp_pthread_set_cfg(&cfg);
GOTO_ON_FALSE( driver = calloc(1, sizeof(uvc_driver_t)) );
GOTO_ON_ERROR( usb_host_client_register(&client_config, &driver->client) );
GOTO_ON_FALSE( driver->client_task_deleted = xSemaphoreCreateBinary() );
STAILQ_INIT(&driver->opened_devices_tailq);
if (s_config.create_background_task) {
GOTO_ON_FALSE( xTaskCreate(usb_client_event_handler, "uvc_events", s_config.stack_size,
NULL, s_config.task_priority, &client_task_handle) );
}
UVC_ENTER_CRITICAL();
if (s_uvc_driver != NULL) {
UVC_EXIT_CRITICAL();
ret = ESP_ERR_TIMEOUT;
goto fail;
}
s_uvc_driver = driver;
UVC_EXIT_CRITICAL();
if (client_task_handle) {
xTaskNotifyGive(client_task_handle);
}
*ctx = (struct libusb_context *)driver;
return LIBUSB_SUCCESS;
fail:
if (driver) {
if (driver->client) {
usb_host_client_deregister(driver->client);
};
if (driver->client_task_deleted) {
vSemaphoreDelete(driver->client_task_deleted);
}
free(driver);
}
if (client_task_handle) {
vTaskDelete(client_task_handle);
}
return esp_to_libusb_error(ret);
}
void libusb_exit(struct libusb_context *ctx)
{
uvc_driver_t *driver = (uvc_driver_t *)ctx;
UVC_ENTER_CRITICAL();
if (driver == NULL) {
UVC_EXIT_CRITICAL();
return;
}
UVC_EXIT_CRITICAL();
if (s_config.create_background_task) {
driver->delete_client_task = true;
}
usb_host_client_unblock(driver->client);
xSemaphoreTake(s_uvc_driver->client_task_deleted, portMAX_DELAY);
if (usb_host_client_deregister(driver->client) != ESP_OK) {
ESP_LOGE(TAG, "Failed to deregister USB client");
}
vSemaphoreDelete(s_uvc_driver->client_task_deleted);
s_uvc_driver = NULL;
free(driver);
}
int32_t libusb_get_device_list(struct libusb_context *ctx, libusb_device ***list)
{
static const size_t DEV_LIST_SIZE = 5;
int actual_count;
uint8_t dev_addr_list[DEV_LIST_SIZE];
usb_host_device_addr_list_fill(DEV_LIST_SIZE, dev_addr_list, &actual_count);
libusb_device **dev_list = calloc(actual_count + 1, sizeof(libusb_device *));
if (dev_list == NULL) {
return 0;
}
for (size_t i = 0; i < actual_count; i++) {
dev_list[i] = (libusb_device *)(uint32_t)dev_addr_list[i];
}
*list = (libusb_device **)dev_list;
return actual_count;
}
void libusb_free_device_list(libusb_device **list, int unref_devices)
{
free(list);
}
// As opposed to LIBUSB, USB_HOST library does not allows to open devices recursively and get descriptors without opening device.
// Thus, libusb_adapter keeps track of how many times the device is opened and closes it only when the count reaches zero.
static esp_err_t open_device_if_closed(uint8_t device_addr, uvc_camera_t **handle)
{
uvc_camera_t *device;
esp_err_t ret = ESP_ERR_NO_MEM;
uvc_camera_t *new_device = calloc(1, sizeof(uvc_camera_t));
if (new_device == NULL) {
return ESP_ERR_NO_MEM;
}
UVC_ENTER_CRITICAL();
STAILQ_FOREACH(device, &s_uvc_driver->opened_devices_tailq, tailq_entry) {
if (device_addr == device->address) {
*handle = device;
device->open_count++;
UVC_EXIT_CRITICAL();
free(new_device);
return ESP_OK;
}
}
new_device->open_count++;
new_device->address = device_addr;
STAILQ_INSERT_TAIL(&s_uvc_driver->opened_devices_tailq, new_device, tailq_entry);
UVC_EXIT_CRITICAL();
GOTO_ON_ERROR( usb_host_device_open(s_uvc_driver->client, device_addr, &new_device->handle) );
GOTO_ON_ERROR( usb_host_transfer_alloc(128, 0, &new_device->control_xfer) );
GOTO_ON_FALSE( new_device->transfer_done = xSemaphoreCreateBinary() );
*handle = new_device;
return ESP_OK;
fail:
UVC_ENTER_CRITICAL();
STAILQ_REMOVE(&s_uvc_driver->opened_devices_tailq, new_device, opened_camera, tailq_entry);
UVC_EXIT_CRITICAL();
free(new_device);
return ret;
}
static esp_err_t close_device(uvc_camera_t *device)
{
bool close = false;
UVC_ENTER_CRITICAL();
if (--device->open_count == 0) {
STAILQ_REMOVE(&s_uvc_driver->opened_devices_tailq, device, opened_camera, tailq_entry);
close = true;
}
UVC_EXIT_CRITICAL();
if (close) {
RETURN_ON_ERROR( usb_host_device_close(s_uvc_driver->client, device->handle) );
RETURN_ON_ERROR( usb_host_transfer_free(device->control_xfer) );
vSemaphoreDelete(device->transfer_done);
free(device);
}
return LIBUSB_SUCCESS;
}
int libusb_open(libusb_device *dev, libusb_device_handle **dev_handle)
{
uint8_t device_addr = (uint8_t)(uint32_t)dev;
uvc_camera_t *device;
RETURN_ON_ERROR_LIBUSB( open_device_if_closed(device_addr, &device) );
*dev_handle = (libusb_device_handle *)device;
return LIBUSB_SUCCESS;
}
void libusb_close(libusb_device_handle *dev_handle)
{
esp_err_t err = close_device((uvc_camera_t *)dev_handle);
if (err) {
ESP_LOGE(TAG, "Failed to close device");
}
}
void libusb_free_transfer(struct libusb_transfer *transfer)
{
uvc_transfer_t *trans = __containerof(transfer, uvc_transfer_t, libusb_xfer);
usb_host_transfer_free(trans->xfer);
free(trans);
}
struct libusb_transfer *libusb_alloc_transfer(int iso_packets)
{
size_t alloc_size = sizeof(uvc_transfer_t) +
sizeof(struct libusb_iso_packet_descriptor) * iso_packets;
uvc_transfer_t *xfer = calloc(1, alloc_size);
if (xfer == NULL) {
return NULL;
}
return &xfer->libusb_xfer;
}
static inline bool is_in_endpoint(uint8_t endpoint)
{
return endpoint & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK ? true : false;
}
// Copies data from usb_transfer_t back to libusb_transfer and invokes user provided callback
void transfer_cb(usb_transfer_t *xfer)
{
uvc_transfer_t *trans = xfer->context;
struct libusb_transfer *libusb_trans = &trans->libusb_xfer;
size_t isoc_actual_length = 0;
for (int i = 0; i < xfer->num_isoc_packets; i++) {
libusb_trans->iso_packet_desc[i].actual_length = xfer->isoc_packet_desc[i].actual_num_bytes;
libusb_trans->iso_packet_desc[i].status = eps_to_libusb_status(xfer->isoc_packet_desc[i].status);
if (libusb_trans->iso_packet_desc[i].status == LIBUSB_TRANSFER_COMPLETED) {
isoc_actual_length += xfer->isoc_packet_desc[i].actual_num_bytes;
}
}
libusb_trans->status = eps_to_libusb_status(xfer->status);
libusb_trans->actual_length = xfer->num_isoc_packets ? isoc_actual_length : xfer->actual_num_bytes;
if (is_in_endpoint(libusb_trans->endpoint)) {
memcpy(libusb_trans->buffer, xfer->data_buffer, libusb_trans->length);
}
libusb_trans->callback(libusb_trans);
}
// This function copies libusb_transfer data into usb_transfer_t structure
int libusb_submit_transfer(struct libusb_transfer *libusb_trans)
{
uvc_transfer_t *trans = __containerof(libusb_trans, uvc_transfer_t, libusb_xfer);
esp_err_t err;
int length = libusb_trans->length;
int num_iso_packets = libusb_trans->num_iso_packets;
uvc_camera_t *device = (uvc_camera_t *)libusb_trans->dev_handle;
// Workaround: libuvc submits interrupt INTR transfers with transfer size
// of 32 bytes, event though MSP of the endpoint might be 64.
// Make in transfer rounded up to MSP of interrupt endpoint.
// ISO transfers should be effected by this, as there are supposed to be 512 bytes long
if (is_in_endpoint(libusb_trans->endpoint)) {
length = usb_round_up_to_mps(length, device->endpoint_mps);
}
// Transfers are allocated/reallocated based on transfer size, as libusb
// doesn't store buffers in DMA capable region
if (!trans->xfer || trans->xfer->data_buffer_size < libusb_trans->length) {
if (trans->xfer) {
usb_host_transfer_free(trans->xfer);
}
err = usb_host_transfer_alloc(length, num_iso_packets, &trans->xfer);
if (err) {
ESP_LOGE(TAG, "Failed to allocate transfer with length: %u", length);
return esp_to_libusb_error(err);
}
}
if (!is_in_endpoint(libusb_trans->endpoint)) {
memcpy(trans->xfer->data_buffer, libusb_trans->buffer, libusb_trans->length);
}
trans->xfer->device_handle = device->handle;
trans->xfer->bEndpointAddress = libusb_trans->endpoint;
trans->xfer->timeout_ms = libusb_trans->timeout;
trans->xfer->callback = transfer_cb;
trans->xfer->num_bytes = length;
trans->xfer->context = trans;
for (int i = 0; i < num_iso_packets; i++) {
trans->xfer->isoc_packet_desc[i].num_bytes = libusb_trans->iso_packet_desc[i].length;
}
err = usb_host_transfer_submit(trans->xfer);
return esp_to_libusb_error(err);
}
int libusb_cancel_transfer(struct libusb_transfer *transfer)
{
return 0;
}
static bool is_in_request(uint8_t bmRequestType)
{
return (bmRequestType & USB_BM_REQUEST_TYPE_DIR_IN) != 0 ? true : false;
}
static bool is_out_request(uint8_t bmRequestType)
{
return (bmRequestType & USB_BM_REQUEST_TYPE_DIR_IN) == 0 ? true : false;
}
static void common_xfer_cb(usb_transfer_t *transfer)
{
uvc_camera_t *device = (uvc_camera_t *)transfer->context;
if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) {
ESP_EARLY_LOGE("Transfer failed", "Status %d", transfer->status);
}
device->transfer_status = transfer->status;
xSemaphoreGive(device->transfer_done);
}
static esp_err_t wait_for_transmition_done(usb_transfer_t *xfer)
{
uvc_camera_t *device = (uvc_camera_t *)xfer->context;
BaseType_t received = xSemaphoreTake(device->transfer_done, pdMS_TO_TICKS(xfer->timeout_ms));
if (received != pdTRUE) {
usb_host_endpoint_halt(xfer->device_handle, xfer->bEndpointAddress);
usb_host_endpoint_flush(xfer->device_handle, xfer->bEndpointAddress);
xSemaphoreTake(device->transfer_done, portMAX_DELAY);
return ESP_ERR_TIMEOUT;
}
if (device->transfer_status != USB_TRANSFER_STATUS_COMPLETED) {
printf("transfer_status: %d", device->transfer_status);
return ESP_FAIL;
}
return ESP_OK;
}
static int control_transfer(libusb_device_handle *dev_handle,
usb_setup_packet_t *request,
unsigned char *data,
unsigned int timeout)
{
return libusb_control_transfer(dev_handle, request->bmRequestType, request->bRequest,
request->wValue, request->wIndex, data,
request->wLength, timeout);
}
int libusb_control_transfer(libusb_device_handle *dev_handle,
uint8_t bmRequestType,
uint8_t bRequest,
uint16_t wValue,
uint16_t wIndex,
unsigned char *data,
uint16_t wLength,
unsigned int timeout)
{
uvc_camera_t *device = (uvc_camera_t *)dev_handle;
usb_transfer_t *xfer = device->control_xfer;
usb_setup_packet_t *ctrl_req = (usb_setup_packet_t *)xfer->data_buffer;
ctrl_req->bmRequestType = bmRequestType;
ctrl_req->bRequest = bRequest;
ctrl_req->wValue = wValue;
ctrl_req->wIndex = wIndex;
ctrl_req->wLength = wLength;
xfer->device_handle = device->handle;
xfer->bEndpointAddress = 0;
xfer->callback = common_xfer_cb;
xfer->timeout_ms = MAX(timeout, 100);
xfer->num_bytes = USB_SETUP_PACKET_SIZE + wLength;
xfer->context = device;
if (is_out_request(bmRequestType)) {
memcpy(xfer->data_buffer + sizeof(usb_setup_packet_t), data, wLength);
}
RETURN_ON_ERROR_LIBUSB( usb_host_transfer_submit_control(s_uvc_driver->client, xfer) );
RETURN_ON_ERROR_LIBUSB( wait_for_transmition_done(xfer) );
if (is_in_request(bmRequestType)) {
memcpy(data, xfer->data_buffer + sizeof(usb_setup_packet_t), wLength);
}
return xfer->actual_num_bytes;
}
int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc)
{
uint8_t device_addr = (uint8_t)(uint32_t)dev;
const usb_device_desc_t *device_desc;
uvc_camera_t *device;
// Open device if closed, as USB host doesn't allow to get descriptor without opening device
RETURN_ON_ERROR_LIBUSB( open_device_if_closed(device_addr, &device) );
RETURN_ON_ERROR_LIBUSB( usb_host_get_device_descriptor(device->handle, &device_desc) );
desc->bLength = device_desc->bLength;
desc->bDescriptorType = device_desc->bDescriptorType;
desc->bcdUSB = device_desc->bcdUSB;
desc->bDeviceClass = device_desc->bDeviceClass;
desc->bDeviceSubClass = device_desc->bDeviceSubClass;
desc->bDeviceProtocol = device_desc->bDeviceProtocol;
desc->bMaxPacketSize0 = device_desc->bMaxPacketSize0;
desc->idVendor = device_desc->idVendor;
desc->idProduct = device_desc->idProduct;
desc->bcdDevice = device_desc->bcdDevice;
desc->iManufacturer = device_desc->iManufacturer;
desc->iProduct = device_desc->iProduct;
desc->iSerialNumber = device_desc->iSerialNumber;
desc->bNumConfigurations = device_desc->bNumConfigurations;
RETURN_ON_ERROR_LIBUSB( close_device(device) );
return LIBUSB_SUCCESS;
}
int libusb_get_config_descriptor(libusb_device *dev,
uint8_t config_index,
struct libusb_config_descriptor **config)
{
uint8_t device_addr = (uint8_t)(uint32_t)dev;
const usb_config_desc_t *config_desc;
uvc_camera_t *device;
// Open device if closed, as USB host doesn't allow to get descriptor without opening device
RETURN_ON_ERROR_LIBUSB( open_device_if_closed(device_addr, &device) );
RETURN_ON_ERROR_LIBUSB( usb_host_get_active_config_descriptor(device->handle, &config_desc) );
int res = raw_desc_to_libusb_config(&config_desc->val[0], config_desc->wTotalLength, config);
device->endpoint_mps = get_interupt_endpoint_mps(config_desc);
RETURN_ON_ERROR_LIBUSB( close_device(device) );
return res;
}
void libusb_free_config_descriptor(struct libusb_config_descriptor *config)
{
clear_config_descriptor(config);
free(config);
}
int libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle,
uint8_t desc_index,
unsigned char *data,
int length)
{
#define US_LANG_ID 0x409
usb_setup_packet_t ctrl_req;
USB_SETUP_PACKET_INIT_GET_STR_DESC(&ctrl_req, desc_index, US_LANG_ID, length);
return control_transfer(dev_handle, &ctrl_req, data, 1000);
}
int libusb_get_ss_endpoint_companion_descriptor(struct libusb_context *ctx,
const struct libusb_endpoint_descriptor *endpoint,
struct libusb_ss_endpoint_companion_descriptor **ep_comp)
{
return 0;
}
void libusb_free_ss_endpoint_companion_descriptor(struct libusb_ss_endpoint_companion_descriptor *ep_comp)
{
}
libusb_device *libusb_ref_device(libusb_device *dev)
{
return dev;
}
void libusb_unref_device(libusb_device *dev)
{
}
int libusb_claim_interface(libusb_device_handle *dev_handle, int interface)
{
uvc_camera_t *device = (uvc_camera_t *)dev_handle;
// Alternate interface will be claimed in libusb_set_interface_alt_setting function,
// as libusb only support claming interface without alternate settings.
return esp_to_libusb_error( usb_host_interface_claim(s_uvc_driver->client, device->handle, interface, 0) );
}
int libusb_release_interface(libusb_device_handle *dev_handle, int interface)
{
uvc_camera_t *device = (uvc_camera_t *)dev_handle;
const usb_config_desc_t *config_desc;
uint8_t endpoint;
RETURN_ON_ERROR_LIBUSB( usb_host_get_active_config_descriptor(device->handle, &config_desc) );
RETURN_ON_ERROR_LIBUSB( find_endpoint_of_interface(config_desc, interface, &endpoint) );
// Cancel any ongoing transfers before releasing interface
usb_host_endpoint_halt(device->handle, endpoint);
usb_host_endpoint_flush(device->handle, endpoint);
usb_host_endpoint_clear(device->handle, endpoint);
return esp_to_libusb_error( usb_host_interface_release(s_uvc_driver->client, device->handle, interface) );
}
int libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, int32_t inferface, int32_t alt_settings)
{
uvc_camera_t *device = (uvc_camera_t *)dev_handle;
usb_host_client_handle_t client = s_uvc_driver->client;
uint8_t data[sizeof(usb_setup_packet_t)];
usb_setup_packet_t request;
// Setting alternate interface 0.0 is special case in UVC specs.
// No interface is to be released, just send control transfer.
if (inferface != 0 || alt_settings != 0) {
RETURN_ON_ERROR_LIBUSB( usb_host_interface_release(client, device->handle, inferface) );
RETURN_ON_ERROR_LIBUSB( usb_host_interface_claim(client, device->handle, inferface, alt_settings) );
}
USB_SETUP_PACKET_INIT_SET_INTERFACE(&request, inferface, alt_settings);
int result = control_transfer(dev_handle, &request, data, 2000);
return result > 0 ? LIBUSB_SUCCESS : result;
}
int libusb_attach_kernel_driver(libusb_device_handle *dev_handle, int interface_number)
{
return 0;
}
int libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number)
{
return 0;
}
int libusb_handle_events_completed(struct libusb_context *ctx, int *completed)
{
// USB events are handled either in client task or by user invoking libuvc_adapter_handle_events,
// as LIBUVC calls this handler only after opening device. USB Host requires to call client handler
// prior to opening device in order to receive USB_HOST_CLIENT_EVENT_NEW_DEV event.
vTaskDelay(pdMS_TO_TICKS(1000));
return 0;
}
int8_t libusb_get_bus_number(libusb_device *device)
{
return 0;
}
int8_t libusb_get_device_address(libusb_device *device)
{
// Device addres is stored directly in libusb_device
return (uint8_t)(uint32_t)device;
}