USB host UVC class
This commit is contained in:
parent
a83313a4ca
commit
768f84b2a4
|
@ -15,6 +15,6 @@ jobs:
|
|||
- name: Upload components to component service
|
||||
uses: espressif/upload-components-ci-action@v1
|
||||
with:
|
||||
directories: "bdc_motor;cbor;jsmn;led_strip;libsodium;pid_ctrl;qrcode;nghttp;sh2lib;expat;esp_encrypted_img;coap;pcap;json_generator;json_parser;usb/usb_host_cdc_acm;usb/usb_host_msc"
|
||||
directories: "bdc_motor;cbor;jsmn;led_strip;libsodium;pid_ctrl;qrcode;nghttp;sh2lib;expat;esp_encrypted_img;coap;pcap;json_generator;json_parser;usb/usb_host_cdc_acm;usb/usb_host_msc;usb/usb_host_uvc"
|
||||
namespace: "espressif"
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
|
|
@ -13,3 +13,6 @@
|
|||
[submodule "coap/libcoap"]
|
||||
path = coap/libcoap
|
||||
url = https://github.com/obgm/libcoap.git
|
||||
[submodule "usb/usb_host_uvc/libuvc"]
|
||||
path = usb/usb_host_uvc/libuvc
|
||||
url = https://github.com/libuvc/libuvc.git
|
||||
|
|
|
@ -4,8 +4,10 @@ cmake_minimum_required(VERSION 3.16)
|
|||
|
||||
set(EXTRA_COMPONENT_DIRS ../usb_host_cdc_acm
|
||||
../usb_host_msc)
|
||||
../usb_host_uvc
|
||||
$ENV{IDF_PATH}/examples/peripherals/usb/host/msc/components/)
|
||||
|
||||
# Set the components to include the tests for.
|
||||
set(TEST_COMPONENTS "usb_host_cdc_acm" "usb_host_msc" CACHE STRING "List of components to test")
|
||||
set(TEST_COMPONENTS "usb_host_cdc_acm" "usb_host_msc" "usb_host_uvc" CACHE STRING "List of components to test")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(usb_test_app)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
configure_file(${CMAKE_CURRENT_LIST_DIR}/libuvc/include/libuvc/libuvc_config.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/libuvc/libuvc_config.h
|
||||
@ONLY)
|
||||
|
||||
set(LIBUVC_SOURCES libuvc/src/ctrl.c
|
||||
libuvc/src/ctrl-gen.c
|
||||
libuvc/src/device.c
|
||||
libuvc/src/diag.c
|
||||
libuvc/src/frame.c
|
||||
libuvc/src/init.c
|
||||
libuvc/src/misc.c
|
||||
libuvc/src/stream.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${LIBUVC_SOURCES} src/descriptor.c src/libusb_adapter.c
|
||||
INCLUDE_DIRS include libuvc/include
|
||||
PRIV_INCLUDE_DIRS private_include
|
||||
REQUIRES usb pthread)
|
||||
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/libuvc/src/device.c PROPERTIES COMPILE_FLAGS -Wno-implicit-fallthrough)
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/libuvc/src/stream.c PROPERTIES COMPILE_FLAGS -Wno-unused-variable)
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/libuvc/src/diag.c PROPERTIES COMPILE_FLAGS -Wno-format)
|
||||
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE LIBUVC_NUM_TRANSFER_BUFS=4)
|
||||
target_include_directories(${COMPONENT_LIB} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include/)
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,21 @@
|
|||
# USB Host UVC Driver
|
||||
|
||||
This directory contains USB host UVC driver based on [libuvc](https://github.com/libuvc/libuvc) library. Support for `libuvc` is achieved by implementing adapter between [libusb](https://github.com/libusb/libusb) (underling host library used by `libuvc`) and [usb_host](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html) library targeted for ESP SOCs.
|
||||
|
||||
## Usage
|
||||
|
||||
Reference [uvc_host_example](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/uvc) is similar to one found in `libuvc` repository with few additions:
|
||||
1. Before calling `uvc_init()`, `initialize_usb_host_lib()` has to be called in order to initialize usb host library.
|
||||
2. Since `libuvc` selects highest possible `dwMaxPayloadTransferSize` by default, user has to manually overwrite obatained value to 512 bytes (maximum transfer size supported by ESP32-S2/S3) before passing it to `uvc_print_stream_ctrl()` function.
|
||||
3. Optionally, user can configure `libusb adaprer` by passing appropriate parameters to `libuvc_adapter_set_config()`.
|
||||
|
||||
## Known limitations
|
||||
|
||||
Having only Full Speed USB peripheral and hardware limited MPS (maximum packet size) to 512 bytes, ESP32-S2/S3 is capable of reading about 0.5 MB of data per second. When connected to Full Speed USB host, cameras normally provide resolution no larger than 640x480 pixels.
|
||||
Following two supported formats are the most common (both encoded in MJPEG):
|
||||
* 320x240 30 FPS
|
||||
* 640x480 15 FPS
|
||||
|
||||
## Tested cameras
|
||||
* Logitech C980
|
||||
* CANYON CNE-CWC2
|
|
@ -0,0 +1,10 @@
|
|||
version: "0.0.2"
|
||||
description: USB Host UVC driver
|
||||
url: https://github.com/espressif/idf-extra-components/tree/master
|
||||
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
|
||||
targets:
|
||||
- esp32s2
|
||||
- esp32s3
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
UVC_DEVICE_CONNECTED = 1,
|
||||
UVC_DEVICE_DISCONNECTED = 2,
|
||||
} libuvc_adapter_event_t;
|
||||
|
||||
typedef void (*libuvc_adapter_cb_t)(libuvc_adapter_event_t);
|
||||
|
||||
/**
|
||||
* @brief Configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
bool create_background_task; /**< Event handling background task is created when set to true.
|
||||
Otherwise, user has to handle event by calling libuvc_adapter_handle_events */
|
||||
uint8_t task_priority; /**< Background task priority */
|
||||
uint32_t stack_size; /**< Background task stack size */
|
||||
libuvc_adapter_cb_t callback; /**< Callback notifying about connection and disconnection events */
|
||||
} libuvc_adapter_config_t;
|
||||
|
||||
/**
|
||||
* @brief Sets configuration for libuvc adapter
|
||||
*
|
||||
* - This function can be called prior to calling `uvc_init` function,
|
||||
* otherwise default configuration will be used
|
||||
*
|
||||
* @param[in] config Configuration structure
|
||||
*/
|
||||
void libuvc_adapter_set_config(libuvc_adapter_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Prints full configuration descriptor
|
||||
*
|
||||
* @param[in] device Device handle obtained from `uvc_open`
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t libuvc_adapter_print_descriptors(uvc_device_handle_t *device);
|
||||
|
||||
/**
|
||||
* @brief Handle USB Client events.
|
||||
*
|
||||
* - This function has to be called periodically, if configuration
|
||||
* was provided with `create_background_task` set to `false`.
|
||||
*
|
||||
* @param[in] timeout_ms Timeout in miliseconds
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t libuvc_adapter_handle_events(uint32_t timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "libuvc/libuvc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
inline static char *uvc_error_string(uvc_error_t error)
|
||||
{
|
||||
switch (error) {
|
||||
case UVC_SUCCESS: return "UVC_SUCCESS";
|
||||
case UVC_ERROR_IO: return "UVC_ERROR_IO";
|
||||
case UVC_ERROR_INVALID_PARAM: return "UVC_ERROR_INVALID_PARAM";
|
||||
case UVC_ERROR_ACCESS: return "UVC_ERROR_ACCESS";
|
||||
case UVC_ERROR_NO_DEVICE: return "UVC_ERROR_NO_DEVICE";
|
||||
case UVC_ERROR_NOT_FOUND: return "UVC_ERROR_NOT_FOUND";
|
||||
case UVC_ERROR_BUSY: return "UVC_ERROR_BUSY";
|
||||
case UVC_ERROR_TIMEOUT: return "UVC_ERROR_TIMEOUT";
|
||||
case UVC_ERROR_OVERFLOW: return "UVC_ERROR_OVERFLOW";
|
||||
case UVC_ERROR_PIPE: return "UVC_ERROR_PIPE";
|
||||
case UVC_ERROR_INTERRUPTED: return "UVC_ERROR_INTERRUPTED";
|
||||
case UVC_ERROR_NO_MEM: return "UVC_ERROR_NO_MEM";
|
||||
case UVC_ERROR_NOT_SUPPORTED: return "UVC_ERROR_NOT_SUPPORTED";
|
||||
case UVC_ERROR_INVALID_DEVICE: return "UVC_ERROR_INVALID_DEVICE";
|
||||
case UVC_ERROR_INVALID_MODE: return "UVC_ERROR_INVALID_MODE";
|
||||
case UVC_ERROR_CALLBACK_EXISTS: return "UVC_ERROR_CALLBACK_EXISTS";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a4de53e7e265f8c6a64df7ccd289f318104e1916
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Converts raw buffer containing config descriptor into libusb_config_descriptor
|
||||
*
|
||||
* @note Call clear_config_descriptor when config descriptor is no longer needed.
|
||||
*
|
||||
* @param[in] buf buffer containing config descriptor
|
||||
* @param[in] size size of buffer
|
||||
* @param[out] config pointer to allocated libusb compatible config descriptor
|
||||
* @return libusb_error
|
||||
*/
|
||||
int raw_desc_to_libusb_config(const uint8_t *buf, int size, struct libusb_config_descriptor **config);
|
||||
|
||||
/**
|
||||
* @brief Releases memory previously allocated by config raw_desc_to_libusb_config
|
||||
*
|
||||
* @param[in] config pointer to allocated config descriptor
|
||||
*/
|
||||
void clear_config_descriptor(struct libusb_config_descriptor *config);
|
||||
|
||||
/**
|
||||
* @brief Prints class specific descriptors
|
||||
*
|
||||
* @param[in] desc pointer to usb_standard_desc_t
|
||||
*/
|
||||
void print_usb_class_descriptors(const usb_standard_desc_t *desc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LIBUSB_CALL static
|
||||
|
||||
#define LIBUSB_DT_DEVICE_SIZE 18
|
||||
#define LIBUSB_DT_CONFIG_SIZE 9
|
||||
#define LIBUSB_DT_INTERFACE_SIZE 9
|
||||
#define LIBUSB_DT_ENDPOINT_SIZE 7
|
||||
#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9
|
||||
|
||||
enum libusb_error {
|
||||
LIBUSB_SUCCESS = 0,
|
||||
LIBUSB_ERROR_IO = -1,
|
||||
LIBUSB_ERROR_INVALID_PARAM = -2,
|
||||
LIBUSB_ERROR_ACCESS = -3,
|
||||
LIBUSB_ERROR_NO_DEVICE = -4,
|
||||
LIBUSB_ERROR_NOT_FOUND = -5,
|
||||
LIBUSB_ERROR_BUSY = -6,
|
||||
LIBUSB_ERROR_TIMEOUT = -7,
|
||||
LIBUSB_ERROR_OVERFLOW = -8,
|
||||
LIBUSB_ERROR_PIPE = -9,
|
||||
LIBUSB_ERROR_INTERRUPTED = -10,
|
||||
LIBUSB_ERROR_NO_MEM = -11,
|
||||
LIBUSB_ERROR_NOT_SUPPORTED = -12,
|
||||
LIBUSB_ERROR_OTHER = -99
|
||||
};
|
||||
|
||||
enum libusb_descriptor_type {
|
||||
LIBUSB_DT_DEVICE = 0x01,
|
||||
LIBUSB_DT_CONFIG = 0x02,
|
||||
LIBUSB_DT_STRING = 0x03,
|
||||
LIBUSB_DT_INTERFACE = 0x04,
|
||||
LIBUSB_DT_ENDPOINT = 0x05,
|
||||
LIBUSB_DT_BOS = 0x0f,
|
||||
LIBUSB_DT_DEVICE_CAPABILITY = 0x10,
|
||||
LIBUSB_DT_HID = 0x21,
|
||||
LIBUSB_DT_REPORT = 0x22,
|
||||
LIBUSB_DT_PHYSICAL = 0x23,
|
||||
LIBUSB_DT_HUB = 0x29,
|
||||
LIBUSB_DT_SUPERSPEED_HUB = 0x2a,
|
||||
LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30
|
||||
};
|
||||
|
||||
struct libusb_device_descriptor {
|
||||
uint8_t bLength; /**< Size of the descriptor in bytes */
|
||||
uint8_t bDescriptorType; /**< DEVICE Descriptor Type */
|
||||
uint16_t bcdUSB; /**< USB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H) */
|
||||
uint8_t bDeviceClass; /**< Class code (assigned by the USB-IF) */
|
||||
uint8_t bDeviceSubClass; /**< Subclass code (assigned by the USB-IF) */
|
||||
uint8_t bDeviceProtocol; /**< Protocol code (assigned by the USB-IF) */
|
||||
uint8_t bMaxPacketSize0; /**< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid) */
|
||||
uint16_t idVendor; /**< Vendor ID (assigned by the USB-IF) */
|
||||
uint16_t idProduct; /**< Product ID (assigned by the manufacturer) */
|
||||
uint16_t bcdDevice; /**< Device release number in binary-coded decimal */
|
||||
uint8_t iManufacturer; /**< Index of string descriptor describing manufacturer */
|
||||
uint8_t iProduct; /**< Index of string descriptor describing product */
|
||||
uint8_t iSerialNumber; /**< Index of string descriptor describing the device’s serial number */
|
||||
uint8_t bNumConfigurations; /**< Number of possible configurations */
|
||||
};
|
||||
|
||||
struct libusb_endpoint_descriptor {
|
||||
uint8_t bLength; /**< Size of the descriptor in bytes */
|
||||
uint8_t bDescriptorType; /**< ENDPOINT Descriptor Type */
|
||||
uint8_t bEndpointAddress; /**< The address of the endpoint on the USB device described by this descriptor */
|
||||
uint8_t bmAttributes; /**< This field describes the endpoint’s attributes when it is configured using the bConfigurationValue. */
|
||||
uint16_t wMaxPacketSize; /**< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. */
|
||||
uint8_t bInterval; /**< Interval for polling Isochronous and Interrupt endpoints. Expressed in frames or microframes depending on the device operating speed (1 ms for Low-Speed and Full-Speed or 125 us for USB High-Speed and above). */
|
||||
uint8_t *extra;
|
||||
size_t extra_length;
|
||||
};
|
||||
|
||||
struct libusb_interface_descriptor {
|
||||
uint8_t bLength; /**< Size of the descriptor in bytes */
|
||||
uint8_t bDescriptorType; /**< INTERFACE Descriptor Type */
|
||||
uint8_t bInterfaceNumber; /**< Number of this interface. */
|
||||
uint8_t bAlternateSetting; /**< Value used to select this alternate setting for the interface identified in the prior field */
|
||||
uint8_t bNumEndpoints; /**< Number of endpoints used by this interface (excluding endpoint zero). */
|
||||
uint8_t bInterfaceClass; /**< Class code (assigned by the USB-IF) */
|
||||
uint8_t bInterfaceSubClass; /**< Subclass code (assigned by the USB-IF) */
|
||||
uint8_t bInterfaceProtocol; /**< Protocol code (assigned by the USB) */
|
||||
uint8_t iInterface; /**< Index of string descriptor describing this interface */
|
||||
uint8_t *extra;
|
||||
size_t extra_length;
|
||||
struct libusb_endpoint_descriptor *endpoint;
|
||||
};
|
||||
|
||||
struct libusb_interface {
|
||||
size_t num_altsetting;
|
||||
struct libusb_interface_descriptor *altsetting;
|
||||
};
|
||||
|
||||
struct libusb_config_descriptor {
|
||||
uint8_t bLength; /**< Size of the descriptor in bytes */
|
||||
uint8_t bDescriptorType; /**< CONFIGURATION Descriptor Type */
|
||||
uint16_t wTotalLength; /**< Total length of data returned for this configuration */
|
||||
uint8_t bNumInterfaces; /**< Number of interfaces supported by this configuration */
|
||||
uint8_t bConfigurationValue; /**< Value to use as an argument to the SetConfiguration() request to select this configuration */
|
||||
uint8_t iConfiguration; /**< Index of string descriptor describing this configuration */
|
||||
uint8_t bmAttributes; /**< Configuration characteristics */
|
||||
uint8_t bMaxPower; /**< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. */
|
||||
uint8_t *extra;
|
||||
size_t extra_length;
|
||||
struct libusb_interface *interface;
|
||||
};
|
||||
|
||||
typedef struct libusb_config_descriptor libusb_config_descriptor_t;
|
||||
typedef struct libusb_interface_descriptor libusb_interface_descriptor_t;
|
||||
typedef struct libusb_endpoint_descriptor libusb_endpoint_descriptor_t;
|
||||
typedef struct libusb_interface libusb_interface_t;
|
||||
|
||||
struct libusb_ss_endpoint_companion_descriptor {
|
||||
uint32_t wBytesPerInterval;
|
||||
};
|
||||
|
||||
struct libusb_device;
|
||||
typedef struct libusb_device libusb_device;
|
||||
|
||||
struct libusb_device_handle;
|
||||
typedef struct libusb_device_handle libusb_device_handle;
|
||||
|
||||
struct libusb_context;
|
||||
|
||||
typedef enum libusb_transfer_status {
|
||||
LIBUSB_TRANSFER_COMPLETED,
|
||||
LIBUSB_TRANSFER_CANCELLED,
|
||||
LIBUSB_TRANSFER_ERROR,
|
||||
LIBUSB_TRANSFER_NO_DEVICE,
|
||||
LIBUSB_TRANSFER_TIMED_OUT,
|
||||
LIBUSB_TRANSFER_STALL,
|
||||
LIBUSB_TRANSFER_OVERFLOW,
|
||||
} libusb_status_t;
|
||||
|
||||
typedef struct libusb_iso_packet_descriptor {
|
||||
size_t length;
|
||||
size_t actual_length;
|
||||
libusb_status_t status;
|
||||
} libusb_iso_packet_t;
|
||||
|
||||
struct libusb_transfer {
|
||||
libusb_device_handle *dev_handle;
|
||||
libusb_status_t status;
|
||||
uint8_t endpoint;
|
||||
uint8_t *buffer;
|
||||
size_t length;
|
||||
size_t actual_length;
|
||||
void *user_data;
|
||||
void (*callback)(struct libusb_transfer *);
|
||||
size_t timeout;
|
||||
size_t num_iso_packets;
|
||||
libusb_iso_packet_t iso_packet_desc[0];
|
||||
};
|
||||
|
||||
typedef void (*libusb_transfer_cb)(struct libusb_transfer *transfer);
|
||||
|
||||
int libusb_init(struct libusb_context **ctx);
|
||||
|
||||
void libusb_exit(struct libusb_context *ctx);
|
||||
|
||||
int libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
|
||||
|
||||
void libusb_close(libusb_device_handle *dev_handle);
|
||||
|
||||
int32_t libusb_get_device_list(struct libusb_context *ctx, libusb_device ***list);
|
||||
|
||||
void libusb_free_device_list(libusb_device **list, int unref_devices);
|
||||
|
||||
int libusb_handle_events_completed(struct libusb_context *ctx, int *completed);
|
||||
|
||||
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);
|
||||
|
||||
void libusb_free_transfer(struct libusb_transfer *transfer);
|
||||
|
||||
int libusb_submit_transfer(struct libusb_transfer *transfer);
|
||||
|
||||
int libusb_cancel_transfer(struct libusb_transfer *transfer);
|
||||
|
||||
inline uint8_t *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t packet_id)
|
||||
{
|
||||
if (packet_id >= transfer->num_iso_packets) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &transfer->buffer[transfer->iso_packet_desc[0].length * packet_id];
|
||||
}
|
||||
|
||||
struct libusb_transfer *libusb_alloc_transfer(int iso_packets);
|
||||
|
||||
inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer,
|
||||
libusb_device_handle *dev,
|
||||
uint8_t bEndpointAddress,
|
||||
uint8_t *buffer,
|
||||
size_t total_transfer_size,
|
||||
size_t packets_per_transfer,
|
||||
libusb_transfer_cb callback,
|
||||
void *user_data,
|
||||
size_t timeout)
|
||||
{
|
||||
transfer->dev_handle = dev;
|
||||
transfer->endpoint = bEndpointAddress;
|
||||
transfer->timeout = timeout;
|
||||
transfer->buffer = buffer;
|
||||
transfer->length = total_transfer_size;
|
||||
transfer->num_iso_packets = packets_per_transfer;
|
||||
transfer->user_data = user_data;
|
||||
transfer->callback = callback;
|
||||
}
|
||||
|
||||
inline void libusb_fill_bulk_transfer (struct libusb_transfer *transfer,
|
||||
libusb_device_handle *dev,
|
||||
uint8_t bEndpointAddress,
|
||||
uint8_t *buffer,
|
||||
size_t length,
|
||||
libusb_transfer_cb callback,
|
||||
void *user_data,
|
||||
size_t timeout)
|
||||
{
|
||||
transfer->dev_handle = dev;
|
||||
transfer->endpoint = bEndpointAddress;
|
||||
transfer->buffer = buffer;
|
||||
transfer->length = length;
|
||||
transfer->callback = callback;
|
||||
transfer->user_data = user_data;
|
||||
transfer->timeout = timeout;
|
||||
transfer->num_iso_packets = 0;
|
||||
}
|
||||
|
||||
inline void libusb_fill_interrupt_transfer (struct libusb_transfer *transfer,
|
||||
libusb_device_handle *dev,
|
||||
uint8_t bEndpointAddress,
|
||||
uint8_t *buffer,
|
||||
size_t length,
|
||||
libusb_transfer_cb callback,
|
||||
void *user_data,
|
||||
size_t timeout)
|
||||
{
|
||||
transfer->dev_handle = dev;
|
||||
transfer->endpoint = bEndpointAddress;
|
||||
transfer->buffer = buffer;
|
||||
transfer->length = length;
|
||||
transfer->callback = callback;
|
||||
transfer->user_data = user_data;
|
||||
transfer->timeout = timeout;
|
||||
transfer->num_iso_packets = 0;
|
||||
}
|
||||
|
||||
inline void libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, size_t length)
|
||||
{
|
||||
for (uint32_t i = 0; i < transfer->num_iso_packets; i++) {
|
||||
transfer->iso_packet_desc[i].length = length;
|
||||
}
|
||||
}
|
||||
|
||||
int libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, int32_t inferface, int32_t alt_settings);
|
||||
|
||||
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);
|
||||
|
||||
int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc);
|
||||
|
||||
int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config);
|
||||
|
||||
void libusb_free_config_descriptor(struct libusb_config_descriptor *config);
|
||||
|
||||
int libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, uint8_t desc_index, unsigned char *data, int length);
|
||||
|
||||
void libusb_free_ss_endpoint_companion_descriptor(struct libusb_ss_endpoint_companion_descriptor *desc);
|
||||
|
||||
int8_t libusb_get_bus_number(libusb_device *device);
|
||||
|
||||
int8_t libusb_get_device_address(libusb_device *device);
|
||||
|
||||
libusb_device *libusb_ref_device(libusb_device *dev);
|
||||
|
||||
void libusb_unref_device(libusb_device *dev);
|
||||
|
||||
int libusb_claim_interface(libusb_device_handle *dev_handle, int interface);
|
||||
|
||||
int libusb_release_interface(libusb_device_handle *dev_handle, int interface);
|
||||
|
||||
int libusb_attach_kernel_driver(libusb_device_handle *dev_handle, int interface_number);
|
||||
|
||||
int libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
idf_component_register(SRCS
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES usb_host_uvc unity)
|
Loading…
Reference in New Issue