Merge pull request #77 from espressif/feature/usb_host_uvc
USB host UVC class
This commit is contained in:
commit
6f8e366562
|
@ -18,6 +18,8 @@ jobs:
|
|||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Build USB Test Application
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS ../usb_host_cdc_acm
|
||||
../usb_host_msc)
|
||||
../usb_host_msc
|
||||
../usb_host_uvc)
|
||||
|
||||
# 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 adapter` 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: "1.0.0"
|
||||
description: USB Host UVC driver
|
||||
url: https://github.com/espressif/idf-extra-components/tree/master/usb/usb_host_uvc
|
||||
|
||||
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
|
|
@ -0,0 +1,767 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include "libusb.h"
|
||||
#include "esp_log.h"
|
||||
#include "usb/usb_host.h"
|
||||
#include "usb/usb_types_ch9.h"
|
||||
#include "sys/param.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubtype;
|
||||
} desc_header_t;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *begin;
|
||||
uint8_t **data;
|
||||
size_t *len;
|
||||
} extra_data_t;
|
||||
|
||||
#define DESC_HEADER_LENGTH 2
|
||||
#define USB_MAXENDPOINTS 32
|
||||
#define USB_MAXINTERFACES 32
|
||||
#define USB_MAXCONFIG 8
|
||||
|
||||
#define TAG "DESC"
|
||||
|
||||
#define USB_DESC_ATTR __attribute__((packed))
|
||||
|
||||
|
||||
#define LIBUSB_GOTO_ON_ERROR(exp) do { \
|
||||
int _res_ = exp; \
|
||||
if(_res_ != LIBUSB_SUCCESS) { \
|
||||
ret = _res_; \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define LIBUSB_GOTO_ON_FALSE(exp) do { \
|
||||
if((exp) == 0) { \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static const usb_intf_desc_t *next_interface_desc(const usb_intf_desc_t *desc, size_t len, int *offset)
|
||||
{
|
||||
return (const usb_intf_desc_t *) usb_parse_next_descriptor_of_type(
|
||||
(const usb_standard_desc_t *)desc, len, USB_B_DESCRIPTOR_TYPE_INTERFACE, offset);
|
||||
}
|
||||
|
||||
void copy_config_desc(libusb_config_descriptor_t *libusb_desc, const usb_config_desc_t *idf_desc)
|
||||
{
|
||||
libusb_desc->bLength = idf_desc->bLength;
|
||||
libusb_desc->bDescriptorType = idf_desc->bDescriptorType;
|
||||
libusb_desc->wTotalLength = idf_desc->wTotalLength;
|
||||
libusb_desc->bNumInterfaces = idf_desc->bNumInterfaces;
|
||||
libusb_desc->bConfigurationValue = idf_desc->bConfigurationValue;
|
||||
libusb_desc->iConfiguration = idf_desc->iConfiguration;
|
||||
libusb_desc->bmAttributes = idf_desc->bmAttributes;
|
||||
libusb_desc->bMaxPower = idf_desc->bMaxPower;
|
||||
}
|
||||
|
||||
void copy_interface_desc(libusb_interface_descriptor_t *libusb_desc, const usb_intf_desc_t *idf_desc)
|
||||
{
|
||||
libusb_desc->bLength = idf_desc->bLength;
|
||||
libusb_desc->bDescriptorType = idf_desc->bDescriptorType;
|
||||
libusb_desc->bInterfaceNumber = idf_desc->bInterfaceNumber;
|
||||
libusb_desc->bAlternateSetting = idf_desc->bAlternateSetting;
|
||||
libusb_desc->bNumEndpoints = idf_desc->bNumEndpoints;
|
||||
libusb_desc->bInterfaceClass = idf_desc->bInterfaceClass;
|
||||
libusb_desc->bInterfaceSubClass = idf_desc->bInterfaceSubClass;
|
||||
libusb_desc->bInterfaceProtocol = idf_desc->bInterfaceProtocol;
|
||||
libusb_desc->iInterface = idf_desc->iInterface;
|
||||
}
|
||||
|
||||
void copy_endpoint_desc(libusb_endpoint_descriptor_t *libusb_desc, const usb_ep_desc_t *idf_desc)
|
||||
{
|
||||
libusb_desc->bLength = idf_desc->bLength;
|
||||
libusb_desc->bDescriptorType = idf_desc->bDescriptorType;
|
||||
libusb_desc->bEndpointAddress = idf_desc->bEndpointAddress;
|
||||
libusb_desc->bmAttributes = idf_desc->bmAttributes;
|
||||
libusb_desc->wMaxPacketSize = idf_desc->wMaxPacketSize;
|
||||
libusb_desc->bInterval = idf_desc->bInterval;
|
||||
}
|
||||
|
||||
static void set_extra_data(extra_data_t *extra, uint8_t **extra_data, size_t *extra_len, const uint8_t *begin)
|
||||
{
|
||||
extra->data = extra_data;
|
||||
extra->len = extra_len;
|
||||
extra->begin = begin;
|
||||
}
|
||||
|
||||
// Copies extra data to previously provided memory.
|
||||
// The function allocates or realllocates memory depending on provided pointer
|
||||
static libusb_status_t add_extra_data(extra_data_t *extra, const void *end)
|
||||
{
|
||||
uint8_t *new_memory = NULL;
|
||||
|
||||
int new_size = (int)((uint8_t *)end - extra->begin);
|
||||
|
||||
if (new_size > 0) {
|
||||
if (*extra->data == NULL) {
|
||||
new_memory = malloc(new_size);
|
||||
} else {
|
||||
new_memory = realloc(*extra->data, *extra->len + new_size);
|
||||
}
|
||||
|
||||
if (!new_memory) {
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
memcpy(new_memory + *extra->len, extra->begin, new_size);
|
||||
*extra->data = new_memory;
|
||||
*extra->len += new_size;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
void clear_config_descriptor(libusb_config_descriptor_t *config)
|
||||
{
|
||||
if (config) {
|
||||
if (config->interface) {
|
||||
for (int i = 0; i < config->bNumInterfaces; i++) {
|
||||
libusb_interface_t *interface = &config->interface[i];
|
||||
if (interface->altsetting) {
|
||||
for (int j = 0; j < interface->num_altsetting; j++) {
|
||||
libusb_interface_descriptor_t *alt = &interface->altsetting[j];
|
||||
if (alt->endpoint) {
|
||||
for (int ep = 0; ep < alt->bNumEndpoints; ep++) {
|
||||
free(alt->endpoint[ep].extra);
|
||||
}
|
||||
free(alt->endpoint);
|
||||
}
|
||||
free(alt->extra);
|
||||
}
|
||||
free(interface->altsetting);
|
||||
}
|
||||
}
|
||||
free(config->interface);
|
||||
}
|
||||
free(config->extra);
|
||||
}
|
||||
}
|
||||
|
||||
int parse_configuration(libusb_config_descriptor_t *config, const uint8_t *buffer, int size)
|
||||
{
|
||||
int offset = 0;
|
||||
extra_data_t extra = { 0 };
|
||||
const usb_ep_desc_t *ep_desc;
|
||||
const usb_config_desc_t *config_start = (const usb_config_desc_t *)buffer;
|
||||
libusb_status_t ret = LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
copy_config_desc(config, (const usb_config_desc_t *)buffer);
|
||||
config->interface = calloc(config->bNumInterfaces, sizeof(libusb_interface_t));
|
||||
LIBUSB_GOTO_ON_FALSE(config->interface);
|
||||
// set pointers to extra data to be used later for class/vendor specific descriptor
|
||||
set_extra_data(&extra, &config->extra, &config->extra_length, buffer + LIBUSB_DT_CONFIG_SIZE);
|
||||
const usb_intf_desc_t *ifc_desc = (const usb_intf_desc_t *)buffer;
|
||||
|
||||
for (int i = 0; i < config->bNumInterfaces; i++) {
|
||||
ifc_desc = next_interface_desc(ifc_desc, config->wTotalLength, &offset);
|
||||
// Copy any unknown descriptors into a storage area for drivers to later parse
|
||||
LIBUSB_GOTO_ON_ERROR( add_extra_data(&extra, ifc_desc) );
|
||||
|
||||
libusb_interface_t *interface = &config->interface[i];
|
||||
// Obtain number of alternate interfaces to given interface number
|
||||
int alt_interfaces = usb_parse_interface_number_of_alternate(config_start, ifc_desc->bInterfaceNumber) + 1;
|
||||
interface->altsetting = calloc(alt_interfaces, sizeof(libusb_interface_descriptor_t));
|
||||
LIBUSB_GOTO_ON_FALSE(interface->altsetting);
|
||||
interface->num_altsetting = alt_interfaces;
|
||||
|
||||
for (int alt = 0; alt < alt_interfaces; alt++) {
|
||||
libusb_interface_descriptor_t *altsetting = &interface->altsetting[alt];
|
||||
copy_interface_desc(altsetting, ifc_desc);
|
||||
set_extra_data(&extra, &altsetting->extra, &altsetting->extra_length, ((uint8_t *)ifc_desc) + LIBUSB_DT_INTERFACE_SIZE);
|
||||
uint8_t endpoints = ifc_desc->bNumEndpoints;
|
||||
|
||||
altsetting->endpoint = calloc(altsetting->bNumEndpoints, sizeof(libusb_endpoint_descriptor_t));
|
||||
LIBUSB_GOTO_ON_FALSE(interface->altsetting);
|
||||
|
||||
for (int ep = 0; ep < endpoints; ep++) {
|
||||
ep_desc = usb_parse_endpoint_descriptor_by_index(ifc_desc, ep, config->wTotalLength, &offset);
|
||||
ifc_desc = (const usb_intf_desc_t *)ep_desc;
|
||||
libusb_endpoint_descriptor_t *endpoint = &altsetting->endpoint[ep];
|
||||
copy_endpoint_desc(endpoint, ep_desc);
|
||||
LIBUSB_GOTO_ON_ERROR( add_extra_data(&extra, ep_desc) );
|
||||
set_extra_data(&extra, &endpoint->extra, &endpoint->extra_length, ((uint8_t *)ep_desc) + ep_desc->bLength);
|
||||
}
|
||||
if (alt + 1 < alt_interfaces) {
|
||||
// go over next alternate interface
|
||||
ifc_desc = next_interface_desc(ifc_desc, config->wTotalLength, &offset);
|
||||
LIBUSB_GOTO_ON_ERROR( add_extra_data(&extra, ifc_desc) );
|
||||
extra.begin = ((uint8_t *)ifc_desc) + LIBUSB_DT_INTERFACE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save any remaining descriptors to extra data
|
||||
LIBUSB_GOTO_ON_ERROR( add_extra_data(&extra, &buffer[config->wTotalLength]) );
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
clear_config_descriptor(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int raw_desc_to_libusb_config(const uint8_t *buf, int size, struct libusb_config_descriptor **config_desc)
|
||||
{
|
||||
libusb_config_descriptor_t *config = calloc(1, sizeof(*config));
|
||||
|
||||
if (!config) {
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
int r = parse_configuration(config, buf, size);
|
||||
if (r < 0) {
|
||||
ESP_LOGE(TAG, "parse_configuration failed with error %d", r);
|
||||
free(config);
|
||||
return r;
|
||||
}
|
||||
|
||||
*config_desc = config;
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bFirstInterface;
|
||||
uint8_t bInterfaceCount;
|
||||
uint8_t bFunctionClass;
|
||||
uint8_t bFunctionSubClass;
|
||||
uint8_t bFunctionProtocol;
|
||||
uint8_t iFunction;
|
||||
} USB_DESC_ATTR ifc_assoc_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint16_t bcdUVC;
|
||||
uint16_t wTotalLength;
|
||||
uint32_t dwClockFrequency;
|
||||
uint8_t bFunctionProtocol;
|
||||
uint8_t bInCollection;
|
||||
uint8_t baInterfaceNr;
|
||||
} USB_DESC_ATTR vc_interface_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bNumFormats;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bFunctionProtocol;
|
||||
uint8_t bmInfo;
|
||||
uint8_t bTerminalLink;
|
||||
uint8_t bStillCaptureMethod;
|
||||
uint8_t bTriggerSupport;
|
||||
uint8_t bTriggerUsage;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmaControls;
|
||||
|
||||
} USB_DESC_ATTR vs_interface_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
uint16_t wObjectiveFocalLengthMin;
|
||||
uint16_t wObjectiveFocalLengthMax;
|
||||
uint16_t wOcularFocalLength;
|
||||
uint8_t bControlSize;
|
||||
uint16_t bmControls;
|
||||
} USB_DESC_ATTR input_terminal_camera_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
} USB_DESC_ATTR input_terminal_composite_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmControls;
|
||||
uint8_t bTransportModeSize;
|
||||
uint8_t bmTransportModes[5];
|
||||
} USB_DESC_ATTR input_terminal_media_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t bSourceID;
|
||||
uint8_t iTerminal;
|
||||
} USB_DESC_ATTR output_terminal_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bUnitID;
|
||||
uint8_t bNrInPins;
|
||||
uint8_t baSourceID1;
|
||||
uint8_t baSourceID2;
|
||||
uint8_t iSelector;
|
||||
} USB_DESC_ATTR selector_unit_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bUnitID;
|
||||
uint8_t bSourceID;
|
||||
uint16_t wMaxMultiplier;
|
||||
uint8_t bControlSize;
|
||||
uint16_t bmControls;
|
||||
uint8_t iProcessing;
|
||||
uint8_t bmVideoStandards;
|
||||
} USB_DESC_ATTR processing_unit_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint16_t wMaxTransferSize;
|
||||
} USB_DESC_ATTR class_specific_endpoint_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
uint8_t bmFlags;
|
||||
uint8_t bDefaultFrameIndex;
|
||||
uint8_t bAspectRatioX;
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
} USB_DESC_ATTR vs_format_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bmCapabilities;
|
||||
uint16_t wWidth;
|
||||
uint16_t wHeigh;
|
||||
uint32_t dwMinBitRate;
|
||||
uint32_t dwMaxBitRate;
|
||||
uint32_t dwMaxVideoFrameBufSize;
|
||||
uint32_t dwDefaultFrameInterval;
|
||||
uint8_t bFrameIntervalType;
|
||||
union {
|
||||
uint32_t dwFrameInterval[16];
|
||||
struct {
|
||||
uint32_t dwMinFrameInterval;
|
||||
uint32_t dwMaxFrameInterval;
|
||||
uint32_t dwFrameIntervalStep;
|
||||
};
|
||||
};
|
||||
} USB_DESC_ATTR vs_frame_desc_t;
|
||||
|
||||
// Helper struct
|
||||
typedef struct {
|
||||
uint16_t wWidth;
|
||||
uint16_t wHeight;
|
||||
} USB_DESC_ATTR WidthHeight_t;
|
||||
|
||||
// Helper struct
|
||||
typedef struct {
|
||||
uint8_t bNumCompressionPtn;
|
||||
uint8_t bCompression;
|
||||
} USB_DESC_ATTR Compression_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bNumImageSizePatterns;
|
||||
uint16_t wWidth;
|
||||
uint16_t wHeight;
|
||||
uint8_t bNumCompressionPtn;
|
||||
uint8_t bCompression;
|
||||
} USB_DESC_ATTR still_image_frame_desc_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bColorPrimaries;
|
||||
uint8_t bTransferCharacteristics;
|
||||
uint8_t bMatrixCoefficients;
|
||||
} USB_DESC_ATTR color_format_desc_t;
|
||||
|
||||
#define TERMINAL_INPUT_CAMERA_TYPE 0x0201
|
||||
#define TERMINAL_INPUT_COMPOSITE_TYPE 0x0401
|
||||
#define ITT_MEDIA_TRANSPORT_INPUT 0x0202
|
||||
|
||||
#define CC_VIDEO 0x0E
|
||||
|
||||
#define USB_DESC_ASC_SIZE 8
|
||||
|
||||
typedef enum {
|
||||
CONFIG_DESC = 0x02,
|
||||
STRING_DESC = 0x03,
|
||||
INTERFACE_DESC = 0x04,
|
||||
ENDPOINT_DESC = 0x05,
|
||||
INTERFACE_ASSOC_DESC = 0x0B,
|
||||
CS_INTERFACE_DESC = 0x24,
|
||||
CS_ENDPOINT_DESC = 0x25,
|
||||
} descriptor_types_t;
|
||||
|
||||
typedef enum {
|
||||
VC_HEADER = 0x01,
|
||||
VC_INPUT_TERMINAL = 0x02,
|
||||
VC_OUTPUT_TERMINAL = 0x03,
|
||||
VC_SELECTOR_UNIT = 0x04,
|
||||
VC_PROCESSING_UNIT = 0x05,
|
||||
VS_FORMAT_MJPEG = 0x06,
|
||||
VS_FRAME_MJPEG = 0x07,
|
||||
VS_STILL_FRAME = 0x03,
|
||||
VS_COLORFORMAT = 0x0D,
|
||||
} descriptor_subtypes_t;
|
||||
|
||||
typedef enum {
|
||||
SC_VIDEOCONTROL = 1,
|
||||
SC_VIDEOSTREAMING = 2,
|
||||
} interface_sub_class_t;
|
||||
|
||||
static interface_sub_class_t interface_sub_class = SC_VIDEOCONTROL;
|
||||
|
||||
static void print_cs_endpoint_desc(const uint8_t *buff)
|
||||
{
|
||||
class_specific_endpoint_desc_t *class_desc = (class_specific_endpoint_desc_t *)buff;
|
||||
printf("\t\t*** Class-specific Interrupt Endpoint Descriptor ***\n");
|
||||
printf("\t\tbLength 0x%x\n", class_desc->bLength);
|
||||
printf("\t\tbDescriptorType 0x%x\n", class_desc->bDescriptorType);
|
||||
printf("\t\tbDescriptorSubType %d\n", class_desc->bDescriptorSubType);
|
||||
printf("\t\twMaxTransferSize %d\n", class_desc->wMaxTransferSize);
|
||||
}
|
||||
|
||||
static void print_interface_assoc_desc(const uint8_t *buff)
|
||||
{
|
||||
const ifc_assoc_desc_t *asc_desc = (const ifc_assoc_desc_t *) buff;
|
||||
printf("\t*** Interface Association Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", asc_desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", asc_desc->bDescriptorType);
|
||||
printf("\tbInterfaceCount %u\n", asc_desc->bInterfaceCount);
|
||||
printf("\tbFirstInterface %d\n", asc_desc->bFirstInterface);
|
||||
printf("\tbFunctionClass %d\n", asc_desc->bFunctionClass);
|
||||
printf("\tbFunctionSubClass %d\n", asc_desc->bFunctionSubClass);
|
||||
printf("\tbFunctionProtocol %d\n", asc_desc->bFunctionProtocol);
|
||||
printf("\tiFunction 0x%x\n", asc_desc->iFunction);
|
||||
}
|
||||
|
||||
|
||||
static void print_class_header_desc(const uint8_t *buff)
|
||||
{
|
||||
if (interface_sub_class == SC_VIDEOCONTROL) {
|
||||
const vc_interface_desc_t *desc = (const vc_interface_desc_t *) buff;
|
||||
printf("\t*** Class-specific VC Interface Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
|
||||
printf("\tbcdUVC %x\n", desc->bcdUVC);
|
||||
printf("\twTotalLength %u\n", desc->wTotalLength);
|
||||
printf("\tdwClockFrequency %lu\n", desc->dwClockFrequency);
|
||||
printf("\tbFunctionProtocol %u\n", desc->bFunctionProtocol);
|
||||
printf("\tbInCollection %u\n", desc->bInCollection);
|
||||
printf("\tbaInterfaceNr %u\n", desc->baInterfaceNr);
|
||||
} else if (interface_sub_class == SC_VIDEOSTREAMING) {
|
||||
const vs_interface_desc_t *desc = (const vs_interface_desc_t *) buff;
|
||||
printf("\t*** Class-specific VS Interface Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
|
||||
printf("\tbNumFormats %x\n", desc->bNumFormats);
|
||||
printf("\twTotalLength %u\n", desc->wTotalLength);
|
||||
printf("\tbEndpointAddress %u\n", desc->bEndpointAddress);
|
||||
printf("\tbFunctionProtocol %u\n", desc->bFunctionProtocol);
|
||||
printf("\tbmInfo 0x%x\n", desc->bmInfo);
|
||||
printf("\tbTerminalLink %u\n", desc->bTerminalLink);
|
||||
printf("\tbStillCaptureMethod %u\n", desc->bStillCaptureMethod);
|
||||
printf("\tbTriggerSupport %u\n", desc->bTriggerSupport);
|
||||
printf("\tbTriggerUsage %u\n", desc->bTriggerUsage);
|
||||
printf("\tbControlSize %u\n", desc->bControlSize);
|
||||
printf("\tbmaControls 0x%x\n", desc->bmaControls);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_vc_input_terminal_desc(const uint8_t *buff)
|
||||
{
|
||||
const input_terminal_camera_desc_t *desc = (const input_terminal_camera_desc_t *) buff;
|
||||
|
||||
const char *type = NULL;
|
||||
|
||||
switch (desc->wTerminalType) {
|
||||
case TERMINAL_INPUT_CAMERA_TYPE: type = "Camera"; break;
|
||||
case TERMINAL_INPUT_COMPOSITE_TYPE: type = "Composite"; break;
|
||||
case ITT_MEDIA_TRANSPORT_INPUT: type = "Media"; break;
|
||||
default: printf("!!!!! Unknown Input terminal descriptor !!!!!\n"); return;
|
||||
|
||||
}
|
||||
|
||||
printf("\t*** Input Terminal Descriptor (%s) ***\n", type);
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
|
||||
printf("\tbTerminalID %x\n", desc->bTerminalID);
|
||||
printf("\twTerminalType %x\n", desc->wTerminalType);
|
||||
printf("\tbAssocTerminal %u\n", desc->bAssocTerminal);
|
||||
printf("\tiTerminal %u\n", desc->iTerminal);
|
||||
|
||||
if (desc->wTerminalType == TERMINAL_INPUT_COMPOSITE_TYPE) {
|
||||
return;
|
||||
} else if (desc->wTerminalType == TERMINAL_INPUT_CAMERA_TYPE) {
|
||||
printf("\twObjectiveFocalLengthMin %u\n", desc->wObjectiveFocalLengthMin);
|
||||
printf("\twObjectiveFocalLengthMax %u\n", desc->wObjectiveFocalLengthMax);
|
||||
printf("\twOcularFocalLength %u\n", desc->wOcularFocalLength);
|
||||
printf("\tbControlSize %u\n", desc->bControlSize);
|
||||
printf("\tbmControls 0x%x\n", desc->bmControls);
|
||||
} else if (desc->wTerminalType == ITT_MEDIA_TRANSPORT_INPUT) {
|
||||
const input_terminal_media_desc_t *desc = (const input_terminal_media_desc_t *) buff;
|
||||
printf("\tbControlSize %u\n", desc->bControlSize);
|
||||
printf("\tbmControls 0x%x\n", desc->bmControls);
|
||||
printf("\tbTransportModeSize %u\n", desc->bTransportModeSize);
|
||||
printf("\tbmTransportModes 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
||||
desc->bmTransportModes[0],
|
||||
desc->bmTransportModes[1],
|
||||
desc->bmTransportModes[2],
|
||||
desc->bmTransportModes[3],
|
||||
desc->bmTransportModes[4]);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_vc_output_terminal_desc(const uint8_t *buff)
|
||||
{
|
||||
const output_terminal_desc_t *desc = (const output_terminal_desc_t *) buff;
|
||||
printf("\t*** Output Terminal Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
|
||||
printf("\tbTerminalID %u\n", desc->bTerminalID);
|
||||
printf("\twTerminalType %x\n", desc->wTerminalType);
|
||||
printf("\tbAssocTerminal %u\n", desc->bAssocTerminal);
|
||||
printf("\tbSourceID %u\n", desc->bSourceID);
|
||||
printf("\tiTerminal %u\n", desc->iTerminal);
|
||||
}
|
||||
|
||||
static void print_vc_selector_unit_desc(const uint8_t *buff)
|
||||
{
|
||||
const selector_unit_desc_t *desc = (const selector_unit_desc_t *) buff;
|
||||
printf("\t*** Selector Unit Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
|
||||
printf("\tbUnitID %u\n", desc->bUnitID);
|
||||
printf("\tbNrInPins %u\n", desc->bNrInPins);
|
||||
printf("\tbaSourceID1 %u\n", desc->baSourceID1);
|
||||
printf("\tbaSourceID2 %u\n", desc->baSourceID2);
|
||||
printf("\tiSelector %u\n", desc->iSelector);
|
||||
}
|
||||
|
||||
static void print_vc_processing_unit_desc(const uint8_t *buff)
|
||||
{
|
||||
const processing_unit_desc_t *desc = (const processing_unit_desc_t *) buff;
|
||||
printf("\t*** Processing Unit Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
|
||||
printf("\tbUnitID %u\n", desc->bUnitID);
|
||||
printf("\tbSourceID %u\n", desc->bSourceID);
|
||||
printf("\twMaxMultiplier %u\n", desc->wMaxMultiplier);
|
||||
printf("\tbControlSize %u\n", desc->bControlSize);
|
||||
printf("\tbmControls 0x%x\n", desc->bmControls);
|
||||
printf("\tiProcessing %u\n", desc->iProcessing);
|
||||
printf("\tbmVideoStandards 0x%x\n", desc->bmVideoStandards);
|
||||
}
|
||||
|
||||
static void print_vs_format_mjpeg_desc(const uint8_t *buff)
|
||||
{
|
||||
const vs_format_desc_t *desc = (const vs_format_desc_t *) buff;
|
||||
printf("\t*** VS Format Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
|
||||
printf("\tbFormatIndex 0x%x\n", desc->bFormatIndex);
|
||||
printf("\tbNumFrameDescriptors %u\n", desc->bNumFrameDescriptors);
|
||||
printf("\tbmFlags 0x%x\n", desc->bmFlags);
|
||||
printf("\tbDefaultFrameIndex %u\n", desc->bDefaultFrameIndex);
|
||||
printf("\tbAspectRatioX %u\n", desc->bAspectRatioX);
|
||||
printf("\tbAspectRatioY %u\n", desc->bAspectRatioY);
|
||||
printf("\tbmInterlaceFlags 0x%x\n", desc->bmInterlaceFlags);
|
||||
printf("\tbCopyProtect %u\n", desc->bCopyProtect);
|
||||
}
|
||||
|
||||
static void print_vs_frame_mjpeg_desc(const uint8_t *buff)
|
||||
{
|
||||
// Copy to local buffer due to potential misalignment issues.
|
||||
uint32_t raw_desc[25];
|
||||
uint32_t desc_size = ((const vs_frame_desc_t *)buff)->bLength;
|
||||
memcpy(raw_desc, buff, desc_size);
|
||||
|
||||
const vs_frame_desc_t *desc = (const vs_frame_desc_t *) raw_desc;
|
||||
printf("\t*** VS Frame Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
|
||||
printf("\tbFormatIndex 0x%x\n", desc->bFormatIndex);
|
||||
printf("\tbmCapabilities 0x%x\n", desc->bmCapabilities);
|
||||
printf("\twWidth %u\n", desc->wWidth);
|
||||
printf("\twHeigh %u\n", desc->wHeigh);
|
||||
printf("\tdwMinBitRate %lu\n", desc->dwMinBitRate);
|
||||
printf("\tdwMaxBitRate %lu\n", desc->dwMaxBitRate);
|
||||
printf("\tdwMaxVideoFrameBufSize %lu\n", desc->dwMaxVideoFrameBufSize);
|
||||
printf("\tdwDefaultFrameInterval %lu\n", desc->dwDefaultFrameInterval);
|
||||
printf("\tbFrameIntervalType %u\n", desc->bFrameIntervalType);
|
||||
|
||||
if (desc->bFrameIntervalType == 0) {
|
||||
// Continuous Frame Intervals
|
||||
printf("\tdwMinFrameInterval %lu\n", desc->dwMinFrameInterval);
|
||||
printf("\tdwMaxFrameInterval %lu\n", desc->dwMaxFrameInterval);
|
||||
printf("\tdwFrameIntervalStep %lu\n", desc->dwFrameIntervalStep);
|
||||
} else {
|
||||
// Discrete Frame Intervals
|
||||
size_t max_intervals = sizeof(desc->dwFrameInterval) / sizeof(desc->dwFrameInterval[0]);
|
||||
size_t num_of_intervals = MIN((desc->bLength - 26) / 4, max_intervals);
|
||||
for (int i = 0; i < num_of_intervals; ++i) {
|
||||
printf("\tFrameInterval[%d] %lu\n", i, desc->dwFrameInterval[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_vs_still_frame_desc(const uint8_t *buff)
|
||||
{
|
||||
const still_image_frame_desc_t *desc = (const still_image_frame_desc_t *) buff;
|
||||
printf("\t*** VS Still Format Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
|
||||
printf("\tbEndpointAddress 0x%x\n", desc->bEndpointAddress);
|
||||
printf("\tbNumImageSizePatterns 0x%x\n", desc->bNumImageSizePatterns);
|
||||
|
||||
WidthHeight_t *wh = (WidthHeight_t *)&desc->wWidth;
|
||||
for (int i = 0; i < desc->bNumImageSizePatterns; ++i, wh++) {
|
||||
printf("\t[%d]: wWidth: %u, wHeight: %u\n", i, wh->wWidth, wh->wHeight);
|
||||
}
|
||||
|
||||
Compression_t *c = (Compression_t *)wh;
|
||||
printf("\tbNumCompressionPtn %u\n", c->bNumCompressionPtn);
|
||||
printf("\tbCompression %u\n", c->bCompression);
|
||||
}
|
||||
|
||||
static void print_vs_color_format_desc(const uint8_t *buff)
|
||||
{
|
||||
const color_format_desc_t *desc = (const color_format_desc_t *) buff;
|
||||
printf("\t*** VS Color Format Descriptor ***\n");
|
||||
printf("\tbLength 0x%x\n", desc->bLength);
|
||||
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
|
||||
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
|
||||
printf("\tbColorPrimaries 0x%x\n", desc->bColorPrimaries);
|
||||
printf("\tbTransferCharacteristics %u\n", desc->bTransferCharacteristics);
|
||||
printf("\tbMatrixCoefficients 0x%x\n", desc->bMatrixCoefficients);
|
||||
}
|
||||
|
||||
static void unknown_desc(const desc_header_t *header)
|
||||
{
|
||||
printf(" *** Unknown Descriptor Length: %d Type: %d Subtype: %d ***\n",
|
||||
header->bLength, header->bDescriptorType, header->bDescriptorSubtype);
|
||||
}
|
||||
|
||||
static void print_class_specific_desc(const uint8_t *buff)
|
||||
{
|
||||
desc_header_t *header = (desc_header_t *)buff;
|
||||
|
||||
switch (header->bDescriptorSubtype) {
|
||||
case VC_HEADER:
|
||||
print_class_header_desc(buff);
|
||||
break;
|
||||
case VC_INPUT_TERMINAL:
|
||||
print_vc_input_terminal_desc(buff);
|
||||
break;
|
||||
case VC_SELECTOR_UNIT:
|
||||
print_vc_selector_unit_desc(buff);
|
||||
break;
|
||||
case VC_PROCESSING_UNIT:
|
||||
print_vc_processing_unit_desc(buff);
|
||||
break;
|
||||
case VS_FORMAT_MJPEG:
|
||||
if (interface_sub_class == SC_VIDEOCONTROL) {
|
||||
printf("\t*** Extension Unit Descriptor unsupported, skipping... ***\n");;
|
||||
return;
|
||||
}
|
||||
print_vs_format_mjpeg_desc(buff);
|
||||
break;
|
||||
case VS_FRAME_MJPEG:
|
||||
print_vs_frame_mjpeg_desc(buff);
|
||||
break;
|
||||
case VS_COLORFORMAT:
|
||||
print_vs_color_format_desc(buff);
|
||||
break;
|
||||
case VC_OUTPUT_TERMINAL: // same as VS_STILL_FRAME
|
||||
if (interface_sub_class == SC_VIDEOCONTROL) {
|
||||
print_vc_output_terminal_desc(buff);
|
||||
} else {
|
||||
print_vs_still_frame_desc(buff);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unknown_desc(header);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void print_usb_class_descriptors(const usb_standard_desc_t *desc)
|
||||
{
|
||||
const uint8_t *buff = (uint8_t *)desc;
|
||||
desc_header_t *header = (desc_header_t *)desc;
|
||||
|
||||
switch (header->bDescriptorType) {
|
||||
case INTERFACE_ASSOC_DESC:
|
||||
print_interface_assoc_desc(buff);
|
||||
break;
|
||||
case CS_INTERFACE_DESC:
|
||||
print_class_specific_desc(buff);
|
||||
break;
|
||||
case CS_ENDPOINT_DESC:
|
||||
print_cs_endpoint_desc(buff);
|
||||
break;
|
||||
default:
|
||||
unknown_desc(header);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -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 "test_uvc.c" "libusb_parse.c"
|
||||
INCLUDE_DIRS "." "../private_include"
|
||||
REQUIRES usb_host_uvc usb unity)
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* SPDX-FileCopyrightText: 2001 Johannes Erdfelt <johannes@erdfelt.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*
|
||||
* SPDX-FileContributor: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
|
||||
/*
|
||||
* USB descriptor handling functions for libusb
|
||||
* Copyright © 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <endian.h>
|
||||
#include <sys/param.h>
|
||||
#include "libusb.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "usb/usb_host.h"
|
||||
#include "usb/usb_types_ch9.h"
|
||||
|
||||
#define DESC_HEADER_LENGTH 2
|
||||
#define USB_MAXENDPOINTS 32
|
||||
#define USB_MAXINTERFACES 32
|
||||
#define USB_MAXCONFIG 8
|
||||
|
||||
const char *TAG = "LIBUSB_PARSE";
|
||||
|
||||
typedef struct {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubtype;
|
||||
} desc_header_t;
|
||||
|
||||
|
||||
static void parse_descriptor(const void *source, const char *descriptor, void *dest)
|
||||
{
|
||||
const uint8_t *sp = source;
|
||||
uint8_t *dp = dest;
|
||||
char field_type;
|
||||
|
||||
while (*descriptor) {
|
||||
field_type = *descriptor++;
|
||||
switch (field_type) {
|
||||
case 'b': /* 8-bit byte */
|
||||
*dp++ = *sp++;
|
||||
break;
|
||||
case 'w': /* 16-bit word, convert from little endian to CPU */
|
||||
dp += ((uintptr_t)dp & 1); /* Align to 16-bit word boundary */
|
||||
|
||||
// *((uint16_t *)dp) = le16toh(*((uint16_t *)sp));
|
||||
*((uint16_t *)dp) = le16toh((uint16_t)sp[0] | sp[1] << 8);
|
||||
sp += 2;
|
||||
dp += 2;
|
||||
break;
|
||||
case 'd': /* 32-bit word, convert from little endian to CPU */
|
||||
dp += 4 - ((uintptr_t)dp & 3); /* Align to 32-bit word boundary */
|
||||
|
||||
*((uint32_t *)dp) = le32toh(((uint32_t)sp[0] | sp[1] << 8 | sp[2] << 16 | sp[3] << 24));
|
||||
sp += 4;
|
||||
dp += 4;
|
||||
break;
|
||||
case 'u': /* 16 byte UUID */
|
||||
memcpy(dp, sp, 16);
|
||||
sp += 16;
|
||||
dp += 16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
|
||||
{
|
||||
free((void *)endpoint->extra);
|
||||
}
|
||||
|
||||
static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint, const uint8_t *buffer, int size)
|
||||
{
|
||||
const desc_header_t *header;
|
||||
const uint8_t *begin;
|
||||
void *extra;
|
||||
int parsed = 0;
|
||||
int len;
|
||||
|
||||
if (size < DESC_HEADER_LENGTH) {
|
||||
ESP_LOGE(TAG, "short endpoint descriptor read %d/%d",
|
||||
size, DESC_HEADER_LENGTH);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
header = (const desc_header_t *)buffer;
|
||||
if (header->bDescriptorType != LIBUSB_DT_ENDPOINT) {
|
||||
ESP_LOGE(TAG, "unexpected descriptor 0x%x (expected 0x%x)",
|
||||
header->bDescriptorType, LIBUSB_DT_ENDPOINT);
|
||||
return parsed;
|
||||
} else if (header->bLength < LIBUSB_DT_ENDPOINT_SIZE) {
|
||||
ESP_LOGE(TAG, "invalid endpoint bLength (%u)", header->bLength);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (header->bLength > size) {
|
||||
ESP_LOGW(TAG, "short endpoint descriptor read %d/%u",
|
||||
size, header->bLength);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
if (header->bLength >= LIBUSB_DT_ENDPOINT_AUDIO_SIZE) {
|
||||
parse_descriptor(buffer, "bbbbwbbb", endpoint);
|
||||
} else {
|
||||
parse_descriptor(buffer, "bbbbwb", endpoint);
|
||||
}
|
||||
|
||||
buffer += header->bLength;
|
||||
size -= header->bLength;
|
||||
parsed += header->bLength;
|
||||
|
||||
/* Skip over the rest of the Class Specific or Vendor Specific */
|
||||
/* descriptors */
|
||||
begin = buffer;
|
||||
while (size >= DESC_HEADER_LENGTH) {
|
||||
header = (const desc_header_t *)buffer;
|
||||
if (header->bLength < DESC_HEADER_LENGTH) {
|
||||
ESP_LOGE(TAG, "invalid extra ep desc len (%u)",
|
||||
header->bLength);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (header->bLength > size) {
|
||||
ESP_LOGW(TAG, "short extra ep desc read %d/%u",
|
||||
size, header->bLength);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* If we find another "proper" descriptor then we're done */
|
||||
if (header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
|
||||
header->bDescriptorType == LIBUSB_DT_INTERFACE ||
|
||||
header->bDescriptorType == LIBUSB_DT_CONFIG ||
|
||||
header->bDescriptorType == LIBUSB_DT_DEVICE) {
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "skipping descriptor 0x%x", header->bDescriptorType);
|
||||
buffer += header->bLength;
|
||||
size -= header->bLength;
|
||||
parsed += header->bLength;
|
||||
}
|
||||
|
||||
/* Copy any unknown descriptors into a storage area for drivers */
|
||||
/* to later parse */
|
||||
len = (int)(buffer - begin);
|
||||
if (len <= 0) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
extra = malloc((size_t)len);
|
||||
if (!extra) {
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
memcpy(extra, begin, len);
|
||||
endpoint->extra = extra;
|
||||
endpoint->extra_length = len;
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static void clear_interface(struct libusb_interface *usb_interface)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (usb_interface->altsetting) {
|
||||
for (i = 0; i < usb_interface->num_altsetting; i++) {
|
||||
struct libusb_interface_descriptor *ifp =
|
||||
(struct libusb_interface_descriptor *)
|
||||
usb_interface->altsetting + i;
|
||||
|
||||
free((void *)ifp->extra);
|
||||
if (ifp->endpoint) {
|
||||
uint8_t j;
|
||||
|
||||
for (j = 0; j < ifp->bNumEndpoints; j++)
|
||||
clear_endpoint((struct libusb_endpoint_descriptor *)
|
||||
ifp->endpoint + j);
|
||||
}
|
||||
free((void *)ifp->endpoint);
|
||||
}
|
||||
}
|
||||
free((void *)usb_interface->altsetting);
|
||||
usb_interface->altsetting = NULL;
|
||||
}
|
||||
|
||||
static int parse_interface(struct libusb_interface *usb_interface, const uint8_t *buffer, int size)
|
||||
{
|
||||
int len;
|
||||
int r;
|
||||
int parsed = 0;
|
||||
int interface_number = -1;
|
||||
const desc_header_t *header;
|
||||
const usb_intf_desc_t *if_desc;
|
||||
struct libusb_interface_descriptor *ifp;
|
||||
const uint8_t *begin;
|
||||
|
||||
while (size >= LIBUSB_DT_INTERFACE_SIZE) {
|
||||
struct libusb_interface_descriptor *altsetting;
|
||||
|
||||
altsetting = realloc((void *)usb_interface->altsetting,
|
||||
sizeof(*altsetting) * (size_t)(usb_interface->num_altsetting + 1));
|
||||
if (!altsetting) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err;
|
||||
}
|
||||
usb_interface->altsetting = altsetting;
|
||||
|
||||
ifp = altsetting + usb_interface->num_altsetting;
|
||||
parse_descriptor(buffer, "bbbbbbbbb", ifp);
|
||||
if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) {
|
||||
ESP_LOGE(TAG, "unexpected descriptor 0x%x (expected 0x%x)",
|
||||
ifp->bDescriptorType, LIBUSB_DT_INTERFACE);
|
||||
return parsed;
|
||||
} else if (ifp->bLength < LIBUSB_DT_INTERFACE_SIZE) {
|
||||
ESP_LOGE(TAG, "invalid interface bLength (%u)",
|
||||
ifp->bLength);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
goto err;
|
||||
} else if (ifp->bLength > size) {
|
||||
ESP_LOGW(TAG, "short intf descriptor read %d/%u",
|
||||
size, ifp->bLength);
|
||||
return parsed;
|
||||
} else if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
|
||||
ESP_LOGE(TAG, "too many endpoints (%u)", ifp->bNumEndpoints);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
usb_interface->num_altsetting++;
|
||||
ifp->extra = NULL;
|
||||
ifp->extra_length = 0;
|
||||
ifp->endpoint = NULL;
|
||||
|
||||
if (interface_number == -1) {
|
||||
interface_number = ifp->bInterfaceNumber;
|
||||
}
|
||||
|
||||
/* Skip over the interface */
|
||||
buffer += ifp->bLength;
|
||||
parsed += ifp->bLength;
|
||||
size -= ifp->bLength;
|
||||
|
||||
begin = buffer;
|
||||
|
||||
/* Skip over any interface, class or vendor descriptors */
|
||||
while (size >= DESC_HEADER_LENGTH) {
|
||||
header = (const desc_header_t *)buffer;
|
||||
if (header->bLength < DESC_HEADER_LENGTH) {
|
||||
ESP_LOGE(TAG, "invalid extra intf desc len (%u)", header->bLength);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
goto err;
|
||||
} else if (header->bLength > size) {
|
||||
ESP_LOGW(TAG, "short extra intf desc read %d/%u", size, header->bLength);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* If we find another "proper" descriptor then we're done */
|
||||
if (header->bDescriptorType == LIBUSB_DT_INTERFACE ||
|
||||
header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
|
||||
header->bDescriptorType == LIBUSB_DT_CONFIG ||
|
||||
header->bDescriptorType == LIBUSB_DT_DEVICE) {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += header->bLength;
|
||||
parsed += header->bLength;
|
||||
size -= header->bLength;
|
||||
}
|
||||
|
||||
/* Copy any unknown descriptors into a storage area for */
|
||||
/* drivers to later parse */
|
||||
len = (int)(buffer - begin);
|
||||
if (len > 0) {
|
||||
void *extra = malloc((size_t)len);
|
||||
|
||||
if (!extra) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(extra, begin, len);
|
||||
ifp->extra = extra;
|
||||
ifp->extra_length = len;
|
||||
}
|
||||
|
||||
if (ifp->bNumEndpoints > 0) {
|
||||
struct libusb_endpoint_descriptor *endpoint;
|
||||
uint8_t i;
|
||||
|
||||
endpoint = calloc(ifp->bNumEndpoints, sizeof(*endpoint));
|
||||
if (!endpoint) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ifp->endpoint = endpoint;
|
||||
for (i = 0; i < ifp->bNumEndpoints; i++) {
|
||||
r = parse_endpoint(endpoint + i, buffer, size);
|
||||
if (r < 0) {
|
||||
goto err;
|
||||
}
|
||||
if (r == 0) {
|
||||
ifp->bNumEndpoints = i;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += r;
|
||||
parsed += r;
|
||||
size -= r;
|
||||
}
|
||||
}
|
||||
|
||||
/* We check to see if it's an alternate to this one */
|
||||
if_desc = (const usb_intf_desc_t *)buffer;
|
||||
if (size < LIBUSB_DT_INTERFACE_SIZE ||
|
||||
if_desc->bDescriptorType != LIBUSB_DT_INTERFACE ||
|
||||
if_desc->bInterfaceNumber != interface_number) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
err:
|
||||
clear_interface(usb_interface);
|
||||
return r;
|
||||
}
|
||||
|
||||
void libusb_clear_config_descriptor(struct libusb_config_descriptor *config)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
if (config->interface) {
|
||||
for (i = 0; i < config->bNumInterfaces; i++) {
|
||||
clear_interface((struct libusb_interface *) config->interface + i);
|
||||
}
|
||||
}
|
||||
free((void *)config->interface);
|
||||
free((void *)config->extra);
|
||||
}
|
||||
|
||||
int libusb_parse_configuration(struct libusb_config_descriptor *config, const uint8_t *buffer, int size)
|
||||
{
|
||||
uint8_t i;
|
||||
int r;
|
||||
const desc_header_t *header;
|
||||
struct libusb_interface *usb_interface;
|
||||
|
||||
if (size < LIBUSB_DT_CONFIG_SIZE) {
|
||||
ESP_LOGE(TAG, "short config descriptor read %d/%d",
|
||||
size, LIBUSB_DT_CONFIG_SIZE);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
parse_descriptor(buffer, "bbwbbbbb", config);
|
||||
if (config->bDescriptorType != LIBUSB_DT_CONFIG) {
|
||||
ESP_LOGE(TAG, "unexpected descriptor 0x%x (expected 0x%x)",
|
||||
config->bDescriptorType, LIBUSB_DT_CONFIG);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
|
||||
ESP_LOGE(TAG, "invalid config bLength (%u)", config->bLength);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (config->bLength > size) {
|
||||
ESP_LOGE(TAG, "short config descriptor read %d/%u",
|
||||
size, config->bLength);
|
||||
return LIBUSB_ERROR_IO;
|
||||
} else if (config->bNumInterfaces > USB_MAXINTERFACES) {
|
||||
ESP_LOGE(TAG, "too many interfaces (%u)", config->bNumInterfaces);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
usb_interface = calloc(config->bNumInterfaces, sizeof(*usb_interface));
|
||||
if (!usb_interface) {
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
config->interface = usb_interface;
|
||||
|
||||
buffer += config->bLength;
|
||||
size -= config->bLength;
|
||||
|
||||
for (i = 0; i < config->bNumInterfaces; i++) {
|
||||
int len;
|
||||
const uint8_t *begin;
|
||||
|
||||
/* Skip over the rest of the Class Specific or Vendor */
|
||||
/* Specific descriptors */
|
||||
begin = buffer;
|
||||
while (size >= DESC_HEADER_LENGTH) {
|
||||
header = (const desc_header_t *)buffer;
|
||||
if (header->bLength < DESC_HEADER_LENGTH) {
|
||||
ESP_LOGE(TAG, "invalid extra config desc len (%u)", header->bLength);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
goto err;
|
||||
} else if (header->bLength > size) {
|
||||
ESP_LOGW(TAG, "short extra config desc read %d/%u", size, header->bLength);
|
||||
config->bNumInterfaces = i;
|
||||
return size;
|
||||
}
|
||||
|
||||
/* If we find another "proper" descriptor then we're done */
|
||||
if (header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
|
||||
header->bDescriptorType == LIBUSB_DT_INTERFACE ||
|
||||
header->bDescriptorType == LIBUSB_DT_CONFIG ||
|
||||
header->bDescriptorType == LIBUSB_DT_DEVICE) {
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "skipping descriptor 0x%x", header->bDescriptorType);
|
||||
buffer += header->bLength;
|
||||
size -= header->bLength;
|
||||
}
|
||||
|
||||
/* Copy any unknown descriptors into a storage area for */
|
||||
/* drivers to later parse */
|
||||
len = (int)(buffer - begin);
|
||||
if (len > 0) {
|
||||
uint8_t *extra = realloc((void *)config->extra,
|
||||
(size_t)(config->extra_length + len));
|
||||
|
||||
if (!extra) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(extra + config->extra_length, begin, len);
|
||||
config->extra = extra;
|
||||
config->extra_length += len;
|
||||
}
|
||||
|
||||
r = parse_interface(usb_interface + i, buffer, size);
|
||||
if (r < 0) {
|
||||
goto err;
|
||||
}
|
||||
if (r == 0) {
|
||||
config->bNumInterfaces = i;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += r;
|
||||
size -= r;
|
||||
}
|
||||
|
||||
return size;
|
||||
|
||||
err:
|
||||
libusb_clear_config_descriptor(config);
|
||||
return r;
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "esp_private/usb_phy.h"
|
||||
#include "usb/usb_host.h"
|
||||
#include "unity.h"
|
||||
|
||||
#include "libusb.h"
|
||||
#include "descriptor.h"
|
||||
|
||||
#define TAG "UVC_TEST"
|
||||
#define COMPARE_DESCRIPTORS(array) compare_descriptors(array, sizeof(array)/sizeof(array[0]), #array)
|
||||
|
||||
int libusb_parse_configuration(struct libusb_config_descriptor *config, const uint8_t *buffer, int size);
|
||||
void libusb_clear_config_descriptor(struct libusb_config_descriptor *config);
|
||||
|
||||
const uint8_t CANYON_CNE_CWC2[] = {
|
||||
0x09, 0x02, 0x7d, 0x02, 0x04, 0x01, 0x00, 0x80, 0xfa, 0x08, 0x0b, 0x00, 0x02, 0x0e, 0x03, 0x00,
|
||||
0x05, 0x09, 0x04, 0x00, 0x00, 0x01, 0x0e, 0x01, 0x00, 0x05, 0x0d, 0x24, 0x01, 0x00, 0x01, 0x4d,
|
||||
0x00, 0xc0, 0xe1, 0xe4, 0x00, 0x01, 0x01, 0x09, 0x24, 0x03, 0x02, 0x01, 0x01, 0x00, 0x04, 0x00,
|
||||
0x1a, 0x24, 0x06, 0x04, 0x70, 0x33, 0xf0, 0x28, 0x11, 0x63, 0x2e, 0x4a, 0xba, 0x2c, 0x68, 0x90,
|
||||
0xeb, 0x33, 0x40, 0x16, 0x08, 0x01, 0x03, 0x01, 0x0f, 0x00, 0x12, 0x24, 0x02, 0x01, 0x01, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x20, 0x00, 0x0b, 0x24, 0x05, 0x03,
|
||||
0x01, 0x00, 0x00, 0x02, 0x3f, 0x04, 0x00, 0x07, 0x05, 0x83, 0x03, 0x10, 0x00, 0x06, 0x05, 0x25,
|
||||
0x03, 0x10, 0x00, 0x09, 0x04, 0x01, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x05, 0x0e, 0x24, 0x01, 0x01,
|
||||
0x33, 0x01, 0x81, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x0b, 0x24, 0x06, 0x01, 0x05, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x32, 0x24, 0x07, 0x01, 0x00, 0x80, 0x02, 0xe0, 0x01, 0x00, 0x00,
|
||||
0x77, 0x01, 0x00, 0x00, 0xca, 0x08, 0x00, 0x60, 0x09, 0x00, 0x15, 0x16, 0x05, 0x00, 0x06, 0x15,
|
||||
0x16, 0x05, 0x00, 0x80, 0x1a, 0x06, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40,
|
||||
0x42, 0x0f, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x32, 0x24, 0x07, 0x02, 0x00, 0x60, 0x01, 0x20, 0x01,
|
||||
0x00, 0xc0, 0x7b, 0x00, 0x00, 0x80, 0xe6, 0x02, 0x00, 0x18, 0x03, 0x00, 0x15, 0x16, 0x05, 0x00,
|
||||
0x06, 0x15, 0x16, 0x05, 0x00, 0x80, 0x1a, 0x06, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a,
|
||||
0x00, 0x40, 0x42, 0x0f, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x32, 0x24, 0x07, 0x03, 0x00, 0x40, 0x01,
|
||||
0xf0, 0x00, 0x00, 0xc0, 0x5d, 0x00, 0x00, 0x80, 0x32, 0x02, 0x00, 0x58, 0x02, 0x00, 0x15, 0x16,
|
||||
0x05, 0x00, 0x06, 0x15, 0x16, 0x05, 0x00, 0x80, 0x1a, 0x06, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a,
|
||||
0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x32, 0x24, 0x07, 0x04, 0x00,
|
||||
0xb0, 0x00, 0x90, 0x00, 0x00, 0xf0, 0x1e, 0x00, 0x00, 0xa0, 0xb9, 0x00, 0x00, 0xc6, 0x00, 0x00,
|
||||
0x15, 0x16, 0x05, 0x00, 0x06, 0x15, 0x16, 0x05, 0x00, 0x80, 0x1a, 0x06, 0x00, 0x20, 0xa1, 0x07,
|
||||
0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x32, 0x24, 0x07,
|
||||
0x05, 0x00, 0xa0, 0x00, 0x78, 0x00, 0x00, 0x70, 0x17, 0x00, 0x00, 0xa0, 0x8c, 0x00, 0x00, 0x96,
|
||||
0x00, 0x00, 0x15, 0x16, 0x05, 0x00, 0x06, 0x15, 0x16, 0x05, 0x00, 0x80, 0x1a, 0x06, 0x00, 0x20,
|
||||
0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x1a,
|
||||
0x24, 0x03, 0x00, 0x05, 0x80, 0x02, 0xe0, 0x01, 0x60, 0x01, 0x20, 0x01, 0x40, 0x01, 0xf0, 0x00,
|
||||
0xb0, 0x00, 0x90, 0x00, 0xa0, 0x00, 0x78, 0x00, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01, 0x04, 0x09,
|
||||
0x04, 0x01, 0x01, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x80, 0x00, 0x01, 0x09,
|
||||
0x04, 0x01, 0x02, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x01, 0x01, 0x09,
|
||||
0x04, 0x01, 0x03, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x02, 0x01, 0x09,
|
||||
0x04, 0x01, 0x04, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x58, 0x02, 0x01, 0x09,
|
||||
0x04, 0x01, 0x05, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x20, 0x03, 0x01, 0x09,
|
||||
0x04, 0x01, 0x06, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0xbc, 0x03, 0x01, 0x08,
|
||||
0x0b, 0x02, 0x02, 0x01, 0x00, 0x00, 0x04, 0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x04,
|
||||
0x09, 0x24, 0x01, 0x00, 0x01, 0x29, 0x00, 0x01, 0x03, 0x0c, 0x24, 0x02, 0x01, 0x01, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x24, 0x06, 0x02, 0x01, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00,
|
||||
0x09, 0x24, 0x03, 0x03, 0x01, 0x01, 0x00, 0x02, 0x00, 0x09, 0x04, 0x03, 0x00, 0x00, 0x01, 0x02,
|
||||
0x00, 0x00, 0x09, 0x04, 0x03, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x07, 0x24, 0x01, 0x03, 0x01,
|
||||
0x01, 0x00, 0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01, 0x80, 0x3e, 0x00, 0x09, 0x05, 0x84,
|
||||
0x05, 0x20, 0x00, 0x04, 0x00, 0x00, 0x07, 0x25, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const uint8_t Logitech_C980[] = {
|
||||
0x09, 0x02, 0x5a, 0x03, 0x05, 0x01, 0x00, 0x80, 0xfa, 0x08, 0x0b, 0x00, 0x02, 0x0e, 0x03, 0x00,
|
||||
0x00, 0x09, 0x04, 0x00, 0x00, 0x01, 0x0e, 0x01, 0x00, 0x00, 0x0d, 0x24, 0x01, 0x00, 0x01, 0xd8,
|
||||
0x00, 0x80, 0xc3, 0xc9, 0x01, 0x01, 0x01, 0x12, 0x24, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2e, 0x2a, 0x02, 0x0b, 0x24, 0x05, 0x03, 0x01, 0x00, 0x40,
|
||||
0x02, 0x5b, 0x17, 0x00, 0x1b, 0x24, 0x06, 0x0e, 0x6a, 0xd1, 0x49, 0x2c, 0xb8, 0x32, 0x85, 0x44,
|
||||
0x3e, 0xa8, 0x64, 0x3a, 0x15, 0x23, 0x62, 0xf2, 0x06, 0x01, 0x03, 0x02, 0x3f, 0x00, 0x00, 0x1b,
|
||||
0x24, 0x06, 0x06, 0xd0, 0x9e, 0xe4, 0x23, 0x78, 0x11, 0x31, 0x4f, 0xae, 0x52, 0xd2, 0xfb, 0x8a,
|
||||
0x8d, 0x3b, 0x48, 0x0e, 0x01, 0x03, 0x02, 0xff, 0x6f, 0x00, 0x1b, 0x24, 0x06, 0x08, 0xe4, 0x8e,
|
||||
0x67, 0x69, 0x0f, 0x41, 0xdb, 0x40, 0xa8, 0x50, 0x74, 0x20, 0xd7, 0xd8, 0x24, 0x0e, 0x08, 0x01,
|
||||
0x03, 0x02, 0x3f, 0x0f, 0x00, 0x1c, 0x24, 0x06, 0x09, 0xa9, 0x4c, 0x5d, 0x1f, 0x11, 0xde, 0x87,
|
||||
0x44, 0x84, 0x0d, 0x50, 0x93, 0x3c, 0x8e, 0xc8, 0xd1, 0x12, 0x01, 0x03, 0x03, 0xff, 0xff, 0x03,
|
||||
0x00, 0x1c, 0x24, 0x06, 0x0a, 0x15, 0x02, 0xe4, 0x49, 0x34, 0xf4, 0xfe, 0x47, 0xb1, 0x58, 0x0e,
|
||||
0x88, 0x50, 0x23, 0xe5, 0x1b, 0x0b, 0x01, 0x03, 0x03, 0xfa, 0xff, 0x01, 0x00, 0x1c, 0x24, 0x06,
|
||||
0x0b, 0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40,
|
||||
0xbd, 0x04, 0x01, 0x03, 0x03, 0x00, 0x41, 0x01, 0x00, 0x09, 0x24, 0x03, 0x04, 0x01, 0x01, 0x00,
|
||||
0x03, 0x00, 0x07, 0x05, 0x85, 0x03, 0x40, 0x00, 0x10, 0x05, 0x25, 0x03, 0x40, 0x00, 0x09, 0x04,
|
||||
0x01, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0f, 0x24, 0x01, 0x02, 0x79, 0x01, 0x81, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x24, 0x04, 0x01, 0x01, 0x59, 0x55, 0x59, 0x32, 0x00,
|
||||
0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, 0x10, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x2a, 0x24, 0x05, 0x01, 0x00, 0xb0, 0x00, 0x90, 0x00, 0x00, 0xf0, 0x1e, 0x00, 0x00, 0xd0,
|
||||
0x5c, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x04, 0x2a, 0x2c, 0x0a, 0x00, 0x40,
|
||||
0x42, 0x0f, 0x00, 0x55, 0x58, 0x14, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01,
|
||||
0x04, 0x0b, 0x24, 0x06, 0x02, 0x05, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x36, 0x24, 0x07, 0x01,
|
||||
0x00, 0x80, 0x02, 0xe0, 0x01, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0xca, 0x08, 0x00, 0x60, 0x09,
|
||||
0x00, 0x15, 0x16, 0x05, 0x00, 0x07, 0x15, 0x16, 0x05, 0x00, 0x9a, 0x5b, 0x06, 0x00, 0x20, 0xa1,
|
||||
0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x55, 0x58, 0x14, 0x00, 0x80, 0x84,
|
||||
0x1e, 0x00, 0x36, 0x24, 0x07, 0x02, 0x00, 0xb0, 0x00, 0x90, 0x00, 0x00, 0xf0, 0x1e, 0x00, 0x00,
|
||||
0xa0, 0xb9, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x15, 0x16, 0x05, 0x00, 0x07, 0x15, 0x16, 0x05, 0x00,
|
||||
0x9a, 0x5b, 0x06, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00,
|
||||
0x55, 0x58, 0x14, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x36, 0x24, 0x07, 0x03, 0x00, 0x40, 0x01, 0xf0,
|
||||
0x00, 0x00, 0xc0, 0x5d, 0x00, 0x00, 0x80, 0x32, 0x02, 0x00, 0x58, 0x02, 0x00, 0x15, 0x16, 0x05,
|
||||
0x00, 0x07, 0x15, 0x16, 0x05, 0x00, 0x9a, 0x5b, 0x06, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c,
|
||||
0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x55, 0x58, 0x14, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x36, 0x24,
|
||||
0x07, 0x04, 0x00, 0xa8, 0x01, 0xf0, 0x00, 0x00, 0x38, 0x7c, 0x00, 0x00, 0x50, 0xe9, 0x02, 0x00,
|
||||
0x1b, 0x03, 0x00, 0x15, 0x16, 0x05, 0x00, 0x07, 0x15, 0x16, 0x05, 0x00, 0x9a, 0x5b, 0x06, 0x00,
|
||||
0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x55, 0x58, 0x14, 0x00,
|
||||
0x80, 0x84, 0x1e, 0x00, 0x36, 0x24, 0x07, 0x05, 0x00, 0x80, 0x02, 0x68, 0x01, 0x00, 0x40, 0x19,
|
||||
0x01, 0x00, 0x80, 0x97, 0x06, 0x00, 0x08, 0x07, 0x00, 0x15, 0x16, 0x05, 0x00, 0x07, 0x15, 0x16,
|
||||
0x05, 0x00, 0x9a, 0x5b, 0x06, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42,
|
||||
0x0f, 0x00, 0x55, 0x58, 0x14, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01, 0x04,
|
||||
0x09, 0x04, 0x01, 0x01, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0xc0, 0x00, 0x01,
|
||||
0x09, 0x04, 0x01, 0x02, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x80, 0x01, 0x01,
|
||||
0x09, 0x04, 0x01, 0x03, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x02, 0x01,
|
||||
0x09, 0x04, 0x01, 0x04, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x80, 0x02, 0x01,
|
||||
0x09, 0x04, 0x01, 0x05, 0x01, 0x0e, 0x02, 0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x20, 0x03, 0x01,
|
||||
0x08, 0x0b, 0x02, 0x02, 0x01, 0x02, 0x00, 0x00, 0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00,
|
||||
0x00, 0x09, 0x24, 0x01, 0x00, 0x01, 0x26, 0x00, 0x01, 0x03, 0x0c, 0x24, 0x02, 0x01, 0x01, 0x02,
|
||||
0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x09, 0x24, 0x03, 0x03, 0x01, 0x01, 0x00, 0x05, 0x00, 0x08,
|
||||
0x24, 0x06, 0x05, 0x01, 0x01, 0x03, 0x00, 0x09, 0x04, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x03, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x07, 0x24, 0x01, 0x03, 0x01, 0x01, 0x00,
|
||||
0x0b, 0x24, 0x02, 0x01, 0x02, 0x02, 0x10, 0x01, 0x80, 0x3e, 0x00, 0x09, 0x05, 0x84, 0x05, 0x44,
|
||||
0x00, 0x04, 0x00, 0x00, 0x07, 0x25, 0x01, 0x01, 0x00, 0x00, 0x00, 0x09, 0x04, 0x04, 0x00, 0x01,
|
||||
0x03, 0x00, 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x6c, 0x00, 0x07, 0x05, 0x87,
|
||||
0x03, 0x02, 0x00, 0x06, 0x06, 0x30, 0x00, 0x00, 0x02, 0x00
|
||||
};
|
||||
|
||||
const uint8_t unknown_camera[] = {
|
||||
0x09, 0x02, 0x69, 0x01, 0x02, 0x01, 0x00, 0x80, 0xfa, 0x08, 0x0b, 0x00, 0x02, 0x0e, 0x03, 0x00,
|
||||
0x05, 0x09, 0x04, 0x00, 0x00, 0x01, 0x0e, 0x01, 0x00, 0x05, 0x0d, 0x24, 0x01, 0x00, 0x01, 0x4d,
|
||||
0x00, 0xc0, 0xe1, 0xe4, 0x00, 0x01, 0x01, 0x09, 0x24, 0x03, 0x05, 0x01, 0x01, 0x00, 0x03, 0x00,
|
||||
0x1a, 0x24, 0x06, 0x03, 0x70, 0x33, 0xf0, 0x28, 0x11, 0x63, 0x2e, 0x4a, 0xba, 0x2c, 0x68, 0x90,
|
||||
0xeb, 0x33, 0x40, 0x16, 0x08, 0x01, 0x02, 0x01, 0x1f, 0x00, 0x12, 0x24, 0x02, 0x01, 0x01, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x0b, 0x24, 0x05, 0x02,
|
||||
0x01, 0x00, 0x00, 0x02, 0x7f, 0x17, 0x00, 0x07, 0x05, 0x83, 0x03, 0x10, 0x00, 0x06, 0x05, 0x25,
|
||||
0x03, 0x10, 0x00, 0x09, 0x04, 0x01, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x05, 0x0f, 0x24, 0x01, 0x02,
|
||||
0x8d, 0x00, 0x81, 0x00, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x0b, 0x24, 0x06, 0x01, 0x01,
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x24, 0x07, 0x01, 0x00, 0x40, 0x01, 0xf0, 0x00, 0x08,
|
||||
0x3c, 0x2f, 0x00, 0x30, 0x68, 0x1b, 0x01, 0x4d, 0x2e, 0x01, 0x00, 0x15, 0x16, 0x05, 0x00, 0x05,
|
||||
0x15, 0x16, 0x05, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x2a, 0x2c, 0x0a, 0x00, 0x40, 0x42, 0x0f, 0x00,
|
||||
0x80, 0x84, 0x1e, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01, 0x04, 0x1b, 0x24, 0x04, 0x02, 0x01, 0x59,
|
||||
0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, 0x10,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x24, 0x05, 0x01, 0x00, 0x40, 0x01, 0xf0, 0x00, 0x00, 0xc0,
|
||||
0x5d, 0x00, 0x00, 0xc0, 0x5d, 0x00, 0x00, 0x58, 0x02, 0x00, 0x80, 0x84, 0x1e, 0x00, 0x01, 0x80,
|
||||
0x84, 0x1e, 0x00, 0x06, 0x24, 0x0d, 0x01, 0x01, 0x04, 0x09, 0x04, 0x01, 0x01, 0x01, 0x0e, 0x02,
|
||||
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x80, 0x00, 0x01, 0x09, 0x04, 0x01, 0x02, 0x01, 0x0e, 0x02,
|
||||
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01, 0x03, 0x01, 0x0e, 0x02,
|
||||
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x00, 0x02, 0x01, 0x09, 0x04, 0x01, 0x04, 0x01, 0x0e, 0x02,
|
||||
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x58, 0x02, 0x01, 0x09, 0x04, 0x01, 0x05, 0x01, 0x0e, 0x02,
|
||||
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0x20, 0x03, 0x01, 0x09, 0x04, 0x01, 0x06, 0x01, 0x0e, 0x02,
|
||||
0x00, 0x00, 0x07, 0x05, 0x81, 0x05, 0xbc, 0x03, 0x01
|
||||
};
|
||||
|
||||
static void compare_descriptors(const uint8_t *config_desc, size_t size, const char *camera_name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Comparing descriptrors for %s camera", camera_name);
|
||||
|
||||
struct libusb_config_descriptor *libusb_config = calloc(1, sizeof(struct libusb_config_descriptor));
|
||||
struct libusb_config_descriptor *config = NULL;
|
||||
|
||||
TEST_ASSERT_NOT_NULL( libusb_config );
|
||||
TEST_ASSERT_EQUAL( libusb_parse_configuration(libusb_config, config_desc, size), LIBUSB_SUCCESS );
|
||||
TEST_ASSERT_EQUAL( raw_desc_to_libusb_config(config_desc, size, &config), LIBUSB_SUCCESS );
|
||||
|
||||
TEST_ASSERT_EQUAL( memcmp(libusb_config, config, LIBUSB_DT_CONFIG_SIZE), 0);
|
||||
TEST_ASSERT_NOT_NULL( libusb_config->interface );
|
||||
TEST_ASSERT_NOT_NULL( config->interface );
|
||||
|
||||
TEST_ASSERT_EQUAL( libusb_config->extra_length, config->extra_length );
|
||||
TEST_ASSERT_EQUAL( memcmp(libusb_config->extra, config->extra, config->extra_length), 0);
|
||||
|
||||
TEST_ASSERT_EQUAL( libusb_config->bNumInterfaces, config->bNumInterfaces );
|
||||
|
||||
printf("checking...\n");
|
||||
|
||||
for (int i = 0; i < libusb_config->bNumInterfaces; i++) {
|
||||
printf("interface %u\n", i);
|
||||
libusb_interface_t *ifc = &libusb_config->interface[i];
|
||||
libusb_interface_t *my_ifc = &config->interface[i];
|
||||
TEST_ASSERT_EQUAL( ifc->num_altsetting, my_ifc->num_altsetting );
|
||||
|
||||
for (int j = 0; j < ifc->num_altsetting; j++) {
|
||||
printf(" altsetting %u\n", j);
|
||||
libusb_interface_descriptor_t *libusb_alt = &ifc->altsetting[j];
|
||||
libusb_interface_descriptor_t *alt = &my_ifc->altsetting[j];
|
||||
TEST_ASSERT_EQUAL( memcmp(libusb_alt, alt, LIBUSB_DT_INTERFACE_SIZE), 0);
|
||||
TEST_ASSERT_EQUAL( libusb_alt->extra_length, alt->extra_length );
|
||||
TEST_ASSERT_EQUAL( memcmp(libusb_alt->extra, alt->extra, alt->extra_length), 0);
|
||||
TEST_ASSERT_EQUAL( libusb_alt->bNumEndpoints, alt->bNumEndpoints );
|
||||
|
||||
for (int ep = 0; ep < libusb_alt->bNumEndpoints; ep++) {
|
||||
printf(" endpoint %u\n", ep);
|
||||
libusb_endpoint_descriptor_t *libusb_endpoint = &libusb_alt->endpoint[ep];
|
||||
libusb_endpoint_descriptor_t *endpoint = &alt->endpoint[ep];
|
||||
TEST_ASSERT_EQUAL( memcmp(libusb_endpoint, endpoint, LIBUSB_DT_ENDPOINT_SIZE), 0);
|
||||
TEST_ASSERT_EQUAL( libusb_endpoint->extra_length, endpoint->extra_length );
|
||||
TEST_ASSERT_EQUAL( memcmp(libusb_endpoint->extra, endpoint->extra, endpoint->extra_length), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libusb_clear_config_descriptor(libusb_config);
|
||||
clear_config_descriptor(config);
|
||||
}
|
||||
|
||||
// Test compares config descriptor created by usb_host_uvc driver and originally used libusb function
|
||||
TEST_CASE("Compare config descriptor parser", "[usb_uvc]")
|
||||
{
|
||||
COMPARE_DESCRIPTORS(CANYON_CNE_CWC2);
|
||||
COMPARE_DESCRIPTORS(Logitech_C980);
|
||||
COMPARE_DESCRIPTORS(unknown_camera);
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue