main
This commit is contained in:
136
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/scripts/check_versions.sh
vendored
Normal file
136
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/scripts/check_versions.sh
vendored
Normal 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
|
15
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/workflows/arduino_lint.yml
vendored
Normal file
15
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/workflows/arduino_lint.yml
vendored
Normal 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
|
30
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/workflows/check_versions.yml
vendored
Normal file
30
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/workflows/check_versions.yml
vendored
Normal 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 }}
|
||||
|
14
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/workflows/pre-commit.yml
vendored
Normal file
14
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.github/workflows/pre-commit.yml
vendored
Normal 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
|
71
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.gitignore
vendored
Normal file
71
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.gitignore
vendored
Normal 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
|
1
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.piopm
Normal file
1
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/.piopm
Normal 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}}
|
@ -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']
|
@ -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
|
69
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/README.md
Normal file
69
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/README.md
Normal file
@ -0,0 +1,69 @@
|
||||
[](https://github.com/esp-arduino-libs/ESP32_USB_Stream/actions/workflows/arduino_lint.yml) [](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).
|
@ -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/'
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
202
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/license.txt
Normal file
202
.pio/libdeps/esp32-s3-devkitc-1/ESP32_USB_STREAM/license.txt
Normal 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.
|
@ -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");
|
||||
}
|
@ -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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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);
|
@ -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
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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
@ -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
|
@ -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
|
@ -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
|
Reference in New Issue
Block a user