This commit is contained in:
2025-06-18 09:21:10 +08:00
commit b2b79996d7
478 changed files with 82728 additions and 0 deletions

View File

@ -0,0 +1,136 @@
#!/bin/bash
# Function: Check version format
# Input parameters: $1 The version number
# Return value: 0 if the version numbers are correct, 1 if the first version is incorrect,
check_version_format() {
version_regex="^v[0-9]+\.[0-9]+\.[0-9]+$"
if [[ ! $1 =~ $version_regex ]]; then
return 1
fi
return 0
}
if [ $# -lt 1 ]; then
latest_version="0.0.0"
echo "Don't get the lastest version, use \"0.0.0\" as default"
else
# Get the first input parameter as the version to be compared
latest_version="$1"
# Check the version format
check_version_format "${latest_version}"
result=$?
if [ ${result} -ne 0 ]; then
echo "The latest release version (${latest_version}) format is incorrect."
exit 1
fi
fi
# Specify the directory path
target_directory="./"
echo "Checking directory: ${target_directory}"
# Function: Check if a file exists
# Input parameters: $1 The file to check
# Return value: 0 if the file exists, 1 if the file does not exist
check_file_exists() {
if [ ! -f "$1" ]; then
echo "File '$1' not found."
return 1
fi
return 0
}
# Function: Compare version numbers
# Input parameters: $1 The first version number, $2 The second version number
# Return value: 0 if the version numbers are equal, 1 if the first version is greater,
# 2 if the second version is greater,
compare_versions() {
version_regex="^v[0-9]+\.[0-9]+\.[0-9]+$"
version1=$(echo "$1" | cut -c 2-) # Remove the 'v' at the beginning of the version number
version2=$(echo "$2" | cut -c 2-)
IFS='.' read -ra v1_parts <<< "$version1"
IFS='.' read -ra v2_parts <<< "$version2"
for ((i=0; i<${#v1_parts[@]}; i++)); do
if [[ "${v1_parts[$i]}" -lt "${v2_parts[$i]}" ]]; then
return 2
elif [[ "${v1_parts[$i]}" -gt "${v2_parts[$i]}" ]]; then
return 1
fi
done
return 0
}
echo "Checking file: library.properties"
# Check if "library.properties" file exists
check_file_exists "${target_directory}/library.properties"
if [ $? -ne 0 ]; then
exit 1
fi
# Read the version information from the file
arduino_version=v$(grep -E '^version=' "${target_directory}/library.properties" | cut -d '=' -f 2)
echo "Get Arduino version: ${arduino_version}"
# Check the version format
check_version_format "${arduino_version}"
result=$?
if [ ${result} -ne 0 ]; then
echo "Arduino version (${arduino_version}) format is incorrect."
exit 1
fi
# Compare Arduino Library version with the latest release version
compare_versions "${arduino_version}" "${latest_version}"
result=$?
if [ ${result} -ne 1 ]; then
if [ ${result} -eq 3 ]; then
echo "Arduino version (${arduino_version}) is incorrect."
else
echo "Arduino version (${arduino_version}) is not greater than the latest release version (${latest_version})."
exit 1
fi
fi
echo "Checking file: idf_component.yml"
# Check if "idf_component.yml" file exists
check_file_exists "${target_directory}/idf_component.yml"
if [ $? -eq 0 ]; then
# Read the version information from the file
idf_version=v$(grep -E '^version:' "${target_directory}/idf_component.yml" | awk -F'"' '{print $2}')
echo "Get IDF component version: ${idf_version}"
# Check the version format
check_version_format "${idf_version}"
result=$?
if [ ${result} -ne 0 ]; then
echo "IDF component (${idf_version}) format is incorrect."
exit 1
fi
# Compare IDF Component version with Arduino Library version
compare_versions ${idf_version} ${arduino_version}
result=$?
if [ ${result} -ne 0 ]; then
if [ ${result} -eq 3 ]; then
echo "IDF component version (${idf_version}) is incorrect."
else
echo "IDF component version (${idf_version}) is not equal to the Arduino version (${arduino_version})."
exit 1
fi
fi
# Compare IDF Component version with the latest release version
compare_versions ${idf_version} ${latest_version}
result=$?
if [ ${result} -ne 1 ]; then
if [ ${result} -eq 3 ]; then
echo "IDF component version (${idf_version}) is incorrect."
else
echo "IDF component version (${idf_version}) is not greater than the latest release version (${latest_version})."
exit 1
fi
fi
fi

View File

@ -0,0 +1,15 @@
name: Arduino Lint Action
on:
workflow_dispatch:
pull_request:
types: [opened, reopened, synchronize]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update

View File

@ -0,0 +1,30 @@
name: Check Versions
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
check_versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Get latest release info of repository
id: last_release
uses: InsonusK/get-latest-release@v1.1.0
with:
myToken: ${{ github.token }}
exclude_types: "draft|prerelease"
view_top: 1
- name: Print result
run: |
echo "id: ${{ steps.last_release.outputs.id }}"
echo "name: ${{ steps.last_release.outputs.name }}"
echo "tag_name: ${{ steps.last_release.outputs.tag_name }}"
echo "created_at: ${{ steps.last_release.outputs.created_at }}"
echo "draft: ${{ steps.last_release.outputs.draft }}"
echo "prerelease: ${{ steps.last_release.outputs.prerelease }}"
echo "url: ${{ steps.last_release.outputs.url }}"
- name: Check & Compare versions
run: bash ./.github/scripts/check_versions.sh ${{ steps.last_release.outputs.tag_name }}

View File

@ -0,0 +1,14 @@
name: pre-commit
on:
workflow_dispatch:
pull_request:
types: [opened, reopened, synchronize]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/action@v2.0.3

View File

@ -0,0 +1,71 @@
.config
*.o
*.pyc
*.orig
# gtags
GTAGS
GRTAGS
GPATH
# emacs
.dir-locals.el
# emacs temp file suffixes
*~
.#*
\#*#
# eclipse setting
.settings
# MacOS directory files
.DS_Store
# Unit Test CMake compile log folder
log_ut_cmake
TEST_LOGS
# gcov coverage reports
*.gcda
*.gcno
coverage.info
coverage_report/
test_multi_heap_host
# VS Code Settings
.vscode/
# VIM files
*.swp
*.swo
# Clion IDE CMake build & config
.idea/
cmake-build-*/
# Results for the checking of the Python coding style and static analysis
.mypy_cache
flake8_output.txt
# esp-idf default build directory name
build
build_esp*/
build_linux*/
size_info.txt
sdkconfig
sdkconfig.old
# lock files for examples and components
dependencies.lock
# managed_components for examples
managed_components
# pytest log
pytest_embedded_log/
pytest_log/
.pytest_cache/
XUNIT_RESULT.xml

View File

@ -0,0 +1 @@
{"type": "library", "name": "ESP32_USB_STREAM", "version": "0.0.1", "spec": {"owner": "esp-arduino-libs", "id": 16303, "name": "ESP32_USB_STREAM", "requirements": null, "uri": null}}

View File

@ -0,0 +1,26 @@
exclude: 'src/original'
repos:
- repo: https://github.com/igrr/astyle_py.git
rev: master
hooks:
- id: astyle_py
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-pointer=name', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper']
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: trailing-whitespace
types_or: [c, c++]
- id: end-of-file-fixer
types_or: [c, c++]
- id: check-merge-conflict
- id: mixed-line-ending
types_or: [c, c++]
args: ['--fix=lf']
description: Forces to replace line ending by the UNIX 'lf' character
- repo: https://github.com/espressif/check-copyright/
rev: v1.0.3
hooks:
- id: check-copyright
args: ['--config', 'check_copyright_config.yaml']

View File

@ -0,0 +1,11 @@
# ChangeLog
## v0.0.1 - [2023-11-10]
### Enhancements:
* Only support for ESP32-S2 and ESP32-S3 SoCs.
* Support video stream through UVC Stream interface.
* Support microphone stream and speaker stream through the UAC Stream interface
* Support volume, mute and other features control through the UAC Control interface
* Support stream separately suspend and resume

View File

@ -0,0 +1,69 @@
[![Arduino Lint](https://github.com/esp-arduino-libs/ESP32_USB_Stream/actions/workflows/arduino_lint.yml/badge.svg)](https://github.com/esp-arduino-libs/ESP32_USB_Stream/actions/workflows/arduino_lint.yml) [![pre-commit](https://github.com/esp-arduino-libs/ESP32_USB_Stream/actions/workflows/pre-commit.yml/badge.svg)](https://github.com/esp-arduino-libs/ESP32_USB_Stream/actions/workflows/pre-commit.yml)
# ESP32_USB_STREAM
ESP32_USB_STREAM is an Arduino library designed to support USB UVC + UAC host driver for ESP32-S2/ESP32-S3. It supports read/write/control multimedia streaming from usb device. For example, at most one UVC + one Microphone + one Speaker streaming can be supported at the same time.
ESP32_USB_STREAM encapsulates the component from the [Espressif Components Registry](https://components.espressif.com/). It is developed based on [arduino-esp32](https://github.com/espressif/arduino-esp32) and can be easily downloaded and integrated into the Arduino IDE.
## Features
* Only support for ESP32-S2 and ESP32-S3 SoCs.
* Support video stream through UVC Stream interface.
* Support microphone stream and speaker stream through the UAC Stream interface
* Support volume, mute and other features control through the UAC Control interface
* Support stream separately suspend and resume
## Supported Drivers
| **Driver** | **Version** |
| ------------------------------------------------------------------ | ----------- |
| [usb_stream](https://components.espressif.com/components/espressif/usb_stream) |1.2.0|
## How to Use
For information on how to use the library in the Arduino IDE, please refer to the documentation for [Arduino IDE v1.x.x](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries) or [Arduino IDE v2.x.x](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library).
## Dependencies Version
| **Name** | **Version** |
| -------------------------------------------------------------------------- | ----------- |
| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= v2.0.14 |
### Examples
* [Getting started with a UVC](examples/GettingStartUVC/): Demonstrates how to use usb video streaming.
* [Getting started with a UAC](examples/GettingStartUAC/): Demonstrates how to use usb audio streaming.
### Detailed Usage
```cpp
#include "USB_STREAM.h"
// Instantiate a Ustream object
USB_STREAM *usb = new USB_STREAM();
// allocate memory
uint8_t *_xferBufferA = (uint8_t *)malloc(55 * 1024);
assert(_xferBufferA != NULL);
uint8_t *_xferBufferB = (uint8_t *)malloc(55 * 1024);
assert(_xferBufferB != NULL);
uint8_t *_frameBuffer = (uint8_t *)malloc(55 * 1024);
assert(_frameBuffer != NULL);
// Config the parameter
usb->uvcConfiguration(FRAME_RESOLUTION_ANY, FRAME_RESOLUTION_ANY, FRAME_INTERVAL_FPS_15, 55 * 1024, _xferBufferA, _xferBufferB, 55 * 1024, _frameBuffer);
//Register the camera frame callback function
usb->uvcCamRegisterFrameCb(&cameraFramecb, NULL);
usb->start();
/*Dont forget to free the allocated memory*/
// free(_xferBufferA);
// free(_xferBufferB);
// free(_frameBuffer);
```
Note: For additional details and information about the **usb_stream** functionality, please refer to the documentation provided by [ESP-IOT Solutions](https://github.com/espressif/esp-iot-solution/tree/master/components/usb/usb_stream).

View File

@ -0,0 +1,41 @@
DEFAULT:
perform_check: yes # should the check be performed?
# Sections setting this to 'no' don't need to include any other options as they are ignored
# When a file is using a section with the option set to 'no', no checks are performed.
# what licenses (or license expressions) are allowed for files in this section
# when setting this option in a section, you need to list all the allowed licenses
allowed_licenses:
- Apache-2.0
license_for_new_files: Apache-2.0 # license to be used when inserting a new copyright notice
new_notice_c: | # notice for new C, CPP, H, HPP and LD files
/*
* SPDX-FileCopyrightText: {years} Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: {license}
*/
new_notice_python: | # notice for new python files
# SPDX-FileCopyrightText: {years} Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: {license}
# comment lines matching:
# SPDX-FileCopyrightText: year[-year] Espressif Systems
# or
# SPDX-FileContributor: year[-year] Espressif Systems
# are replaced with this template prefixed with the correct comment notation (# or // or *) and SPDX- notation
espressif_copyright: '{years} Espressif Systems (Shanghai) CO LTD'
# You can create your own rules for files or group of files
examples_and_unit_tests:
include:
- 'test_apps/'
allowed_licenses:
- Apache-2.0
- Unlicense
- CC0-1.0
license_for_new_files: CC0-1.0
ignore: # You can also select ignoring files here
perform_check: no # Don't check files from that block
include:
- 'examples/'

View File

@ -0,0 +1,45 @@
#include <Arduino.h>
#include "USB_STREAM.h"
/* Define the Mic frame callback function implementation */
static void onMicFrameCallback(mic_frame_t *frame, void *ptr)
{
// We should using higher baudrate here, to reduce the blocking time here
Serial.printf("mic callback! bit_resolution = %u, samples_frequence = %"PRIu32", data_bytes = %"PRIu32"\n", frame->bit_resolution, frame->samples_frequence, frame->data_bytes);
}
void setup()
{
Serial.begin(115200);
// Instantiate a Ustream object
USB_STREAM *usb = new USB_STREAM();
// Config the parameter
usb->uacConfiguration(UAC_CH_ANY, UAC_BITS_ANY, UAC_FREQUENCY_ANY, 6400, UAC_CH_ANY, UAC_BITS_ANY, UAC_FREQUENCY_ANY, 6400);
//Register the camera frame callback function
usb->uacMicRegisterCb(&onMicFrameCallback, NULL);
usb->start();
usb->connectWait(1000);
delay(5000);
usb->uacMicMute((void *)0);
delay(5000);
usb->uacMicVolume((void *)60);
usb->uacMicSuspend(NULL);
delay(5000);
usb->uacMicResume(NULL);
}
// The loop function runs repeatedly
void loop()
{
// Delay the task for 100ms
vTaskDelay(5000);
}

View File

@ -0,0 +1,50 @@
#include <Arduino.h>
#include "USB_STREAM.h"
/* Define the camera frame callback function implementation */
static void onCameraFrameCallback(uvc_frame *frame, void *user_ptr)
{
Serial.printf("uvc callback! frame_format = %d, seq = %" PRIu32 ", width = %" PRIu32", height = %" PRIu32 ", length = %u, ptr = %d\n",
frame->frame_format, frame->sequence, frame->width, frame->height, frame->data_bytes, (int)user_ptr);
}
void setup()
{
Serial.begin(115200);
// Instantiate an object
USB_STREAM *usb = new USB_STREAM();
// allocate memory
uint8_t *_xferBufferA = (uint8_t *)malloc(55 * 1024);
assert(_xferBufferA != NULL);
uint8_t *_xferBufferB = (uint8_t *)malloc(55 * 1024);
assert(_xferBufferB != NULL);
uint8_t *_frameBuffer = (uint8_t *)malloc(55 * 1024);
assert(_frameBuffer != NULL);
// Config the parameter
usb->uvcConfiguration(FRAME_RESOLUTION_ANY, FRAME_RESOLUTION_ANY, FRAME_INTERVAL_FPS_15, 55 * 1024, _xferBufferA, _xferBufferB, 55 * 1024, _frameBuffer);
//Register the camera frame callback function
usb->uvcCamRegisterCb(&onCameraFrameCallback, NULL);
usb->start();
usb->connectWait(1000);
delay(5000);
usb->uvcCamSuspend(NULL);
delay(5000);
usb->uvcCamResume(NULL);
/*Dont forget to free the allocated memory*/
// free(_xferBufferA);
// free(_xferBufferB);
// free(_frameBuffer);
}
void loop()
{
vTaskDelay(100);
}

View File

@ -0,0 +1,10 @@
name=ESP32_USB_STREAM
version=0.0.1
author=espressif
maintainer=alibukharai
sentence=ESP32_USB_STREAM is a specialized library created to facilitate the implementation of USB stream functionality on ESP SoCs.
paragraph=This means that it provides a convenient and efficient way to transmit audio and video data through USB connections, making it an invaluable tool for a wide range of applications such as audio and video streaming, data transfer, and more. Currently, it is only competible with ESP32-S2 and ESP32-S3.
category=Other
architectures=esp32
url=https://github.com/esp-arduino-libs/ESP32_USB_Stream
includes=USB_STREAM.h

View File

@ -0,0 +1,202 @@
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.

View File

@ -0,0 +1,337 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "USB_STREAM.h"
#include "original/include/usb_stream.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
// Define the static variable TAG with a string "driver"
static const char *TAG = "arduino-usb";
#define CHECK_ESP_ERROR(result, message) \
if (result != ESP_OK) { \
ESP_LOGE(TAG, "%s(%d): %s, Error Code: %d", __FUNCTION__, __LINE__, message, result); \
}
// A constructor with default values
USB_STREAM::USB_STREAM()
{
_frame_width = FRAME_RESOLUTION_ANY;
_frame_height = FRAME_RESOLUTION_ANY;
_frame_interval = FPS2INTERVAL(15);
_spk_ch_num = UAC_CH_ANY;
_spk_bit_resolution = UAC_BITS_ANY;
_spk_samples_frequency = UAC_FREQUENCY_ANY;
_spk_buf_size = 6400;
_mic_ch_num = UAC_CH_ANY;
_mic_bit_resolution = UAC_BITS_ANY;
_mic_samples_frequency = UAC_FREQUENCY_ANY;
_mic_buf_size = 6400;
}
// A destructor with default values
USB_STREAM::~USB_STREAM()
{}
// Method to register a user-defined callback function
void USB_STREAM::uvcCamRegisterCb(uvc_frame_callback_t *newFunction, void *cb_arg)
{
if (newFunction == NULL) {
ESP_LOGE(TAG, "registerCallBack function error\n");
return;
} else {
this->_user_frame_cb = newFunction;
this->_user_frame_cb_arg = cb_arg;
}
}
// A static function that serves as the callback function for the camera frame
static void _camera_frame_cb(uvc_frame_t *frame, void *ptr)
{
USB_STREAM *my_instance = (USB_STREAM *)ptr;
if (my_instance->_user_frame_cb != NULL) {
my_instance->_user_frame_cb(frame, my_instance->_user_frame_cb_arg);
}
}
// Method to register a user-defined callback function
void USB_STREAM::uacMicRegisterCb(mic_callback_t *newFunction, void *cb_arg)
{
if (newFunction == NULL) {
ESP_LOGE(TAG, "registerCallBack function error\n");
return;
} else {
this->_user_mic_frame_cb = newFunction;
this->_user_mic_frame_cb_arg = cb_arg;
}
}
// A static function that serves as the callback function for the camera frame
static void _mic_frame_cb(mic_frame_t *frame, void *ptr)
{
USB_STREAM *my_instance = (USB_STREAM *)ptr;
if (my_instance->_user_mic_frame_cb != NULL) {
my_instance->_user_mic_frame_cb(frame, my_instance->_user_frame_cb_arg);
}
}
// Method to configure the uvc camera stream
void USB_STREAM::uvcConfiguration(uint16_t width, uint16_t height, uint32_t frameInterval, uint32_t transferBufferSize, uint8_t *transferBufferA, uint8_t *transferBufferB, uint32_t frameBufferSize, uint8_t *frameBuffer)
{
if (transferBufferA == nullptr || transferBufferB == nullptr || frameBuffer == nullptr) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
_frame_width = width;
_frame_height = height;
_frame_interval = frameInterval;
uvc_config_t uvc_config = {
.frame_width = _frame_width,
.frame_height = _frame_height,
.frame_interval = _frame_interval,
.xfer_buffer_size = transferBufferSize,
.xfer_buffer_a = transferBufferA,
.xfer_buffer_b = transferBufferB,
.frame_buffer_size = frameBufferSize,
.frame_buffer = frameBuffer,
.frame_cb = &_camera_frame_cb,
.frame_cb_arg = this,
};
// Configure the UVC streaming with the provided configuration
CHECK_ESP_ERROR(uvc_streaming_config(&uvc_config), "UVC streaming config fail");
}
// Method to configure the uac mic stream
void USB_STREAM::uacConfiguration(uint8_t mic_ch_num, uint16_t mic_bit_resolution, uint32_t mic_samples_frequency, uint32_t mic_buf_size, uint8_t spk_ch_num, uint16_t spk_bit_resolution, uint32_t spk_samples_frequency, uint32_t spk_buf_size)
{
_mic_ch_num = mic_ch_num;
_mic_bit_resolution = mic_bit_resolution;
_mic_samples_frequency = mic_samples_frequency;
_mic_buf_size = mic_buf_size;
_spk_ch_num = spk_ch_num;
_spk_bit_resolution = spk_bit_resolution;
_spk_samples_frequency = spk_samples_frequency;
_spk_buf_size = spk_buf_size;
uac_config_t uac_config = {
.spk_ch_num = _spk_ch_num,
.mic_ch_num = _mic_ch_num,
.mic_bit_resolution = _mic_bit_resolution,
.mic_samples_frequence = _mic_samples_frequency,
.spk_bit_resolution = _spk_bit_resolution,
.spk_samples_frequence = _spk_samples_frequency,
.spk_buf_size = _spk_buf_size,
.mic_buf_size = _mic_buf_size,
.mic_cb = &_mic_frame_cb,
.mic_cb_arg = this,
};
CHECK_ESP_ERROR(uac_streaming_config(&uac_config), "UAC streaming config fail");
}
// Method to start the USB streaming
void USB_STREAM::start()
{
CHECK_ESP_ERROR(usb_streaming_start(), "USB streaming start fail");
}
// Method to stop the USB streaming
void USB_STREAM::stop()
{
CHECK_ESP_ERROR(usb_streaming_stop(), "USB streaming stop fail");
}
// Method to wait for usb stream to connect
void USB_STREAM::connectWait(int timeoutMs)
{
CHECK_ESP_ERROR(usb_streaming_connect_wait(timeoutMs), "USB streaming wait fail");
}
// Method to register state for usb stream
void USB_STREAM::registerState(StateChangeCallback newFunction, void *userData)
{
if (!newFunction) {
ESP_LOGE(TAG, "Callback function not defined");
return;
}
// Register the provided callback function
CHECK_ESP_ERROR(usb_streaming_state_register(newFunction, userData), "state register fail");
}
// Method to suspend uvc camera stream
void USB_STREAM::uvcCamSuspend(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UVC, CTRL_SUSPEND, ctrl_value), "uvc camera suspend fail");
}
// Method to resume uvc camera stream
void USB_STREAM::uvcCamResume(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UVC, CTRL_RESUME, ctrl_value), "uvc camera resume fail");
}
// Method to suspend uac mic stream
void USB_STREAM::uacMicSuspend(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_MIC, CTRL_SUSPEND, ctrl_value), "uac mic suspend fail");
}
// Method to resume uac mic stream
void USB_STREAM::uacMicResume(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_MIC, CTRL_RESUME, ctrl_value), "uac mic resume fail");
}
// Method to mute uac mic
void USB_STREAM::uacMicMute(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_MIC, CTRL_UAC_MUTE, ctrl_value), "uac mic mute fail");
}
// Method to adjust uac mic volume
void USB_STREAM::uacMicVolume(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_MIC, CTRL_UAC_VOLUME, ctrl_value), "uac mic volume fail");
}
// Method to suspend uac spk stream
void USB_STREAM::uacSpkSuspend(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_SPK, CTRL_SUSPEND, ctrl_value), "uac spk suspend fail");
}
// Method to resume uac spk stream
void USB_STREAM::uacSpkResume(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_SPK, CTRL_RESUME, ctrl_value), "uac spk resume fail");
}
// Method to mute uac spk
void USB_STREAM::uacSpkMute(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_MIC, CTRL_UAC_MUTE, ctrl_value), "uac spk mute fail");
}
// Method to adjust uac spk volume
void USB_STREAM::uacSpkVolume(void *ctrl_value)
{
CHECK_ESP_ERROR(usb_streaming_control(STREAM_UAC_MIC, CTRL_UAC_VOLUME, ctrl_value), "uac spk volume fail");
}
// Method to get uvc frame size
uvc_frame_size_t *USB_STREAM::uvcCamGetFrameSize(uvc_frame_size_t *uvc_frame_list)
{
if (uvc_frame_list == nullptr) {
return NULL;
}
CHECK_ESP_ERROR(uvc_frame_size_list_get(uvc_frame_list, NULL, NULL), "uvc cam get frame size fail");
return uvc_frame_list;
}
// Method to get uvc frame list size
void USB_STREAM::uvcCamGetFrameListSize(size_t *frame_size, size_t *frame_index)
{
if (frame_size == nullptr || frame_index == nullptr) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
CHECK_ESP_ERROR(uvc_frame_size_list_get(nullptr, frame_size, frame_index), "get frame list size fail");
}
// Method to reset uvc cam frame
void USB_STREAM::uvcCamFrameReset(uint16_t frame_width, uint16_t frame_height, uint32_t frame_interval)
{
if (frame_width == NULL || frame_height == NULL || frame_interval == NULL) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
CHECK_ESP_ERROR(uvc_frame_size_reset(frame_width, frame_height, frame_interval), "reset camera frame size fail");
}
// Method to read mic
void USB_STREAM::uacReadMic(uint8_t *buffer, size_t buf_size, size_t *data_bytes, size_t timeout_ms)
{
if (buffer == nullptr || data_bytes == nullptr || buf_size == 0) {
ESP_LOGE(TAG, "Invalid parameters for uacReadMic");
return;
}
CHECK_ESP_ERROR(uac_mic_streaming_read(buffer, buf_size, data_bytes, timeout_ms), "read mic data error");
}
// Method to get uac spk frame size
uac_frame_size_t *USB_STREAM::uacSpkGetFrameSize(uac_frame_size_t *uac_frame_list)
{
if (uac_frame_list == nullptr) {
return NULL;
}
CHECK_ESP_ERROR(uac_frame_size_list_get(STREAM_UAC_SPK, uac_frame_list, NULL, NULL), "uac spk get frame size fail");
return uac_frame_list;
}
// Method to get uac mic frame size
uac_frame_size_t *USB_STREAM::uacMicGetFrameSize(uac_frame_size_t *uac_frame_list)
{
if (uac_frame_list == nullptr) {
return NULL;
}
CHECK_ESP_ERROR(uac_frame_size_list_get(STREAM_UAC_MIC, uac_frame_list, NULL, NULL), "uac mic get frame size fail");
return uac_frame_list;
}
// Method to get uac frame list size
void USB_STREAM::uacMicGetFrameListSize(size_t *frame_size, size_t *frame_index)
{
if (frame_size == nullptr || frame_index == nullptr) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
CHECK_ESP_ERROR(uac_frame_size_list_get(STREAM_UAC_MIC, nullptr, frame_size, frame_index), "get frame list size fail");
}
// Method to get uac spk frame list size
void USB_STREAM::uacSpkGetFrameListSize(size_t *frame_size, size_t *frame_index)
{
if (frame_size == nullptr || frame_index == nullptr) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
CHECK_ESP_ERROR(uac_frame_size_list_get(STREAM_UAC_MIC, nullptr, frame_size, frame_index), "get frame list size fail");
}
// Method to reset uac mic frame
void USB_STREAM::uacMicFrameReset(uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequency)
{
if (ch_num == NULL || bit_resolution == NULL || samples_frequency == NULL) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
CHECK_ESP_ERROR(uac_frame_size_reset(STREAM_UAC_MIC, ch_num, bit_resolution, samples_frequency), "reset Mic frame size fail");
}
// Method to reset uac spk frame
void USB_STREAM::uacSpkFrameReset(uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequency)
{
if (ch_num == NULL || bit_resolution == NULL || samples_frequency == NULL) {
ESP_LOGE(TAG, "arguments cannot be null");
return;
}
CHECK_ESP_ERROR(uac_frame_size_reset(STREAM_UAC_SPK, ch_num, bit_resolution, samples_frequency), "reset Spk frame size fail");
}
// Method to write uac frame
void USB_STREAM::uacWriteSpk(uint16_t *buffer, size_t data_bytes, size_t timeout_ms)
{
if (buffer == nullptr || data_bytes == NULL) {
ESP_LOGE(TAG, "Invalid parameters for uacWriteSpk");
return;
}
CHECK_ESP_ERROR(uac_spk_streaming_write(buffer, data_bytes, timeout_ms), "write spk data error");
}

View File

@ -0,0 +1,283 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdio.h>
#include <assert.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "original/include/usb_stream.h"
class USB_STREAM {
public:
//Public member variables for storing user-defined callback function and arguments
void *_user_mic_frame_cb_arg = NULL;
void *_user_frame_cb_arg = NULL;
uvc_frame_callback_t *_user_frame_cb = NULL;
mic_callback_t *_user_mic_frame_cb = NULL;
typedef void (*StateChangeCallback)(usb_stream_state_t event, void *arg);
/**
* @brief Construct a new USB_STREAM object
*
*/
USB_STREAM();
/**
* @brief Destroy the USB_STREAM object
*
*/
~USB_STREAM();
/**
* @brief Start usb streaming with pre-configs, usb driver will create internal tasks to handle usb data from stream pipe, and run user's callback after new frame ready.
*/
void start();
/**
* @brief Stop current usb streaming, internal tasks will be delete, related resourse will be free
*/
void stop();
/*
* @brief Wait for USB device connection
*/
void connectWait(int timeoutMs);
/**
* @brief This function registers a callback for USB streaming, please note that only one callback
* can be registered, the later registered callback will overwrite the previous one.
*
* @param newFunction A pointer to a function that will be called when the USB streaming state changes.
* @param usr_data usr_data is a void pointer.
*/
void registerState(StateChangeCallback newFunction, void *usr_data);
/**
* @brief Register a callback fucntion to an object
*
* @param newFunction Callback function
* @param cb_arg callback args
*/
void uvcCamRegisterCb(uvc_frame_callback_t *newFunction, void *cb_arg);
/**
* @brief Configuration for an object
*
* @param width width of a frame
* @param height height of a frame
* @param frameInterval frame interval
* @param transferBufferSize Transfer buffer size, using double buffer here, must larger than one frame size
* @param transferBufferA buffer a for usb payload
* @param transferBufferB Buffer b for usb payload
* @param frameBufferSize Frame buffer size, must larger than one frame size
* @param frameBuffer Buffer for one frame
*/
void uvcConfiguration(uint16_t width, uint16_t height, uint32_t frameInterval, uint32_t transferBufferSize, uint8_t *transferBufferA, uint8_t *transferBufferB, uint32_t frameBufferSize, uint8_t *frameBuffer);
/**
* @brief Suspends USB Camera streaming
*
* @param ctrl_value control value
*/
void uvcCamSuspend(void *ctrl_value);
/**
* @brief Resumes USB Camera streaming
*
* @param ctrl_value control value
*/
void uvcCamResume(void *ctrl_value);
/**
* @brief Get the frame size list of current connected camera
*
* @param frame_size the frame size list
*/
uvc_frame_size_t *uvcCamGetFrameSize(uvc_frame_size_t *frame_size);
/**
* @brief Get the frame list size of current connected Cam
*
* @param frame_size the frame list size
* @param frame_index current frame index
*/
void uvcCamGetFrameListSize(size_t *frame_size, size_t *frame_index);
/**
* @brief Reset the expected frame size and frame interval, please reset when uvc streaming
* in suspend state.The new configs will be effective after streaming resume.
*
* Note: frame_width and frame_height can be set to 0 at the same time, which means
* no change on frame size.
*
* @param frame_width frame width, FRAME_RESOLUTION_ANY means any width
* @param frame_height frame height, FRAME_RESOLUTION_ANY means any height
* @param frame_interval frame interval, 0 means no change
*/
void uvcCamFrameReset(uint16_t frame_width, uint16_t frame_height, uint32_t frame_interval);
/**
* @brief Suspends USB Mic streaming
*
* @param ctrl_value control value
*/
void uacMicSuspend(void *ctrl_value);
/**
* @brief Resumes USB Mic streaming
*
* @param ctrl_value control value
*/
void uacMicResume(void *ctrl_value);
/**
* @brief Mute USB Mic streaming
*
* @param ctrl_value control value
*/
void uacMicMute(void *ctrl_value);
/**
* @brief Control Mic volume
*
* @param ctrl_value control value
*/
void uacMicVolume(void *ctrl_value);
/**
* @brief Get the frame size list of current connected Mic
*
* @param frame_size the frame size list
*/
// uac_frame_size_t *uacMicGetFrameSize(size_t *frame_size);
uac_frame_size_t *uacMicGetFrameSize(uac_frame_size_t *frame_size);
/**
* @brief Get the frame list size of current connected Mic
*
* @param frame_size the frame list size
* @param frame_index current frame index
*/
void uacMicGetFrameListSize(size_t *frame_size, size_t *frame_index);
/**
* @brief Configuration for UAC object
*
* @param mic_ch_num microphone channel numbers
* @param mic_bit_resolution microphone resolution(bits)
* @param mic_samples_frequency microphone frequency(Hz)
* @param mic_buf_size mic receive buffer size, 0 if not use
*/
void uacConfiguration(uint8_t mic_ch_num, uint16_t mic_bit_resolution, uint32_t mic_samples_frequency, uint32_t mic_buf_size, uint8_t spk_ch_num, uint16_t spk_bit_resolution, uint32_t spk_samples_frequency, uint32_t spk_buf_size);
/**
* @brief Register a callback fucntion to an UAC Mic object
*
* @param newFunction Callback function
* @param cb_arg callback args
*/
void uacMicRegisterCb(mic_callback_t *newFunction, void *cb_arg);
/**
* @brief Read data from internal mic buffer, the actual size will be returned
*
* @param buffer pointer to the buffer to store the received data
* @param buf_size The size of the data buffer.
* @param data_bytes The actual size read from buffer
* @param timeout_ms The timeout value for the read operation.
*/
void uacReadMic(uint8_t *buffer, size_t buf_size, size_t *data_bytes, size_t timeout_ms);
/**
* @brief Reset Mic audio channel number, bit resolution and samples frequency, please reset when the streaming
* in suspend state. The new configs will be effective after streaming resume.
*
* @param ch_num audio channel numbers
* @param bit_resolution audio bit resolution
* @param samples_frequency audio samples frequency
*/
void uacMicFrameReset(uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequency);
/**
* @brief Control Mic volume
*
* @param ctrl_value control value
*/
void uacSpkSuspend(void *ctrl_value);
/**
* @brief Resumes USB Spk streaming
*
* @param ctrl_value control value
*/
void uacSpkResume(void *ctrl_value);
/**
* @brief Mute USB spk streaming
* @param ctrl_value control value
*/
void uacSpkMute(void *ctrl_value);
/**
* @brief Control Spk volume
*
* @param ctrl_value control value
*/
void uacSpkVolume(void *ctrl_value);
/**
* @brief Get the frame size list of current connected Spk
*
* @param frame_size the frame size list
*/
uac_frame_size_t *uacSpkGetFrameSize(uac_frame_size_t *frame_size);
/**
* @brief Get the frame list size of current connected Spk
*
* @param frame_size the frame list size
* @param frame_index current frame index
*/
void uacSpkGetFrameListSize(size_t *frame_size, size_t *frame_index);
/**
* @brief Write data to the speaker buffer, will be send out when USB device is ready
*
* @param buffer The data to be written.
* @param data_bytes The size of the data to be written.
* @param timeout_ms The timeout value for writing data to the buffer.
*/
void uacWriteSpk(uint16_t *buffer, size_t data_bytes, size_t timeout_ms);
/**
* @brief Reset Spk audio channel number, bit resolution and samples frequency, please reset when the streaming
* in suspend state. The new configs will be effective after streaming resume.
*
* @param ch_num audio channel numbers
* @param bit_resolution audio bit resolution
* @param samples_frequency audio samples frequency
*/
void uacSpkFrameReset(uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequency);
private:
uint16_t _frame_width;
uint16_t _frame_height;
uint32_t _frame_interval;
uint8_t _spk_ch_num;
uint16_t _spk_bit_resolution;
uint32_t _spk_samples_frequency;
uint32_t _spk_buf_size;
uint8_t _mic_ch_num;
uint16_t _mic_bit_resolution;
uint32_t _mic_samples_frequency;
uint32_t _mic_buf_size;
};

View File

@ -0,0 +1,526 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
#include "esp_log.h"
#include "usb/usb_host.h"
#include "usb/usb_types_ch9.h"
#include "usb_stream_descriptor.h"
// esp32/tools/esp32-arduino-libs/idf-release_v5.1-6b1f40b9bf/esp32s3/include/usb/include/usb/
void print_device_descriptor(const uint8_t *buff)
{
if (buff == NULL) {
return;
}
#ifdef CONFIG_UVC_PRINT_DESC
const usb_device_desc_t *devc_desc = (const usb_device_desc_t *)buff;
printf("*** Device descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("bLength %d\n", devc_desc->bLength);
printf("bDescriptorType %d\n", devc_desc->bDescriptorType);
#endif
printf("bcdUSB %d.%d0\n", ((devc_desc->bcdUSB >> 8) & 0xF), ((devc_desc->bcdUSB >> 4) & 0xF));
printf("bDeviceClass 0x%x\n", devc_desc->bDeviceClass);
printf("bDeviceSubClass 0x%x\n", devc_desc->bDeviceSubClass);
printf("bDeviceProtocol 0x%x\n", devc_desc->bDeviceProtocol);
printf("bMaxPacketSize0 %d\n", devc_desc->bMaxPacketSize0);
printf("idVendor 0x%x\n", devc_desc->idVendor);
printf("idProduct 0x%x\n", devc_desc->idProduct);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("bcdDevice %d.%d0\n", ((devc_desc->bcdDevice >> 8) & 0xF), ((devc_desc->bcdDevice >> 4) & 0xF));
printf("iManufacturer %d\n", devc_desc->iManufacturer);
printf("iProduct %d\n", devc_desc->iProduct);
printf("iSerialNumber %d\n", devc_desc->iSerialNumber);
#endif
printf("bNumConfigurations %d\n", devc_desc->bNumConfigurations);
#endif
}
void print_uvc_header_desc(const uint8_t *buff, uint8_t sub_class)
{
if (buff == NULL) {
return;
}
#ifdef CONFIG_UVC_PRINT_DESC
if (sub_class == VIDEO_SUBCLASS_CONTROL) {
const vc_interface_desc_t *desc = (const vc_interface_desc_t *) buff;
printf("\t*** Class-specific VC Interface Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
#endif
printf("\tbcdUVC %x\n", desc->bcdUVC);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\twTotalLength %u\n", desc->wTotalLength);
printf("\tdwClockFrequency %"PRIu32"\n", desc->dwClockFrequency);
printf("\tbFunctionProtocol %u\n", desc->bFunctionProtocol);
printf("\tbInCollection %u\n", desc->bInCollection);
printf("\tbaInterfaceNr %u\n", desc->baInterfaceNr);
#endif
} else if (sub_class == VIDEO_SUBCLASS_STREAMING) {
const vs_interface_desc_t *desc = (const vs_interface_desc_t *) buff;
printf("\t*** Class-specific VS Interface Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
#endif
printf("\tbNumFormats %x\n", desc->bNumFormats);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
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);
#endif
}
#endif
}
void parse_vs_format_mjpeg_desc(const uint8_t *buff, uint8_t *format_idx, uint8_t *frame_num)
{
if (buff == NULL) {
return;
}
const vs_format_desc_t *desc = (const vs_format_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** VS Format MJPEG Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
#endif
printf("\tbFormatIndex 0x%x\n", desc->bFormatIndex);
printf("\tbNumFrameDescriptors %u\n", desc->bNumFrameDescriptors);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbmFlags 0x%x\n", desc->bmFlags);
#endif
printf("\tbDefaultFrameIndex %u\n", desc->bDefaultFrameIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
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);
#endif
#endif
if (format_idx) {
*format_idx = desc->bFormatIndex;
}
if (frame_num) {
*frame_num = desc->bNumFrameDescriptors;
}
}
void parse_vs_frame_mjpeg_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t *width, uint16_t *heigh, uint8_t *interval_type, const uint32_t **pp_interval, uint32_t *dflt_interval)
{
if (buff == NULL) {
return;
}
const vs_frame_desc_t *desc = (const vs_frame_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** VS MJPEG Frame Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
#endif
printf("\tbFrameIndex 0x%x\n", desc->bFrameIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbmCapabilities 0x%x\n", desc->bmCapabilities);
#endif
printf("\twWidth %u\n", desc->wWidth);
printf("\twHeigh %u\n", desc->wHeigh);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tdwMinBitRate %"PRIu32"\n", desc->dwMinBitRate);
printf("\tdwMaxBitRate %"PRIu32"\n", desc->dwMaxBitRate);
printf("\tdwMaxVideoFrameBufSize %"PRIu32"\n", desc->dwMaxVideoFrameBufSize);
printf("\tdwDefaultFrameInterval %"PRIu32"\n", desc->dwDefaultFrameInterval);
printf("\tbFrameIntervalType %u\n", desc->bFrameIntervalType);
#endif
if (desc->bFrameIntervalType == 0) {
// Continuous Frame Intervals
printf("\tdwMinFrameInterval %"PRIu32"\n", desc->dwMinFrameInterval);
printf("\tdwMaxFrameInterval %"PRIu32"\n", desc->dwMaxFrameInterval);
printf("\tdwFrameIntervalStep %"PRIu32"\n", desc->dwFrameIntervalStep);
} else {
// Discrete Frame Intervals
size_t num_of_intervals = (desc->bLength - 26) / 4;
uint32_t *interval = (uint32_t *)&desc->dwFrameInterval;
for (int i = 0; i < num_of_intervals; ++i) {
printf("\tFrameInterval[%d] %"PRIu32"\n", i, interval[i]);
}
}
#endif
if (width) {
*width = desc->wWidth;
}
if (heigh) {
*heigh = desc->wHeigh;
}
if (frame_idx) {
*frame_idx = desc->bFrameIndex;
}
if (interval_type) {
*interval_type = desc->bFrameIntervalType;
}
if (pp_interval) {
*pp_interval = &(desc->dwFrameInterval);
}
if (dflt_interval) {
*dflt_interval = desc->dwDefaultFrameInterval;
}
}
void print_ep_desc(const uint8_t *buff)
{
if (buff == NULL) {
return;
}
#ifdef CONFIG_UVC_PRINT_DESC
const usb_ep_desc_t *ep_desc = (const usb_ep_desc_t *)buff;
const char *ep_type_str;
int type = ep_desc->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK;
switch (type) {
case USB_BM_ATTRIBUTES_XFER_CONTROL:
ep_type_str = "CTRL";
break;
case USB_BM_ATTRIBUTES_XFER_ISOC:
ep_type_str = "ISOC";
break;
case USB_BM_ATTRIBUTES_XFER_BULK:
ep_type_str = "BULK";
break;
case USB_BM_ATTRIBUTES_XFER_INT:
ep_type_str = "INT";
break;
default:
ep_type_str = NULL;
break;
}
printf("\t\t*** Endpoint descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\t\tbLength %d\n", ep_desc->bLength);
printf("\t\tbDescriptorType 0x%x\n", ep_desc->bDescriptorType);
#endif
printf("\t\tbEndpointAddress 0x%x\tEP %d %s\n", ep_desc->bEndpointAddress,
USB_EP_DESC_GET_EP_NUM(ep_desc),
USB_EP_DESC_GET_EP_DIR(ep_desc) ? "IN" : "OUT");
printf("\t\tbmAttributes 0x%x\t%s\n", ep_desc->bmAttributes, ep_type_str);
printf("\t\twMaxPacketSize %d\n", ep_desc->wMaxPacketSize);
printf("\t\tbInterval %d\n", ep_desc->bInterval);
#endif
}
void parse_ep_desc(const uint8_t *buff, uint16_t *ep_mps, uint8_t *ep_addr, uint8_t *ep_attr)
{
if (buff == NULL) {
return;
}
const usb_ep_desc_t *ep_desc = (const usb_ep_desc_t *)buff;
if (ep_addr) {
*ep_addr = ep_desc->bEndpointAddress;
}
if (ep_mps) {
*ep_mps = ep_desc->wMaxPacketSize;
}
if (ep_attr) {
*ep_attr = ep_desc->bmAttributes;
}
}
void print_intf_desc(const uint8_t *buff)
{
if (buff == NULL) {
return;
}
#ifdef CONFIG_UVC_PRINT_DESC
const usb_intf_desc_t *intf_desc = (const usb_intf_desc_t *)buff;
printf("\t*** Interface descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", intf_desc->bLength);
printf("\tbDescriptorType 0x%x\n", intf_desc->bDescriptorType);
#endif
printf("\tbInterfaceNumber %d\n", intf_desc->bInterfaceNumber);
printf("\tbAlternateSetting %d\n", intf_desc->bAlternateSetting);
printf("\tbNumEndpoints %d\n", intf_desc->bNumEndpoints);
printf("\tbInterfaceClass 0x%x (%s)\n", intf_desc->bInterfaceClass,
intf_desc->bInterfaceClass==USB_CLASS_VIDEO?"Video":
(intf_desc->bInterfaceClass==USB_CLASS_AUDIO?"Audio":"Unknown"));
printf("\tbInterfaceSubClass 0x%x\n", intf_desc->bInterfaceSubClass);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbInterfaceProtocol 0x%x\n", intf_desc->bInterfaceProtocol);
printf("\tiInterface %d\n", intf_desc->iInterface);
#endif
#endif
}
void print_assoc_desc(const uint8_t *buff)
{
if (buff == NULL) {
return;
}
#ifdef CONFIG_UVC_PRINT_DESC
const ifc_assoc_desc_t *assoc_desc = (const ifc_assoc_desc_t *)buff;
printf("*** Interface Association Descriptor: ");
if (assoc_desc->bFunctionClass == USB_CLASS_VIDEO) {
printf("Video");
} else if (assoc_desc->bFunctionClass == USB_CLASS_AUDIO) {
printf("Audio");
} else {
printf("Unknown");
}
printf(" ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("bLength %d\n", assoc_desc->bLength);
printf("bDescriptorType 0x%x\n", assoc_desc->bDescriptorType);
printf("bFirstInterface %d\n", assoc_desc->bFirstInterface);
printf("bInterfaceCount %d\n", assoc_desc->bInterfaceCount);
printf("bFunctionClass 0x%x\n", assoc_desc->bFunctionClass);
printf("bFunctionSubClass 0x%x\n", assoc_desc->bFunctionSubClass);
printf("bFunctionProtocol 0x%x\n", assoc_desc->bFunctionProtocol);
printf("iFunction %d\n", assoc_desc->iFunction);
#endif
#endif
}
void print_cfg_desc(const uint8_t *buff)
{
if (buff == NULL) {
return;
}
#ifdef CONFIG_UVC_PRINT_DESC
const usb_config_desc_t *cfg_desc = (const usb_config_desc_t *)buff;
printf("*** Configuration descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("bLength %d\n", cfg_desc->bLength);
printf("bDescriptorType 0x%x\n", cfg_desc->bDescriptorType);
#endif
printf("wTotalLength %d\n", cfg_desc->wTotalLength);
printf("bNumInterfaces %d\n", cfg_desc->bNumInterfaces);
printf("bConfigurationValue %d\n", cfg_desc->bConfigurationValue);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("iConfiguration %d\n", cfg_desc->iConfiguration);
printf("bmAttributes 0x%x\n", cfg_desc->bmAttributes);
printf("bMaxPower %dmA\n", cfg_desc->bMaxPower * 2);
#endif
#endif
}
void parse_ac_header_desc(const uint8_t *buff, uint16_t *bcdADC, uint8_t *intf_num)
{
if (buff == NULL) {
return;
}
const ac_interface_header_desc_t *desc = (const ac_interface_header_desc_t *)buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** Audio control header descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubtype 0x%x\n", desc->bDescriptorSubtype);
#endif
printf("\tbcdADC 0x%x\n", desc->bcdADC);
printf("\twTotalLength %d\n", desc->wTotalLength);
printf("\tbInCollection %d\n", desc->bInCollection);
if (desc->bInCollection) {
const uint8_t *p_intf = desc->baInterfaceNr;
for (int i = 0; i < desc->bInCollection; ++i) {
printf("\t\tInterface number[%d] = %d\n", i, p_intf[i]);
}
}
#endif
if (bcdADC) {
*bcdADC = desc->bcdADC;
}
if (intf_num) {
*intf_num = desc->bInCollection;
}
}
void parse_ac_input_desc(const uint8_t *buff, uint8_t *terminal_idx, uint16_t *terminal_type)
{
if (buff == NULL) {
return;
}
const ac_interface_input_terminal_desc_t *desc = (const ac_interface_input_terminal_desc_t *)buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** Audio control input terminal descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubtype 0x%x\n", desc->bDescriptorSubtype);
#endif
printf("\tbTerminalID %d\n", desc->bTerminalID);
printf("\twTerminalType 0x%x\n", desc->wTerminalType);
printf("\tbAssocTerminal %d\n", desc->bAssocTerminal);
printf("\tbNrChannels %d\n", desc->bNrChannels);
printf("\twChannelConfig 0x%04x\n", desc->wChannelConfig);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tiChannelNames %d\n", desc->iChannelNames);
printf("\tiTerminal %d\n", desc->iTerminal);
#endif
#endif
if (terminal_idx) {
*terminal_idx = desc->bTerminalID;
}
if (terminal_type) {
*terminal_type = desc->wTerminalType;
}
}
void parse_ac_output_desc(const uint8_t *buff, uint8_t *terminal_idx, uint16_t *terminal_type)
{
if (buff == NULL) {
return;
}
const ac_interface_output_terminal_desc_t *desc = (const ac_interface_output_terminal_desc_t *)buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** Audio control output terminal descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubtype 0x%x\n", desc->bDescriptorSubtype);
#endif
printf("\tbTerminalID %d\n", desc->bTerminalID);
printf("\twTerminalType 0x%x\n", desc->wTerminalType);
printf("\tbAssocTerminal %d\n", desc->bAssocTerminal);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbSourceID %d\n", desc->bSourceID);
printf("\tiTerminal %d\n", desc->iTerminal);
#endif
#endif
if (terminal_idx) {
*terminal_idx = desc->bTerminalID;
}
if (terminal_type) {
*terminal_type = desc->wTerminalType;
}
}
void parse_ac_feature_desc(const uint8_t *buff, uint8_t *source_idx, uint8_t *feature_unit_idx, uint8_t *volume_ch, uint8_t *mute_ch)
{
if (buff == NULL) {
return;
}
const ac_interface_feature_unit_desc_t *desc = (const ac_interface_feature_unit_desc_t *)buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** Audio control feature unit descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubtype 0x%x\n", desc->bDescriptorSubtype);
#endif
printf("\tbUnitID %d\n", desc->bUnitID);
printf("\tbSourceID %d\n", desc->bSourceID);
printf("\tbControlSize %d\n", desc->bControlSize);
for (size_t i = 0; i < (desc->bLength-7)/desc->bControlSize; i += desc->bControlSize) {
printf("\tbmaControls[ch%d] 0x%x\n", i, desc->bmaControls[i]);
}
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tiFeature %d\n", desc->iFeature);
#endif
#endif
if (feature_unit_idx) {
*feature_unit_idx = desc->bUnitID;
}
if (source_idx) {
*source_idx = desc->bSourceID;
}
uint8_t ch_num = 0;
for (size_t i = 0; i < (desc->bLength-7)/desc->bControlSize; i += desc->bControlSize) {
if ((desc->bmaControls[i] & AUDIO_FEATURE_CONTROL_VOLUME) && volume_ch) {
*volume_ch = *volume_ch | (1 << ch_num);
}
if ((desc->bmaControls[i] & AUDIO_FEATURE_CONTROL_MUTE) && mute_ch) {
*mute_ch = *mute_ch | (1 << ch_num);
}
ch_num++;
}
}
void parse_as_general_desc(const uint8_t *buff, uint8_t *source_idx, uint16_t *format_tag)
{
if (buff == NULL) {
return;
}
const as_interface_header_desc_t *desc = (const as_interface_header_desc_t *)buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** Audio stream general descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubtype 0x%x\n", desc->bDescriptorSubtype);
#endif
printf("\tbTerminalLink %d\n", desc->bTerminalLink);
printf("\tbDelay %d\n", desc->bDelay);
printf("\twFormatTag %d\n", desc->wFormatTag);
#endif
if (source_idx) {
*source_idx = desc->bTerminalLink;
}
if (format_tag) {
*format_tag = desc->wFormatTag;
}
}
void parse_as_type_desc(const uint8_t *buff, uint8_t *channel_num, uint8_t *bit_resolution, uint8_t *freq_type, const uint8_t **pp_samfreq)
{
if (buff == NULL) {
return;
}
const as_interface_type_I_format_desc_t *desc = (const as_interface_type_I_format_desc_t *)buff;
#ifdef CONFIG_UVC_PRINT_DESC
printf("\t*** Audio control header descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength %d\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubtype 0x%x\n", desc->bDescriptorSubtype);
#endif
printf("\tbFormatType %d\n", desc->bFormatType);
printf("\tbNrChannels %d\n", desc->bNrChannels);
printf("\tbSubframeSize %d\n", desc->bSubframeSize);
printf("\tbBitResolution %d\n", desc->bBitResolution);
printf("\tbSamFreqType %d\n", desc->bSamFreqType);
if (desc->bSamFreqType == 0) {
// Continuous Frame Intervals
const uint8_t *p_samfreq = desc->tSamFreq;
uint32_t min_samfreq = (p_samfreq[2] << 16) + (p_samfreq[1] << 8) + p_samfreq[0];
uint32_t max_samfreq = (p_samfreq[5] << 16) + (p_samfreq[4] << 8) + p_samfreq[3];
printf("\ttLowerSamFreq %"PRIu32"\n", min_samfreq);
printf("\ttUpperSamFreq %"PRIu32"\n", max_samfreq);
} else {
const uint8_t *p_samfreq = desc->tSamFreq;
for (int i = 0; i < desc->bSamFreqType; ++i) {
printf("\ttSamFreq[%d] %"PRIu32"\n", i, (uint32_t)((p_samfreq[3 * i + 2] << 16) + (p_samfreq[3 * i + 1] << 8) + p_samfreq[3 * i]));
}
}
#endif
if (pp_samfreq) {
*pp_samfreq = desc->tSamFreq;
}
if (channel_num) {
*channel_num = desc->bNrChannels;
}
if (bit_resolution) {
*bit_resolution = desc->bBitResolution;
}
if (freq_type) {
*freq_type = desc->bSamFreqType;
}
}

View File

@ -0,0 +1,539 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <sys/queue.h>
#include "esp_err.h"
#include "usb_private.h"
#include "usb/usb_types_ch9.h"
// ------------------------------------------------- Macros & Types ----------------------------------------------------
// ----------------------- States --------------------------
/**
* @brief States of the HCD port
*
* @note The port can be thought of as an abstraction of the Root Hub that contains
* a single port.
* @note These states roughly match the port states outlined in 11.5.1 of the
* USB2.0 specification.
*/
typedef enum {
HCD_PORT_STATE_NOT_POWERED, /**< The port is not powered */
HCD_PORT_STATE_DISCONNECTED, /**< The port is powered but no device is connected */
HCD_PORT_STATE_DISABLED, /**< A device has connected to the port but has not been reset. SOF/keep alive are not being sent */
HCD_PORT_STATE_RESETTING, /**< The port is issuing a reset condition */
HCD_PORT_STATE_SUSPENDED, /**< The port has been suspended. */
HCD_PORT_STATE_RESUMING, /**< The port is issuing a resume condition */
HCD_PORT_STATE_ENABLED, /**< The port has been enabled. SOF/keep alive are being sent */
HCD_PORT_STATE_RECOVERY, /**< Port needs to be recovered from a fatal error (port error, overcurrent, or sudden disconnection) */
} hcd_port_state_t;
/**
* @brief States of an HCD pipe
*
* Active:
* - Pipe is able to transmit data. URBs can be enqueued.
* - Even if pipe has no URBs enqueued, it can still be in the active state.
* Halted:
* - An error has occurred on the pipe. URBs will no longer be executed.
* - Halt should be cleared using the HCD_PIPE_CMD_CLEAR command
*/
typedef enum {
HCD_PIPE_STATE_ACTIVE, /**< The pipe is active */
HCD_PIPE_STATE_HALTED, /**< The pipe is halted */
} hcd_pipe_state_t;
// ----------------------- Events --------------------------
/**
* @brief HCD port events
*
* On receiving a port event, hcd_port_handle_event() should be called to handle that event
*/
typedef enum {
HCD_PORT_EVENT_NONE, /**< No event has occurred. Or the previous event is no longer valid */
HCD_PORT_EVENT_CONNECTION, /**< A device has been connected to the port */
HCD_PORT_EVENT_DISCONNECTION, /**< A device disconnection has been detected */
HCD_PORT_EVENT_ERROR, /**< A port error has been detected. Port is now HCD_PORT_STATE_RECOVERY */
HCD_PORT_EVENT_OVERCURRENT, /**< Overcurrent detected on the port. Port is now HCD_PORT_STATE_RECOVERY */
} hcd_port_event_t;
/**
* @brief HCD pipe events
*
* @note Pipe error events will put the pipe into the HCD_PIPE_STATE_HALTED state
*/
typedef enum {
HCD_PIPE_EVENT_NONE, /**< The pipe has no events (used to indicate no events when polling) */
HCD_PIPE_EVENT_URB_DONE, /**< The pipe has completed an URB. The URB can be dequeued */
HCD_PIPE_EVENT_ERROR_XFER, /**< Excessive (three consecutive) transaction errors (e.g., no ACK, bad CRC etc) */
HCD_PIPE_EVENT_ERROR_URB_NOT_AVAIL, /**< URB was not available */
HCD_PIPE_EVENT_ERROR_OVERFLOW, /**< Received more data than requested. Usually a Packet babble error
(i.e., an IN packet has exceeded the endpoint's MPS) */
HCD_PIPE_EVENT_ERROR_STALL, /**< Pipe received a STALL response received */
} hcd_pipe_event_t;
// ---------------------- Commands -------------------------
/**
* @brief HCD port commands
*/
typedef enum {
HCD_PORT_CMD_POWER_ON, /**< Power ON the port */
HCD_PORT_CMD_POWER_OFF, /**< Power OFF the port. If the port is enabled, this will cause a HCD_PORT_EVENT_SUDDEN_DISCONN event.
If the port is disabled, this will cause a HCD_PORT_EVENT_DISCONNECTION event. */
HCD_PORT_CMD_RESET, /**< Issue a reset on the port */
HCD_PORT_CMD_SUSPEND, /**< Suspend the port. All pipes must be halted */
HCD_PORT_CMD_RESUME, /**< Resume the port */
HCD_PORT_CMD_DISABLE, /**< Disable the port (stops the SOFs or keep alive). All pipes must be halted. */
} hcd_port_cmd_t;
/**
* @brief HCD pipe commands
*
* The pipe commands represent the list of pipe manipulations outlined in 10.5.2.2. of USB2.0 specification.
*/
typedef enum {
HCD_PIPE_CMD_HALT, /**< Halt an active pipe. The currently executing URB will be canceled. Enqueued URBs are left untouched */
HCD_PIPE_CMD_FLUSH, /**< Can only be called when halted. Will cause all enqueued URBs to be canceled */
HCD_PIPE_CMD_CLEAR, /**< Causes a halted pipe to become active again. Any enqueued URBs will being executing.*/
} hcd_pipe_cmd_t;
// -------------------- Object Types -----------------------
/**
* @brief Port handle type
*/
typedef void *hcd_port_handle_t;
/**
* @brief Pipe handle type
*/
typedef void *hcd_pipe_handle_t;
/**
* @brief Port event callback type
*
* This callback is run when a port event occurs
*/
typedef bool (*hcd_port_callback_t)(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr);
/**
* @brief Pipe event callback
*
* This callback is run when a pipe event occurs
*/
typedef bool (*hcd_pipe_callback_t)(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr);
typedef enum {
HCD_PORT_FIFO_BIAS_BALANCED, /**< Balanced FIFO sizing for RX, Non-periodic TX, and periodic TX */
HCD_PORT_FIFO_BIAS_RX, /**< Bias towards a large RX FIFO */
HCD_PORT_FIFO_BIAS_PTX, /**< Bias towards periodic TX FIFO */
} hcd_port_fifo_bias_t;
/**
* @brief HCD configuration structure
*/
typedef struct {
int intr_flags; /**< Interrupt flags for HCD interrupt */
} hcd_config_t;
/**
* @brief Port configuration structure
*/
typedef struct {
hcd_port_fifo_bias_t fifo_bias; /**< HCD port internal FIFO biasing */
hcd_port_callback_t callback; /**< HCD port event callback */
void *callback_arg; /**< User argument for HCD port callback */
void *context; /**< Context variable used to associate the port with upper layer object */
} hcd_port_config_t;
/**
* @brief Pipe configuration structure
*
* @note The callback can be set to NULL if no callback is required (e.g., using HCD in a polling manner).
*/
typedef struct {
hcd_pipe_callback_t callback; /**< HCD pipe event ISR callback */
void *callback_arg; /**< User argument for HCD pipe callback */
void *context; /**< Context variable used to associate the pipe with upper layer object */
const usb_ep_desc_t *ep_desc; /**< Pointer to endpoint descriptor of the pipe */
usb_speed_t dev_speed; /**< Speed of the device */
uint8_t dev_addr; /**< Device address of the pipe */
} hcd_pipe_config_t;
// --------------------------------------------- Host Controller Driver ------------------------------------------------
/**
* @brief Installs the Host Controller Driver
*
* - Allocates memory and interrupt resources for the HCD and underlying ports
*
* @note This function must be called before any other HCD function is called
* @note Before calling this function, the Host Controller must already be un-clock gated and reset. The USB PHY
* (internal or external, and associated GPIOs) must already be configured.
*
* @param config HCD configuration
* @retval ESP_OK: HCD successfully installed
* @retval ESP_ERR_NO_MEM: Insufficient memory
* @retval ESP_ERR_INVALID_STATE: HCD is already installed
* @retval ESP_ERR_NOT_FOUND: HCD could not allocate interrupt
* @retval ESP_ERR_INVALID_ARG: Arguments are invalid
*/
esp_err_t hcd_install(const hcd_config_t *config);
/**
* @brief Uninstalls the HCD
*
* Before uninstalling the HCD, the following conditions should be met:
* - All ports must be uninitialized, all pipes freed
*
* @note This function will simply free the resources used by the HCD. The underlying Host Controller and USB PHY will
* not be disabled.
*
* @retval ESP_OK: HCD successfully uninstalled
* @retval ESP_ERR_INVALID_STATE: HCD is not in the right condition to be uninstalled
*/
esp_err_t hcd_uninstall(void);
// ---------------------------------------------------- HCD Port -------------------------------------------------------
/**
* @brief Initialize a particular port of the HCD
*
* After a port is initialized, it will be put into the HCD_PORT_STATE_NOT_POWERED state.
*
* @note The host controller only has one port, thus the only valid port_number is 1
*
* @param[in] port_number Port number
* @param[in] port_config Port configuration
* @param[out] port_hdl Port handle
* @retval ESP_OK: Port enabled
* @retval ESP_ERR_NO_MEM: Insufficient memory
* @retval ESP_ERR_INVALID_STATE: The port is already enabled
* @retval ESP_ERR_NOT_FOUND: Port number not found
* @retval ESP_ERR_INVALID_ARG: Arguments are invalid
*/
esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, hcd_port_handle_t *port_hdl);
/**
* @brief Deinitialize a particular port
*
* The port must be placed in the HCD_PORT_STATE_NOT_POWERED or HCD_PORT_STATE_RECOVERY state before it can be
* deinitialized.
*
* @param port_hdl Port handle
* @retval ESP_OK: Port disabled
* @retval ESP_ERR_INVALID_STATE: The port is not in a condition to be disabled (not unpowered)
*/
esp_err_t hcd_port_deinit(hcd_port_handle_t port_hdl);
/**
* @brief Execute a port command
*
* Call this function to manipulate a port (e.g., powering it ON, sending a reset etc). The following conditions
* must be met when calling this function:
* - The port is in the correct state for the command (e.g., port must be suspended in order to use the resume command)
* - The port does not have any pending events
*
* @note This function is internally protected by a mutex. If multiple threads call this function, this function will
* can block.
* @note The function can block
* @note For some of the commands that involve a blocking delay (e.g., reset and resume), if the port's state changes
* unexpectedly (e.g., a disconnect during a resume), this function will return ESP_ERR_INVALID_RESPONSE.
*
* @param port_hdl Port handle
* @param command Command for the HCD port
* @retval ESP_OK: Command executed successfully
* @retval ESP_ERR_INVALID_STATE: Conditions have not been met to call this function
* @retval ESP_ERR_INVALID_RESPONSE: The command is no longer valid due to a change in the port's state
*/
esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command);
/**
* @brief Get the port's current state
*
* @param port_hdl Port handle
* @return hcd_port_state_t Current port state
*/
hcd_port_state_t hcd_port_get_state(hcd_port_handle_t port_hdl);
/**
* @brief Get the speed of the port
*
* The speed of the port is determined by the speed of the device connected to it.
*
* @note This function is only valid after a device directly to the port and has been reset
*
* @param[in port_hdl Port handle
* @param[out] speed Speed of the port
* @retval ESP_OK Device speed obtained
* @retval ESP_ERR_INVALID_STATE: No valid device connected to the port
* @retval ESP_ERR_INVALID_ARG: Invalid arguments
*/
esp_err_t hcd_port_get_speed(hcd_port_handle_t port_hdl, usb_speed_t *speed);
/**
* @brief Handle a ports event
*
* When an port event occurs (as indicated by a callback), this function should be called the handle this event. A
* port's event should always be handled before attempting to execute a port command. Note that is actually handled
* may be different than the event reflected in the callback.
*
* If the port has no events, this function will return HCD_PORT_EVENT_NONE.
*
* @note If callbacks are not used, this function can also be used in a polling manner to repeatedly check for and
* handle a port's events.
* @note This function is internally protected by a mutex. If multiple threads call this function, this function will
* can block.
*
* @param port_hdl Port handle
* @return hcd_port_event_t The port event that was handled
*/
hcd_port_event_t hcd_port_handle_event(hcd_port_handle_t port_hdl);
/**
* @brief Recover a port after a fatal error has occurred on it
*
* The port must be in the HCD_PORT_STATE_RECOVERY state to be called. Recovering the port will involve issuing a soft
* reset on the underlying USB controller. The port will be returned to the HCD_PORT_STATE_NOT_POWERED state.
*
* @param port_hdl Port handle
* @retval ESP_OK Port recovered successfully
* @retval ESP_ERR_INVALID_STATE Port is not in the HCD_PORT_STATE_RECOVERY state
*/
esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl);
/**
* @brief Get the context variable of a port
*
* @param port_hdl Port handle
* @return void* Context variable
*/
void *hcd_port_get_context(hcd_port_handle_t port_hdl);
/**
* @brief Set the bias of the HCD port's internal FIFO
*
* @note This function can only be called when the following conditions are met:
* - Port is initialized
* - Port does not have any pending events
* - Port does not have any allocated pipes
*
* @param port_hdl Port handle
* @param bias Fifo bias
* @retval ESP_OK FIFO sizing successfully set
* @retval ESP_ERR_INVALID_STATE Incorrect state for FIFO sizes to be set
*/
esp_err_t hcd_port_set_fifo_bias(hcd_port_handle_t port_hdl, hcd_port_fifo_bias_t bias);
// --------------------------------------------------- HCD Pipes -------------------------------------------------------
/**
* @brief Allocate a pipe
*
* When allocating a pipe, the HCD will assess whether there are sufficient resources (i.e., bus time, and controller
* channels). If sufficient, the pipe will be allocated.
*
* @note The host port must be in the enabled state before a pipe can be allocated
*
* @param[in] port_hdl Handle of the port this pipe will be routed through
* @param[in] pipe_config Pipe configuration
* @param[out] pipe_hdl Pipe handle
*
* @retval ESP_OK: Pipe successfully allocated
* @retval ESP_ERR_NO_MEM: Insufficient memory
* @retval ESP_ERR_INVALID_ARG: Arguments are invalid
* @retval ESP_ERR_INVALID_STATE: Host port is not in the correct state to allocate a pipe
* @retval ESP_ERR_NOT_SUPPORTED: The pipe's configuration cannot be supported
*/
esp_err_t hcd_pipe_alloc(hcd_port_handle_t port_hdl, const hcd_pipe_config_t *pipe_config, hcd_pipe_handle_t *pipe_hdl);
/**
* @brief Free a pipe
*
* Frees the resources used by an HCD pipe. The pipe's handle should be discarded after calling this function. The pipe
* must be in following condition before it can be freed:
* - All URBs have been dequeued
*
* @param pipe_hdl Pipe handle
*
* @retval ESP_OK: Pipe successfully freed
* @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be freed
*/
esp_err_t hcd_pipe_free(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Update a pipe's maximum packet size
*
* This function is intended to be called on default pipes during enumeration in order to update the pipe's maximum
* packet size. This function can only be called on a pipe that has met the following conditions:
* - Pipe is not current processing a command
* - Pipe does not have any enqueued URBs
* - Port cannot be resetting
*
* @param pipe_hdl Pipe handle
* @param mps New Maximum Packet Size
*
* @retval ESP_OK: Pipe successfully updated
* @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be updated
*/
esp_err_t hcd_pipe_update_mps(hcd_pipe_handle_t pipe_hdl, int mps);
/**
* @brief Update a pipe's device address
*
* This function is intended to be called on default pipes during enumeration in order to update the pipe's device
* address. This function can only be called on a pipe that has met the following conditions:
* - Pipe is not current processing a command
* - Pipe does not have any enqueued URBs
* - Port cannot be resetting
*
* @param pipe_hdl Pipe handle
* @param dev_addr New device address
*
* @retval ESP_OK: Pipe successfully updated
* @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be updated
*/
esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr);
/**
* @brief Update a pipe's callback
*
* This function is intended to be called on default pipes at the end of enumeration to switch to a callback that
* handles the completion of regular control transfer.
* - Pipe is not current processing a command
* - Pipe does not have any enqueued URBs
* - Port cannot be resetting
*
* @param pipe_hdl Pipe handle
* @param callback Callback
* @param user_arg Callback argument
* @return esp_err_t
*/
esp_err_t hcd_pipe_update_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_callback_t callback, void *user_arg);
/**
* @brief Make a pipe persist through a run time reset
*
* Normally when a HCD_PORT_CMD_RESET is called, all pipes should already have been freed. However There may be cases
* (such as during enumeration) when a pipe must persist through a reset. This function will mark a pipe as
* persistent allowing it to survive a reset. When HCD_PORT_CMD_RESET is called, the pipe can continue to be used after
* the reset.
*
* @param pipe_hdl Pipe handle
* @retval ESP_OK: Pipe successfully marked as persistent
* @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be made persistent
*/
esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Get the context variable of a pipe from its handle
*
* @param pipe_hdl Pipe handle
* @return void* Context variable
*/
void *hcd_pipe_get_context(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Get the current state of the pipe
*
* @param pipe_hdl Pipe handle
* @return hcd_pipe_state_t Current state of the pipe
*/
hcd_pipe_state_t hcd_pipe_get_state(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Get the number of in-flight URBs in the pipe
*
* Returns the current number of URBs that have been enqueued (via hcd_urb_enqueue()) and have yet to be dequeued (via
* hcd_urb_dequeue()).
*
* @param pipe_hdl Pipe handle
* @return Number of in-flight URBs
*/
unsigned int hcd_pipe_get_num_urbs(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Execute a command on a particular pipe
*
* Pipe commands allow a pipe to be manipulated (such as clearing a halt, retiring all URBs etc)
*
* @note This function can block
*
* @param pipe_hdl Pipe handle
* @param command Pipe command
* @retval ESP_OK: Command executed successfully
* @retval ESP_ERR_INVALID_STATE: The pipe is not in the correct state/condition too execute the command
*/
esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command);
/**
* @brief Get the last event that occurred on a pipe
*
* This function allows a pipe to be polled for events (i.e., when callbacks are not used). Once an event has been
* obtained, this function reset the last event of the pipe to HCD_PIPE_EVENT_NONE.
*
* @param pipe_hdl Pipe handle
* @return hcd_pipe_event_t Last pipe event to occur
*/
hcd_pipe_event_t hcd_pipe_get_event(hcd_pipe_handle_t pipe_hdl);
// ---------------------------------------------------- HCD URBs -------------------------------------------------------
/**
* @brief Enqueue an URB to a particular pipe
*
* The following conditions must be met before an URB can be enqueued:
* - The URB is properly initialized (data buffer and transfer length are set)
* - The URB must not already be enqueued
* - The pipe must be in the HCD_PIPE_STATE_ACTIVE state
* - The pipe cannot be executing a command
*
* @param pipe_hdl Pipe handle
* @param urb URB to enqueue
* @retval ESP_OK: URB enqueued successfully
* @retval ESP_ERR_INVALID_STATE: Conditions not met to enqueue URB
*/
esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb);
/**
* @brief Dequeue an URB from a particular pipe
*
* This function should be called on a pipe after a pipe receives a HCD_PIPE_EVENT_URB_DONE event. If a pipe has
* multiple URBs that can be dequeued, this function should be called repeatedly until all URBs are dequeued. If a pipe
* has no more URBs to dequeue, this function will return NULL.
*
* @param pipe_hdl Pipe handle
* @return urb_t* Dequeued URB, or NULL if no more URBs to dequeue
*/
urb_t *hcd_urb_dequeue(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Abort an enqueued URB
*
* This function will attempt to abort an URB that is already enqueued. If the URB has yet to be executed, it will be
* "canceled" and can then be dequeued. If the URB is currently in-flight or has already completed, the URB will not be
* affected by this function.
*
* @param urb URB to abort
* @retval ESP_OK: URB successfully aborted, or was not affected by this function
* @retval ESP_ERR_INVALID_STATE: URB was never enqueued
*/
esp_err_t hcd_urb_abort(urb_t *urb);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,88 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include "esp_err.h"
#include "usb_private.h"
#include "usbh.h"
#ifdef __cplusplus
extern "C" {
#endif
// ------------------------------------------------------ Types --------------------------------------------------------
/**
* @brief Hub driver configuration
*/
typedef struct {
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
void *proc_req_cb_arg; /**< Processing request callback argument */
} hub_config_t;
// ---------------------------------------------- Hub Driver Functions -------------------------------------------------
/**
* @brief Install Hub driver
*
* Entry:
* - USBH must already be installed
* Exit:
* - Install Hub driver memory resources
* - Initializes the HCD root port
*
* @param[in] hub_config Hub driver configuration
* @return esp_err_t
*/
esp_err_t hub_install(hub_config_t *hub_config);
/**
* @brief Uninstall Hub driver
*
* This must be called before uninstalling the USBH
* Entry:
* - Must have stopped the root port
* Exit:
* - HCD root port deinitialized
*
* @return esp_err_t
*/
esp_err_t hub_uninstall(void);
/**
* @brief Start the Hub driver's root port
*
* This will power the root port ON
*
* @return esp_err_t
*/
esp_err_t hub_root_start(void);
/**
* @brief Stops the Hub driver's root port
*
* This will power OFF the root port
*
* @return esp_err_t
*/
esp_err_t hub_root_stop(void);
/**
* @brief Hub driver's processing function
*
* Hub driver handling function that must be called repeatdly to process the Hub driver's events. If blocking, the
* caller can block on the notification callback of source USB_PROC_REQ_SOURCE_HUB to run this function.
*
* @return esp_err_t
*/
esp_err_t hub_process(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,80 @@
/*
* Automatically generated file. DO NOT EDIT.
* Espressif IoT Development Framework (ESP-IDF) #define CONFIGuration Header
*/
#pragma once
#define CONFIG_SAMPLE_PROC_TASK_STACK_SIZE 3072
#define CONFIG_SAMPLE_PROC_TASK_PRIORITY 2
#define CONFIG_SAMPLE_PROC_TASK_CORE 0
#define CONFIG_USB_PROC_TASK_STACK_SIZE 3072
#define CONFIG_USB_PROC_TASK_PRIORITY 5
#define CONFIG_USB_PROC_TASK_CORE 1
#define CONFIG_NUM_ISOC_STREAM_URBS 3
#define CONFIG_NUM_PACKETS_PER_URB_URB 4
#define CONFIG_UVC_GET_CONFIG_DESC 1
#define USB_STREAM_VER_MAJOR (1) //version of USB Stream check .yml
#define USB_STREAM_VER_MINOR (0)
#define USB_STREAM_VER_PATCH (4)
#define CONFIG_CTRL_TRANSFER_DATA_MAX_BYTES 1024 //Max data length assumed in control transfer
#define CONFIG_NUM_BULK_STREAM_URBS 2 //Number of bulk stream URBS created for continuous enqueue
#define CONFIG_NUM_BULK_BYTES_PER_URB 2048 //Required transfer bytes of each URB, check
#define CONFIG_NUM_ISOC_UVC_URBS 3 //Number of isochronous stream URBS created for continuous enqueue
#define CONFIG_NUM_PACKETS_PER_URB 4 //Number of packets in each isochronous stream URB
#define CONFIG_USB_WAITING_AFTER_CONN_MS 50 //Waiting n ms for usb device ready after connection
#define CONFIG_NUM_ISOC_SPK_URBS 6 //Number of isochronous stream URBS created for continuous enqueue
#define CONFIG_NUM_ISOC_MIC_URBS 3 //Number of isochronous stream URBS created for continuous enqueue
#define CONFIG_UAC_MIC_CB_MIN_MS_DEFAULT 16 //Default min ms for mic callback
#define CONFIG_UAC_SPK_ST_MAX_MS_DEFAULT 16 //Default max ms for speaker stream
#define CONFIG_UAC_SPK_VOLUME_LEVEL_DEFAULT 80 //Default volume level for speaker
#define CONFIG_UAC_MIC_VOLUME_LEVEL_DEFAULT 80 //Default volume level for mic
#define CONFIG_UAC_MIC_PACKET_COMPENSATION 1 //padding data if mic packet loss
#define CONFIG_UAC_SPK_PACKET_COMPENSATION 1 //padding zero if speaker buffer empty
#define CONFIG_UAC_SPK_PACKET_COMPENSATION_SIZE_MS 10 //padding n MS zero if speaker buffer empty
#define CONFIG_UAC_SPK_PACKET_COMPENSATION_TIMEOUT_MS 1000 //padding n MS zero if speaker buffer empty
#define CONFIG_USB_PRE_ALLOC_CTRL_TRANSFER_URB 1 //Pre-allocate URB for control transfer
#define CONFIG_UVC_GET_DEVICE_DESC 1
#define CONFIG_UVC_PRINT_DESC 1
#define CONFIG_USB_ENUM_FAILED_RETRY 1
#define CONFIG_USB_ENUM_FAILED_RETRY_COUNT 10
#define CONFIG_USB_ENUM_FAILED_RETRY_DELAY_MS 200
#define CONFIG_UVC_DROP_OVERFLOW_FRAME 1
#define CONFIG_UVC_PRINT_PROBE_RESULT 1
#define CONFIG_UVC_CHECK_BULK_JPEG_HEADER 1
#define CONFIG_CTRL_TRANSFER_DATA_MAX_BYTES 1024
#define CONFIG_UVC_GET_DEVICE_DESC 1
#define CONFIG_UVC_GET_CONFIG_DESC 1
#define CONFIG_UVC_PRINT_DESC 1
#define CONFIG_USB_PRE_ALLOC_CTRL_TRANSFER_URB 1
#define CONFIG_USB_PROC_TASK_PRIORITY 5
#define CONFIG_USB_PROC_TASK_CORE 1
#define CONFIG_USB_PROC_TASK_STACK_SIZE 3072
#define CONFIG_USB_WAITING_AFTER_CONN_MS 50
#define CONFIG_USB_ENUM_FAILED_RETRY 1
#define CONFIG_USB_ENUM_FAILED_RETRY_COUNT 10
#define CONFIG_USB_ENUM_FAILED_RETRY_DELAY_MS 200
#define CONFIG_SAMPLE_PROC_TASK_PRIORITY 2
#define CONFIG_SAMPLE_PROC_TASK_CORE 0
#define CONFIG_SAMPLE_PROC_TASK_STACK_SIZE 3072
#define CONFIG_UVC_PRINT_PROBE_RESULT 1
#define CONFIG_UVC_CHECK_BULK_JPEG_HEADER 1
#define CONFIG_UVC_DROP_OVERFLOW_FRAME 1
#define CONFIG_NUM_BULK_STREAM_URBS 2
#define CONFIG_NUM_BULK_BYTES_PER_URB 2048
#define CONFIG_NUM_ISOC_UVC_URBS 3
#define CONFIG_NUM_PACKETS_PER_URB 4
#define CONFIG_NUM_ISOC_SPK_URBS 6
#define CONFIG_NUM_ISOC_MIC_URBS 3
#define CONFIG_UAC_MIC_CB_MIN_MS_DEFAULT 16
#define CONFIG_UAC_SPK_ST_MAX_MS_DEFAULT 16
#define CONFIG_UAC_MIC_PACKET_COMPENSATION 1
#define CONFIG_UAC_SPK_PACKET_COMPENSATION 1
#define CONFIG_UAC_SPK_PACKET_COMPENSATION_TIMEOUT_MS 1000
#define CONFIG_UAC_SPK_PACKET_COMPENSATION_SIZE_MS 10
#define CONFIG_UAC_SPK_VOLUME_LEVEL_DEFAULT 80
#define CONFIG_UAC_MIC_VOLUME_LEVEL_DEFAULT 80
#define CONFIG_USB_CTRL_XFER_TIMEOUT_MS 1000

View File

@ -0,0 +1,195 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
/** Handle on an open UVC device.
* only one device supported
*/
typedef void* uvc_device_handle_t;
/** UVC error types, based on libusb errors
* @ingroup diag
*/
typedef enum uvc_error {
/** Success (no error) */
UVC_SUCCESS = 0,
/** Input/output error */
UVC_ERROR_IO = -1,
/** Invalid parameter */
UVC_ERROR_INVALID_PARAM = -2,
/** Access denied */
UVC_ERROR_ACCESS = -3,
/** No such device */
UVC_ERROR_NO_DEVICE = -4,
/** Entity not found */
UVC_ERROR_NOT_FOUND = -5,
/** Resource busy */
UVC_ERROR_BUSY = -6,
/** Operation timed out */
UVC_ERROR_TIMEOUT = -7,
/** Overflow */
UVC_ERROR_OVERFLOW = -8,
/** Pipe error */
UVC_ERROR_PIPE = -9,
/** System call interrupted */
UVC_ERROR_INTERRUPTED = -10,
/** Insufficient memory */
UVC_ERROR_NO_MEM = -11,
/** Operation not supported */
UVC_ERROR_NOT_SUPPORTED = -12,
/** Device is not UVC-compliant */
UVC_ERROR_INVALID_DEVICE = -50,
/** Mode not supported */
UVC_ERROR_INVALID_MODE = -51,
/** Resource has a callback (can't use polling and async) */
UVC_ERROR_CALLBACK_EXISTS = -52,
/** Undefined error */
UVC_ERROR_OTHER = -99
} uvc_error_t;
/** Color coding of stream, transport-independent
* @ingroup streaming
*/
enum uvc_frame_format {
UVC_FRAME_FORMAT_UNKNOWN = 0,
/** Any supported format */
UVC_FRAME_FORMAT_ANY = 0,
UVC_FRAME_FORMAT_UNCOMPRESSED,
UVC_FRAME_FORMAT_COMPRESSED,
/** YUYV/YUV2/YUV422: YUV encoding with one luminance value per pixel and
* one UV (chrominance) pair for every two pixels.
*/
UVC_FRAME_FORMAT_YUYV,
UVC_FRAME_FORMAT_UYVY,
/** 24-bit RGB */
UVC_FRAME_FORMAT_RGB,
UVC_FRAME_FORMAT_BGR,
/** Motion-JPEG (or JPEG) encoded images */
UVC_FRAME_FORMAT_MJPEG,
UVC_FRAME_FORMAT_H264,
/** Greyscale images */
UVC_FRAME_FORMAT_GRAY8,
UVC_FRAME_FORMAT_GRAY16,
/* Raw colour mosaic images */
UVC_FRAME_FORMAT_BY8,
UVC_FRAME_FORMAT_BA81,
UVC_FRAME_FORMAT_SGRBG8,
UVC_FRAME_FORMAT_SGBRG8,
UVC_FRAME_FORMAT_SRGGB8,
UVC_FRAME_FORMAT_SBGGR8,
/** YUV420: NV12 */
UVC_FRAME_FORMAT_NV12,
/** Number of formats understood */
UVC_FRAME_FORMAT_COUNT,
};
/** Converts an unaligned four-byte little-endian integer into an int32 */
#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
/** Converts an unaligned two-byte little-endian integer into an int16 */
#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8))
/** Converts an int16 into an unaligned two-byte little-endian integer */
#define SHORT_TO_SW(s, p) \
(p)[0] = (s); \
(p)[1] = (s) >> 8;
/** Converts an int32 into an unaligned four-byte little-endian integer */
#define INT_TO_DW(i, p) \
(p)[0] = (i); \
(p)[1] = (i) >> 8; \
(p)[2] = (i) >> 16; \
(p)[3] = (i) >> 24;
/** Streaming mode, includes all information needed to select stream
* @ingroup streaming
*/
typedef struct uvc_stream_ctrl {
uint16_t bmHint;
uint8_t bFormatIndex;
uint8_t bFrameIndex;
uint32_t dwFrameInterval;
uint16_t wKeyFrameRate;
uint16_t wPFrameRate;
uint16_t wCompQuality;
uint16_t wCompWindowSize;
uint16_t wDelay;
uint32_t dwMaxVideoFrameSize;
uint32_t dwMaxPayloadTransferSize;
uint32_t dwClockFrequency;
uint8_t bmFramingInfo;
uint8_t bPreferredVersion;
uint8_t bMinVersion;
uint8_t bMaxVersion;
uint8_t bInterfaceNumber;
} uvc_stream_ctrl_t;
/** UVC request code (A.8) */
enum uvc_req_code {
UVC_RC_UNDEFINED = 0x00,
UVC_SET_CUR = 0x01,
UVC_GET_CUR = 0x81,
UVC_GET_MIN = 0x82,
UVC_GET_MAX = 0x83,
UVC_GET_RES = 0x84,
UVC_GET_LEN = 0x85,
UVC_GET_INFO = 0x86,
UVC_GET_DEF = 0x87
};
/** VideoStreaming interface control selector (A.9.7) */
enum uvc_vs_ctrl_selector {
UVC_VS_CONTROL_UNDEFINED = 0x00,
UVC_VS_PROBE_CONTROL = 0x01,
UVC_VS_COMMIT_CONTROL = 0x02,
UVC_VS_STILL_PROBE_CONTROL = 0x03,
UVC_VS_STILL_COMMIT_CONTROL = 0x04,
UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05,
UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06,
UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07,
UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08,
UVC_VS_SYNC_DELAY_CONTROL = 0x09
};
/** An image frame received from the UVC device
* @ingroup streaming
*/
typedef struct uvc_frame {
/** Image data for this frame */
void *data;
/** Size of image data buffer */
size_t data_bytes;
/** Width of image in pixels */
uint32_t width;
/** Height of image in pixels */
uint32_t height;
/** Pixel data format */
enum uvc_frame_format frame_format;
/** Number of bytes per horizontal line (undefined for compressed format) */
size_t step;
/** Frame number (may skip, but is strictly monotonically increasing) */
uint32_t sequence;
/** Estimate of system time when the device started capturing the image */
struct timeval capture_time;
/** Estimate of system time when the device finished receiving the image */
struct timespec capture_time_finished;
/** Handle on the device that produced the image.
* @warning You must not call any uvc_* functions during a callback. */
uvc_device_handle_t *source;
/** Is the data buffer owned by the library?
* If 1, the data buffer can be arbitrarily reallocated by frame conversion
* functions.
* If 0, the data buffer will not be reallocated or freed by the library.
* Set this field to zero if you are supplying the buffer.
*/
uint8_t library_owns_data;
/** Metadata for this frame if available */
void *metadata;
/** Size of metadata buffer */
size_t metadata_bytes;
} uvc_frame_t;
/** A callback function to handle incoming assembled UVC frames
* @ingroup streaming
*/
typedef void(uvc_frame_callback_t)(struct uvc_frame *frame, void *user_ptr);

View File

@ -0,0 +1,360 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "esp_err.h"
#include "usb/usb_types_stack.h"
#include "libuvc_def.h"
#include "arduino_config.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FRAME_RESOLUTION_ANY __UINT16_MAX__ /*!< any uvc frame resolution */
#define UAC_FREQUENCY_ANY __UINT32_MAX__ /*!< any uac sample frequency */
#define UAC_BITS_ANY __UINT16_MAX__ /*!< any uac bit resolution */
#define UAC_CH_ANY 0 /*!< any uac channel number */
#define FPS2INTERVAL(fps) (10000000ul / fps) /*!< convert fps to uvc frame interval */
#define FRAME_INTERVAL_FPS_5 FPS2INTERVAL(5) /*!< 5 fps */
#define FRAME_INTERVAL_FPS_10 FPS2INTERVAL(10) /*!< 10 fps */
#define FRAME_INTERVAL_FPS_15 FPS2INTERVAL(15) /*!< 15 fps */
#define FRAME_INTERVAL_FPS_20 FPS2INTERVAL(20) /*!< 20 fps */
#define FRAME_INTERVAL_FPS_30 FPS2INTERVAL(25) /*!< 25 fps */
#define FLAG_UVC_SUSPEND_AFTER_START (1 << 0) /*!< suspend uvc after usb_streaming_start */
#define FLAG_UAC_SPK_SUSPEND_AFTER_START (1 << 1) /*!< suspend uac speaker after usb_streaming_start */
#define FLAG_UAC_MIC_SUSPEND_AFTER_START (1 << 2) /*!< suspend uac microphone after usb_streaming_start */
/**
* @brief UVC stream usb transfer type, most camera using isochronous mode,
* bulk mode can also be support for higher bandwidth
*/
typedef enum {
UVC_XFER_ISOC = 0, /*!< Isochronous Transfer Mode */
UVC_XFER_BULK, /*!< Bulk Transfer Mode */
UVC_XFER_UNKNOWN, /*!< Unknown Mode */
} uvc_xfer_t;
/**
* @brief Stream id, used for control
*
*/
typedef enum {
STREAM_UVC = 0, /*!< usb video stream */
STREAM_UAC_SPK, /*!< usb audio speaker stream */
STREAM_UAC_MIC, /*!< usb audio microphone stream */
STREAM_MAX, /*!< max stream id */
} usb_stream_t;
/**
* @brief USB device connection status
*
*/
typedef enum {
STREAM_CONNECTED = 0,
STREAM_DISCONNECTED,
} usb_stream_state_t;
/**
* @brief Stream control type, which also depends on if device support
*
*/
typedef enum {
CTRL_NONE = 0, /*!< None */
CTRL_SUSPEND, /*!< streaming suspend control. ctrl_data NULL */
CTRL_RESUME, /*!< streaming resume control. ctrl_data NULL */
CTRL_UAC_MUTE, /*!< mute control. ctrl_data (false/true) */
CTRL_UAC_VOLUME, /*!< volume control. ctrl_data (0~100) */
CTRL_MAX, /*!< max type value */
} stream_ctrl_t;
/**
* @brief UVC configurations, for params with (optional) label, users do not need to specify manually,
* unless there is a problem with descriptors, or users want to skip the get and process descriptors steps
*/
typedef struct uvc_config {
uint16_t frame_width; /*!< Picture width, set FRAME_RESOLUTION_ANY for any resolution */
uint16_t frame_height; /*!< Picture height, set FRAME_RESOLUTION_ANY for any resolution */
uint32_t frame_interval; /*!< Frame interval in 100-ns units, 666666 ~ 15 Fps*/
uint32_t xfer_buffer_size; /*!< Transfer buffer size, using double buffer here, must larger than one frame size */
uint8_t *xfer_buffer_a; /*!< Buffer a for usb payload */
uint8_t *xfer_buffer_b; /*!< Buffer b for usb payload */
uint32_t frame_buffer_size; /*!< Frame buffer size, must larger than one frame size */
uint8_t *frame_buffer; /*!< Buffer for one frame */
uvc_frame_callback_t *frame_cb; /*!< callback function to handle incoming frame */
void *frame_cb_arg; /*!< callback function arg */
/*!< Optional configs, Users need to specify parameters manually when they want to
skip the get and process descriptors steps (used to speed up startup)*/
uvc_xfer_t xfer_type; /*!< (optional) UVC stream transfer type, UVC_XFER_ISOC or UVC_XFER_BULK */
uint8_t format_index; /*!< (optional) Format index of MJPEG */
uint8_t frame_index; /*!< (optional) Frame index, to choose resolution */
uint16_t interface; /*!< (optional) UVC stream interface number */
uint16_t interface_alt; /*!< (optional) UVC stream alternate interface, to choose MPS (Max Packet Size), bulk fix to 0*/
uint8_t ep_addr; /*!< (optional) endpoint address of selected alternate interface*/
uint32_t ep_mps; /*!< (optional) MPS of selected interface_alt */
uint32_t flags; /*!< (optional) flags to control the driver behavers */
} uvc_config_t;
/**
* @brief mic frame type
* */
typedef struct {
void *data; /*!< mic data */
uint32_t data_bytes; /*!< mic data size */
uint16_t bit_resolution; /*!< bit resolution in buffer */
uint32_t samples_frequence; /*!< mic sample frequency */
} mic_frame_t;
/**
* @brief uvc frame type
* */
typedef struct {
uint16_t width; /*!< frame width */
uint16_t height; /*!< frame height */
uint32_t interval; /*!< frame interval */
uint32_t interval_min; /*!< frame min interval */
uint32_t interval_max; /*!< frame max interval */
uint32_t interval_step; /*!< frame interval step */
} uvc_frame_size_t;
/**
* @brief uac frame type
* */
typedef struct {
uint8_t ch_num; /*!< channel numbers */
uint16_t bit_resolution; /*!< bit resolution in buffer */
uint32_t samples_frequence; /*!< sample frequency */
uint32_t samples_frequence_min; /*!< min sample frequency */
uint32_t samples_frequence_max; /*!< max sample frequency */
} uac_frame_size_t;
/**
* @brief user callback function to handle incoming mic frames
*
*/
typedef void(mic_callback_t)(mic_frame_t *frame, void *user_ptr);
/**
* @brief user callback function to handle usb device connection status
*
*/
typedef void(state_callback_t)(usb_stream_state_t state, void *user_ptr);
/**
* @brief UAC configurations, for params with (optional) label, users do not need to specify manually,
* unless there is a problem with descriptor parse, or a problem with the device descriptor
*/
typedef struct {
uint8_t spk_ch_num; /*!< speaker channel numbers, UAC_CH_ANY for any channel number */
uint8_t mic_ch_num; /*!< microphone channel numbers, UAC_CH_ANY for any channel number */
uint16_t mic_bit_resolution; /*!< microphone resolution(bits), UAC_BITS_ANY for any bit resolution */
uint32_t mic_samples_frequence; /*!< microphone frequence(Hz), UAC_FREQUENCY_ANY for any frequency */
uint16_t spk_bit_resolution; /*!< speaker resolution(bits), UAC_BITS_ANY for any */
uint32_t spk_samples_frequence; /*!< speaker frequence(Hz), UAC_FREQUENCY_ANY for any frequency */
uint32_t spk_buf_size; /*!< size of speaker send buffer, should be a multiple of spk_ep_mps */
uint32_t mic_buf_size; /*!< mic receive buffer size, 0 if not use */
mic_callback_t *mic_cb; /*!< mic callback, can not block in here!, NULL if not use */
void *mic_cb_arg; /*!< mic callback args, NULL if not use */
/*!< Optional configs, Users need to specify parameters manually when they want to
skip the get and process descriptors steps (used to speed up startup)*/
uint16_t mic_interface; /*!< (optional) microphone stream interface number, set 0 if not use */
uint8_t mic_ep_addr; /*!< (optional) microphone interface endpoint address */
uint32_t mic_ep_mps; /*!< (optional) microphone interface endpoint mps */
uint16_t spk_interface; /*!< (optional) speaker stream interface number, set 0 if not use */
uint8_t spk_ep_addr; /*!< (optional) speaker interface endpoint address */
uint32_t spk_ep_mps; /*!< (optional) speaker interface endpoint mps */
uint16_t ac_interface; /*!< (optional) audio control interface number, set 0 if not use */
uint8_t mic_fu_id; /*!< (optional) microphone feature unit id, set 0 if not use */
uint8_t spk_fu_id; /*!< (optional) speaker feature unit id, set 0 if not use */
uint32_t flags; /*!< (optional) flags to control the driver behavers */
} uac_config_t;
/**
* @brief Config UVC streaming with user defined parameters.For normal use, user only need to specify
* no-optional parameters, and set optional parameters to 0 (the driver will find the correct value from the device descriptors).
* For quick start mode, user should specify all parameters manually to skip get and process descriptors steps.
*
* @param config parameters defined in uvc_config_t
* @return esp_err_t
* ESP_ERR_INVALID_STATE USB streaming is running, user need to stop streaming first
* ESP_ERR_INVALID_ARG Invalid argument
* ESP_OK Success
*/
esp_err_t uvc_streaming_config(const uvc_config_t *config);
/**
* @brief Config UAC streaming with user defined parameters.For normal use, user only need to specify
* no-optional parameters, and set optional parameters to 0 (the driver will find the correct value from the device descriptors).
* For quick start mode, user should specify all parameters manually to skip get and process descriptors steps.
*
* @param config parameters defined in uvc_config_t
* @return esp_err_t
* ESP_ERR_INVALID_STATE USB streaming is running, user need to stop streaming first
* ESP_ERR_INVALID_ARG Invalid argument
* ESP_OK Success
*/
esp_err_t uac_streaming_config(const uac_config_t *config);
/**
* @brief Start usb streaming with pre-configs, usb driver will create internal tasks
* to handle usb data from stream pipe, and run user's callback after new frame ready.
*
* @return
* ESP_ERR_INVALID_STATE streaming not configured, or streaming running already
* ESP_FAIL start failed
* ESP_OK start succeed
*/
esp_err_t usb_streaming_start(void);
/**
* @brief Stop current usb streaming, internal tasks will be delete, related resourse will be free
*
* @return
* ESP_ERR_INVALID_STATE streaming not started
* ESP_ERR_TIMEOUT stop wait timeout
* ESP_OK stop succeed
*/
esp_err_t usb_streaming_stop(void);
/**
* @brief Wait for USB device connection
*
* @param timeout_ms timeout in ms
* @return esp_err_t
* ESP_ERR_INVALID_STATE: usb streaming not started
* ESP_ERR_TIMEOUT: timeout
* ESP_OK: device connected
*/
esp_err_t usb_streaming_connect_wait(size_t timeout_ms);
/**
* @brief This function registers a callback for USB streaming, please note that only one callback
* can be registered, the later registered callback will overwrite the previous one.
*
* @param cb A pointer to a function that will be called when the USB streaming state changes.
* @param user_ptr user_ptr is a void pointer.
*
* @return esp_err_t
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE USB streaming is running, callback need register before start
*/
esp_err_t usb_streaming_state_register(state_callback_t *cb, void *user_ptr);
/**
* @brief Control USB streaming with specific stream and control type
* @param stream stream type defined in usb_stream_t
* @param ctrl_type control type defined in stream_ctrl_t
* @param ctrl_value control value
* @return
* ESP_ERR_INVALID_ARG invalid arg
* ESP_ERR_INVALID_STATE driver not configured or not started
* ESP_ERR_NOT_SUPPORTED current device not support this control type
* ESP_FAIL control failed
* ESP_OK succeed
*/
esp_err_t usb_streaming_control(usb_stream_t stream, stream_ctrl_t ctrl_type, void *ctrl_value);
/**
* @brief Write data to the speaker buffer, will be send out when USB device is ready
*
* @param data The data to be written.
* @param data_bytes The size of the data to be written.
* @param timeout_ms The timeout value for writing data to the buffer.
*
* @return
* ESP_ERR_INVALID_STATE spk stream not config
* ESP_ERR_NOT_FOUND spk interface not found
* ESP_ERR_TIMEOUT spk ringbuf full
* ESP_OK succeed
*/
esp_err_t uac_spk_streaming_write(void *data, size_t data_bytes, size_t timeout_ms);
/**
* @brief Read data from internal mic buffer, the actual size will be returned
*
* @param buf pointer to the buffer to store the received data
* @param buf_size The size of the data buffer.
* @param data_bytes The actual size read from buffer
* @param timeout_ms The timeout value for the read operation.
*
* @return
* ESP_ERR_INVALID_ARG parameter error
* ESP_ERR_INVALID_STATE mic stream not config
* ESP_ERR_NOT_FOUND mic interface not found
* ESP_TIMEOUT timeout
* ESP_OK succeed
*/
esp_err_t uac_mic_streaming_read(void *buf, size_t buf_size, size_t *data_bytes, size_t timeout_ms);
/**
* @brief Get the audio frame size list of current stream, the list contains audio channel number, bit resolution and samples frequence.
* IF list_size equals 1 and the samples_frequence equals 0, which means the frequency can be set to any value between samples_frequence_min
* and samples_frequence_max.
*
*
* @param stream the stream type
* @param frame_list the output frame list, NULL to only get the list size
* @param list_size frame list size
* @param cur_index current frame index
* @return esp_err_t
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_INVALID_STATE USB device not active
* - ESP_OK Success
*/
esp_err_t uac_frame_size_list_get(usb_stream_t stream, uac_frame_size_t *frame_list, size_t *list_size, size_t *cur_index);
/**
* @brief Reset audio channel number, bit resolution and samples frequence, please reset when the streaming
* in suspend state. The new configs will be effective after streaming resume.
*
* @param stream stream type
* @param ch_num audio channel numbers
* @param bit_resolution audio bit resolution
* @param samples_frequence audio samples frequence
* @return esp_err_t
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_INVALID_STATE USB device not active
* - ESP_ERR_NOT_FOUND frequency not found
* - ESP_OK Success
* - ESP_FAIL Reset failed
*/
esp_err_t uac_frame_size_reset(usb_stream_t stream, uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequence);
/**
* @brief Get the frame size list of current connected camera
*
* @param frame_list the frame size list, can be NULL if only want to get list size
* @param list_size the list size
* @param cur_index current frame index
* @return esp_err_t
* ESP_ERR_INVALID_ARG parameter error
* ESP_ERR_INVALID_STATE uvc stream not config or not active
* ESP_OK succeed
*/
esp_err_t uvc_frame_size_list_get(uvc_frame_size_t *frame_list, size_t *list_size, size_t *cur_index);
/**
* @brief Reset the expected frame size and frame interval, please reset when uvc streaming
* in suspend state.The new configs will be effective after streaming resume.
*
* Note: frame_width and frame_height can be set to 0 at the same time, which means
* no change on frame size.
*
* @param frame_width frame width, FRAME_RESOLUTION_ANY means any width
* @param frame_height frame height, FRAME_RESOLUTION_ANY means any height
* @param frame_interval frame interval, 0 means no change
* @return esp_err_t
*/
esp_err_t uvc_frame_size_reset(uint16_t frame_width, uint16_t frame_height, uint32_t frame_interval);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,367 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_idf_version.h"
#include "esp_attr.h"
#include "esp_private/usb_phy.h"
#include "usb_host_helpers.h"
#include "esp_intr_alloc.h"
#define USB_PORT_NUM 1 //Default port number
static const char *TAG = "USB_STREAM";
/*------------------------------------------------ USB URB Code ----------------------------------------------------*/
void _usb_urb_clear(urb_t *urb)
{
UVC_CHECK_RETURN_VOID(NULL != urb, "urb = NULL");
usb_transfer_dummy_t *transfer_dummy = (usb_transfer_dummy_t *)&urb->transfer;
uint8_t *data_buffer = transfer_dummy->data_buffer;
size_t data_buffer_size = transfer_dummy->data_buffer_size;
int num_isoc_packets = transfer_dummy->num_isoc_packets;
void *context = transfer_dummy->context;
memset(urb, 0, sizeof(urb_t) + (num_isoc_packets * sizeof(usb_isoc_packet_desc_t)));
transfer_dummy->data_buffer = data_buffer;
transfer_dummy->data_buffer_size = data_buffer_size;
transfer_dummy->num_isoc_packets = num_isoc_packets;
transfer_dummy->context = context;
}
urb_t *_usb_urb_alloc(int num_isoc_packets, size_t packet_data_buffer_size, void *context)
{
uint8_t *data_buffer = NULL;
urb_t *urb = heap_caps_calloc(1, sizeof(urb_t) + (num_isoc_packets * sizeof(usb_isoc_packet_desc_t)), MALLOC_CAP_DMA);
UVC_CHECK_GOTO(NULL != urb, "urb alloc failed", _alloc_failed);
//Allocate data buffer for each URB and assign them
if (num_isoc_packets) {
/* ISOC urb: num_isoc_packets must be 0 for isoc urb */
data_buffer = heap_caps_calloc(num_isoc_packets, packet_data_buffer_size, MALLOC_CAP_DMA);
} else {
/* no ISOC urb */
data_buffer = heap_caps_calloc(1, packet_data_buffer_size, MALLOC_CAP_DMA);
}
UVC_CHECK_GOTO(NULL != data_buffer, "urb data_buffer alloc failed", _alloc_failed);
//Initialize URB and underlying transfer structure. Need to cast to dummy due to const fields
size_t data_buffer_size = packet_data_buffer_size * (num_isoc_packets == 0 ? 1 : num_isoc_packets);
usb_transfer_dummy_t *transfer_dummy = (usb_transfer_dummy_t *)&urb->transfer;
transfer_dummy->data_buffer = data_buffer;
transfer_dummy->data_buffer_size = data_buffer_size;
transfer_dummy->num_isoc_packets = num_isoc_packets;
transfer_dummy->context = context;
ESP_LOGV(TAG, "urb(%p), alloc size %d = %d * %d", urb, data_buffer_size, packet_data_buffer_size, num_isoc_packets == 0 ? 1 : num_isoc_packets);
return urb;
_alloc_failed:
free(urb);
free(data_buffer);
return NULL;
}
void _usb_urb_free(urb_t *urb)
{
UVC_CHECK_RETURN_VOID(NULL != urb, "urb = NULL");
//Free data buffers of URB
if (urb->transfer.data_buffer) {
heap_caps_free(urb->transfer.data_buffer);
}
//Free the URB
heap_caps_free(urb);
ESP_LOGD(TAG, "urb free(%p)", urb);
}
urb_t **_usb_urb_list_alloc(uint32_t urb_num, uint32_t num_isoc_packets, uint32_t bytes_per_packet)
{
ESP_LOGD(TAG, "urb list alloc urb_num = %"PRId32", isoc packets = %"PRId32 ", bytes_per_packet = %"PRId32, urb_num, num_isoc_packets, bytes_per_packet);
urb_t **urb_list = heap_caps_calloc(urb_num, sizeof(urb_t *), MALLOC_CAP_DMA);
UVC_CHECK(urb_list != NULL, "p_urb alloc failed", NULL);
for (int i = 0; i < urb_num; i++) {
urb_list[i] = _usb_urb_alloc(num_isoc_packets, bytes_per_packet, NULL);
UVC_CHECK_GOTO(urb_list[i] != NULL, "stream urb alloc failed", failed_);
urb_list[i]->transfer.num_bytes = (num_isoc_packets == 0 ? 1 : num_isoc_packets) * bytes_per_packet;
for (size_t j = 0; j < num_isoc_packets; j++) {
//We need to initialize each individual isoc packet descriptor of the URB
urb_list[i]->transfer.isoc_packet_desc[j].num_bytes = bytes_per_packet;
}
}
return urb_list;
failed_:
for (size_t i = 0; i < urb_num; i++) {
_usb_urb_free(urb_list[i]);
urb_list[i] = NULL;
}
free(urb_list);
return NULL;
}
void _usb_urb_list_clear(urb_t **urb_list, uint32_t urb_num, uint32_t packets_per_urb, uint32_t bytes_per_packet)
{
if (urb_list) {
for (size_t i = 0; i < urb_num; i++) {
_usb_urb_clear(urb_list[i]);
usb_transfer_dummy_t *transfer_dummy = (usb_transfer_dummy_t *)&urb_list[i]->transfer;
transfer_dummy->num_isoc_packets = packets_per_urb;
transfer_dummy->num_bytes = (packets_per_urb == 0 ? 1 : packets_per_urb) * bytes_per_packet;
for (size_t j = 0; j < packets_per_urb; j++) {
//We need to initialize each individual isoc packet descriptor of the URB
urb_list[i]->transfer.isoc_packet_desc[j].num_bytes = bytes_per_packet;
}
}
}
}
esp_err_t _usb_urb_list_enqueue(hcd_pipe_handle_t pipe_handle, urb_t **urb_list, uint32_t urb_num)
{
UVC_CHECK(pipe_handle != NULL, "stream pipe not init", ESP_ERR_INVALID_ARG);
UVC_CHECK(urb_list != NULL, "stream urb list not init", ESP_ERR_INVALID_ARG);
UVC_CHECK(urb_num > 0, "stream urb num invalid", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
for (int i = 0; i < urb_num; i++) {
UVC_CHECK(urb_list[i] != NULL, "urb not init", ESP_FAIL);
ret = hcd_urb_enqueue(pipe_handle, urb_list[i]);
UVC_CHECK(ESP_OK == ret, "pipe enqueue failed", ESP_FAIL);
}
return ESP_OK;
}
void _usb_urb_list_free(urb_t **urb_list, uint32_t urb_num)
{
if (urb_list) {
for (size_t i = 0; i < urb_num; i++) {
_usb_urb_free(urb_list[i]);
}
free(urb_list);
}
}
/*------------------------------------------------ USB Port Code ----------------------------------------------------*/
hcd_port_event_t _usb_port_event_dflt_process(hcd_port_handle_t port_hdl, hcd_port_event_t event)
{
(void)event;
UVC_CHECK(port_hdl != NULL, "port handle can not be NULL", HCD_PORT_EVENT_NONE);
hcd_port_event_t actual_evt = hcd_port_handle_event(port_hdl);
switch (actual_evt) {
case HCD_PORT_EVENT_CONNECTION:
//Reset newly connected device
ESP_LOGI(TAG, "line %u HCD_PORT_EVENT_CONNECTION", __LINE__);
break;
case HCD_PORT_EVENT_DISCONNECTION:
ESP_LOGW(TAG, "line %u HCD_PORT_EVENT_DISCONNECTION", __LINE__);
break;
case HCD_PORT_EVENT_ERROR:
ESP_LOGW(TAG, "line %u HCD_PORT_EVENT_ERROR", __LINE__);
break;
case HCD_PORT_EVENT_OVERCURRENT:
ESP_LOGW(TAG, "line %u HCD_PORT_EVENT_OVERCURRENT", __LINE__);
break;
case HCD_PORT_EVENT_NONE:
ESP_LOGD(TAG, "line %u HCD_PORT_EVENT_NONE", __LINE__);
break;
default:
ESP_LOGE(TAG, "line %u invalid HCD_PORT_EVENT%d", __LINE__, actual_evt);
break;
}
return actual_evt;
}
hcd_port_handle_t _usb_port_init(hcd_port_callback_t callback, void *callback_arg)
{
UVC_CHECK( callback != NULL && callback_arg != NULL, "invalid args", NULL);
esp_err_t ret = ESP_OK;
hcd_port_handle_t port_hdl = NULL;
usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_INT,
.otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
.otg_io_conf = NULL,
#else
.gpio_conf = NULL,
#endif
};
usb_phy_handle_t phy_handle = NULL;
ret = usb_new_phy(&phy_config, &phy_handle);
UVC_CHECK(ESP_OK == ret, "USB PHY init failed", NULL);
hcd_config_t hcd_config = {
.intr_flags = ESP_INTR_FLAG_LEVEL2,
};
ret = hcd_install(&hcd_config);
UVC_CHECK_GOTO(ESP_OK == ret, "HCD Install failed", hcd_init_err);
hcd_port_config_t port_cfg = {
.fifo_bias = HCD_PORT_FIFO_BIAS_BALANCED,
.callback = callback,
.callback_arg = callback_arg,
.context = phy_handle,
};
ret = hcd_port_init(USB_PORT_NUM, &port_cfg, &port_hdl);
UVC_CHECK_GOTO(ESP_OK == ret, "HCD Port init failed", port_init_err);
ret = hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_ON);
UVC_CHECK_GOTO(ESP_OK == ret, "HCD Port Power on failed", port_power_err);
ESP_LOGD(TAG, "Port=%d init succeed, context = %p", USB_PORT_NUM, phy_handle);
return port_hdl;
port_power_err:
hcd_port_deinit(port_hdl);
port_init_err:
hcd_uninstall();
hcd_init_err:
usb_del_phy(phy_handle);
return NULL;
}
esp_err_t _usb_port_deinit(hcd_port_handle_t port_hdl)
{
esp_err_t ret;
ret = hcd_port_command(port_hdl, HCD_PORT_CMD_DISABLE);
UVC_CHECK_CONTINUE(ESP_OK == ret, "port disable failed");
ret = hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_OFF);
UVC_CHECK_CONTINUE(ESP_OK == ret, "Port power off failed");
usb_phy_handle_t phy_handle = hcd_port_get_context(port_hdl);
ret = usb_del_phy(phy_handle);
UVC_CHECK_CONTINUE(ESP_OK == ret, "phy delete failed");
ret = hcd_port_deinit(port_hdl);
UVC_CHECK_CONTINUE(ESP_OK == ret, "port deinit failed");
ret = hcd_uninstall();
UVC_CHECK_CONTINUE(ESP_OK == ret, "hcd uninstall failed");
ESP_LOGD(TAG, "Port=%d deinit succeed, context = %p", USB_PORT_NUM, phy_handle);
return ret;
}
esp_err_t _usb_port_get_speed(hcd_port_handle_t port_hdl, usb_speed_t *port_speed)
{
UVC_CHECK(port_hdl != NULL && port_speed != NULL, "invalid args", ESP_ERR_INVALID_ARG);
esp_err_t ret = hcd_port_get_speed(port_hdl, port_speed);
UVC_CHECK(ESP_OK == ret, "port speed get failed", ESP_FAIL);
return ret;
}
#ifdef RANDOM_ERROR_TEST
#include "esp_random.h"
#endif
/*------------------------------------------------ USB Pipe Code ----------------------------------------------------*/
IRAM_ATTR hcd_pipe_event_t _pipe_event_dflt_process(hcd_pipe_handle_t pipe_handle, const char *pipe_name, hcd_pipe_event_t pipe_event)
{
UVC_CHECK(pipe_handle != NULL, "pipe handle can not be NULL", pipe_event);
hcd_pipe_event_t actual_evt = pipe_event;
#ifdef RANDOM_ERROR_TEST
if (HCD_PIPE_EVENT_URB_DONE == pipe_event)
actual_evt = (esp_random() % 10 > 8) ? HCD_PIPE_EVENT_ERROR_XFER : HCD_PIPE_EVENT_URB_DONE;
#endif
switch (pipe_event) {
case HCD_PIPE_EVENT_NONE:
break;
case HCD_PIPE_EVENT_URB_DONE:
ESP_LOGV(TAG, "Pipe(%s): XFER_DONE", pipe_name);
break;
case HCD_PIPE_EVENT_ERROR_XFER:
ESP_LOGW(TAG, "Pipe(%s): ERROR_XFER", pipe_name);
break;
case HCD_PIPE_EVENT_ERROR_URB_NOT_AVAIL:
ESP_LOGW(TAG, "Pipe(%s): ERROR_URB_NOT_AVAIL", pipe_name);
break;
case HCD_PIPE_EVENT_ERROR_OVERFLOW:
ESP_LOGW(TAG, "Pipe(%s): ERROR_OVERFLOW", pipe_name);
break;
case HCD_PIPE_EVENT_ERROR_STALL:
ESP_LOGW(TAG, "Pipe(%s): ERROR_STALL", pipe_name);
break;
default:
ESP_LOGW(TAG, "Pipe(%s): invalid EVENT = %d", pipe_name, pipe_event);
break;
}
return actual_evt;
}
hcd_pipe_handle_t _usb_pipe_init(hcd_port_handle_t port_hdl, usb_ep_desc_t *ep_desc, uint8_t dev_addr, usb_speed_t dev_speed, void *context, hcd_pipe_callback_t callback, void *callback_arg)
{
UVC_CHECK(port_hdl != NULL, "invalid args", NULL);
hcd_pipe_config_t pipe_cfg = {
.callback = callback,
.callback_arg = callback_arg,
.context = context,
.ep_desc = ep_desc, //NULL EP descriptor to create a default pipe
.dev_addr = dev_addr,
.dev_speed = dev_speed,
};
esp_err_t ret = ESP_OK;
hcd_pipe_handle_t pipe_handle = NULL;
ret = hcd_pipe_alloc(port_hdl, &pipe_cfg, &pipe_handle);
UVC_CHECK(ESP_OK == ret, "pipe alloc failed", NULL);
ESP_LOGD(TAG, "pipe(%p) init succeed", pipe_handle);
return pipe_handle;
}
esp_err_t _usb_pipe_flush(hcd_pipe_handle_t pipe_hdl, size_t urb_num)
{
UVC_CHECK(pipe_hdl != NULL, "invalid args", ESP_ERR_INVALID_ARG);
ESP_LOGD(TAG, "pipe flushing: state = %d", hcd_pipe_get_state(pipe_hdl));
esp_err_t ret = hcd_pipe_command(pipe_hdl, HCD_PIPE_CMD_HALT);
if (ESP_OK != ret) {
ESP_LOGW(TAG, "pipe=%p halt failed", pipe_hdl);
}
ret = hcd_pipe_command(pipe_hdl, HCD_PIPE_CMD_FLUSH);
if (ESP_OK != ret) {
ESP_LOGW(TAG, "pipe=%p flush failed", pipe_hdl);
}
for (size_t i = 0; i < urb_num; i++) {
urb_t *urb = hcd_urb_dequeue(pipe_hdl);
ESP_LOGV(TAG, "urb dequeue handle = %p", urb);
}
ESP_LOGD(TAG, "urb dequeued num = %d", urb_num);
ESP_LOGD(TAG, "pipe(%p) flush succeed", pipe_hdl);
return ESP_OK;
}
esp_err_t _usb_pipe_clear(hcd_pipe_handle_t pipe_hdl, size_t urb_num)
{
UVC_CHECK(pipe_hdl != NULL, "invalid args", ESP_ERR_INVALID_ARG);
ESP_LOGD(TAG, "pipe clearing: state = %d", hcd_pipe_get_state(pipe_hdl));
for (size_t i = 0; i < urb_num; i++) {
urb_t *urb = hcd_urb_dequeue(pipe_hdl);
ESP_LOGV(TAG, "urb dequeue handle = %p", urb);
}
ESP_LOGD(TAG, "urb dequeued num = %d", urb_num);
if (hcd_pipe_get_state(pipe_hdl) == HCD_PIPE_STATE_ACTIVE) {
return ESP_OK;
}
esp_err_t ret = hcd_pipe_command(pipe_hdl, HCD_PIPE_CMD_CLEAR);
if (ESP_OK != ret) {
ESP_LOGD(TAG, "pipe=%p clear failed", pipe_hdl);
return ESP_FAIL;
}
ESP_LOGD(TAG, "pipe(%p) clear succeed", pipe_hdl);
return ESP_OK;
}
esp_err_t _usb_pipe_deinit(hcd_pipe_handle_t pipe_hdl, size_t urb_num)
{
UVC_CHECK(pipe_hdl != NULL, "invalid args", ESP_ERR_INVALID_ARG);
esp_err_t ret = _usb_pipe_flush(pipe_hdl, urb_num);
if (ESP_OK != ret) {
ESP_LOGW(TAG, "pipe=%p flush failed", pipe_hdl);
}
ret = hcd_pipe_free(pipe_hdl);
if (ESP_OK != ret) {
ESP_LOGW(TAG, "pipe=%p free failed", pipe_hdl);
return ret;
}
ESP_LOGD(TAG, "pipe(%p) deinit succeed", pipe_hdl);
return ESP_OK;
}

View File

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "hcd.h"
#include "usb/usb_types_stack.h"
#include "usb_private.h"
#define UVC_CHECK(a, str, ret) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret); \
}
#define UVC_CHECK_ABORT(a, str) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
abort(); \
}
#define UVC_CHECK_RETURN_VOID(a, str) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return; \
}
#define UVC_CHECK_CONTINUE(a, str) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
}
#define UVC_CHECK_GOTO(a, str, label) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
goto label; \
}
/* The usb host helper functions, only for usb_stream driver */
void _usb_urb_clear(urb_t *urb);
urb_t *_usb_urb_alloc(int num_isoc_packets, size_t packet_data_buffer_size, void *context);
void _usb_urb_free(urb_t *urb);
urb_t **_usb_urb_list_alloc(uint32_t urb_num, uint32_t num_isoc_packets, uint32_t bytes_per_packet);
void _usb_urb_list_clear(urb_t **urb_list, uint32_t urb_num, uint32_t packets_per_urb, uint32_t bytes_per_packet);
esp_err_t _usb_urb_list_enqueue(hcd_pipe_handle_t pipe_handle, urb_t **urb_list, uint32_t urb_num);
void _usb_urb_list_free(urb_t **urb_list, uint32_t urb_num);
hcd_port_event_t _usb_port_event_dflt_process(hcd_port_handle_t port_hdl, hcd_port_event_t event);
hcd_pipe_event_t _pipe_event_dflt_process(hcd_pipe_handle_t pipe_handle, const char *pipe_name, hcd_pipe_event_t pipe_event);;
hcd_port_handle_t _usb_port_init(hcd_port_callback_t callback, void *callback_arg);
esp_err_t _usb_port_deinit(hcd_port_handle_t port_hdl);
esp_err_t _usb_port_get_speed(hcd_port_handle_t port_hdl, usb_speed_t *port_speed);
hcd_pipe_handle_t _usb_pipe_init(hcd_port_handle_t port_hdl, usb_ep_desc_t *ep_desc, uint8_t dev_addr, usb_speed_t dev_speed, void *context, hcd_pipe_callback_t callback, void *callback_arg);
esp_err_t _usb_pipe_flush(hcd_pipe_handle_t pipe_hdl, size_t urb_num);
esp_err_t _usb_pipe_clear(hcd_pipe_handle_t pipe_hdl, size_t urb_num);
esp_err_t _usb_pipe_deinit(hcd_pipe_handle_t pipe_hdl, size_t urb_num);

View File

@ -0,0 +1,98 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/queue.h>
#include "usb/usb_types_ch9.h"
#include "usb/usb_types_stack.h"
#ifdef __cplusplus
extern "C" {
#endif
// ------------------------------------------------------ Types --------------------------------------------------------
typedef struct {
uint8_t *data_buffer;
size_t data_buffer_size;
int num_bytes;
int actual_num_bytes;
uint32_t flags;
usb_device_handle_t device_handle;
uint8_t bEndpointAddress;
usb_transfer_status_t status;
uint32_t timeout;
usb_transfer_cb_t callback;
void *context;
int num_isoc_packets;
usb_isoc_packet_desc_t isoc_packet_desc[0];
} usb_transfer_dummy_t;
_Static_assert(sizeof(usb_transfer_dummy_t) == sizeof(usb_transfer_t), "usb_transfer_dummy_t does not match usb_transfer_t");
struct urb_s {
TAILQ_ENTRY(urb_s) tailq_entry;
// HCD Layer: Handler pointer and variables. Must be initialized to NULL and 0 respectively
void *hcd_ptr;
uint32_t hcd_var;
// Host Lib Layer:
void *usb_host_client; // Currently only used when submitted to shared pipes (i.e., Device default pipes)
size_t usb_host_header_size; // USB Host may need the data buffer to have a transparent header
bool usb_host_inflight; // Debugging variable, used to prevent re-submitting URBs already inflight
// Public transfer structure. Must be last due to variable length array
usb_transfer_t transfer;
};
typedef struct urb_s urb_t;
/**
* @brief Processing request source
*
* Enum to indicate which layer of the USB Host stack requires processing. The main handling loop should then call that
* layer's processing function (i.e., xxx_process()).
*/
typedef enum {
USB_PROC_REQ_SOURCE_USBH = 0x01,
USB_PROC_REQ_SOURCE_HUB = 0x02,
} usb_proc_req_source_t;
/**
* @brief Processing request callback
*
* Callback function provided to each layer of the USB Host stack so that each layer can request calls to their
* processing function.
*/
typedef bool (*usb_proc_req_cb_t)(usb_proc_req_source_t source, bool in_isr, void *context);
// --------------------------------------------------- Allocation ------------------------------------------------------
/**
* @brief Allocate a URB
*
* - Data buffer is allocated in DMA capable memory
* - The constant fields of the URB are also set
* - The data_buffer field of the URB is set to point to start of the allocated data buffer AFTER the header. To access
* the header, users need a negative offset from data_buffer.
*
* @param data_buffer_size Size of the URB's data buffer
* @param header_size Size of header to put in front of URB's data buffer
* @param num_isoc_packets Number of isochronous packet descriptors
* @return urb_t* URB object
*/
urb_t *urb_alloc(size_t data_buffer_size, size_t header_size, int num_isoc_packets);
/**
* @brief Free a URB
*
* @param urb URB object
*/
void urb_free(urb_t *urb);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,395 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdio.h>
#include <string.h>
#include "usb/usb_types_ch9.h"
#include "usb/usb_types_stack.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UAC_VERSION_1 0x0100
#define UAC_VERSION_2 0x0200
#define UAC_FORMAT_TYPEI 0x0001
typedef enum {
CS_INTERFACE_DESC = 0x24,
CS_ENDPOINT_DESC = 0x25,
} descriptor_types_t;
typedef enum {
VIDEO_SUBCLASS_UNDEFINED = 0x00,
VIDEO_SUBCLASS_CONTROL = 0x01,
VIDEO_SUBCLASS_STREAMING = 0x02,
VIDEO_SUBCLASS_INTERFACE_COLLECTION = 0x03,
} video_subclass_type_t;
typedef enum {
AUDIO_SUBCLASS_UNDEFINED = 0x00,
AUDIO_SUBCLASS_CONTROL = 0x01,
AUDIO_SUBCLASS_STREAMING = 0x02,
AUDIO_SUBCLASS_MIDI_STREAMING = 0x03,
} audio_subclass_type_t;
typedef enum {
AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20,
} audio_function_protocol_code_t;
typedef enum {
AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
} audio_terminal_type_t;
typedef enum {
AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
} audio_terminal_input_type_t;
typedef enum {
AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
AUDIO_TERM_TYPE_HEADSET = 0x0402,
} audio_terminal_output_type_t;
typedef enum {
AUDIO_FEATURE_CONTROL_MUTE = 0x0001,
AUDIO_FEATURE_CONTROL_VOLUME = 0x0002,
AUDIO_FEATURE_CONTROL_BASS = 0x0004,
AUDIO_FEATURE_CONTROL_MID = 0x0008,
AUDIO_FEATURE_CONTROL_TREBLE = 0x0010,
AUDIO_FEATURE_CONTROL_GRAPHIC_EQUALIZER = 0x0020,
AUDIO_FEATURE_CONTROL_AUTOMATIC_GAIN = 0x0040,
AUDIO_FEATURE_CONTROL_DEALY = 0x0080,
} audio_feature_unit_pos_t;
typedef enum {
AUDIO_EP_CONTROL_UNDEF = 0x00,
AUDIO_EP_CONTROL_SAMPLING_FEQ = 0x01,
AUDIO_EP_CONTROL_PITCH = 0x02,
} audio_ep_ctrl_pos_t;
typedef enum {
VIDEO_CS_ITF_VC_UNDEFINED = 0x00,
VIDEO_CS_ITF_VC_HEADER = 0x01,
VIDEO_CS_ITF_VC_INPUT_TERMINAL = 0x02,
VIDEO_CS_ITF_VC_OUTPUT_TERMINAL = 0x03,
VIDEO_CS_ITF_VC_SELECTOR_UNIT = 0x04,
VIDEO_CS_ITF_VC_PROCESSING_UNIT = 0x05,
VIDEO_CS_ITF_VC_EXTENSION_UNIT = 0x06,
VIDEO_CS_ITF_VC_ENCODING_UNIT = 0x07,
VIDEO_CS_ITF_VC_MAX,
} video_cs_vc_interface_subtype_t;
typedef enum {
VIDEO_CS_ITF_VS_UNDEFINED = 0x00,
VIDEO_CS_ITF_VS_INPUT_HEADER = 0x01,
VIDEO_CS_ITF_VS_OUTPUT_HEADER = 0x02,
VIDEO_CS_ITF_VS_STILL_IMAGE_FRAME = 0x03,
VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED = 0x04,
VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED = 0x05,
VIDEO_CS_ITF_VS_FORMAT_MJPEG = 0x06,
VIDEO_CS_ITF_VS_FRAME_MJPEG = 0x07,
VIDEO_CS_ITF_VS_FORMAT_MPEG2TS = 0x0A,
VIDEO_CS_ITF_VS_FORMAT_DV = 0x0C,
VIDEO_CS_ITF_VS_COLORFORMAT = 0x0D,
VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED = 0x10,
VIDEO_CS_ITF_VS_FRAME_FRAME_BASED = 0x11,
VIDEO_CS_ITF_VS_FORMAT_STREAM_BASED = 0x12,
VIDEO_CS_ITF_VS_FORMAT_H264 = 0x13,
VIDEO_CS_ITF_VS_FRAME_H264 = 0x14,
VIDEO_CS_ITF_VS_FORMAT_H264_SIMULCAST = 0x15,
VIDEO_CS_ITF_VS_FORMAT_VP8 = 0x16,
VIDEO_CS_ITF_VS_FRAME_VP8 = 0x17,
VIDEO_CS_ITF_VS_FORMAT_VP8_SIMULCAST = 0x18,
} video_cs_vs_interface_subtype_t;
typedef enum {
AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
} audio_cs_ac_interface_subtype_t;
typedef enum {
AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
} audio_cs_as_interface_subtype_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
} desc_header_t;
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;
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 bFrameIndex;
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;
struct {
uint32_t dwMinFrameInterval;
uint32_t dwMaxFrameInterval;
uint32_t dwFrameIntervalStep;
};
};
} USB_DESC_ATTR vs_frame_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdADC;
uint16_t wTotalLength;
uint8_t bInCollection;
uint8_t baInterfaceNr[];
} USB_DESC_ATTR ac_interface_header_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bTerminalID;
uint16_t wTerminalType;
uint8_t bAssocTerminal;
uint8_t bNrChannels;
uint16_t wChannelConfig;
uint8_t iChannelNames;
uint8_t iTerminal;
} USB_DESC_ATTR ac_interface_input_terminal_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 ac_interface_output_terminal_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bUnitID;
uint8_t bSourceID;
uint8_t bControlSize;
uint8_t bmaControls[2];
uint8_t iFeature;
} USB_DESC_ATTR ac_interface_feature_unit_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bTerminalLink;
uint8_t bDelay;
uint16_t wFormatTag;
} USB_DESC_ATTR as_interface_header_desc_t;
/// AUDIO Type I Format
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bFormatType;
uint8_t bNrChannels;
uint8_t bSubframeSize;
uint8_t bBitResolution;
uint8_t bSamFreqType;
uint8_t tSamFreq[3];
} USB_DESC_ATTR as_interface_type_I_format_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bmAttributes;
uint8_t bLockDelayUnits;
uint16_t wLockDelay;
} USB_DESC_ATTR as_cs_ep_desc_t;
#ifdef SUPPORT_UAC_V2
/* UAC v2.0 is incompatible with UAC v1.0 */
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t bcdADC;
uint8_t bCategory;
uint16_t wTotalLength;
uint8_t bmControls;
} USB_DESC_ATTR ac2_interface_header_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t wTerminalType;
uint8_t bAssocTerminal;
uint8_t bCSourceID;
uint8_t bNrChannels;
uint32_t bmChannelConfig;
uint16_t bmControls;
uint8_t iTerminal;
} USB_DESC_ATTR ac2_interface_input_terminal_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bUnitID;
uint8_t bSourceID;
struct TU_ATTR_PACKED {
uint32_t bmaControls;
} controls[2];
} USB_DESC_ATTR ac2_interface_feature_unit_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bTerminalLink;
uint8_t bmControls;
uint8_t bFormatType;
uint32_t bmFormats;
uint8_t bNrChannels;
uint32_t bmChannelConfig;
uint8_t iChannelNames;
} USB_DESC_ATTR as2_interface_header_desc_t;
/// AUDIO Type I Format
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatType;
uint8_t bSubslotSize;
uint8_t bBitResolution;
} USB_DESC_ATTR as2_interface_type_I_format_desc_t;
#endif
void parse_ac_header_desc(const uint8_t *buff, uint16_t *bcdADC, uint8_t *intf_num);
void parse_ac_input_desc(const uint8_t *buff, uint8_t *terminal_idx, uint16_t *terminal_type);
void parse_ac_output_desc(const uint8_t *buff, uint8_t *terminal_idx, uint16_t *terminal_type);
void parse_ac_feature_desc(const uint8_t *buff, uint8_t *source_idx, uint8_t *feature_unit_idx, uint8_t *volume_ch, uint8_t *mute_ch);
void parse_as_general_desc(const uint8_t *buff, uint8_t *source_idx, uint16_t *format_tag);
void parse_as_type_desc(const uint8_t *buff, uint8_t *channel_num, uint8_t *bit_resolution, uint8_t *freq_type, const uint8_t **pp_samfreq);
void print_cfg_desc(const uint8_t *buff);
void print_assoc_desc(const uint8_t *buff);
void print_intf_desc(const uint8_t *buff);
void parse_ep_desc(const uint8_t *buff, uint16_t *ep_mps, uint8_t *ep_addr, uint8_t *ep_attr);
void parse_vs_format_mjpeg_desc(const uint8_t *buff, uint8_t *format_idx, uint8_t *frame_num);
void parse_vs_frame_mjpeg_desc(const uint8_t *buff, uint8_t *frame_idx, uint16_t *width, uint16_t *heigh, uint8_t *interval_type, const uint32_t **pp_interval, uint32_t *dflt_interval);
void print_uvc_header_desc(const uint8_t *buff, uint8_t sub_class);
void print_device_descriptor(const uint8_t *buff);
void print_ep_desc(const uint8_t *buff);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#if CONFIG_APPTRACE_SV_ENABLE && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
#include "SEGGER_SYSVIEW.h"
#define SYSVIEW_MARKER_DFT_PIPE_HANDLE_ID 10
#define SYSVIEW_MARKER_UVC_PIPE_HANDLE_ID 11
#define SYSVIEW_MARKER_UAC_MIC_PIPE_HANDLE_ID 12
#define SYSVIEW_MARKER_UAC_SPK_PIPE_HANDLE_ID 13
#define SYSVIEW_DFLT_PIPE_XFER_START() SEGGER_SYSVIEW_OnUserStart(SYSVIEW_MARKER_DFT_PIPE_HANDLE_ID)
#define SYSVIEW_DFLT_PIPE_XFER_STOP() SEGGER_SYSVIEW_OnUserStop(SYSVIEW_MARKER_DFT_PIPE_HANDLE_ID)
#define SYSVIEW_UVC_PIPE_HANDLE_START() SEGGER_SYSVIEW_OnUserStart(SYSVIEW_MARKER_UVC_PIPE_HANDLE_ID)
#define SYSVIEW_UVC_PIPE_HANDLE_STOP() SEGGER_SYSVIEW_OnUserStop(SYSVIEW_MARKER_UVC_PIPE_HANDLE_ID)
#define SYSVIEW_UAC_MIC_PIPE_HANDLE_START() SEGGER_SYSVIEW_OnUserStart(SYSVIEW_MARKER_UAC_MIC_PIPE_HANDLE_ID)
#define SYSVIEW_UAC_MIC_PIPE_HANDLE_STOP() SEGGER_SYSVIEW_OnUserStop(SYSVIEW_MARKER_UAC_MIC_PIPE_HANDLE_ID)
#define SYSVIEW_UAC_SPK_PIPE_HANDLE_START() SEGGER_SYSVIEW_OnUserStart(SYSVIEW_MARKER_UAC_SPK_PIPE_HANDLE_ID)
#define SYSVIEW_UAC_SPK_PIPE_HANDLE_STOP() SEGGER_SYSVIEW_OnUserStop(SYSVIEW_MARKER_UAC_SPK_PIPE_HANDLE_ID)
#else
#define SYSVIEW_DFLT_PIPE_XFER_START()
#define SYSVIEW_DFLT_PIPE_XFER_STOP()
#define SYSVIEW_UVC_PIPE_HANDLE_START()
#define SYSVIEW_UVC_PIPE_HANDLE_STOP()
#define SYSVIEW_UAC_MIC_PIPE_HANDLE_START()
#define SYSVIEW_UAC_MIC_PIPE_HANDLE_STOP()
#define SYSVIEW_UAC_SPK_PIPE_HANDLE_START()
#define SYSVIEW_UAC_SPK_PIPE_HANDLE_STOP()
#endif

View File

@ -0,0 +1,526 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <sys/queue.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hcd.h"
#include "usb/usb_types_ch9.h"
#include "usb/usb_types_stack.h"
#ifdef __cplusplus
extern "C" {
#endif
// ------------------------------------------------------ Types --------------------------------------------------------
// ----------------------- Handles -------------------------
/**
* @brief Handle of a allocated endpoint
*/
typedef struct usbh_ep_handle_s *usbh_ep_handle_t;
// ----------------------- Events --------------------------
typedef enum {
USBH_EVENT_DEV_NEW, /**< A new device has been enumerated and added to the device pool */
USBH_EVENT_DEV_GONE, /**< A device is gone. Clients should close the device */
USBH_EVENT_DEV_ALL_FREE, /**< All devices have been freed */
} usbh_event_t;
/**
* @brief Endpoint events
*
* @note Optimization: Keep this identical to hcd_pipe_event_t
*/
typedef enum {
USBH_EP_EVENT_NONE, /**< The EP has no events (used to indicate no events when polling) */
USBH_EP_EVENT_URB_DONE, /**< The EP has completed a URB. The URB can be dequeued */
USBH_EP_EVENT_ERROR_XFER, /**< The EP encountered excessive errors when transferring a URB i.e., three three consecutive transaction errors (e.g., no ACK, bad CRC etc) */
USBH_EP_EVENT_ERROR_URB_NOT_AVAIL, /**< The EP tried to execute a transfer but no URB was available */
USBH_EP_EVENT_ERROR_OVERFLOW, /**< The EP received more data than requested. Usually a Packet babble error (i.e., an IN packet has exceeded the EP's MPS) */
USBH_EP_EVENT_ERROR_STALL, /**< EP received a STALL response */
} usbh_ep_event_t;
/**
* @brief Hub driver events for the USBH
*
* These events as passed by the Hub driver to the USBH via usbh_hub_pass_event()
*
* USBH_HUB_EVENT_PORT_ERROR:
* - The port has encountered an error (such as a sudden disconnection). The device connected to that port is no longer valid.
* - The USBH should:
* - Trigger a USBH_EVENT_DEV_GONE
* - Prevent further transfers to the device
* - Trigger the device's cleanup if it is already closed
* - When the last client closes the device via usbh_dev_close(), free the device object and issue a USBH_HUB_REQ_PORT_RECOVER request
*
* USBH_HUB_EVENT_PORT_DISABLED:
* - A previous USBH_HUB_REQ_PORT_DISABLE has completed.
* - The USBH should free the device object
*/
typedef enum {
USBH_HUB_EVENT_PORT_ERROR, /**< The port has encountered an error (such as a sudden disconnection). The device
connected to that port should be marked gone. */
USBH_HUB_EVENT_PORT_DISABLED, /**< Previous USBH_HUB_REQ_PORT_DISABLE request completed */
} usbh_hub_event_t;
// ------------------ Requests/Commands --------------------
/**
* @brief Hub driver requests
*
* Various requests of the Hub driver that the USBH can make.
*/
typedef enum {
USBH_HUB_REQ_PORT_DISABLE, /**< Request that the Hub driver disable a particular port (occurs after a device
has been freed). Hub driver should respond with a USBH_HUB_EVENT_PORT_DISABLED */
USBH_HUB_REQ_PORT_RECOVER, /**< Request that the Hub driver recovers a particular port (occurs after a gone
device has been freed). */
} usbh_hub_req_t;
/**
* @brief Endpoint commands
*
* @note Optimization: Keep this identical to hcd_pipe_cmd_t
*/
typedef enum {
USBH_EP_CMD_HALT, /**< Halt an active endpoint. Any currently executing URB will be canceled. Enqueued URBs are left untouched */
USBH_EP_CMD_FLUSH, /**< Can only be called when halted. Will cause all enqueued URBs to be canceled */
USBH_EP_CMD_CLEAR, /**< Causes a halted endpoint to become active again. Any enqueued URBs will being executing.*/
} usbh_ep_cmd_t;
// ---------------------- Callbacks ------------------------
/**
* @brief Callback used to indicate completion of control transfers submitted usbh_dev_submit_ctrl_urb()
* @note This callback is called from within usbh_process()
*/
typedef void (*usbh_ctrl_xfer_cb_t)(usb_device_handle_t dev_hdl, urb_t *urb, void *arg);
/**
* @brief Callback used to indicate that the USBH has an event
*
* @note This callback is called from within usbh_process()
* @note On a USBH_EVENT_DEV_ALL_FREE event, the dev_hdl argument is set to NULL
*/
typedef void (*usbh_event_cb_t)(usb_device_handle_t dev_hdl, usbh_event_t usbh_event, void *arg);
/**
* @brief Callback used by the USBH to request actions from the Hub driver
*
* The Hub Request Callback allows the USBH to request the Hub actions on a particular port. Conversely, the Hub driver
* will indicate completion of some of these requests to the USBH via the usbh_hub_event() funtion.
*/
typedef void (*usbh_hub_req_cb_t)(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg);
/**
* @brief Callback used to indicate an event on an endpoint
*
* Return whether to yield or not if called from an ISR. Always return false if not called from an ISR
*/
typedef bool (*usbh_ep_cb_t)(usbh_ep_handle_t ep_hdl, usbh_ep_event_t ep_event, void *arg, bool in_isr);
// ----------------------- Objects -------------------------
/**
* @brief Configuration for an endpoint being allocated using usbh_ep_alloc()
*/
typedef struct {
uint8_t bInterfaceNumber; /**< Interface number */
uint8_t bAlternateSetting; /**< Alternate setting number of the interface */
uint8_t bEndpointAddress; /**< Endpoint address */
usbh_ep_cb_t ep_cb; /**< Endpoint event callback */
void *ep_cb_arg; /**< Endpoint callback argument */
void *context; /**< Endpoint context */
} usbh_ep_config_t;
/**
* @brief USBH configuration used in usbh_install()
*/
typedef struct {
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
void *proc_req_cb_arg; /**< Processing request callback argument */
usbh_ctrl_xfer_cb_t ctrl_xfer_cb; /**< Control transfer callback */
void *ctrl_xfer_cb_arg; /**< Control transfer callback argument */
usbh_event_cb_t event_cb; /**< USBH event callback */
void *event_cb_arg; /**< USBH event callback argument */
} usbh_config_t;
// ------------------------------------------------- USBH Functions ----------------------------------------------------
/**
* @brief Installs the USBH driver
*
* - This function will internally install the HCD
* - This must be called before calling any Hub driver functions
*
* @note Before calling this function, the Host Controller must already be un-clock gated and reset. The USB PHY
* (internal or external, and associated GPIOs) must already be configured.
* @param usbh_config USBH driver configuration
* @return esp_err_t
*/
esp_err_t usbh_install(const usbh_config_t *usbh_config);
/**
* @brief Uninstall the USBH driver
*
* - This function will uninstall the HCD
* - The Hub driver must be uninstalled before calling this function
*
* @note This function will simply free the resources used by the USBH. The underlying Host Controller and USB PHY will
* not be disabled.
* @return esp_err_t
*/
esp_err_t usbh_uninstall(void);
/**
* @brief USBH processing function
*
* - USBH processing function that must be called repeatedly to process USBH events
* - If blocking, the caller can block until the proc_req_cb() is called with USB_PROC_REQ_SOURCE_USBH as the request
* source. The USB_PROC_REQ_SOURCE_USBH source indicates that this function should be called.
*
* @note This function can block
* @return esp_err_t
*/
esp_err_t usbh_process(void);
/**
* @brief Get the current number of devices
*
* @note This function can block
* @param[out] num_devs_ret Current number of devices
* @return esp_err_t
*/
esp_err_t usbh_num_devs(int *num_devs_ret);
// ------------------------------------------------ Device Functions ---------------------------------------------------
// --------------------- Device Pool -----------------------
/**
* @brief Fill list with address of currently connected devices
*
* - This function fills the provided list with the address of current connected devices
* - Device address can then be used in usbh_dev_open()
* - If there are more devices than the list_len, this function will only fill
* up to list_len number of devices.
*
* @param[in] list_len Length of empty list
* @param[inout] dev_addr_list Empty list to be filled
* @param[out] num_dev_ret Number of devices filled into list
* @return esp_err_t
*/
esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret);
/**
* @brief Open a device by address
*
* A device must be opened before it can be used
*
* @param[in] dev_addr Device address
* @param[out] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_dev_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl);
/**
* @brief CLose a device
*
* Device can be opened by calling usbh_dev_open()
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl);
/**
* @brief Mark that all devices should be freed at the next possible opportunity
*
* A device marked as free will not be freed until the last client using the device has called usbh_dev_close()
*
* @return
* - ESP_OK: There were no devices to free to begin with. Current state is all free
* - ESP_ERR_NOT_FINISHED: One or more devices still need to be freed (but have been marked "to be freed")
*/
esp_err_t usbh_dev_mark_all_free(void);
// ------------------- Single Device ----------------------
/**
* @brief Get a device's address
*
* @note Can be called without opening the device
*
* @param[in] dev_hdl Device handle
* @param[out] dev_addr Device's address
* @return esp_err_t
*/
esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr);
/**
* @brief Get a device's information
*
* @note This function can block
* @param[in] dev_hdl Device handle
* @param[out] dev_info Device information
* @return esp_err_t
*/
esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_info);
/**
* @brief Get a device's device descriptor
*
* - The device descriptor is cached when the device is created by the Hub driver
*
* @param[in] dev_hdl Device handle
* @param[out] dev_desc_ret Device descriptor
* @return esp_err_t
*/
esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t **dev_desc_ret);
/**
* @brief Get a device's active configuration descriptor
*
* Simply returns a reference to the internally cached configuration descriptor
*
* @note This function can block
* @param[in] dev_hdl Device handle
* @param config_desc_ret
* @return esp_err_t
*/
esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc_ret);
/**
* @brief Submit a control transfer (URB) to a device
*
* @param[in] dev_hdl Device handle
* @param[in] urb URB
* @return esp_err_t
*/
esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb);
// ----------------------------------------------- Endpoint Functions -------------------------------------------------
/**
* @brief Allocate an endpoint on a device
*
* This function allows clients to allocate a non-default endpoint (i.e., not EP0) on a connected device
*
* - A client must have opened the device using usbh_dev_open() before attempting to allocate an endpoint on the device
* - A client should call this function to allocate all endpoints in an interface that the client has claimed.
* - A client must allocate an endpoint using this function before attempting to communicate with it
* - Once the client allocates an endpoint, the client is now owns/manages the endpoint. No other client should use or
* deallocate the endpoint.
*
* @note This function can block
* @note Default endpoints (EP0) are owned by the USBH. For control transfers, use usbh_dev_submit_ctrl_urb() instead
*
* @param[in] dev_hdl Device handle
* @param[in] ep_config Endpoint configuration
* @param[out] ep_hdl_ret Endpoint handle
* @return esp_err_t
*/
esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, usbh_ep_handle_t *ep_hdl_ret);
/**
* @brief Free and endpoint on a device
*
* This function frees an endpoint previously allocated by the client using usbh_ep_alloc()
*
* - Only the client that allocated the endpoint should free it
* - The client must have halted and flushed the endpoint using usbh_ep_command() before attempting to free it
* - The client must ensure that there are no more function calls to the endpoint before freeing it
*
* @note This function can block
* @param[in] ep_hdl Endpoint handle
* @return esp_err_t
*/
esp_err_t usbh_ep_free(usbh_ep_handle_t ep_hdl);
/**
* @brief Get the handle of an endpoint using its address
*
* The endpoint must have been previously allocated using usbh_ep_alloc()
*
* @param[in] dev_hdl Device handle
* @param[in] bEndpointAddress Endpoint address
* @param[out] ep_hdl_ret Endpoint handle
* @return esp_err_t
*/
esp_err_t usbh_ep_get_handle(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress, usbh_ep_handle_t *ep_hdl_ret);
/**
* @brief Enqueue a URB to an endpoint
*
* The URB will remain enqueued until it completes (successfully or errors out). Use usbh_ep_dequeue_urb() to dequeue
* a completed URB.
*
* @param[in] ep_hdl Endpoint handle
* @param[in] urb URB to enqueue
* @return esp_err_t
*/
esp_err_t usbh_ep_enqueue_urb(usbh_ep_handle_t ep_hdl, urb_t *urb);
/**
* @brief Dequeue a URB from an endpoint
*
* Dequeue a completed URB from an endpoint. The USBH_EP_EVENT_URB_DONE indicates that URBs can be dequeued
*
* @param[in] ep_hdl Endpoint handle
* @param[out] urb_ret Dequeued URB, or NULL if no more URBs to dequeue
* @return esp_err_t
*/
esp_err_t usbh_ep_dequeue_urb(usbh_ep_handle_t ep_hdl, urb_t **urb_ret);
/**
* @brief Execute a command on a particular endpoint
*
* Endpoint commands allows executing a certain action on an endpoint (e.g., halting, flushing, clearing etc)
*
* @param[in] ep_hdl Endpoint handle
* @param[in] command Endpoint command
* @return esp_err_t
*/
esp_err_t usbh_ep_command(usbh_ep_handle_t ep_hdl, usbh_ep_cmd_t command);
/**
* @brief Get the context of an endpoint
*
* Get the context variable assigned to and endpoint on allocation.
*
* @note This function can block
* @param[in] ep_hdl Endpoint handle
* @return Endpoint context
*/
void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl);
// -------------------------------------------------- Hub Functions ----------------------------------------------------
// ------------------- Device Related ----------------------
/**
* @brief Indicates to USBH that the Hub driver is installed
*
* - The Hub driver must call this function in its installation to indicate the the USBH that it has been installed.
* - This should only be called after the USBH has already be installed
*
* @note Hub Driver only
* @param[in] hub_req_callback Hub request callback
* @param[in] callback_arg Callback argument
* @return esp_err_t
*/
esp_err_t usbh_hub_is_installed(usbh_hub_req_cb_t hub_req_callback, void *callback_arg);
/**
* @brief Indicates to USBH the start of enumeration for a device
*
* - The Hub driver calls this function before it starts enumerating a new device.
* - The USBH will allocate a new device that will be initialized by the Hub driver using the remaining hub enumeration
* functions.
* - The new device's default pipe handle is returned to all the Hub driver to be used during enumeration.
*
* @note Hub Driver only
* @param[in] port_hdl Handle of the port that the device is connected to
* @param[in] dev_speed Device's speed
* @param[out] new_dev_hdl Device's handle
* @param[out] default_pipe_hdl Device's default pipe handle
* @return esp_err_t
*/
esp_err_t usbh_hub_add_dev(hcd_port_handle_t port_hdl, usb_speed_t dev_speed, usb_device_handle_t *new_dev_hdl, hcd_pipe_handle_t *default_pipe_hdl);
/**
* @brief Indicates to the USBH that a hub event has occurred for a particular device
*
* @param dev_hdl Device handle
* @param hub_event Hub event
* @return esp_err_t
*/
esp_err_t usbh_hub_pass_event(usb_device_handle_t dev_hdl, usbh_hub_event_t hub_event);
// ----------------- Enumeration Related -------------------
/**
* @brief Assign the enumerating device's address
*
* @note Hub Driver only
* @note Must call in sequence
* @param[in] dev_hdl Device handle
* @param dev_addr
* @return esp_err_t
*/
esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
/**
* @brief Fill the enumerating device's descriptor
*
* @note Hub Driver only
* @note Must call in sequence
* @param[in] dev_hdl Device handle
* @param device_desc
* @return esp_err_t
*/
esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc);
/**
* @brief Fill the enumerating device's active configuration descriptor
*
* @note Hub Driver only
* @note Must call in sequence
* @note This function can block
* @param[in] dev_hdl Device handle
* @param config_desc_full
* @return esp_err_t
*/
esp_err_t usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full);
/**
* @brief Fill one of the string descriptors of the enumerating device
*
* @note Hub Driver only
* @note Must call in sequence
* @param dev_hdl Device handle
* @param str_desc Pointer to string descriptor
* @param select Select which string descriptor. 0/1/2 for Manufacturer/Product/Serial Number string descriptors respectively
* @return esp_err_t
*/
esp_err_t usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select);
/**
* @brief Indicate the device enumeration is completed
*
* This will all the device to be opened by clients, and also trigger a USBH_EVENT_DEV_NEW event.
*
* @note Hub Driver only
* @note Must call in sequence
* @note This function can block
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_hub_enum_done(usb_device_handle_t dev_hdl);
/**
* @brief Indicate that device enumeration has failed
*
* This will cause the enumerating device's resources to be cleaned up
* The Hub Driver must guarantee that the enumerating device's default pipe is already halted, flushed, and dequeued.
*
* @note Hub Driver only
* @note Must call in sequence
* @note This function can block
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_hub_enum_failed(usb_device_handle_t dev_hdl);
#ifdef __cplusplus
}
#endif