Merge branch 'refs/heads/master' into audio_open

This commit is contained in:
hathach
2025-12-12 21:38:48 +07:00
59 changed files with 2070 additions and 1578 deletions

View File

@ -9,7 +9,7 @@ jobs:
executor: continuation/default
docker:
- image: cimg/base:current
resource_class: small
resource_class: large
steps:
- checkout
- run:
@ -18,48 +18,76 @@ jobs:
MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py)
echo "MATRIX_JSON=$MATRIX_JSON"
BUILDSYSTEM_TOOLCHAIN=(
"cmake aarch64-gcc"
"cmake arm-clang"
"cmake arm-gcc"
"cmake esp-idf"
"cmake msp430-gcc"
"cmake riscv-gcc"
BUILDSYSTEM_LIST=(
"cmake"
"make"
)
TOOLCHAIN_LIST=(
"aarch64-gcc"
"arm-clang"
"arm-gcc"
"esp-idf"
"msp430-gcc"
"riscv-gcc"
)
# only build IAR if not forked PR, since IAR token is not shared
if [ -z $CIRCLE_PR_USERNAME ]; then
BUILDSYSTEM_TOOLCHAIN+=("cmake arm-iar")
TOOLCHAIN_LIST+=("arm-iar")
fi
gen_build_entry() {
local build_system="$1"
local toolchain="$2"
local family="$3"
local resource_class="$4"
local build_args=""
if [[ "$toolchain" == "arm-iar" || "$build_system" == "make" ]]; then
build_args="--one-random"
fi
if [[ "$toolchain" == "esp-idf" ]]; then
echo " - build-vm:" >> .circleci/config2.yml
else
echo " - build:" >> .circleci/config2.yml
fi
echo " matrix:" >> .circleci/config2.yml
echo " alias: build-${build_system}-${toolchain}" >> .circleci/config2.yml
echo " parameters:" >> .circleci/config2.yml
echo " build-system: ['$build_system']" >> .circleci/config2.yml
echo " toolchain: ['$toolchain']" >> .circleci/config2.yml
echo " family: $family" >> .circleci/config2.yml
echo " resource_class: ['$resource_class']" >> .circleci/config2.yml
echo " resource_class: ['large']" >> .circleci/config2.yml
echo " build-args: ['$build_args']" >> .circleci/config2.yml
}
for e in "${BUILDSYSTEM_TOOLCHAIN[@]}"; do
e_arr=($e)
build_system="${e_arr[0]}"
toolchain="${e_arr[1]}"
FAMILY=$(echo $MATRIX_JSON | jq -r ".\"$toolchain\"")
echo "FAMILY_${toolchain}=$FAMILY"
# Collect all build aliases for code-metrics requires (cmake only, exclude esp-idf)
BUILD_ALIASES=()
gen_build_entry "$build_system" "$toolchain" "$FAMILY" "large"
for build_system in "${BUILDSYSTEM_LIST[@]}"; do
for toolchain in "${TOOLCHAIN_LIST[@]}"; do
# make does not support these toolchains
if [ "$build_system" == "make" ] && { [ "$toolchain" == "arm-clang" ] || [ "$toolchain" == "arm-iar" ] || [ "$toolchain" == "esp-idf" ]; }; then
continue
fi
FAMILY=$(echo $MATRIX_JSON | jq -r ".\"$toolchain\"")
echo "FAMILY_${toolchain}=$FAMILY"
gen_build_entry "$build_system" "$toolchain" "$FAMILY"
# Only add cmake builds: excluding esp-idf or build_args="--one-random" to metrics requirements
if [ "$build_system" == "cmake" ] && [ "$toolchain" != "esp-idf" ] && [ "$toolchain" != "arm-iar" ]; then
BUILD_ALIASES+=("build-${build_system}-${toolchain}")
fi
done
done
# Add code-metrics job that requires all build jobs
echo " - code-metrics:" >> .circleci/config2.yml
echo " requires:" >> .circleci/config2.yml
for alias in "${BUILD_ALIASES[@]}"; do
echo " - $alias" >> .circleci/config2.yml
done
- continuation/continue:
@ -67,9 +95,5 @@ jobs:
workflows:
set-matrix:
# Only build PR here, Push will be built by github action.
when:
and:
- not: << pipeline.git.branch.is_default >>
jobs:
- set-matrix

View File

@ -66,6 +66,9 @@ commands:
type: string
family:
type: string
build-args:
type: string
default: ""
steps:
- checkout
@ -107,7 +110,7 @@ commands:
no_output_timeout: 20m
command: |
if [ << parameters.toolchain >> == esp-idf ]; then
docker run --rm -v $PWD:/project -w /project espressif/idf:v5.3.2 python tools/build.py << parameters.family >>
docker run --rm -v $PWD:/project -w /project espressif/idf:v5.3.2 python tools/build.py << parameters.build-args >> << parameters.family >>
else
# Toolchain option default is gcc
if [ << parameters.toolchain >> == arm-clang ]; then
@ -119,9 +122,40 @@ commands:
TOOLCHAIN_OPTION="--toolchain gcc"
fi
python tools/build.py -s << parameters.build-system >> $TOOLCHAIN_OPTION << parameters.family >>
# circleci docker return $nproc as 36 core, limit parallel to 4 (resource-class = large)
# Required for IAR, also prevent crashed/killed by docker
python tools/build.py -s << parameters.build-system >> $TOOLCHAIN_OPTION -j 4 << parameters.build-args >> << parameters.family >>
fi
# Only collect and persist metrics for cmake builds (excluding esp-idf and --one-random)
- when:
condition:
and:
- equal: [ cmake, << parameters.build-system >> ]
- not:
equal: [ esp-idf, << parameters.toolchain >> ]
- not:
equal: [ arm-iar, << parameters.toolchain >> ]
steps:
- run:
name: Collect Metrics
command: |
# Create unique directory per toolchain to avoid workspace conflicts
METRICS_DIR="/tmp/metrics/<< parameters.toolchain >>"
mkdir -p "${METRICS_DIR}"
# Copy all metrics.json files
for f in cmake-build/cmake-build-*/metrics.json; do
if [ -f "$f" ]; then
BOARD_DIR=$(dirname "$f" | xargs basename)
cp "$f" "${METRICS_DIR}/${BOARD_DIR}.json"
fi
done
- persist_to_workspace:
root: /tmp
paths:
- metrics/<< parameters.toolchain >>
jobs:
# Build using docker
build:
@ -135,9 +169,13 @@ jobs:
type: string
family:
type: string
build-args:
type: string
default: ""
docker:
- image: cimg/base:current
working_directory: ~/project/tinyusb
resource_class: << parameters.resource_class >>
steps:
@ -145,6 +183,7 @@ jobs:
build-system: << parameters.build-system >>
toolchain: << parameters.toolchain >>
family: << parameters.family >>
build-args: << parameters.build-args >>
# Build using VM
build-vm:
@ -158,9 +197,13 @@ jobs:
type: string
family:
type: string
build-args:
type: string
default: ""
machine:
image: ubuntu-2404:current
working_directory: ~/project/tinyusb
resource_class: << parameters.resource_class >>
steps:
@ -168,21 +211,80 @@ jobs:
build-system: << parameters.build-system >>
toolchain: << parameters.toolchain >>
family: << parameters.family >>
build-args: << parameters.build-args >>
# Aggregate code metrics from all builds
code-metrics:
docker:
- image: cimg/python:3.12
resource_class: large
steps:
- checkout
- attach_workspace:
at: /tmp
- run:
name: Aggregate Code Metrics
command: |
python tools/get_deps.py
# Combine all metrics files from all toolchain subdirectories
ls -R /tmp/metrics
if ls /tmp/metrics/*/*.json 1> /dev/null 2>&1; then
python tools/metrics.py combine -j -m -f tinyusb/src /tmp/metrics/*/*.json
else
echo "No metrics files found"
exit 1
fi
- store_artifacts:
path: metrics.json
destination: metrics.json
# Compare with base master metrics on PR branches
- when:
condition:
not:
equal: [ master, << pipeline.git.branch >> ]
steps:
- run:
name: Download Base Branch Metrics
command: |
# Download metrics.json artifact from the latest successful build on master branch
mkdir -p base-metrics
# Use CircleCI API to get the latest artifact
curl -s -L "https://dl.circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/latest/artifacts?branch=master&filter=successful" \
-H "Circle-Token: ${CIRCLE_TOKEN:-}" | \
jq -r '.items[] | select(.path == "metrics.json") | .url' | \
head -1 | xargs -I {} curl -s -L -o base-metrics/metrics.json {} || true
- run:
name: Compare with Base Branch
command: |
if [ -f base-metrics/metrics.json ]; then
python tools/metrics.py compare -f tinyusb/src base-metrics/metrics.json metrics.json
cat metrics_compare.md
else
echo "No base metrics found, skipping comparison"
cp metrics.md metrics_compare.md
fi
- store_artifacts:
path: metrics_compare.md
destination: metrics_compare.md
workflows:
build:
jobs:
# The jobs below are populated dynamically by config.yml set-matrix job
# Example entries that will be generated:
# - build:
# matrix:
# alias: build-cmake-arm-gcc
# parameters:
# toolchain: [ 'arm-gcc' ]
# build-system: [ 'cmake' ]
# family: [ 'nrf' ]
# resource_class: ['large']
# - build-vm:
# matrix:
# parameters:
# toolchain: ['esp-idf']
# build-system: ['cmake']
# family: ['-bespressif_kaluga_1']
# resource_class: ['large']
# - code-metrics:
# requires:
# - build-cmake-arm-gcc

View File

@ -56,12 +56,12 @@ jobs:
echo "hil_matrix=$HIL_MATRIX_JSON"
echo "hil_matrix=$HIL_MATRIX_JSON" >> $GITHUB_OUTPUT
# ---------------------------------------
# Build CMake: only build on push with one-per-family.
# Full built is done by CircleCI in PR
# ---------------------------------------
# ------------------------------------------------------------------------------
# CMake build: only one board per family (first alphabetically). Full build is done by CircleCI in PR
# Note:
# For Make and IAR build: will be done on CircleCI only (one random per family as well)
# ------------------------------------------------------------------------------
cmake:
if: github.event_name == 'push'
needs: set-matrix
uses: ./.github/workflows/build_util.yml
strategy:
@ -71,59 +71,84 @@ jobs:
- 'aarch64-gcc'
#- 'arm-clang'
- 'arm-gcc'
- 'esp-idf'
#- 'esp-idf'
- 'msp430-gcc'
- 'riscv-gcc'
with:
build-system: 'cmake'
toolchain: ${{ matrix.toolchain }}
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
one-per-family: true
build-options: '--one-first'
upload-metrics: true
# ---------------------------------------
# Build Make: only build on push with one-per-family
# ---------------------------------------
make:
if: github.event_name == 'push'
needs: set-matrix
uses: ./.github/workflows/build_util.yml
strategy:
fail-fast: false
matrix:
toolchain:
- 'aarch64-gcc'
#- 'arm-clang'
- 'arm-gcc'
- 'msp430-gcc'
- 'riscv-gcc'
- 'rx-gcc'
with:
build-system: 'make'
toolchain: ${{ matrix.toolchain }}
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
one-per-family: true
code-metrics:
needs: cmake
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Checkout TinyUSB
uses: actions/checkout@v4
# ---------------------------------------
# Build IAR
# Since IAR Token secret is not passed to forked PR, only build non-forked PR with make.
# cmake is built by circle-ci. Due to IAR limit capacity, only build oe per family
# ---------------------------------------
arm-iar:
if: false # disable for now since we got reach capacity limit too often
#if: github.event_name == 'push' && github.repository_owner == 'hathach'
needs: set-matrix
uses: ./.github/workflows/build_util.yml
secrets: inherit
strategy:
fail-fast: false
matrix:
build-system:
- 'make'
with:
build-system: ${{ matrix.build-system }}
toolchain: 'arm-iar'
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)['arm-iar']) }}
one-per-family: true
- name: Download Artifacts
uses: actions/download-artifact@v5
with:
pattern: metrics-*
path: cmake-build
merge-multiple: true
- name: Aggregate Code Metrics
run: |
python tools/get_deps.py
python tools/metrics.py combine -j -m -f tinyusb/src cmake-build/*/metrics.json
- name: Upload Metrics Artifact
if: github.event_name == 'push'
uses: actions/upload-artifact@v5
with:
name: metrics-tinyusb
path: metrics.json
- name: Download Base Branch Metrics
if: github.event_name != 'push'
uses: dawidd6/action-download-artifact@v11
with:
workflow: build.yml
branch: ${{ github.base_ref }}
name: metrics-tinyusb
path: base-metrics
continue-on-error: true
- name: Compare with Base Branch
if: github.event_name != 'push'
run: |
if [ -f base-metrics/metrics.json ]; then
python tools/metrics.py compare -m -f tinyusb/src base-metrics/metrics.json metrics.json
cat metrics_compare.md
else
echo "No base metrics found, skipping comparison"
cp metrics.md metrics_compare.md
fi
- name: Save PR number
if: github.event_name == 'pull_request'
run: echo ${{ github.event.number }} > pr_number.txt
- name: Upload Metrics Comment Artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v5
with:
name: metrics-comment
path: |
metrics_compare.md
pr_number.txt
- name: Post Code Metrics as PR Comment
if: github.event_name != 'push'
uses: marocchino/sticky-pull-request-comment@v2
with:
header: code-metrics
path: metrics_compare.md
# ---------------------------------------
# Build Make/CMake on Windows/MacOS
@ -134,14 +159,13 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]
build-system: [ 'make', 'cmake' ]
os: [ windows-latest, macos-latest ]
with:
os: ${{ matrix.os }}
build-system: ${{ matrix.build-system }}
build-system: 'cmake-make'
toolchain: 'arm-gcc-${{ matrix.os }}'
build-args: '["stm32h7"]'
one-per-family: true
build-options: '--one-random'
# ---------------------------------------
# Zephyr
@ -184,7 +208,6 @@ jobs:
build-system: 'cmake'
toolchain: ${{ matrix.toolchain }}
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.hil_json)[matrix.toolchain]) }}
one-per-family: true
upload-artifacts: true
# ---------------------------------------
@ -192,11 +215,9 @@ jobs:
# self-hosted on local VM, for attached hardware checkout HIL_JSON
# ---------------------------------------
hil-tinyusb:
if: |
github.repository_owner == 'hathach' &&
(github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch')
if: github.repository_owner == 'hathach' && github.event_name != 'push'
needs: hil-build
runs-on: [self-hosted, X64, hathach, hardware-in-the-loop]
runs-on: [ self-hosted, X64, hathach, hardware-in-the-loop ]
steps:
- name: Get Skip Boards from previous run
if: github.run_attempt != '1'
@ -221,6 +242,7 @@ jobs:
- name: Download Artifacts
uses: actions/download-artifact@v5
with:
pattern: binaries-*
path: cmake-build
merge-multiple: true
@ -237,8 +259,8 @@ jobs:
if: |
github.repository_owner == 'hathach' &&
github.event.pull_request.head.repo.fork == false &&
(github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch')
runs-on: [self-hosted, Linux, X64, hifiphile]
github.event_name != 'push'
runs-on: [ self-hosted, Linux, X64, hifiphile ]
env:
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
steps:

View File

@ -12,11 +12,15 @@ on:
build-args:
required: true
type: string
one-per-family:
build-options:
required: false
default: ''
type: string
upload-artifacts:
required: false
default: false
type: boolean
upload-artifacts:
upload-metrics:
required: false
default: false
type: boolean
@ -47,16 +51,6 @@ jobs:
with:
arg: ${{ matrix.arg }}
- name: Set build one-per-family option
id: set-one-per-family
run: |
if [[ "${{ inputs.one-per-family }}" == "true" ]]; then
BUILD_OPTION="--one-per-family"
fi
echo "build_option=$BUILD_OPTION"
echo "build_option=$BUILD_OPTION" >> $GITHUB_OUTPUT
shell: bash
- name: Build
env:
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
@ -64,16 +58,26 @@ jobs:
run: |
if [ "$TOOLCHAIN" == "esp-idf" ]; then
docker run --rm -v $PWD:/project -w /project espressif/idf:tinyusb python tools/build.py ${{ matrix.arg }}
elif [ "${{ inputs.build-system }}" == "cmake-make" ] || [ "${{ inputs.build-system }}" == "make-cmake" ]; then
python tools/build.py -s make ${{ steps.setup-toolchain.outputs.build_option }} ${{ inputs.build-options }} ${{ matrix.arg }}
python tools/build.py -s cmake ${{ steps.setup-toolchain.outputs.build_option }} ${{ inputs.build-options }} ${{ matrix.arg }}
else
python tools/build.py -s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ steps.set-one-per-family.outputs.build_option }} ${{ matrix.arg }}
python tools/build.py -s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ inputs.build-options }} ${{ matrix.arg }}
fi
shell: bash
- name: Upload Artifacts for Metrics
if: ${{ inputs.upload-metrics }}
uses: actions/upload-artifact@v5
with:
name: metrics-${{ matrix.arg }}
path: cmake-build/cmake-build-*/metrics.json
- name: Upload Artifacts for Hardware Testing
if: ${{ inputs.upload-artifacts }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.arg }}
name: binaries-${{ matrix.arg }}
path: |
cmake-build/cmake-build-*/*/*/*.elf
cmake-build/cmake-build-*/*/*/*.bin

View File

@ -15,39 +15,29 @@ toolchain_list = [
# family: [supported toolchain]
family_list = {
"at32f402_405 at32f403a_407 at32f413 at32f415 at32f423 at32f425 at32f435_437": ["arm-gcc"],
"broadcom_32bit": ["arm-gcc"],
"at32f402_405 at32f403a_407 at32f413 at32f415 at32f423 at32f425 at32f435_437 broadcom_32bit da1469x": ["arm-gcc"],
"broadcom_64bit": ["aarch64-gcc"],
"ch32v10x ch32v20x ch32v30x fomu gd32vf103": ["riscv-gcc"],
"da1469x": ["arm-gcc"],
"imxrt": ["arm-gcc", "arm-clang"],
"kinetis_k kinetis_kl kinetis_k32l2": ["arm-gcc", "arm-clang"],
"lpc11 lpc13 lpc15": ["arm-gcc", "arm-clang"],
"lpc17 lpc18 lpc40 lpc43": ["arm-gcc", "arm-clang"],
"lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43": ["arm-gcc", "arm-clang"],
"lpc51 lpc54 lpc55": ["arm-gcc", "arm-clang"],
"maxim": ["arm-gcc"],
"mcx": ["arm-gcc"],
"mm32": ["arm-gcc"],
"maxim mcx mm32 msp432e4 tm4c": ["arm-gcc"],
"msp430": ["msp430-gcc"],
"msp432e4 tm4c": ["arm-gcc"],
"nrf": ["arm-gcc", "arm-clang"],
"nuc100_120 nuc121_125 nuc126 nuc505": ["arm-gcc"],
"nuc100_120 nuc121_125 nuc126 nuc505 xmc4000": ["arm-gcc"],
"ra": ["arm-gcc"],
"rp2040": ["arm-gcc"],
"rx": ["rx-gcc"],
"samd11 samd2x_l2x": ["arm-gcc", "arm-clang"],
"samd5x_e5x samg": ["arm-gcc", "arm-clang"],
"samd11 samd2x_l2x samd5x_e5x samg": ["arm-gcc", "arm-clang"],
"stm32c0 stm32f0 stm32f1 stm32f2 stm32f3": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32f4": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32f7": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32g0 stm32g4 stm32h5": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32h7": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32h7rs": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32l0 stm32l4": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32h7rs stm32l0 stm32l4": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32n6": ["arm-gcc"],
"stm32u0 stm32u5 stm32wb": ["arm-gcc", "arm-clang", "arm-iar"],
"stm32wba": ["arm-gcc", "arm-clang"],
"xmc4000": ["arm-gcc"],
"stm32u0 stm32u5 stm32wb stm32wba": ["arm-gcc", "arm-clang", "arm-iar"],
"-bespressif_s2_devkitc": ["esp-idf"],
# S3, P4 will be built by hil test
# "-bespressif_s3_devkitm": ["esp-idf"],

38
.github/workflows/metrics_comment.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Metrics Comment
on:
workflow_run:
workflows: ["Build"]
types:
- completed
jobs:
post-comment:
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
permissions:
pull-requests: write
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
name: metrics-comment
- name: Read PR Number
id: pr_number
run: |
if [ -f pr_number.txt ]; then
echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT
fi
- name: Post Code Metrics as PR Comment
if: steps.pr_number.outputs.number != ''
uses: marocchino/sticky-pull-request-comment@v2
with:
header: code-metrics
path: metrics_compare.md
number: ${{ steps.pr_number.outputs.number }}

5
.gitignore vendored
View File

@ -30,7 +30,8 @@ settings/
/examples/*/*/build*
test_old/
tests_obsolete/
_build
_build/
build/
/examples/*/*/ses
/examples/*/*/ozone
/examples/obsolete
@ -42,11 +43,11 @@ cov-int
*-build-dir
/_bin/
__pycache__
cmake-build/
cmake-build-*
sdkconfig
.PVS-Studio
.vscode/
build
CMakeFiles
Debug
RelWithDebInfo

1
.idea/cmake.xml generated
View File

@ -124,6 +124,7 @@
<configuration PROFILE_NAME="stm32h743eval" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32h743eval -DLOG=1 -DLOGGER=RTT -DTRACE_ETM=1" />
<configuration PROFILE_NAME="stm32h743eval-DMA" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32h743eval -DLOG=1 -DLOGGER=RTT -DTRACE_ETM=1 -DCFLAGS_CLI=&quot;-DCFG_TUD_DWC2_DMA_ENABLE=1&quot;" />
<configuration PROFILE_NAME="stm32h743eval_host1" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32h743eval -DRHPORT_HOST=1 -DLOG=1 -DLOGGER=RTT -DTRACE_ETM=1" />
<configuration PROFILE_NAME="stm32h743eval IAR" ENABLED="false" CONFIG_NAME="Debug" TOOLCHAIN_NAME="iccarm" GENERATION_OPTIONS="-DBOARD=stm32h743eval -DLOG=1 -DLOGGER=RTT -DTRACE_ETM=1 -DIAR_CSTAT=1" />
<configuration PROFILE_NAME="stm32h743nucleo" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32h743nucleo -DLOG=1" />
<configuration PROFILE_NAME="stm32l0538disco" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32l0538disco -DLOG=0 -DLOGGER=RTT" />
<configuration PROFILE_NAME="stm32l476disco" ENABLED="false" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBOARD=stm32l476disco -DLOG=1 -DLOGGER=RTT" />

View File

@ -40,7 +40,7 @@ Minimal Example
// init host stack on roothub port 1 for fullspeed host
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_DEVICE,
.role = TUSB_ROLE_HOST,
.speed = TUSB_SPEED_FULL
};
tusb_init(1, &host_init);

View File

@ -5,7 +5,25 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/../hw/bsp/family_support.cmake)
project(tinyusb_examples C CXX ASM)
add_subdirectory(device)
add_subdirectory(dual)
add_subdirectory(host)
add_subdirectory(typec)
set(EXAMPLES_LIST
device
dual
host
typec
)
set(MAPJSON_PATTERNS "")
foreach (example ${EXAMPLES_LIST})
add_subdirectory(${example})
list(APPEND MAPJSON_PATTERNS "${CMAKE_BINARY_DIR}/${example}/*/*.map.json")
endforeach ()
# Post-build: run metrics.py on all map.json files
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_custom_target(tinyusb_metrics
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../tools/metrics.py
combine -f tinyusb/src -j -o ${CMAKE_BINARY_DIR}/metrics
${MAPJSON_PATTERNS}
COMMENT "Generating average code size metrics"
VERBATIM
)

View File

@ -7,7 +7,6 @@ if (NOT DEFINED CMAKE_CXX_COMPILER)
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(TOOLCHAIN_ASM_FLAGS "-x assembler-with-cpp")
find_program(CMAKE_SIZE llvm-size)
find_program(CMAKE_OBJCOPY llvm-objcopy)

View File

@ -24,7 +24,8 @@ set(CMAKE_C_ICSTAT ${CMAKE_IAR_CSTAT}
--checks=${CMAKE_CURRENT_LIST_DIR}/cstat_sel_checks.txt
--db=${CMAKE_BINARY_DIR}/cstat.db
--sarif_dir=${CMAKE_BINARY_DIR}/cstat_sarif
--exclude ${TOP}/hw/mcu --exclude ${TOP}/lib
--exclude=${TOP}/hw/mcu
--exclude=${TOP}/lib
)
endif ()

View File

@ -20,37 +20,32 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_CPU}.cmake)
# ----------------------------------------------------------------------------
# Compile flags
# ----------------------------------------------------------------------------
set(TOOLCHAIN_C_FLAGS)
set(TOOLCHAIN_ASM_FLAGS)
set(TOOLCHAIN_EXE_LINKER_FLAGS)
if (TOOLCHAIN STREQUAL "gcc" OR TOOLCHAIN STREQUAL "clang")
list(APPEND TOOLCHAIN_COMMON_FLAGS
-fdata-sections
-ffunction-sections
# -fsingle-precision-constant # not supported by clang
-fno-strict-aliasing
-g # include debug info for bloaty
)
list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
-Wl,--print-memory-usage
-Wl,--gc-sections
-Wl,--cref
)
set(TOOLCHAIN_EXE_LINKER_FLAGS "-Wl,--print-memory-usage -Wl,--gc-sections -Wl,--cref")
if (TOOLCHAIN STREQUAL clang)
set(TOOLCHAIN_ASM_FLAGS "-x assembler-with-cpp")
endif ()
elseif (TOOLCHAIN STREQUAL "iar")
list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
--diag_suppress=Li065
)
set(TOOLCHAIN_C_FLAGS --debug)
set(TOOLCHAIN_EXE_LINKER_FLAGS --diag_suppress=Li065)
endif ()
# join the toolchain flags into a single string
list(JOIN TOOLCHAIN_COMMON_FLAGS " " TOOLCHAIN_COMMON_FLAGS)
foreach (LANG IN ITEMS C CXX ASM)
set(CMAKE_${LANG}_FLAGS_INIT ${TOOLCHAIN_COMMON_FLAGS})
# optimization flags for LOG, LOGGER ?
#set(CMAKE_${LANG}_FLAGS_RELEASE_INIT "-Os")
#set(CMAKE_${LANG}_FLAGS_DEBUG_INIT "-O0")
endforeach ()
# Assembler
if (DEFINED TOOLCHAIN_ASM_FLAGS)
set(CMAKE_ASM_FLAGS_INIT "${CMAKE_ASM_FLAGS_INIT} ${TOOLCHAIN_ASM_FLAGS}")
endif ()
# Linker
list(JOIN TOOLCHAIN_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS_INIT)
set(CMAKE_C_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_C_FLAGS}")
set(CMAKE_CXX_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_C_FLAGS}")
set(CMAKE_ASM_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_ASM_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_INIT ${TOOLCHAIN_EXE_LINKER_FLAGS})

View File

@ -31,6 +31,9 @@ CFLAGS += \
-Wreturn-type \
-Wredundant-decls \
CFLAGS_CLANG += \
-Wno-error=unknown-warning-option
# -Wmissing-prototypes \
# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion
# -Wconversion

View File

@ -6,31 +6,38 @@ project(tinyusb_device_examples C CXX ASM)
family_initialize_project(tinyusb_device_examples ${CMAKE_CURRENT_LIST_DIR})
# family_add_subdirectory will filter what to actually add based on selected FAMILY
family_add_subdirectory(audio_4_channel_mic)
family_add_subdirectory(audio_test)
family_add_subdirectory(audio_4_channel_mic_freertos)
family_add_subdirectory(audio_test_freertos)
family_add_subdirectory(audio_test_multi_rate)
family_add_subdirectory(board_test)
family_add_subdirectory(cdc_dual_ports)
family_add_subdirectory(cdc_msc)
family_add_subdirectory(cdc_msc_freertos)
family_add_subdirectory(cdc_uac2)
family_add_subdirectory(dfu)
family_add_subdirectory(dfu_runtime)
family_add_subdirectory(dynamic_configuration)
family_add_subdirectory(hid_boot_interface)
family_add_subdirectory(hid_composite)
family_add_subdirectory(hid_composite_freertos)
family_add_subdirectory(hid_generic_inout)
family_add_subdirectory(hid_multiple_interface)
family_add_subdirectory(midi_test)
family_add_subdirectory(msc_dual_lun)
family_add_subdirectory(mtp)
family_add_subdirectory(net_lwip_webserver)
family_add_subdirectory(uac2_headset)
family_add_subdirectory(uac2_speaker_fb)
family_add_subdirectory(usbtmc)
family_add_subdirectory(video_capture)
family_add_subdirectory(video_capture_2ch)
family_add_subdirectory(webusb_serial)
set(EXAMPLE_LIST
audio_4_channel_mic
audio_4_channel_mic_freertos
audio_test
audio_test_freertos
audio_test_multi_rate
board_test
cdc_dual_ports
cdc_msc
cdc_msc_freertos
cdc_uac2
dfu
dfu_runtime
dynamic_configuration
hid_boot_interface
hid_composite
hid_composite_freertos
hid_generic_inout
hid_multiple_interface
midi_test
midi_test_freertos
msc_dual_lun
mtp
net_lwip_webserver
uac2_headset
uac2_speaker_fb
usbtmc
video_capture
video_capture_2ch
webusb_serial
)
foreach (example ${EXAMPLE_LIST})
family_add_subdirectory(${example})
endforeach ()

View File

@ -9,6 +9,12 @@ if (FAMILY STREQUAL "rp2040" AND NOT TARGET tinyusb_pico_pio_usb)
message("Skipping dual host/device mode examples as Pico-PIO-USB is not available")
else ()
# family_add_subdirectory will filter what to actually add based on selected FAMILY
family_add_subdirectory(host_hid_to_device_cdc)
family_add_subdirectory(host_info_to_device_cdc)
set(EXAMPLE_LIST
host_hid_to_device_cdc
host_info_to_device_cdc
)
foreach (example ${EXAMPLE_LIST})
family_add_subdirectory(${example})
endforeach ()
endif ()

View File

@ -6,10 +6,16 @@ project(tinyusb_host_examples C CXX ASM)
family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR})
# family_add_subdirectory will filter what to actually add based on selected FAMILY
family_add_subdirectory(bare_api)
family_add_subdirectory(cdc_msc_hid)
family_add_subdirectory(cdc_msc_hid_freertos)
family_add_subdirectory(device_info)
family_add_subdirectory(hid_controller)
family_add_subdirectory(midi_rx)
family_add_subdirectory(msc_file_explorer)
set(EXAMPLE_LIST
bare_api
cdc_msc_hid
cdc_msc_hid_freertos
device_info
hid_controller
midi_rx
msc_file_explorer
)
foreach (example ${EXAMPLE_LIST})
family_add_subdirectory(${example})
endforeach ()

View File

@ -9,6 +9,8 @@ set(TOP "${CMAKE_CURRENT_LIST_DIR}/../..")
get_filename_component(TOP ${TOP} ABSOLUTE)
set(UF2CONV_PY ${TOP}/tools/uf2/utils/uf2conv.py)
set(LINKERMAP_PY ${TOP}/tools/linkermap/linkermap.py)
set(METRICS_PY ${TOP}/tools/metrics.py)
function(family_resolve_board BOARD_NAME BOARD_PATH_OUT)
if ("${BOARD_NAME}" STREQUAL "")
@ -223,6 +225,50 @@ function(family_initialize_project PROJECT DIR)
endif()
endfunction()
# Add bloaty (https://github.com/google/bloaty/) target, required compile with -g (debug)
function(family_add_bloaty TARGET)
find_program(BLOATY_EXE bloaty)
if (BLOATY_EXE STREQUAL BLOATY_EXE-NOTFOUND)
return()
endif ()
set(OPTION "--domain=vm -d compileunits,sections,symbols")
if (DEFINED BLOATY_OPTION)
string(APPEND OPTION " ${BLOATY_OPTION}")
endif ()
separate_arguments(OPTION_LIST UNIX_COMMAND ${OPTION})
add_custom_target(${TARGET}-bloaty
DEPENDS ${TARGET}
COMMAND ${BLOATY_EXE} ${OPTION_LIST} $<TARGET_FILE:${TARGET}>
VERBATIM)
# post build
# add_custom_command(TARGET ${TARGET} POST_BUILD
# COMMAND ${BLOATY_EXE} --csv ${OPTION_LIST} $<TARGET_FILE:${TARGET}> > ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_bloaty.csv
# VERBATIM
# )
endfunction()
# Add linkermap target (https://github.com/hathach/linkermap)
function(family_add_linkermap TARGET)
set(OPTION "-j")
if (DEFINED LINKERMAP_OPTION)
string(APPEND OPTION " ${LINKERMAP_OPTION}")
endif ()
separate_arguments(OPTION_LIST UNIX_COMMAND ${OPTION})
add_custom_target(${TARGET}-linkermap
COMMAND python ${LINKERMAP_PY} ${OPTION_LIST} $<TARGET_FILE:${TARGET}>.map
VERBATIM
)
# post build
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND python ${LINKERMAP_PY} ${OPTION_LIST} $<TARGET_FILE:${TARGET}>.map
VERBATIM)
endfunction()
#-------------------------------------------------------------
# Common Target Configure
# Most families use these settings except rp2040 and espressif
@ -332,6 +378,12 @@ function(family_configure_common TARGET RTOS)
endif ()
endif ()
if (NOT RTOS STREQUAL zephyr)
# Analyze size with bloaty and linkermap
family_add_bloaty(${TARGET})
family_add_linkermap(${TARGET})
endif ()
# run size after build
# find_program(SIZE_EXE ${CMAKE_SIZE})
# if(NOT ${SIZE_EXE} STREQUAL SIZE_EXE-NOTFOUND)

View File

@ -9,11 +9,13 @@ CFLAGS += \
-DCFG_TUSB_MCU=OPT_MCU_KINETIS_K \
LDFLAGS += \
-nostartfiles \
--specs=nosys.specs --specs=nano.specs \
-Wl,--defsym,__stack_size__=0x400 \
-Wl,--defsym,__heap_size__=0
LDFLAGS_GCC += \
-nostartfiles \
--specs=nosys.specs --specs=nano.specs \
SRC_C += \
src/portable/nxp/khci/dcd_khci.c \
src/portable/nxp/khci/hcd_khci.c \

View File

@ -9,11 +9,13 @@ CFLAGS += \
-DCFG_TUSB_MCU=OPT_MCU_KINETIS_KL \
LDFLAGS += \
-nostartfiles \
-specs=nosys.specs -specs=nano.specs \
-Wl,--defsym,__stack_size__=0x400 \
-Wl,--defsym,__heap_size__=0
LDFLAGS_GCC += \
-nostartfiles \
-specs=nosys.specs -specs=nano.specs \
SRC_C += \
src/portable/nxp/khci/dcd_khci.c \
src/portable/nxp/khci/hcd_khci.c \

View File

@ -222,6 +222,8 @@ function(family_add_default_example_warnings TARGET)
endif()
endfunction()
# TODO merge with family_configure_common from family_support.cmake
function(family_configure_target TARGET RTOS)
if (RTOS STREQUAL noos OR RTOS STREQUAL "")
set(RTOS_SUFFIX "")
@ -239,10 +241,15 @@ function(family_configure_target TARGET RTOS)
pico_add_extra_outputs(${TARGET})
pico_enable_stdio_uart(${TARGET} 1)
target_link_options(${TARGET} PUBLIC "LINKER:-Map=$<TARGET_FILE:${TARGET}>.map")
target_link_libraries(${TARGET} PUBLIC pico_stdlib tinyusb_board${RTOS_SUFFIX} tinyusb_additions)
family_flash_openocd(${TARGET})
family_flash_jlink(${TARGET})
# Generate linkermap target and post build. LINKERMAP_OPTION can be set with -D to change default options
family_add_linkermap(${TARGET})
endfunction()

View File

@ -1,7 +0,0 @@
# boards in this files are skipped when running CI with this family
adafruit_feather_rp2040_usb_host
adafruit_fruit_jam
adafruit_metro_rp2350
feather_rp2040_max3421
pico_sdk
raspberry_pi_pico_w

View File

@ -65,40 +65,6 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
// Use ring buffer if it's available, some MCUs need extra RAM requirements
// For DWC2 enable ring buffer will disable DMA (if available)
#ifndef TUD_AUDIO_PREFER_RING_BUFFER
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
defined(TUP_USBIP_DWC2)
#define TUD_AUDIO_PREFER_RING_BUFFER 0
#else
#define TUD_AUDIO_PREFER_RING_BUFFER 1
#endif
#endif
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer
// is available or driver is would need to be changed dramatically
// Only STM32 and ChipIdea HS use non-linear buffer for now
// Ring buffer is incompatible with dcache, since neither address nor size is aligned to cache line
#if defined(TUP_USBIP_DWC2) || \
defined(TUP_USBIP_FSDEV) || \
CFG_TUSB_MCU == OPT_MCU_RX63X || \
CFG_TUSB_MCU == OPT_MCU_RX65X || \
CFG_TUSB_MCU == OPT_MCU_RX72N || \
CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
CFG_TUSB_MCU == OPT_MCU_MSP432E4
#if TUD_AUDIO_PREFER_RING_BUFFER && !CFG_TUD_MEM_DCACHE_ENABLE
#define USE_LINEAR_BUFFER 0
#else
#define USE_LINEAR_BUFFER 1
#endif
#else
#define USE_LINEAR_BUFFER 1
#endif
// Declaration of buffers
// Check for maximum supported numbers
@ -107,12 +73,12 @@
#endif
// Put swap buffer in USB section only if necessary
#if USE_LINEAR_BUFFER
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
#define IN_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
#if USE_LINEAR_BUFFER
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
#define OUT_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
@ -135,7 +101,7 @@ tu_static IN_SW_BUF_MEM_ATTR struct {
// Linear buffer TX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX);
@ -147,7 +113,7 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX);
#endif
} lin_buf_in;
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
// EP OUT software buffers
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
@ -166,7 +132,7 @@ tu_static OUT_SW_BUF_MEM_ATTR struct {
// Linear buffer RX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX);
@ -178,7 +144,7 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX);
#endif
} lin_buf_out;
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
// Control buffer
CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ];
@ -287,14 +253,12 @@ typedef struct
#endif
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *lin_buf_out;
#define USE_LINEAR_BUFFER_RX 1
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *lin_buf_in;
#define USE_LINEAR_BUFFER_TX 1
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@ -302,14 +266,6 @@ typedef struct
#endif
} audiod_function_t;
#ifndef USE_LINEAR_BUFFER_TX
#define USE_LINEAR_BUFFER_TX 0
#endif
#ifndef USE_LINEAR_BUFFER_RX
#define USE_LINEAR_BUFFER_RX 0
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_out_ff)
#else
@ -485,7 +441,8 @@ uint16_t tud_audio_n_read(uint8_t func_id, void *buffer, uint16_t bufsize) {
bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
return true;
}
tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
@ -498,7 +455,7 @@ tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) {
uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
#if USE_LINEAR_BUFFER_RX
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
// Data currently is in linear buffer, copy into EP OUT FIFO
TU_VERIFY(0 < tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
@ -536,7 +493,8 @@ uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) {
bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
return true;
}
tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
@ -572,7 +530,7 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16
#else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
#endif
#if USE_LINEAR_BUFFER_TX
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx, true));
#else
@ -730,32 +688,31 @@ void audiod_init(void) {
break;
#endif
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Initialize linear buffers
#if USE_LINEAR_BUFFER_TX
// Initialize linear buffers
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
case 0:
audio->lin_buf_in = lin_buf_in.buf_1;
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
case 1:
audio->lin_buf_in = lin_buf_in.buf_2;
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
case 2:
audio->lin_buf_in = lin_buf_in.buf_3;
break;
#endif
#endif
}
#endif// USE_LINEAR_BUFFER_TX
#endif// !CFG_TUD_EDPT_DEDICATED_HWFIFO
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Initialize OUT EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
case 0:
@ -773,28 +730,28 @@ void audiod_init(void) {
break;
#endif
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Initialize linear buffers
#if USE_LINEAR_BUFFER_RX
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
// Initialize linear buffers
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
case 0:
audio->lin_buf_out = lin_buf_out.buf_1;
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
case 1:
audio->lin_buf_out = lin_buf_out.buf_2;
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
case 2:
audio->lin_buf_out = lin_buf_out.buf_3;
break;
#endif
#endif
}
#endif// USE_LINEAR_BUFFER_RX
#endif// !CFG_TUD_EDPT_DEDICATED_HWFIFO
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
switch (i) {
@ -1231,7 +1188,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
audiod_parse_flow_control_params(audio, p_desc_parse_for_params);
#endif
// Schedule first transmit if alternate interface is not zero, as sample data is available a ZLP is loaded
#if USE_LINEAR_BUFFER_TX
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0, false));
#else
// Send everything in ISO EP FIFO
@ -1250,7 +1207,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
// Prepare for incoming data
#if USE_LINEAR_BUFFER_RX
#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz, false));
#else
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz, false));

View File

@ -43,8 +43,6 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
typedef struct {
uint8_t rhport;
uint8_t itf_num;
@ -55,13 +53,11 @@ typedef struct {
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
char wanted_char;
struct {
tu_edpt_stream_t tx;
tu_edpt_stream_t rx;
tu_edpt_stream_t tx_stream;
tu_edpt_stream_t rx_stream;
uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
} stream;
uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
} cdcd_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, line_coding)
@ -125,7 +121,7 @@ static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) {
for (uint8_t idx = 0; idx < CFG_TUD_CDC; idx++) {
const cdcd_interface_t *p_cdc = &_cdcd_itf[idx];
if (ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr ||
if (ep_addr == p_cdc->rx_stream.ep_addr || ep_addr == p_cdc->tx_stream.ep_addr ||
(ep_addr == p_cdc->ep_notify && ep_addr != 0)) {
return idx;
}
@ -147,8 +143,8 @@ bool tud_cdc_n_ready(uint8_t itf) {
TU_VERIFY(tud_ready());
const cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
const bool in_opened = tu_edpt_stream_is_opened(&p_cdc->stream.tx);
const bool out_opened = tu_edpt_stream_is_opened(&p_cdc->stream.rx);
const bool in_opened = tu_edpt_stream_is_opened(&p_cdc->tx_stream);
const bool out_opened = tu_edpt_stream_is_opened(&p_cdc->rx_stream);
return in_opened && out_opened;
}
@ -199,25 +195,25 @@ void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_available(uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_CDC, 0);
return tu_edpt_stream_read_available(&_cdcd_itf[itf].stream.rx);
return tu_edpt_stream_read_available(&_cdcd_itf[itf].rx_stream);
}
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
TU_VERIFY(itf < CFG_TUD_CDC, 0);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
return tu_edpt_stream_read(p_cdc->rhport, &p_cdc->stream.rx, buffer, bufsize);
return tu_edpt_stream_read(&p_cdc->rx_stream, buffer, bufsize);
}
bool tud_cdc_n_peek(uint8_t itf, uint8_t *chr) {
TU_VERIFY(itf < CFG_TUD_CDC);
return tu_edpt_stream_peek(&_cdcd_itf[itf].stream.rx, chr);
return tu_edpt_stream_peek(&_cdcd_itf[itf].rx_stream, chr);
}
void tud_cdc_n_read_flush(uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_CDC, );
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
tu_edpt_stream_clear(&p_cdc->stream.rx);
tu_edpt_stream_read_xfer(p_cdc->rhport, &p_cdc->stream.rx);
tu_edpt_stream_clear(&p_cdc->rx_stream);
tu_edpt_stream_read_xfer(&p_cdc->rx_stream);
}
//--------------------------------------------------------------------+
@ -226,25 +222,26 @@ void tud_cdc_n_read_flush(uint8_t itf) {
uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
TU_VERIFY(itf < CFG_TUD_CDC, 0);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
return tu_edpt_stream_write(p_cdc->rhport, &p_cdc->stream.tx, buffer, bufsize);
return tu_edpt_stream_write(&p_cdc->tx_stream, buffer, bufsize);
}
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_CDC, 0);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
return tu_edpt_stream_write_xfer(p_cdc->rhport, &p_cdc->stream.tx);
return tu_edpt_stream_write_xfer(&p_cdc->tx_stream);
}
uint32_t tud_cdc_n_write_available(uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_CDC, 0);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
return tu_edpt_stream_write_available(p_cdc->rhport, &p_cdc->stream.tx);
return tu_edpt_stream_write_available(&p_cdc->tx_stream);
}
bool tud_cdc_n_write_clear(uint8_t itf) {
TU_VERIFY(itf < CFG_TUD_CDC);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
return tu_edpt_stream_clear(&p_cdc->stream.tx);
tu_edpt_stream_clear(&p_cdc->tx_stream);
return true;
}
//--------------------------------------------------------------------+
@ -270,22 +267,22 @@ void cdcd_init(void) {
uint8_t *epin_buf = _cdcd_epbuf[i].epin;
#endif
tu_edpt_stream_init(&p_cdc->stream.rx, false, false, false, p_cdc->stream.rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE,
epout_buf, CFG_TUD_CDC_EP_BUFSIZE);
tu_edpt_stream_init(&p_cdc->rx_stream, false, false, false, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, epout_buf,
CFG_TUD_CDC_EP_BUFSIZE);
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
// know if data is actually polled by terminal. This way the most current data is prioritized.
// Default: is overwritable
tu_edpt_stream_init(&p_cdc->stream.tx, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected,
p_cdc->stream.tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, epin_buf, CFG_TUD_CDC_EP_BUFSIZE);
tu_edpt_stream_init(&p_cdc->tx_stream, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected, p_cdc->tx_ff_buf,
CFG_TUD_CDC_TX_BUFSIZE, epin_buf, CFG_TUD_CDC_EP_BUFSIZE);
}
}
bool cdcd_deinit(void) {
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
tu_edpt_stream_deinit(&p_cdc->stream.rx);
tu_edpt_stream_deinit(&p_cdc->stream.tx);
tu_edpt_stream_deinit(&p_cdc->rx_stream);
tu_edpt_stream_deinit(&p_cdc->tx_stream);
}
return true;
}
@ -297,9 +294,9 @@ void cdcd_reset(uint8_t rhport) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
tu_fifo_set_overwritable(&p_cdc->stream.tx.ff, _cdcd_cfg.tx_overwritabe_if_not_connected); // back to default
tu_edpt_stream_close(&p_cdc->stream.rx);
tu_edpt_stream_close(&p_cdc->stream.tx);
tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, _cdcd_cfg.tx_overwritabe_if_not_connected); // back to default
tu_edpt_stream_close(&p_cdc->rx_stream);
tu_edpt_stream_close(&p_cdc->tx_stream);
}
}
@ -350,22 +347,22 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_t *stream_tx = &p_cdc->stream.tx;
tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream;
tu_edpt_stream_open(stream_tx, desc_ep);
tu_edpt_stream_open(stream_tx, rhport, desc_ep);
if (_cdcd_cfg.tx_persistent) {
tu_edpt_stream_write_xfer(rhport, stream_tx); // flush pending data
tu_edpt_stream_write_xfer(stream_tx); // flush pending data
} else {
tu_edpt_stream_clear(stream_tx);
}
} else {
tu_edpt_stream_t *stream_rx = &p_cdc->stream.rx;
tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream;
tu_edpt_stream_open(stream_rx, desc_ep);
tu_edpt_stream_open(stream_rx, rhport, desc_ep);
if (!_cdcd_cfg.rx_persistent) {
tu_edpt_stream_clear(stream_rx);
}
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare for incoming data
TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare for incoming data
}
}
@ -430,9 +427,9 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
// If enabled: fifo overwriting is disabled if DTR bit is set and vice versa
if (_cdcd_cfg.tx_overwritabe_if_not_connected) {
tu_fifo_set_overwritable(&p_cdc->stream.tx.ff, !dtr);
tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, !dtr);
} else {
tu_fifo_set_overwritable(&p_cdc->stream.tx.ff, false);
tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, false);
}
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
@ -461,13 +458,14 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
}
bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void)rhport;
(void)result;
uint8_t itf = find_cdc_itf(ep_addr);
TU_ASSERT(itf < CFG_TUD_CDC);
cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
tu_edpt_stream_t *stream_rx = &p_cdc->stream.rx;
tu_edpt_stream_t *stream_tx = &p_cdc->stream.tx;
tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream;
tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream;
// Received new data, move to fifo
if (ep_addr == stream_rx->ep_addr) {
@ -511,7 +509,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
tud_cdc_rx_cb(itf);
}
tu_edpt_stream_read_xfer(rhport, stream_rx); // prepare for more data
tu_edpt_stream_read_xfer(stream_rx); // prepare for more data
}
// Data sent to host, we continue to fetch from tx fifo to send.
@ -519,9 +517,9 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
if (ep_addr == stream_tx->ep_addr) {
tud_cdc_tx_complete_cb(itf); // invoke callback to possibly refill tx fifo
if (0 == tu_edpt_stream_write_xfer(rhport, stream_tx)) {
if (0 == tu_edpt_stream_write_xfer(stream_tx)) {
// If there is no data left, a ZLP should be sent if needed
tu_edpt_stream_write_zlp_if_needed(rhport, stream_tx, xferred_bytes);
tu_edpt_stream_write_zlp_if_needed(stream_tx, xferred_bytes);
}
}

View File

@ -29,6 +29,8 @@
#include "tusb_option.h"
#include <stdint.h>
#if (CFG_TUH_ENABLED && CFG_TUH_CDC)
#include "host/usbh.h"
@ -41,13 +43,14 @@
#include "serial/pl2303.h"
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
#ifndef CFG_TUH_CDC_LOG_LEVEL
#define CFG_TUH_CDC_LOG_LEVEL 2
#endif
#ifndef CFG_TUH_CDC_LOG_LEVEL
#define CFG_TUH_CDC_LOG_LEVEL 2
#endif
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
#define TU_LOG_CDC(_cdc, _format, ...) TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \
serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__)
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
#define TU_LOG_CDC(_cdc, _format, ...) \
TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \
serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__)
//--------------------------------------------------------------------+
// Host CDC Interface
@ -113,63 +116,59 @@ static void cdch_set_line_coding_stage1_baudrate_complete(tuh_xfer_t *xfer);
static void cdch_set_line_coding_stage2_data_format_complete(tuh_xfer_t *xfer);
//------------- ACM prototypes -------------//
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
//------------- FTDI prototypes -------------//
#if CFG_TUH_CDC_FTDI
//------------- FTDI prototypes -------------//
#if CFG_TUH_CDC_FTDI
static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t *xfer);
static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void ftdi_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- CP210X prototypes -------------//
#if CFG_TUH_CDC_CP210X
//------------- CP210X prototypes -------------//
#if CFG_TUH_CDC_CP210X
static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static uint16_t cp210x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool cp210x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool cp210x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool cp210x_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
static bool cp210x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool cp210x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- CH34x prototypes -------------//
#if CFG_TUH_CDC_CH34X
//------------- CH34x prototypes -------------//
#if CFG_TUH_CDC_CH34X
static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static uint16_t ch34x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool ch34x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ch34x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ch34x_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ch34x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- PL2303 prototypes -------------//
#if CFG_TUH_CDC_PL2303
//------------- PL2303 prototypes -------------//
#if CFG_TUH_CDC_PL2303
static uint16_t const pl2303_vid_pid_list[][2] = {CFG_TUH_CDC_PL2303_VID_PID_LIST};
static const pl2303_type_data_t pl2303_type_data[PL2303_TYPE_COUNT] = {PL2303_TYPE_DATA};
static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void pl2303_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
static uint16_t pl2303_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void pl2303_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool pl2303_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool pl2303_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- Common -------------//
enum {
@ -197,11 +196,12 @@ enum {
typedef bool (*serial_driver_func_t)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
typedef struct {
uint16_t const (*vid_pid_list)[2];
uint16_t const vid_pid_count;
bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
bool (*const process_set_config)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer);
void (*const request_complete)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer); // internal request complete handler to update line state
const uint16_t (*vid_pid_list)[2];
const uint16_t vid_pid_count;
uint16_t (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
bool (*const process_set_config)(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
// internal request complete handler to update line state
void (*const request_complete)(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
serial_driver_func_t set_control_line_state, set_baudrate, set_data_format, set_line_coding;
@ -470,25 +470,26 @@ bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t * line_coding)
uint32_t tuh_cdc_write(uint8_t idx, void const * buffer, uint32_t bufsize) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write(p_cdc->daddr, &p_cdc->stream.tx, buffer, bufsize);
return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize);
}
uint32_t tuh_cdc_write_flush(uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write_xfer(p_cdc->daddr, &p_cdc->stream.tx);
return tu_edpt_stream_write_xfer(&p_cdc->stream.tx);
}
bool tuh_cdc_write_clear(uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_clear(&p_cdc->stream.tx);
tu_edpt_stream_clear(&p_cdc->stream.tx);
return true;
}
uint32_t tuh_cdc_write_available(uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write_available(p_cdc->daddr, &p_cdc->stream.tx);
return tu_edpt_stream_write_available(&p_cdc->stream.tx);
}
//--------------------------------------------------------------------+
@ -498,7 +499,7 @@ uint32_t tuh_cdc_write_available(uint8_t idx) {
uint32_t tuh_cdc_read (uint8_t idx, void * buffer, uint32_t bufsize) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_read(p_cdc->daddr, &p_cdc->stream.rx, buffer, bufsize);
return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize);
}
uint32_t tuh_cdc_read_available(uint8_t idx) {
@ -517,9 +518,9 @@ bool tuh_cdc_read_clear (uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx);
(void)tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
return ret;
tu_edpt_stream_clear(&p_cdc->stream.rx);
(void)tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
return true;
}
//--------------------------------------------------------------------+
@ -692,10 +693,10 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
if (ep_addr == p_cdc->stream.tx.ep_addr) {
tuh_cdc_tx_complete_cb(idx); // invoke transmit complete callback
if (0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx)) {
if (0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx)) {
// If there is no data left, a ZLP should be sent if:
// - xferred_bytes is multiple of EP Packet size and not zero
(void)tu_edpt_stream_write_zlp_if_needed(daddr, &p_cdc->stream.tx, xferred_bytes);
(void)tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
}
} else if (ep_addr == p_cdc->stream.rx.ep_addr) {
#if CFG_TUH_CDC_FTDI
@ -715,7 +716,7 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
}
// prepare for next transfer if needed
tu_edpt_stream_read_xfer(daddr, &p_cdc->stream.rx);
tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
} else if (ep_addr == p_cdc->ep_notif) {
// TODO handle notification endpoint
} else {
@ -730,12 +731,11 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
//--------------------------------------------------------------------+
static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t const *desc_ep) {
for (size_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
tu_edpt_stream_t *stream =
(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx;
tu_edpt_stream_open(stream, desc_ep);
const uint8_t ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
tu_edpt_stream_t *stream = (ep_dir == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx;
tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep);
tu_edpt_stream_clear(stream);
desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(desc_ep);
@ -744,8 +744,8 @@ static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t co
return true;
}
bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
(void) rhport;
uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
(void)rhport;
// For CDC: only support ACM subclass
// Note: Protocol 0xFF can be RNDIS device
if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
@ -754,15 +754,15 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
} else if (SERIAL_DRIVER_COUNT > 1 &&
TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
uint16_t vid, pid;
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid), 0);
for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
const cdch_serial_driver_t *driver = &serial_drivers[dr];
for (size_t drv = 1; drv < SERIAL_DRIVER_COUNT; drv++) {
const cdch_serial_driver_t *driver = &serial_drivers[drv];
for (size_t i = 0; i < driver->vid_pid_count; i++) {
if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
const bool ret = driver->open(daddr, itf_desc, max_len);
TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, ret ? "OK" : "FAILED");
return ret;
const uint16_t drv_len = driver->open(daddr, itf_desc, max_len);
TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, drv_len > 0 ? "OK" : "FAILED");
return drv_len;
}
}
}
@ -770,7 +770,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
// not supported class
}
return false;
return 0;
}
bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
@ -798,7 +798,7 @@ static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
p_cdc->mounted = true;
tuh_cdc_mount_cb(idx);
// Prepare for incoming data
tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
} else {
// clear the interface entry
p_cdc->daddr = 0;
@ -1011,19 +1011,19 @@ enum {
CONFIG_ACM_COMPLETE = 0
};
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
uint8_t const *p_desc_end = ((uint8_t const *) itf_desc) + max_len;
static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
const uint8_t *p_desc = (const uint8_t *)itf_desc;
const uint8_t *desc_end = p_desc + max_len;
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_ACM;
//------------- Control Interface -------------//
uint8_t const *p_desc = tu_desc_next(itf_desc);
p_desc = tu_desc_next(p_desc);
// Communication Functional Descriptors
while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
while ((p_desc < desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
// save ACM bmCapabilities
p_cdc->acm.capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
@ -1034,26 +1034,27 @@ static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint1
// Open notification endpoint of control interface if any
if (itf_desc->bNumEndpoints == 1) {
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass)) {
// next to endpoint descriptor
p_desc = tu_desc_next(p_desc);
if (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
const tusb_desc_interface_t *data_itf = (const tusb_desc_interface_t *)p_desc;
if (data_itf->bInterfaceClass == TUSB_CLASS_CDC_DATA) {
p_desc = tu_desc_next(p_desc); // next to endpoint descriptor
// data endpoints expected to be in pairs
TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
// data endpoints expected to be in pairs
TU_ASSERT(open_ep_stream_pair(p_cdc, (const tusb_desc_endpoint_t *)p_desc), 0);
p_desc += data_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t);
}
}
return true;
return (uint16_t)((uintptr_t)p_desc - (uintptr_t)itf_desc);
}
static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@ -1186,28 +1187,30 @@ enum {
CONFIG_FTDI_COMPLETE
};
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// FTDI Interface includes 1 vendor interface + 2 bulk endpoints
TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff &&
itf_desc->bNumEndpoints == 2);
TU_VERIFY(sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t) <= max_len);
itf_desc->bNumEndpoints == 2,
0);
const uint16_t drv_len =
(uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
// endpoint pair
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
/*
* NOTE: Some customers have programmed FT232R/FT245R devices
* with an endpoint size of 0 - not good.
*/
TU_ASSERT(desc_ep->wMaxPacketSize != 0);
/* NOTE: Some users have programmed FT232R/FT245R devices
* with an endpoint size of 0 !!! */
TU_ASSERT(desc_ep->wMaxPacketSize != 0, 0);
// data endpoints expected to be in pairs
return open_ep_stream_pair(p_cdc, desc_ep);
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), 0);
return drv_len;
}
static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@ -1578,21 +1581,23 @@ enum {
CONFIG_CP210X_COMPLETE
};
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
static uint16_t cp210x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// CP210x Interface includes 1 vendor interface + 2 bulk endpoints
TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
TU_VERIFY(sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t) <= max_len);
TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2, 0);
const uint16_t drv_len =
(uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
// endpoint pair
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
// data endpoints expected to be in pairs
return open_ep_stream_pair(p_cdc, desc_ep);
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
return drv_len;
}
static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@ -1752,29 +1757,30 @@ enum {
CONFIG_CH34X_COMPLETE
};
static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
static uint16_t ch34x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
TU_VERIFY(itf_desc->bNumEndpoints == 3);
TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
TU_VERIFY(itf_desc->bNumEndpoints == 3, 0);
const uint16_t drv_len =
(uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
// data endpoints expected to be in pairs
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
desc_ep += 2;
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), 0);
desc_ep = (const tusb_desc_endpoint_t *)((uintptr_t)desc_ep + 2 * sizeof(tusb_desc_endpoint_t));
// Interrupt endpoint: not used for now
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
return true;
return drv_len;
}
static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@ -2088,13 +2094,15 @@ enum {
CONFIG_PL2303_COMPLETE
};
static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
static uint16_t pl2303_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// PL2303 Interface includes 1 vendor interface + 1 interrupt endpoints + 2 bulk
TU_VERIFY(itf_desc->bNumEndpoints == 3);
TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
TU_VERIFY(itf_desc->bNumEndpoints == 3, 0);
const uint16_t drv_len =
(uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_PL2303;
p_cdc->pl2303.quirks = 0;
@ -2103,16 +2111,15 @@ static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
// Interrupt endpoint: not used for now
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
desc_ep += 1;
// data endpoints expected to be in pairs
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), 0);
return true;
return drv_len;
}
static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {

View File

@ -30,7 +30,7 @@
#include "cdc.h"
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//--------------------------------------------------------------------+
@ -39,22 +39,22 @@
// RX FIFO size
#ifndef CFG_TUH_CDC_RX_BUFSIZE
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
// RX Endpoint size
#ifndef CFG_TUH_CDC_RX_EPSIZE
#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
#endif
// TX FIFO size
#ifndef CFG_TUH_CDC_TX_BUFSIZE
#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
// TX Endpoint size
#ifndef CFG_TUH_CDC_TX_EPSIZE
#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
#endif
//--------------------------------------------------------------------+
@ -67,7 +67,7 @@ uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
// Get Interface information
// return true if index is correct and interface is currently mounted
bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t *info);
// Check if an interface is mounted
bool tuh_cdc_mounted(uint8_t idx);
@ -75,7 +75,7 @@ bool tuh_cdc_mounted(uint8_t idx);
// Get local (cached) line state
// This function should return correct values if tuh_cdc_set_control_line_state() / tuh_cdc_get_control_line_state()
// are invoked previously or CFG_TUH_CDC_LINE_STATE_ON_ENUM is defined.
bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t* line_state);
bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t *line_state);
// Get current DTR status
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_get_dtr(uint8_t idx) {
@ -100,7 +100,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) {
// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
// NOTE: This function does not make any USB transfer request to device.
bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t* line_coding);
bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t *line_coding);
#define tuh_cdc_get_local_line_coding tuh_cdc_get_line_coding_local // backward compatibility
@ -112,7 +112,7 @@ bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t* line_coding);
uint32_t tuh_cdc_write_available(uint8_t idx);
// Write to cdc interface
uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize);
uint32_t tuh_cdc_write(uint8_t idx, const void *buffer, uint32_t bufsize);
// Force sending data if possible, return number of forced bytes
uint32_t tuh_cdc_write_flush(uint8_t idx);
@ -128,13 +128,13 @@ bool tuh_cdc_write_clear(uint8_t idx);
uint32_t tuh_cdc_read_available(uint8_t idx);
// Read from cdc interface
uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize);
uint32_t tuh_cdc_read(uint8_t idx, void *buffer, uint32_t bufsize);
// Get a byte from RX FIFO without removing it
bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
bool tuh_cdc_peek(uint8_t idx, uint8_t *ch);
// Clear the received FIFO
bool tuh_cdc_read_clear (uint8_t idx);
bool tuh_cdc_read_clear(uint8_t idx);
//--------------------------------------------------------------------+
// Control Request API
@ -149,16 +149,18 @@ bool tuh_cdc_read_clear (uint8_t idx);
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to Set DTR
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
cdc_line_control_state_t line_state = { .dtr = dtr_state };
line_state.rts = tuh_cdc_get_rts(idx);
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb,
uintptr_t user_data) {
cdc_line_control_state_t line_state = {.dtr = dtr_state};
line_state.rts = tuh_cdc_get_rts(idx);
return tuh_cdc_set_control_line_state(idx, line_state.value, complete_cb, user_data);
}
// Request to Set RTS
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
cdc_line_control_state_t line_state = { .rts = rts_state };
line_state.dtr = tuh_cdc_get_dtr(idx);
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb,
uintptr_t user_data) {
cdc_line_control_state_t line_state = {.rts = rts_state};
line_state.dtr = tuh_cdc_get_dtr(idx);
return tuh_cdc_set_control_line_state(idx, line_state.value, complete_cb, user_data);
}
@ -166,11 +168,13 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_s
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to set data format
bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to Set Line Coding = baudrate + data format
// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool tuh_cdc_set_line_coding(uint8_t idx, const cdc_line_coding_t *line_coding, tuh_xfer_cb_t complete_cb,
uintptr_t user_data);
// Request to Get Line Coding (ACM only)
// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and
@ -179,11 +183,13 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// Connect by set both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb,
user_data);
}
// Disconnect by clear both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb,
uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
}
@ -192,7 +198,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfe
// Each Function will make a USB control transfer request to/from device the function will block until request is
// complete. The function will return the transfer request result
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_control_line_state_sync(uint8_t idx, uint16_t line_state) {
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_control_line_state_sync(uint8_t idx,
uint16_t line_state) {
TU_API_SYNC(tuh_cdc_set_control_line_state, idx, line_state);
}
@ -208,11 +215,13 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_baudrate_sync
TU_API_SYNC(tuh_cdc_set_baudrate, idx, baudrate);
}
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_data_format_sync(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_data_format_sync(uint8_t idx, uint8_t stop_bits,
uint8_t parity, uint8_t data_bits) {
TU_API_SYNC(tuh_cdc_set_data_format, idx, stop_bits, parity, data_bits);
}
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_line_coding_sync(uint8_t idx, cdc_line_coding_t const* line_coding) {
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t
tuh_cdc_set_line_coding_sync(uint8_t idx, const cdc_line_coding_t *line_coding) {
TU_API_SYNC(tuh_cdc_set_line_coding, idx, line_coding);
}
@ -244,15 +253,15 @@ extern void tuh_cdc_tx_complete_cb(uint8_t idx);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
bool cdch_init (void);
bool cdch_deinit (void);
bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void cdch_close (uint8_t dev_addr);
bool cdch_init(void);
bool cdch_deinit(void);
uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num);
bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void cdch_close(uint8_t dev_addr);
#ifdef __cplusplus
}
}
#endif
#endif /* TUSB_CDC_HOST_H_ */

View File

@ -1,289 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "cdc_host.h"
#include "cdc_rndis_host.h"
#if 0 // TODO remove subtask related macros later
// Sub Task
#define OSAL_SUBTASK_BEGIN
#define OSAL_SUBTASK_END return TUSB_ERROR_NONE;
#define STASK_RETURN(_error) return _error;
#define STASK_INVOKE(_subtask, _status) (_status) = _subtask
#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
// TODO Microsoft requires message length for any get command must be at least 4096 bytes
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static tusb_error_t rndis_body_subtask(void);
static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
uint8_t * p_mess, uint32_t mess_length,
uint8_t *p_response );
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
{
TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA);
memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6);
return TUSB_ERROR_NONE;
}
//--------------------------------------------------------------------+
// IMPLEMENTATION
//--------------------------------------------------------------------+
// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
// forever loop cannot have any return at all.
OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;)
{
OSAL_TASK_BEGIN
rndis_body_subtask();
OSAL_TASK_END
}
static tusb_error_t rndis_body_subtask(void)
{
static uint8_t relative_addr;
OSAL_SUBTASK_BEGIN
for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++)
{
}
tusb_time_delay_ms_api(100);
OSAL_SUBTASK_END
}
//--------------------------------------------------------------------+
// RNDIS-CDC Driver API
//--------------------------------------------------------------------+
void rndish_init(void)
{
tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX);
//------------- Task creation -------------//
//------------- semaphore creation for notification pipe -------------//
for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
{
rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
}
}
void rndish_close(uint8_t dev_addr)
{
osal_semaphore_reset( rndish_data[dev_addr-1].sem_notification_hdl );
// tu_memclr(&rndish_data[dev_addr-1], sizeof(rndish_data_t)); TODO need to move semaphore & its handle out before memclr
}
static rndis_msg_initialize_t const msg_init =
{
.type = RNDIS_MSG_INITIALIZE,
.length = sizeof(rndis_msg_initialize_t),
.request_id = 1, // TODO should use some magic number
.major_version = 1,
.minor_version = 0,
.max_xfer_size = 0x4000 // TODO mimic windows
};
static rndis_msg_query_t const msg_query_permanent_addr =
{
.type = RNDIS_MSG_QUERY,
.length = sizeof(rndis_msg_query_t)+6,
.request_id = 1,
.oid = RNDIS_OID_802_3_PERMANENT_ADDRESS,
.buffer_length = 6,
.buffer_offset = 20,
};
static rndis_msg_set_t const msg_set_packet_filter =
{
.type = RNDIS_MSG_SET,
.length = sizeof(rndis_msg_set_t)+4,
.request_id = 1,
.oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
.buffer_length = 4,
.buffer_offset = 20,
};
tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
{
tusb_error_t error;
OSAL_SUBTASK_BEGIN
//------------- Message Initialize -------------//
memcpy(msg_payload, &msg_init, sizeof(rndis_msg_initialize_t));
STASK_INVOKE(
send_message_get_response_subtask( dev_addr, p_cdc,
msg_payload, sizeof(rndis_msg_initialize_t),
msg_payload),
error
);
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
// TODO currently not support multiple data packets per xfer
rndis_msg_initialize_cmplt_t * const p_init_cmpt = (rndis_msg_initialize_cmplt_t *) msg_payload;
STASK_ASSERT(p_init_cmpt->type == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS &&
p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX);
rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size;
//------------- Message Query 802.3 Permanent Address -------------//
memcpy(msg_payload, &msg_query_permanent_addr, sizeof(rndis_msg_query_t));
tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address
STASK_INVOKE(
send_message_get_response_subtask( dev_addr, p_cdc,
msg_payload, sizeof(rndis_msg_query_t) + 6,
msg_payload),
error
);
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
rndis_msg_query_cmplt_t * const p_query_cmpt = (rndis_msg_query_cmplt_t *) msg_payload;
STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS);
memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6);
//------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------//
memcpy(msg_payload, &msg_set_packet_filter, sizeof(rndis_msg_set_t));
tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags
((rndis_msg_set_t*) msg_payload)->oid_buffer[0] = (RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_MULTICAST | RNDIS_PACKET_TYPE_BROADCAST);
STASK_INVOKE(
send_message_get_response_subtask( dev_addr, p_cdc,
msg_payload, sizeof(rndis_msg_set_t) + 4,
msg_payload),
error
);
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
rndis_msg_set_cmplt_t * const p_set_cmpt = (rndis_msg_set_cmplt_t *) msg_payload;
STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS);
tusbh_cdc_rndis_mounted_cb(dev_addr);
OSAL_SUBTASK_END
}
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
{
if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) )
{
osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl );
}
}
//--------------------------------------------------------------------+
// INTERNAL & HELPER
//--------------------------------------------------------------------+
static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
uint8_t * p_mess, uint32_t mess_length,
uint8_t *p_response)
{
tusb_error_t error;
OSAL_SUBTASK_BEGIN
//------------- Send RNDIS Control Message -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number,
mess_length, p_mess),
error
);
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
//------------- waiting for Response Available notification -------------//
(void) usbh_edpt_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8);
osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error);
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
STASK_ASSERT(msg_notification[dev_addr-1][0] == 1);
//------------- Get RNDIS Message Initialize Complete -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number,
RNDIS_MSG_PAYLOAD_MAX, p_response),
error
);
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
OSAL_SUBTASK_END
}
//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
//{
// tusb_error_t error;
//
// OSAL_SUBTASK_BEGIN
//
// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t)
// {
// .type = RNDIS_MSG_INITIALIZE,
// .length = sizeof(rndis_msg_initialize_t),
// .request_id = 1, // TODO should use some magic number
// .major_version = 1,
// .minor_version = 0,
// .max_xfer_size = 0x4000 // TODO mimic windows
// };
//
//
//
// OSAL_SUBTASK_END
//}
#endif

View File

@ -1,63 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
/** \ingroup CDC_RNDIS
* \defgroup CDC_RNSID_Host Host
* @{ */
#ifndef TUSB_CDC_RNDIS_HOST_H_
#define TUSB_CDC_RNDIS_HOST_H_
#include "common/tusb_common.h"
#include "host/usbh.h"
#include "cdc_rndis.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// INTERNAL RNDIS-CDC Driver API
//--------------------------------------------------------------------+
typedef struct {
OSAL_SEM_DEF(semaphore_notification);
osal_semaphore_handle_t sem_notification_hdl; // used to wait on notification pipe
uint32_t max_xfer_size; // got from device's msg initialize complete
uint8_t mac_address[6];
}rndish_data_t;
void rndish_init(void);
bool rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc);
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
void rndish_close(uint8_t dev_addr);
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CDC_RNDIS_HOST_H_ */
/** @} */

View File

@ -141,7 +141,9 @@ static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
static hidh_interface_t* find_new_itf(void) {
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
if (_hidh_itf[i].daddr == 0) {
return &_hidh_itf[i];
}
}
return NULL;
}
@ -152,7 +154,9 @@ static hidh_interface_t* find_new_itf(void) {
uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
uint8_t count = 0;
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
if (_hidh_itf[i].daddr == daddr) count++;
if (_hidh_itf[i].daddr == daddr) {
count++;
}
}
return count;
}
@ -160,7 +164,9 @@ uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
uint8_t tuh_hid_itf_get_total_count(void) {
uint8_t count = 0;
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
if (_hidh_itf[i].daddr != 0) count++;
if (_hidh_itf[i].daddr != 0) {
count++;
}
}
return count;
}
@ -506,36 +512,43 @@ void hidh_close(uint8_t daddr) {
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
uint16_t hidh_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
(void) rhport;
(void) max_len;
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
// len = interface + hid + n*endpoints
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(max_len >= drv_len);
uint8_t const* p_desc = (uint8_t const*) desc_itf;
const uint16_t drv_len = (uint16_t)(sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(drv_len <= max_len, 0);
const uint8_t *p_desc = (const uint8_t *)desc_itf;
//------------- HID descriptor -------------//
// HID descriptor: mostly right after interface descriptor, in some rare case it might be after endpoint descriptors
p_desc = tu_desc_next(p_desc);
tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc;
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
const tusb_hid_descriptor_hid_t *desc_hid;
if (tu_desc_type(p_desc) == HID_DESC_TYPE_HID) {
// HID after interface
desc_hid = (const tusb_hid_descriptor_hid_t *)p_desc;
p_desc = tu_desc_next(p_desc);
} else {
// HID after endpoint
desc_hid = (const tusb_hid_descriptor_hid_t *)(p_desc + sizeof(tusb_desc_endpoint_t) * desc_itf->bNumEndpoints);
TU_ASSERT(tu_desc_type(desc_hid) == HID_DESC_TYPE_HID, 0);
}
hidh_interface_t* p_hid = find_new_itf();
TU_ASSERT(p_hid); // not enough interface, try to increase CFG_TUH_HID
p_hid->daddr = daddr;
// Allocate new interface
hidh_interface_t *p_hid = find_new_itf();
TU_ASSERT(p_hid, 0); // not enough interface, try to increase CFG_TUH_HID
p_hid->daddr = daddr;
p_hid->itf_num = desc_itf->bInterfaceNumber;
//------------- Endpoint Descriptors -------------//
p_desc = tu_desc_next(p_desc);
tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
for (int i = 0; i < desc_itf->bNumEndpoints; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
// Endpoint Descriptors
for (uint8_t i = 0; i < desc_itf->bNumEndpoints; i++) {
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType, 0);
TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
p_hid->ep_in = desc_ep->bEndpointAddress;
@ -546,11 +559,8 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_
}
p_desc = tu_desc_next(p_desc);
desc_ep = (tusb_desc_endpoint_t const*) p_desc;
}
p_hid->itf_num = desc_itf->bInterfaceNumber;
// Assume bNumDescriptors = 1
p_hid->report_desc_type = desc_hid->bReportType;
// Use offsetof to avoid pointer to the odd/misaligned address
@ -562,7 +572,7 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
}
return true;
return drv_len;
}
//--------------------------------------------------------------------+

View File

@ -39,22 +39,22 @@ extern "C" {
// TODO Highspeed interrupt can be up to 512 bytes
#ifndef CFG_TUH_HID_EPIN_BUFSIZE
#define CFG_TUH_HID_EPIN_BUFSIZE 64
#define CFG_TUH_HID_EPIN_BUFSIZE 64
#endif
#ifndef CFG_TUH_HID_EPOUT_BUFSIZE
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
#endif
typedef struct {
uint8_t report_id;
uint8_t usage;
uint8_t report_id;
uint8_t usage;
uint16_t usage_page;
// TODO still use the endpoint size for now
// uint8_t in_len; // length of IN report
// uint8_t out_len; // length of OUT report
// uint8_t in_len; // length of IN report
// uint8_t out_len; // length of OUT report
} tuh_hid_report_info_t;
//--------------------------------------------------------------------+
@ -68,10 +68,10 @@ uint8_t tuh_hid_itf_get_count(uint8_t dev_addr);
uint8_t tuh_hid_itf_get_total_count(void);
// backward compatible rename
#define tuh_hid_instance_count tuh_hid_itf_get_count
#define tuh_hid_instance_count tuh_hid_itf_get_count
// Get Interface information
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* itf_info);
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t *itf_info);
// Get Interface index from device address + interface number
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
@ -85,8 +85,8 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count,
uint8_t const* desc_report, uint16_t desc_len);
TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t *reports_info_arr, uint8_t arr_count,
const uint8_t *desc_report, uint16_t desc_len);
//--------------------------------------------------------------------+
// Control Endpoint API
@ -107,12 +107,13 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
// Get Report using control endpoint
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void *report,
uint16_t len);
// Set Report using control endpoint
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
void* report, uint16_t len);
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void *report,
uint16_t len);
//--------------------------------------------------------------------+
// Interrupt Endpoint API
@ -133,8 +134,9 @@ bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx);
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
// Send report using interrupt endpoint
// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent.
bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len);
// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is
// sent.
bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void *report, uint16_t len);
//--------------------------------------------------------------------+
// Callbacks (Weak is optional)
@ -145,25 +147,27 @@ bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const
// can be used to parse common/simple enough descriptor.
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
// therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len);
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report_desc, uint16_t desc_len);
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx);
// Invoked when received report from device via interrupt endpoint
// Note: if there is report ID (composite), it is 1st byte of report
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report, uint16_t len);
// Invoked when sent report to device successfully via interrupt endpoint
void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report, uint16_t len);
// Invoked when Get Report to device via either control endpoint
// len = 0 indicate there is error in the transfer e.g stalled response
void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
uint16_t len);
// Invoked when Sent Report to device via either control endpoint
// len = 0 indicate there is error in the transfer e.g stalled response
void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
uint16_t len);
// Invoked when Set Protocol request is complete
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
@ -171,12 +175,12 @@ void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t pro
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
bool hidh_init(void);
bool hidh_deinit(void);
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void hidh_close(uint8_t dev_addr);
bool hidh_init(void);
bool hidh_deinit(void);
uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void hidh_close(uint8_t dev_addr);
#ifdef __cplusplus
}

View File

@ -171,16 +171,13 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, ui
bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]) {
midid_interface_t *p_midi = &_midid_itf[itf];
tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
TU_VERIFY(tu_edpt_stream_is_opened(ep_str));
return 4 == tu_edpt_stream_read(p_midi->rhport, ep_str, packet, 4);
return 4 == tu_edpt_stream_read(ep_str, packet, 4);
}
uint32_t tud_midi_n_packet_read_n(uint8_t itf, uint8_t packets[], uint32_t max_packets) {
midid_interface_t *p_midi = &_midid_itf[itf];
tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
const uint32_t num_read = tu_edpt_stream_read(p_midi->rhport, ep_str, packets, 4u * max_packets);
const uint32_t num_read = tu_edpt_stream_read(ep_str, packets, 4u * max_packets);
return num_read >> 2u;
}
@ -195,7 +192,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *
uint32_t i = 0;
while (i < bufsize) {
if (tu_edpt_stream_write_available(p_midi->rhport, ep_str) < 4) {
if (tu_edpt_stream_write_available(ep_str) < 4) {
break;
}
@ -268,7 +265,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *
stream->buffer[idx] = 0;
}
const uint32_t count = tu_edpt_stream_write(p_midi->rhport, ep_str, stream->buffer, 4);
const uint32_t count = tu_edpt_stream_write(ep_str, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = stream->total = 0;
@ -278,7 +275,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *
}
}
(void)tu_edpt_stream_write_xfer(p_midi->rhport, ep_str);
(void)tu_edpt_stream_write_xfer(ep_str);
return i;
}
@ -288,9 +285,9 @@ bool tud_midi_n_packet_write (uint8_t itf, const uint8_t packet[4]) {
tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
TU_VERIFY(tu_edpt_stream_is_opened(ep_str));
TU_VERIFY(tu_edpt_stream_write_available(p_midi->rhport, ep_str) >= 4);
TU_VERIFY(tu_edpt_stream_write(p_midi->rhport, ep_str, packet, 4) > 0);
(void)tu_edpt_stream_write_xfer(p_midi->rhport, ep_str);
TU_VERIFY(tu_edpt_stream_write_available(ep_str) >= 4);
TU_VERIFY(tu_edpt_stream_write(ep_str, packet, 4) > 0);
(void)tu_edpt_stream_write_xfer(ep_str);
return true;
}
@ -300,11 +297,11 @@ uint32_t tud_midi_n_packet_write_n(uint8_t itf, const uint8_t packets[], uint32_
tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
uint32_t n_bytes = tu_edpt_stream_write_available(p_midi->rhport, ep_str);
uint32_t n_bytes = tu_edpt_stream_write_available(ep_str);
n_bytes = tu_min32(tu_align4(n_bytes), n_packets << 2u);
const uint32_t n_write = tu_edpt_stream_write(p_midi->rhport, ep_str, packets, n_bytes);
(void)tu_edpt_stream_write_xfer(p_midi->rhport, ep_str);
const uint32_t n_write = tu_edpt_stream_write(ep_str, packets, n_bytes);
(void)tu_edpt_stream_write_xfer(ep_str);
return n_write >> 2u;
}
@ -411,13 +408,13 @@ uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint1
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
tu_edpt_stream_t *stream_tx = &p_midi->ep_stream.tx;
tu_edpt_stream_open(stream_tx, desc_ep);
tu_edpt_stream_open(stream_tx, rhport, desc_ep);
tu_edpt_stream_clear(stream_tx);
} else {
tu_edpt_stream_t *stream_rx = &p_midi->ep_stream.rx;
tu_edpt_stream_open(stream_rx, desc_ep);
tu_edpt_stream_open(stream_rx, rhport, desc_ep);
tu_edpt_stream_clear(stream_rx);
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare to receive data
TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare to receive data
}
p_desc = tu_desc_next(p_desc); // skip CS Endpoint descriptor
@ -439,6 +436,7 @@ bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_req
}
bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void)rhport;
(void)result;
uint8_t idx = find_midi_itf(ep_addr);
@ -454,12 +452,12 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32
tu_edpt_stream_read_xfer_complete(ep_st_rx, xferred_bytes);
tud_midi_rx_cb(idx); // invoke callback
}
tu_edpt_stream_read_xfer(rhport, ep_st_rx); // prepare for next data
tu_edpt_stream_read_xfer(ep_st_rx); // prepare for next data
} else if (ep_addr == ep_st_tx->ep_addr && result == XFER_RESULT_SUCCESS) {
// sent complete: try to send more if possible
if (0 == tu_edpt_stream_write_xfer(rhport, ep_st_tx)) {
if (0 == tu_edpt_stream_write_xfer(ep_st_tx)) {
// If there is no data left, a ZLP should be sent if needed
(void)tu_edpt_stream_write_zlp_if_needed(rhport, ep_st_tx, xferred_bytes);
(void)tu_edpt_stream_write_zlp_if_needed(ep_st_tx, xferred_bytes);
}
} else {
return false;

View File

@ -175,14 +175,14 @@ bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint
tuh_midi_rx_cb(idx, xferred_bytes);
}
tu_edpt_stream_read_xfer(dev_addr, ep_str_rx); // prepare for next transfer
tu_edpt_stream_read_xfer(ep_str_rx); // prepare for next transfer
} else if (ep_addr == ep_str_tx->ep_addr) {
tuh_midi_tx_cb(idx, xferred_bytes);
if (0 == tu_edpt_stream_write_xfer(dev_addr, ep_str_tx)) {
if (0 == tu_edpt_stream_write_xfer(ep_str_tx)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
tu_edpt_stream_write_zlp_if_needed(dev_addr, ep_str_tx, xferred_bytes);
tu_edpt_stream_write_zlp_if_needed(ep_str_tx, xferred_bytes);
}
}
@ -192,15 +192,16 @@ bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
(void) rhport;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
const uint8_t *p_end = ((const uint8_t *) desc_itf) + max_len;
const uint8_t *p_desc = (const uint8_t *) desc_itf;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass, 0);
const uint8_t *desc_start = (const uint8_t *)desc_itf;
const uint8_t *p_desc = desc_start;
const uint8_t *desc_end = desc_start + max_len;
const uint8_t idx = find_new_midi_index();
TU_VERIFY(idx < CFG_TUH_MIDI);
TU_VERIFY(idx < CFG_TUH_MIDI, 0);
midih_interface_t *p_midi = &_midi_host[idx];
p_midi->itf_count = 0;
@ -217,29 +218,30 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
// driver after parsing the audio control interface and then resume parsing
// the streaming audio interface.
if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) {
TU_VERIFY(max_len > 2*sizeof(tusb_desc_interface_t) + sizeof(midi10_desc_cs_ac_interface_t));
TU_VERIFY(max_len > 2 * sizeof(tusb_desc_interface_t) + sizeof(midi10_desc_cs_ac_interface_t), 0);
p_desc = tu_desc_next(p_desc);
TU_VERIFY(tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
tu_desc_subtype(p_desc) == AUDIO10_CS_AC_INTERFACE_HEADER);
tu_desc_subtype(p_desc) == AUDIO10_CS_AC_INTERFACE_HEADER,
0);
desc_cb.desc_audio_control = desc_itf;
p_desc = tu_desc_next(p_desc);
desc_itf = (const tusb_desc_interface_t *)p_desc;
p_midi->itf_count = 1;
// skip non-interface and non-midi streaming descriptors
while (tu_desc_in_bounds(p_desc, p_end) &&
(desc_itf->bDescriptorType != TUSB_DESC_INTERFACE || (desc_itf->bInterfaceClass == TUSB_CLASS_AUDIO && desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING))) {
while (tu_desc_in_bounds(p_desc, desc_end) && (desc_itf->bDescriptorType != TUSB_DESC_INTERFACE ||
(desc_itf->bInterfaceClass == TUSB_CLASS_AUDIO &&
desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING))) {
if (desc_itf->bDescriptorType == TUSB_DESC_INTERFACE && desc_itf->bAlternateSetting == 0) {
p_midi->itf_count++;
}
p_desc = tu_desc_next(p_desc);
desc_itf = (tusb_desc_interface_t const *)p_desc;
desc_itf = (const tusb_desc_interface_t *)p_desc;
}
TU_VERIFY(p_desc < p_end); // TODO: If MIDI interface comes after Audio Streaming, then max_len did not include the MIDI interface descriptor
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
TU_VERIFY(p_desc < desc_end, 0);
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass, 0);
}
TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass);
TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, 0);
TU_LOG_DRV("MIDI opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
@ -250,7 +252,7 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
p_desc = tu_desc_next(p_desc); // next to CS Header
bool found_new_interface = false;
while (tu_desc_in_bounds(p_desc, p_end) && !found_new_interface) {
while (tu_desc_in_bounds(p_desc, desc_end) && !found_new_interface) {
switch (tu_desc_type(p_desc)) {
case TUSB_DESC_INTERFACE:
found_new_interface = true;
@ -287,8 +289,9 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
case TUSB_DESC_ENDPOINT: {
const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
p_desc = tu_desc_next(p_desc); // next to CS endpoint
TU_VERIFY(p_desc < p_end && tu_desc_next(p_desc) <= p_end);
TU_VERIFY(tu_desc_in_bounds(p_desc, desc_end), 0);
const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
TU_LOG_DRV(" Endpoint and CS_Endpoint descriptor %02x\r\n", p_ep->bEndpointAddress);
@ -302,8 +305,8 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
desc_cb.desc_epin = p_ep;
ep_stream = &p_midi->ep_stream.rx;
}
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
tu_edpt_stream_open(ep_stream, p_ep);
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep), 0);
tu_edpt_stream_open(ep_stream, dev_addr, p_ep);
tu_edpt_stream_clear(ep_stream);
break;
@ -313,12 +316,12 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
}
p_desc = tu_desc_next(p_desc);
}
desc_cb.desc_midi_total_len = (uint16_t) ((uintptr_t)p_desc - (uintptr_t) desc_itf);
desc_cb.desc_midi_total_len = (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_start);
p_midi->daddr = dev_addr;
tuh_midi_descriptor_cb(idx, &desc_cb);
return true;
return desc_cb.desc_midi_total_len;
}
bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
@ -335,7 +338,7 @@ bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
};
tuh_midi_mount_cb(idx, &mount_cb_data);
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for incoming data
tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx); // prepare for incoming data
// No special config things to do for MIDI
usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
@ -414,7 +417,7 @@ uint32_t tuh_midi_read_available(uint8_t idx) {
uint32_t tuh_midi_write_flush(uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
return tu_edpt_stream_write_xfer(p_midi->daddr, &p_midi->ep_stream.tx);
return tu_edpt_stream_write_xfer(&p_midi->ep_stream.tx);
}
//--------------------------------------------------------------------+
@ -427,7 +430,7 @@ uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize)
uint32_t count4 = tu_min32(bufsize, tu_edpt_stream_read_available(&p_midi->ep_stream.rx));
count4 = tu_align4(count4); // round down to multiple of 4
TU_VERIFY(count4 > 0, 0);
return tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, buffer, count4);
return tu_edpt_stream_read(&p_midi->ep_stream.rx, buffer, count4);
}
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
@ -436,7 +439,7 @@ uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bu
const uint32_t bufsize4 = tu_align4(bufsize);
TU_VERIFY(bufsize4 > 0, 0);
return tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, buffer, bufsize4);
return tu_edpt_stream_write(&p_midi->ep_stream.tx, buffer, bufsize4);
}
//--------------------------------------------------------------------+
@ -450,8 +453,8 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
midi_driver_stream_t *stream = &p_midi->stream_write;
uint32_t byte_count = 0;
while ((byte_count < bufsize) && (tu_edpt_stream_write_available(p_midi->daddr, &p_midi->ep_stream.tx) >= 4)) {
uint8_t const data = buffer[byte_count];
while ((byte_count < bufsize) && (tu_edpt_stream_write_available(&p_midi->ep_stream.tx) >= 4)) {
const uint8_t data = buffer[byte_count];
byte_count++;
if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
// real-time messages need to be sent right away
@ -460,7 +463,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
streamrt.buffer[1] = data;
streamrt.index = 2;
streamrt.total = 2;
uint32_t const count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, streamrt.buffer, 4);
const uint32_t count = tu_edpt_stream_write(&p_midi->ep_stream.tx, streamrt.buffer, 4);
TU_ASSERT(count == 4, byte_count); // Check FIFO overflown, since we already check fifo remaining. It is probably race condition
} else if (stream->index == 0) {
//------------- New event packet -------------//
@ -529,7 +532,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
}
TU_LOG3_MEM(stream->buffer, 4, 2);
const uint32_t count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, stream->buffer, 4);
const uint32_t count = tu_edpt_stream_write(&p_midi->ep_stream.tx, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = 0;
@ -551,7 +554,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
return 0;
}
*p_cable_num = (one_byte >> 4) & 0xf;
uint32_t nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
uint32_t nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
static uint16_t cable_sysex_in_progress;// bit i is set if received MIDI_STATUS_SYSEX_START but not MIDI_STATUS_SYSEX_END
while (nread == 4 && bytes_buffered < bufsize) {
*p_cable_num = (p_midi->stream_read.buffer[0] >> 4) & 0x0f;
@ -579,7 +582,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
}
else {
// bad packet discard
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
continue;
}
} else if (status < MIDI_STATUS_SYSEX_START) {
@ -626,7 +629,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
}
else {
// bad packet discard
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
continue;
}
@ -639,7 +642,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
uint8_t new_cable = (one_byte >> 4) & 0xf;
if (new_cable == *p_cable_num) {
// still on the same cable. Continue reading the stream
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
}
}
}

View File

@ -31,45 +31,45 @@
#include "midi.h"
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUH_MIDI_RX_BUFSIZE
#define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
#ifndef CFG_TUH_MIDI_TX_BUFSIZE
#define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
#ifndef CFG_TUH_MIDI_EP_BUFSIZE
#define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
// Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet
// Disable this can save driver footprint.
#ifndef CFG_TUH_MIDI_STREAM_API
#define CFG_TUH_MIDI_STREAM_API 1
#define CFG_TUH_MIDI_STREAM_API 1
#endif
//--------------------------------------------------------------------+
// Application Types
//--------------------------------------------------------------------+
typedef struct {
const tusb_desc_interface_t* desc_audio_control;
const tusb_desc_interface_t* desc_midi; // start of whole midi interface descriptor
uint16_t desc_midi_total_len;
const tusb_desc_interface_t *desc_audio_control;
const tusb_desc_interface_t *desc_midi; // start of whole midi interface descriptor
uint16_t desc_midi_total_len;
const uint8_t* desc_header;
const uint8_t* desc_element;
const tusb_desc_endpoint_t* desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
const tusb_desc_endpoint_t* desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
const uint8_t *desc_header;
const uint8_t *desc_element;
const tusb_desc_endpoint_t *desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
const tusb_desc_endpoint_t *desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
uint8_t jack_num;
const uint8_t* desc_jack[32]; // list of jack descriptors (embedded + external)
uint8_t jack_num;
const uint8_t *desc_jack[32]; // list of jack descriptors (embedded + external)
} tuh_midi_descriptor_cb_t;
typedef struct {
@ -92,7 +92,7 @@ uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num);
// Get Interface information
// return true if index is correct and interface is currently mounted
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t *info);
// return the number of virtual midi cables on the device's IN endpoint
uint8_t tuh_midi_get_rx_cable_count(uint8_t idx);
@ -115,24 +115,22 @@ uint32_t tuh_midi_write_flush(uint8_t idx);
// Read all available MIDI packets from the connected device
// Return number of bytes read (always multiple of 4)
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize);
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t *buffer, uint32_t bufsize);
// Read a raw MIDI packet from the connected device
// Return true if a packet was returned
TU_ATTR_ALWAYS_INLINE static inline
bool tuh_midi_packet_read (uint8_t idx, uint8_t packet[4]) {
return 4 == tuh_midi_packet_read_n(idx, packet, 4);
TU_ATTR_ALWAYS_INLINE static inline bool tuh_midi_packet_read(uint8_t idx, uint8_t packet[4]) {
return 4 == tuh_midi_packet_read_n(idx, packet, 4);
}
// Write all 4-byte packets, data is locally buffered and only transferred when buffered bytes
// reach the endpoint packet size or tuh_midi_write_flush() is called
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize);
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t *buffer, uint32_t bufsize);
// Write a 4-bytes packet to the device.
// Returns true if the packet was successfully queued.
TU_ATTR_ALWAYS_INLINE static inline
bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
return 4 == tuh_midi_packet_write_n(idx, packet, 4);
TU_ATTR_ALWAYS_INLINE static inline bool tuh_midi_packet_write(uint8_t idx, const uint8_t packet[4]) {
return 4 == tuh_midi_packet_write_n(idx, packet, 4);
}
//--------------------------------------------------------------------+
@ -143,7 +141,7 @@ bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
// Queue a message to the device using stream API. data is locally buffered and only transferred when buffered bytes
// reach the endpoint packet size or tuh_midi_write_flush() is called
// Returns number of bytes was successfully queued.
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *p_buffer, uint32_t bufsize);
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, const uint8_t *p_buffer, uint32_t bufsize);
// Get the MIDI stream from the device. Set the value pointed
// to by p_cable_num to the MIDI cable number intended to receive it.
@ -162,10 +160,10 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
// Invoked when MIDI interface is detected in enumeration. Application can copy/parse descriptor if needed.
// Note: may be fired before tuh_midi_mount_cb(), therefore midi interface is not mounted/ready.
void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data);
void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t *desc_cb_data);
// Invoked when device with MIDI interface is mounted.
void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data);
void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t *mount_cb_data);
// Invoked when device with MIDI interface is un-mounted
void tuh_midi_umount_cb(uint8_t idx);
@ -179,12 +177,12 @@ void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
bool midih_init (void);
bool midih_deinit (void);
bool midih_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
bool midih_set_config (uint8_t dev_addr, uint8_t itf_num);
bool midih_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void midih_close (uint8_t daddr);
bool midih_init(void);
bool midih_deinit(void);
uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
bool midih_set_config(uint8_t dev_addr, uint8_t itf_num);
bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void midih_close(uint8_t daddr);
#ifdef __cplusplus
}

View File

@ -379,22 +379,21 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_d
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
(void) rhport;
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
TU_VERIFY(MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass && MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol, 0);
// msc driver length is fixed
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(drv_len <= max_len);
const uint16_t drv_len =
(uint16_t)(sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(drv_len <= max_len, 0);
msch_interface_t* p_msc = get_itf(dev_addr);
tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
msch_interface_t *p_msc = get_itf(dev_addr);
const tusb_desc_endpoint_t *ep_desc = (const tusb_desc_endpoint_t *)tu_desc_next(desc_itf);
for (uint32_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer, 0);
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc), 0);
if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
p_msc->ep_in = ep_desc->bEndpointAddress;
@ -407,7 +406,7 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* de
p_msc->itf_num = desc_itf->bInterfaceNumber;
return true;
return drv_len;
}
bool msch_set_config(uint8_t daddr, uint8_t itf_num) {

View File

@ -30,7 +30,7 @@
#include "msc.h"
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//--------------------------------------------------------------------+
@ -38,17 +38,17 @@
//--------------------------------------------------------------------+
#ifndef CFG_TUH_MSC_MAXLUN
#define CFG_TUH_MSC_MAXLUN 4
#define CFG_TUH_MSC_MAXLUN 4
#endif
typedef struct {
msc_cbw_t const* cbw; // SCSI command
msc_csw_t const* csw; // SCSI status
void* scsi_data; // SCSI Data
uintptr_t user_arg; // user argument
}tuh_msc_complete_data_t;
const msc_cbw_t *cbw; // SCSI command
const msc_csw_t *csw; // SCSI status
void *scsi_data; // SCSI Data
uintptr_t user_arg; // user argument
} tuh_msc_complete_data_t;
typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data);
//--------------------------------------------------------------------+
// Application API
@ -74,12 +74,14 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
// Complete callback is invoked when SCSI op is complete.
// return true if success, false if there is already pending operation.
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
bool tuh_msc_scsi_command(uint8_t daddr, const msc_cbw_t *cbw, void *data, tuh_msc_complete_cb_t complete_cb,
uintptr_t arg);
// Perform SCSI Inquiry command
// Complete callback is invoked when SCSI op is complete.
// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t *response, tuh_msc_complete_cb_t complete_cb,
uintptr_t arg);
// Perform SCSI Test Unit Ready command
// Complete callback is invoked when SCSI op is complete.
@ -88,23 +90,27 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
// Perform SCSI Request Sense 10 command
// Complete callback is invoked when SCSI op is complete.
// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb,
uintptr_t arg);
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
// Complete callback is invoked when SCSI op is complete.
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void *buffer, uint32_t lba, uint16_t block_count,
tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
// Complete callback is invoked when SCSI op is complete.
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, const void *buffer, uint32_t lba, uint16_t block_count,
tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Read Capacity 10 command
// Complete callback is invoked when SCSI op is complete.
// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t *response,
tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
//------------- Application Callback -------------//
@ -118,15 +124,15 @@ void tuh_msc_umount_cb(uint8_t dev_addr);
// Internal Class Driver API
//--------------------------------------------------------------------+
bool msch_init (void);
bool msch_deinit (void);
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
bool msch_set_config (uint8_t daddr, uint8_t itf_num);
void msch_close (uint8_t dev_addr);
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
bool msch_init(void);
bool msch_deinit(void);
uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
bool msch_set_config(uint8_t daddr, uint8_t itf_num);
void msch_close(uint8_t dev_addr);
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
}
#endif
#endif

View File

@ -422,10 +422,10 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
cb_data.total_xferred_bytes = p_mtp->xferred_len;
bool is_complete = false;
// complete if ZLP or short packet or overflow
// complete if ZLP or short packet or total length reached
if (xferred_bytes == 0 || // ZLP
(xferred_bytes & (bulk_mps - 1)) || // short packet
p_mtp->xferred_len > p_mtp->total_len) {
p_mtp->xferred_len >= p_mtp->total_len) { // total length reached
is_complete = true;
}

View File

@ -119,20 +119,14 @@ bool tud_vendor_n_peek(uint8_t idx, uint8_t *u8) {
uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize) {
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
vendord_interface_t *p_itf = &_vendord_itf[idx];
return tu_edpt_stream_read(p_itf->rhport, &p_itf->stream.rx, buffer, bufsize);
}
uint32_t tud_vendor_n_read_discard(uint8_t idx, uint32_t count) {
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
vendord_interface_t *p_itf = &_vendord_itf[idx];
return tu_edpt_stream_discard(&p_itf->stream.rx, count);
return tu_edpt_stream_read(&p_itf->stream.rx, buffer, bufsize);
}
void tud_vendor_n_read_flush(uint8_t idx) {
TU_VERIFY(idx < CFG_TUD_VENDOR, );
vendord_interface_t *p_itf = &_vendord_itf[idx];
tu_edpt_stream_clear(&p_itf->stream.rx);
tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->stream.rx);
tu_edpt_stream_read_xfer(&p_itf->stream.rx);
}
#endif
@ -140,7 +134,7 @@ void tud_vendor_n_read_flush(uint8_t idx) {
bool tud_vendor_n_read_xfer(uint8_t idx) {
TU_VERIFY(idx < CFG_TUD_VENDOR);
vendord_interface_t *p_itf = &_vendord_itf[idx];
return tu_edpt_stream_read_xfer(p_itf->rhport, &p_itf->stream.rx);
return tu_edpt_stream_read_xfer(&p_itf->stream.rx);
}
#endif
@ -151,20 +145,20 @@ bool tud_vendor_n_read_xfer(uint8_t idx) {
uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) {
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
vendord_interface_t *p_itf = &_vendord_itf[idx];
return tu_edpt_stream_write(p_itf->rhport, &p_itf->stream.tx, buffer, (uint16_t)bufsize);
return tu_edpt_stream_write(&p_itf->stream.tx, buffer, (uint16_t)bufsize);
}
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
uint32_t tud_vendor_n_write_flush(uint8_t idx) {
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
vendord_interface_t *p_itf = &_vendord_itf[idx];
return tu_edpt_stream_write_xfer(p_itf->rhport, &p_itf->stream.tx);
return tu_edpt_stream_write_xfer(&p_itf->stream.tx);
}
uint32_t tud_vendor_n_write_available(uint8_t idx) {
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
vendord_interface_t *p_itf = &_vendord_itf[idx];
return tu_edpt_stream_write_available(p_itf->rhport, &p_itf->stream.tx);
return tu_edpt_stream_write_available(&p_itf->stream.tx);
}
#endif
@ -276,15 +270,15 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_t *stream_tx = &p_vendor->stream.tx;
if (stream_tx->ep_addr == 0) {
tu_edpt_stream_open(stream_tx, desc_ep);
tu_edpt_stream_write_xfer(rhport, stream_tx); // flush pending data
tu_edpt_stream_open(stream_tx, rhport, desc_ep);
tu_edpt_stream_write_xfer(stream_tx); // flush pending data
}
} else {
tu_edpt_stream_t *stream_rx = &p_vendor->stream.rx;
if (stream_rx->ep_addr == 0) {
tu_edpt_stream_open(stream_rx, desc_ep);
tu_edpt_stream_open(stream_rx, rhport, desc_ep);
#if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare for incoming data
TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare for incoming data
#endif
}
}
@ -297,14 +291,17 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin
}
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
(void)rhport;
(void)result;
const uint8_t idx = find_vendor_itf(ep_addr);
TU_VERIFY(idx < CFG_TUD_VENDOR);
vendord_interface_t *p_vendor = &_vendord_itf[idx];
if (ep_addr == p_vendor->stream.rx.ep_addr) {
#if CFG_TUD_VENDOR_RX_BUFSIZE
// Received new data: put into stream's fifo
tu_edpt_stream_read_xfer_complete(&p_vendor->stream.rx, xferred_bytes);
#endif
// invoke callback
#if CFG_TUD_VENDOR_RX_BUFSIZE == 0
@ -314,7 +311,7 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
#endif
#if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
tu_edpt_stream_read_xfer(rhport, &p_vendor->stream.rx); // prepare next data
tu_edpt_stream_read_xfer(&p_vendor->stream.rx); // prepare next data
#endif
} else if (ep_addr == p_vendor->stream.tx.ep_addr) {
// Send complete
@ -322,9 +319,9 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
// try to send more if possible
if (0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->stream.tx)) {
if (0 == tu_edpt_stream_write_xfer(&p_vendor->stream.tx)) {
// If there is no data left, a ZLP should be sent if xferred_bytes is multiple of EP Packet size and not zero
tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->stream.tx, xferred_bytes);
tu_edpt_stream_write_zlp_if_needed(&p_vendor->stream.tx, xferred_bytes);
}
#endif
}

View File

@ -73,9 +73,6 @@ bool tud_vendor_n_peek(uint8_t idx, uint8_t *ui8);
// Read from RX FIFO
uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize);
// Discard count bytes in RX FIFO
uint32_t tud_vendor_n_read_discard(uint8_t idx, uint32_t count);
// Flush (clear) RX FIFO
void tud_vendor_n_read_flush(uint8_t idx);
#endif
@ -124,10 +121,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void *buffer, uint3
return tud_vendor_n_read(0, buffer, bufsize);
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read_discard(uint32_t count) {
return tud_vendor_n_read_discard(0, count);
}
TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) {
tud_vendor_n_read_flush(0);
}

View File

@ -388,10 +388,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_subtype(void const* desc) {
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_desc_in_bounds(const uint8_t *p_desc, const uint8_t *desc_end) {
if (p_desc >= desc_end) {
return false;
}
return tu_desc_next(p_desc) <= desc_end;
return p_desc < desc_end && tu_desc_next(p_desc) <= desc_end;
}
// find descriptor that match byte1 (type)

View File

@ -24,21 +24,13 @@
* This file is part of the TinyUSB stack.
*/
/** \ingroup Group_Common
* \defgroup Group_Compiler Compiler
* \brief Group_Compiler brief
* @{ */
#ifndef TUSB_COMPILER_H_
#define TUSB_COMPILER_H_
#pragma once
#define TU_TOKEN(x) x
#define TU_STRING(x) #x ///< stringify without expand
#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
#define TU_STRCAT(a, b) a##b ///< concat without expand
#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand
#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens
@ -139,18 +131,20 @@
#define TU_FUNC_OPTIONAL_ARG(func, ...) TU_XSTRCAT(func##_arg, TU_ARGS_NUM(__VA_ARGS__))(__VA_ARGS__)
//--------------------------------------------------------------------+
// Compiler porting with Attribute and Endian
// Compiler Attribute Abstraction
//--------------------------------------------------------------------+
#if defined(__GNUC__) || defined(__ICCARM__) || defined(__TI_COMPILER_VERSION__)
#if defined(__ICCARM__)
#include <intrinsics.h> // for builtin functions
#endif
// TODO refactor since __attribute__ is supported across many compiler
#if defined(__GNUC__)
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#define TU_ATTR_ALIGNED(Bytes) __attribute__((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__((packed))
#define TU_ATTR_WEAK __attribute__((weak))
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
#define TU_ATTR_ALWAYS_INLINE __attribute__((always_inline))
#endif
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
@ -161,18 +155,17 @@
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
#define TU_ATTR_BIT_FIELD_ORDER_END
#if __GNUC__ < 5
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
#if (defined(__has_attribute) && __has_attribute(__fallthrough__)) || defined(__TI_COMPILER_VERSION__)
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
#else
#if __has_attribute(__fallthrough__)
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
#else
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
#endif
#define TU_ATTR_FALLTHROUGH \
do { \
} while (0) /* fallthrough */
#endif
// Endian conversion use well-known host to network (big endian) naming
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
// Endian conversion use well-known host to network (big endian) naming
// For TI ARM compiler, __BYTE_ORDER__ is not defined for MSP430 but still LE
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__MSP430__)
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
#else
#define TU_BYTE_ORDER TU_BIG_ENDIAN
@ -190,39 +183,12 @@
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
#endif
#ifndef __ARMCC_VERSION
// List of obsolete callback function that is renamed and should not be defined.
// Put it here since only gcc support this pragma
#pragma GCC poison tud_vendor_control_request_cb
#endif
#elif defined(__TI_COMPILER_VERSION__)
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
#define TU_ATTR_USED __attribute__ ((used))
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
#define TU_ATTR_PACKED_BEGIN
#define TU_ATTR_PACKED_END
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
#define TU_ATTR_BIT_FIELD_ORDER_END
// __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian)
#if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__)
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
#else
#define TU_BYTE_ORDER TU_BIG_ENDIAN
#if !defined(__ARMCC_VERSION) && !defined(__ICCARM__)
#pragma GCC poison tud_vendor_control_request_cb
#endif
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
#elif defined(__ICCARM__)
#include <intrinsics.h>
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
@ -316,7 +282,3 @@
#else
#error Byte order is undefined
#endif
#endif /* TUSB_COMPILER_H_ */
/// @}

View File

@ -84,7 +84,7 @@ bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_si
}
// clear fifo by resetting read and write indices
bool tu_fifo_clear(tu_fifo_t *f) {
void tu_fifo_clear(tu_fifo_t *f) {
ff_lock(f->mutex_wr);
ff_lock(f->mutex_rd);
@ -93,13 +93,12 @@ bool tu_fifo_clear(tu_fifo_t *f) {
ff_unlock(f->mutex_wr);
ff_unlock(f->mutex_rd);
return true;
}
// Change the fifo overwritable mode
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
void tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
if (f->overwritable == overwritable) {
return true;
return;
}
ff_lock(f->mutex_wr);
@ -109,8 +108,6 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
ff_unlock(f->mutex_wr);
ff_unlock(f->mutex_rd);
return true;
}
//--------------------------------------------------------------------+
@ -292,7 +289,7 @@ static void ff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd
// Advance an absolute index
// "absolute" index is only in the range of [0..2*depth)
TU_ATTR_ALWAYS_INLINE static inline uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) {
static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) {
// We limit the index space of p such that a correct wrap around happens
// Check for a wrap around or if we are in unused index space - This has to be checked first!!
// We are exploiting the wrap around to the correct index
@ -316,7 +313,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t idx2ptr(uint16_t depth, uint16_t id
// Works on local copies of w
// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms a full fifo
TU_ATTR_ALWAYS_INLINE static inline uint16_t correct_read_index(tu_fifo_t *f, uint16_t wr_idx) {
static uint16_t correct_read_index(tu_fifo_t *f, uint16_t wr_idx) {
uint16_t rd_idx;
if (wr_idx >= f->depth) {
rd_idx = wr_idx - f->depth;

View File

@ -157,8 +157,8 @@ typedef enum {
// Setup API
//--------------------------------------------------------------------+
bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_size, bool overwritable);
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
void tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
void tu_fifo_clear(tu_fifo_t *f);
#if OSAL_MUTEX_REQUIRED
TU_ATTR_ALWAYS_INLINE static inline
@ -224,10 +224,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_write_n(tu_fifo_t *f, const
//--------------------------------------------------------------------+
// return overflowable count (index difference), which can be used to determine both fifo count and an overflow state
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_ff_overflow_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
if (wr_idx >= rd_idx) {
return (uint16_t)(wr_idx - rd_idx);
const int32_t diff = (int32_t)wr_idx - (int32_t)rd_idx;
if (diff >= 0) {
return (uint16_t)diff;
} else {
return (uint16_t)(2 * depth - (rd_idx - wr_idx));
return (uint16_t)(2 * depth + diff);
}
}

View File

@ -33,6 +33,18 @@
extern "C" {
#endif
//--------------------------------------------------------------------+
// Configuration
//--------------------------------------------------------------------+
#if CFG_TUD_ENABLED && CFG_TUD_VENDOR && (CFG_TUD_VENDOR_TX_BUFSIZE == 0 || CFG_TUD_VENDOR_RX_BUFSIZE == 0)
#define CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED 1
#endif
#ifndef CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED
#define CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED 0
#endif
#define TUP_USBIP_CONTROLLER_NUM 2
extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM];
@ -50,36 +62,40 @@ typedef struct TU_ATTR_PACKED {
volatile uint8_t busy : 1;
volatile uint8_t stalled : 1;
volatile uint8_t claimed : 1;
}tu_edpt_state_t;
} tu_edpt_state_t;
typedef struct {
struct TU_ATTR_PACKED {
bool is_host : 1; // 1: host, 0: device
bool is_mps512 : 1; // 1: 512, 0: 64 since stream is used for Bulk only
};
uint8_t hwid; // device: rhport, host: daddr
bool is_host; // 1: host, 0: device
uint8_t ep_addr;
uint16_t ep_bufsize;
uint16_t mps;
uint16_t ep_bufsize;
uint8_t *ep_buf; // set to NULL to use xfer_fifo when CFG_TUD_EDPT_DEDICATED_HWFIFO = 1
tu_fifo_t ff;
// mutex: read if rx, otherwise write
OSAL_MUTEX_DEF(ff_mutexdef);
}tu_edpt_stream_t;
//--------------------------------------------------------------------+
// Endpoint
//--------------------------------------------------------------------+
// Check if endpoint descriptor is valid per USB specs
bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed, bool is_host);
// Check if endpoint descriptor is valid per USB specs if debug is enabled
#if CFG_TUSB_DEBUG
bool tu_edpt_validate(const tusb_desc_endpoint_t *desc_ep, tusb_speed_t speed);
#else
TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_validate(const tusb_desc_endpoint_t *desc_ep, tusb_speed_t speed) {
(void)desc_ep;
(void)speed;
return true;
}
#endif
// Bind all endpoint of a interface descriptor to class driver
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
// Calculate total length of n interfaces (depending on IAD)
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len);
// Bind drivers to all interfaces and endpoints in the provided configuration descriptor
bool tu_bind_driver_to_ep_itf(uint8_t driver_id, uint8_t ep2drv[][2], uint8_t itf2drv[], uint8_t itf_max,
const uint8_t *p_desc, uint16_t desc_len);
// Claim an endpoint with provided mutex
bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
@ -92,16 +108,28 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
//--------------------------------------------------------------------+
// Init an endpoint stream
bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize);
bool tu_edpt_stream_init(tu_edpt_stream_t *s, bool is_host, bool is_tx, bool overwritable, void *ff_buf,
uint16_t ff_bufsize, uint8_t *ep_buf, uint16_t ep_bufsize);
// Deinit an endpoint stream
bool tu_edpt_stream_deinit(tu_edpt_stream_t* s);
TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_deinit(tu_edpt_stream_t *s) {
(void)s;
#if OSAL_MUTEX_REQUIRED
if (s->ff.mutex_wr) {
osal_mutex_delete(s->ff.mutex_wr);
}
if (s->ff.mutex_rd) {
osal_mutex_delete(s->ff.mutex_rd);
}
#endif
}
// Open an endpoint stream
TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_open(tu_edpt_stream_t* s, tusb_desc_endpoint_t const *desc_ep) {
TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_open(tu_edpt_stream_t *s, uint8_t hwid,
const tusb_desc_endpoint_t *desc_ep) {
s->hwid = hwid;
s->ep_addr = desc_ep->bEndpointAddress;
s->is_mps512 = tu_edpt_packet_size(desc_ep) == 512;
s->mps = tu_edpt_packet_size(desc_ep);
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_is_opened(const tu_edpt_stream_t *s) {
@ -112,8 +140,8 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_close(tu_edpt_stream_t*
s->ep_addr = 0;
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_clear(tu_edpt_stream_t* s) {
return tu_fifo_clear(&s->ff);
TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_clear(tu_edpt_stream_t *s) {
tu_fifo_clear(&s->ff);
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_empty(tu_edpt_stream_t *s) {
@ -125,42 +153,40 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_empty(tu_edpt_stream_t *
//--------------------------------------------------------------------+
// Write to stream
uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize);
uint32_t tu_edpt_stream_write(tu_edpt_stream_t *s, const void *buffer, uint32_t bufsize);
// Start an usb transfer if endpoint is not busy. Return number of queued bytes
uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s);
uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t *s);
// Start an zero-length packet if needed
bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint32_t last_xferred_bytes);
bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t *s, uint32_t last_xferred_bytes);
// Get the number of bytes available for writing to FIFO
// Note: if no fifo, return endpoint size if not busy, 0 otherwise
uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s);
uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t *s);
//--------------------------------------------------------------------+
// Stream Read
//--------------------------------------------------------------------+
// Read from stream
uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize);
uint32_t tu_edpt_stream_read(tu_edpt_stream_t *s, void *buffer, uint32_t bufsize);
// Start an usb transfer if endpoint is not busy
uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s);
uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t *s);
// Complete read transfer by writing EP -> FIFO. Must be called in the transfer complete callback
TU_ATTR_ALWAYS_INLINE static inline
void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) {
if (0u != tu_fifo_depth(&s->ff) && s->ep_buf != NULL) {
tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes);
if (s->ep_buf != NULL) {
tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t)xferred_bytes);
}
}
// Complete read transfer with provided buffer
TU_ATTR_ALWAYS_INLINE static inline
void tu_edpt_stream_read_xfer_complete_with_buf(tu_edpt_stream_t *s, const void *buf, uint32_t xferred_bytes) {
if (0u != tu_fifo_depth(&s->ff)) {
tu_fifo_write_n(&s->ff, buf, (uint16_t) xferred_bytes);
}
tu_fifo_write_n(&s->ff, buf, (uint16_t)xferred_bytes);
}
// Get the number of bytes available for reading
@ -172,10 +198,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_peek(tu_edpt_stream_t *s
return tu_fifo_peek(&s->ff, ch);
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_edpt_stream_discard(tu_edpt_stream_t *s, uint32_t len) {
return (uint32_t)tu_fifo_discard_n(&s->ff, (uint16_t)len);
}
#ifdef __cplusplus
}
#endif

View File

@ -115,30 +115,29 @@ TU_ATTR_WEAK bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_si
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
// Invalid driver ID in itf2drv[] ep2drv[][] mapping
enum { DRVID_INVALID = 0xFFu };
typedef struct {
struct TU_ATTR_PACKED {
volatile uint8_t connected : 1;
volatile uint8_t addressed : 1;
volatile uint8_t suspended : 1;
// Note: these may share an enum state
volatile uint8_t connected;
volatile uint8_t addressed;
volatile uint8_t suspended;
uint8_t remote_wakeup_en : 1; // enable/disable by host
uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute
uint8_t self_powered : 1; // configuration descriptor's attribute
union {
struct TU_ATTR_PACKED {
uint8_t self_powered : 1; // configuration descriptor's attribute;
uint8_t remote_wakeup_en : 1; // enable/disable by host
};
uint8_t dev_state_bm;
};
volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
uint8_t speed;
uint8_t cfg_num; // current active configuration (0x00 is not configured)
uint8_t speed;
volatile uint8_t sof_consumer;
uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2];
}usbd_device_t;
} usbd_device_t;
static usbd_device_t _usbd_dev;
static volatile uint8_t _usbd_queued_setup;
@ -146,11 +145,11 @@ static volatile uint8_t _usbd_queued_setup;
//--------------------------------------------------------------------+
// Class Driver
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
#define DRIVER_NAME(_name) _name
#else
#define DRIVER_NAME(_name) NULL
#endif
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
#define DRIVER_NAME(_name) _name
#else
#define DRIVER_NAME(_name) NULL
#endif
// Built-in class drivers
static const usbd_class_driver_t _usbd_driver[] = {
@ -343,7 +342,7 @@ enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
static const usbd_class_driver_t *_app_driver = NULL;
static uint8_t _app_driver_count = 0;
#define TOTAL_DRIVER_COUNT ((uint8_t) (_app_driver_count + BUILTIN_DRIVER_COUNT))
#define TOTAL_DRIVER_COUNT ((uint8_t) (_app_driver_count + BUILTIN_DRIVER_COUNT))
// virtually joins built-in and application drivers together.
// Application is positioned first to allow overwriting built-in ones.
@ -475,8 +474,8 @@ bool tud_suspended(void) {
}
bool tud_remote_wakeup(void) {
// only wake up host if this feature is supported and enabled and we are suspended
TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en);
// only wake up host if this feature is enabled and we are suspended
TU_VERIFY(_usbd_dev.suspended && _usbd_dev.remote_wakeup_en);
dcd_remote_wakeup(_usbd_rhport);
return true;
}
@ -507,9 +506,9 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true; // skip if already initialized
}
TU_ASSERT(rh_init);
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
char const* speed_str = 0;
switch (rh_init->speed) {
switch (rh_init->speed) {
case TUSB_SPEED_HIGH:
speed_str = "High";
break;
@ -611,8 +610,8 @@ static void configuration_reset(uint8_t rhport) {
}
tu_varclr(&_usbd_dev);
(void) memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping
(void) memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping
(void)memset(_usbd_dev.itf2drv, TUSB_INDEX_INVALID_8, sizeof(_usbd_dev.itf2drv)); // invalid mapping
(void)memset(_usbd_dev.ep2drv, TUSB_INDEX_INVALID_8, sizeof(_usbd_dev.ep2drv)); // invalid mapping
}
static void usbd_reset(uint8_t rhport) {
@ -655,7 +654,9 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
}
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup
if (event.event_id == DCD_EVENT_SETUP_RECEIVED) {
TU_LOG_USBD("\r\n"); // extra line for setup
}
TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
#endif
@ -883,7 +884,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
case TUSB_REQ_FEATURE_REMOTE_WAKEUP:
TU_LOG_USBD(" Enable Remote Wakeup\r\n");
// Host may enable remote wake up before suspending especially HID device
_usbd_dev.remote_wakeup_en = true;
_usbd_dev.remote_wakeup_en = 1;
tud_control_status(rhport, p_request);
break;
@ -912,15 +913,15 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
TU_LOG_USBD(" Disable Remote Wakeup\r\n");
// Host may disable remote wake up after resuming
_usbd_dev.remote_wakeup_en = false;
_usbd_dev.remote_wakeup_en = 0;
tud_control_status(rhport, p_request);
break;
case TUSB_REQ_GET_STATUS: {
// Device status bit mask
// - Bit 0: Self Powered
// - Bit 0: Self Powered TODO must invoke callback to get actual status
// - Bit 1: Remote Wakeup enabled
uint16_t status = (uint16_t) ((_usbd_dev.self_powered ? 1u : 0u) | (_usbd_dev.remote_wakeup_en ? 2u : 0u));
uint16_t status = (uint16_t)_usbd_dev.dev_state_bm;
tud_control_xfer(rhport, p_request, &status, 2);
break;
}
@ -1034,117 +1035,46 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// Process Set Configure Request
// This function parse configuration descriptor & open drivers accordingly
static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
{
static bool process_set_config(uint8_t rhport, uint8_t cfg_num) {
// index is cfg_num-1
tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1);
const tusb_desc_configuration_t *desc_cfg =
(const tusb_desc_configuration_t *)tud_descriptor_configuration_cb(cfg_num - 1);
TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION);
// Parse configuration descriptor
_usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1u : 0u;
_usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED ) ? 1u : 0u;
_usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1u : 0u;
// Parse interface descriptor
uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t);
uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength);
while( p_desc < desc_end )
{
uint8_t assoc_itf_count = 1;
// Class will always starts with Interface Association (if any) and then Interface descriptor
if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
{
tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc;
assoc_itf_count = desc_iad->bInterfaceCount;
const uint8_t *p_desc = ((const uint8_t *)desc_cfg) + sizeof(tusb_desc_configuration_t);
const uint8_t *desc_end = ((const uint8_t *)desc_cfg) + tu_le16toh(desc_cfg->wTotalLength);
while (tu_desc_in_bounds(p_desc, desc_end)) {
// Class will always start with Interface Association (if any) and then Interface descriptor
if (TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc)) {
p_desc = tu_desc_next(p_desc); // next to Interface
// IAD's first interface number and class should match with opened interface
//TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber &&
// desc_iad->bFunctionClass == desc_itf->bInterfaceClass);
continue;
}
TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc;
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc));
const tusb_desc_interface_t *desc_itf = (const tusb_desc_interface_t *)p_desc;
// Find driver for this interface
uint16_t const remaining_len = (uint16_t) (desc_end-p_desc);
uint8_t drv_id;
for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++)
{
usbd_class_driver_t const *driver = get_driver(drv_id);
const uint16_t remaining_len = (uint16_t)(desc_end - p_desc);
uint8_t drv_id;
for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
const usbd_class_driver_t *driver = get_driver(drv_id);
TU_ASSERT(driver);
uint16_t const drv_len = driver->open(rhport, desc_itf, remaining_len);
const uint16_t drv_len = driver->open(rhport, desc_itf, remaining_len);
if ( (sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len) )
{
if ((sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len)) {
// Open successfully
TU_LOG_USBD(" %s opened\r\n", driver->name);
// Some drivers use 2 or more interfaces but may not have IAD e.g MIDI (always) or
// BTH (even CDC) with class in device descriptor (single interface)
if (assoc_itf_count == 1) {
#if CFG_TUD_CDC
if ( driver->open == cdcd_open ) {
assoc_itf_count = 2;
}
#endif
#if CFG_TUD_MIDI
if (driver->open == midid_open) {
// If there is a class-compliant Audio Control Class, then 2 interfaces. Otherwise, only one
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
assoc_itf_count = 2;
}
}
#endif
#if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT
if ( driver->open == btd_open ) assoc_itf_count = 2;
#endif
#if CFG_TUD_AUDIO
if (driver->open == audiod_open) {
// UAC1 device doesn't have IAD, needs to read AS interface count from CS AC descriptor
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
uint8_t const* p = tu_desc_next(p_desc);
uint8_t const* const itf_end = p_desc + remaining_len;
while (p < itf_end) {
if (TUSB_DESC_CS_INTERFACE == tu_desc_type(p) &&
AUDIO10_CS_AC_INTERFACE_HEADER == ((audio10_desc_cs_ac_interface_1_t const *) p)->bDescriptorSubType) {
audio10_desc_cs_ac_interface_1_t const * p_header = (audio10_desc_cs_ac_interface_1_t const *) p;
// AC + AS interfaces
assoc_itf_count = p_header->bInCollection + 1;
break;
}
p = tu_desc_next(p);
}
}
}
#endif
}
// bind (associated) interfaces to found driver
for(uint8_t i=0; i<assoc_itf_count; i++)
{
uint8_t const itf_num = desc_itf->bInterfaceNumber+i;
// Interface number must not be used already
TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[itf_num]);
_usbd_dev.itf2drv[itf_num] = drv_id;
}
// bind all endpoints to found driver
tu_edpt_bind_driver(_usbd_dev.ep2drv, desc_itf, drv_len, drv_id);
// next Interface
p_desc += drv_len;
// bind found driver to all interfaces and endpoint within drv_len
TU_ASSERT(tu_bind_driver_to_ep_itf(drv_id, _usbd_dev.ep2drv, _usbd_dev.itf2drv, CFG_TUD_INTERFACE_MAX, p_desc,
drv_len));
p_desc += drv_len; // next Interface
break; // exit driver find loop
}
}
@ -1363,20 +1293,17 @@ void usbd_spin_unlock(bool in_isr) {
}
// Parse consecutive endpoint descriptors (IN & OUT)
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
{
for(int i=0; i<ep_count; i++)
{
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
bool usbd_open_edpt_pair(uint8_t rhport, const uint8_t *p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t *ep_out,
uint8_t *ep_in) {
for (int i = 0; i < ep_count; i++) {
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && xfer_type == desc_ep->bmAttributes.xfer);
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
{
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
(*ep_in) = desc_ep->bEndpointAddress;
}else
{
} else {
(*ep_out) = desc_ep->bEndpointAddress;
}
@ -1406,7 +1333,7 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
rhport = _usbd_rhport;
TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX);
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed, false));
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed));
return dcd_edpt_open(rhport, desc_ep);
}
@ -1480,7 +1407,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
TU_LOG_USBD(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes);
TU_LOG_USBD(" Queue FIFO EP %02X with %u bytes ... ", ep_addr, total_bytes);
// Attempt to transfer on a busy endpoint, sound like a race condition !
TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
@ -1616,7 +1543,7 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep)
uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress);
TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX);
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed, false));
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed));
_usbd_dev.ep_status[epnum][dir].stalled = 0;
_usbd_dev.ep_status[epnum][dir].busy = 0;

View File

@ -218,27 +218,27 @@ bool hub_deinit(void) {
return true;
}
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
uint16_t hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
(void) rhport;
TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass &&
0 == itf_desc->bInterfaceSubClass);
TU_VERIFY(itf_desc->bInterfaceProtocol <= 1); // not support multiple TT yet
0 == itf_desc->bInterfaceSubClass, 0);
TU_VERIFY(itf_desc->bInterfaceProtocol <= 1, 0); // not support multiple TT yet
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
TU_ASSERT(drv_len <= max_len);
const uint16_t drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
TU_ASSERT(drv_len <= max_len, 0);
// Interrupt Status endpoint
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep), 0);
hub_interface_t* p_hub = get_hub_itf(dev_addr);
p_hub->itf_num = itf_desc->bInterfaceNumber;
p_hub->ep_in = desc_ep->bEndpointAddress;
return true;
return drv_len;
}
void hub_close(uint8_t dev_addr) {

View File

@ -206,12 +206,12 @@ bool hub_clear_feature(uint8_t hub_addr, uint8_t feature, tuh_xfer_cb_t complete
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
bool hub_init (void);
bool hub_deinit (void);
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
bool hub_set_config (uint8_t daddr, uint8_t itf_num);
bool hub_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hub_close (uint8_t dev_addr);
bool hub_init(void);
bool hub_deinit(void);
uint16_t hub_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
bool hub_set_config(uint8_t daddr, uint8_t itf_num);
bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hub_close(uint8_t dev_addr);
#ifdef __cplusplus
}

View File

@ -1066,7 +1066,14 @@ static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) {
}
bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const* desc_ep) {
TU_ASSERT(tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr), true));
// HACK: some device incorrectly always report 512 bulk regardless of link speed, overwrite descriptor to force 64
if (desc_ep->bmAttributes.xfer == TUSB_XFER_BULK && tu_edpt_packet_size(desc_ep) > 64 &&
tuh_speed_get(dev_addr) == TUSB_SPEED_FULL) {
TU_LOG1(" WARN: EP max packet size is 512 in fullspeed, force to 64\r\n");
tusb_desc_endpoint_t *hacked_ep = (tusb_desc_endpoint_t *)(uintptr_t)desc_ep;
hacked_ep->wMaxPacketSize = tu_htole16(64);
}
TU_ASSERT(tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)));
return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, desc_ep);
}
@ -1815,85 +1822,51 @@ static bool enum_parse_configuration_desc(uint8_t dev_addr, tusb_desc_configurat
TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len);
// parse each interfaces
while( p_desc < desc_end ) {
if ( 0 == tu_desc_len(p_desc) ) {
while (tu_desc_in_bounds(p_desc, desc_end)) {
if (0 == tu_desc_len(p_desc)) {
// A zero length descriptor indicates that the device is off spec (e.g. wrong wTotalLength).
// Parsed interfaces should still be usable
TU_LOG_USBH("Encountered a zero-length descriptor after %" PRIu32 " bytes\r\n", (uint32_t)p_desc - (uint32_t)desc_cfg);
break;
}
uint8_t assoc_itf_count = 1;
// Class will always starts with Interface Association (if any) and then Interface descriptor
if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) {
tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc;
assoc_itf_count = desc_iad->bInterfaceCount;
p_desc = tu_desc_next(p_desc); // next to Interface
// IAD's first interface number and class should match with opened interface
//TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber &&
// desc_iad->bFunctionClass == desc_itf->bInterfaceClass);
// skip if not interface
if (TUSB_DESC_INTERFACE != tu_desc_type(p_desc)) {
p_desc = tu_desc_next(p_desc);
continue;
}
const tusb_desc_interface_t *desc_itf = (const tusb_desc_interface_t *)p_desc;
TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc;
#if CFG_TUH_MIDI
// MIDI has 2 interfaces (Audio Control v1 + MIDIStreaming) but does not have IAD
// manually force associated count = 2
if (1 == assoc_itf_count &&
TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
assoc_itf_count = 2;
}
#endif
#if CFG_TUH_CDC
// Some legacy CDC device does not use IAD but rather use device class as hint to combine 2 interfaces
// manually force associated count = 2
if (1 == assoc_itf_count &&
TUSB_CLASS_CDC == desc_itf->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == desc_itf->bInterfaceSubClass) {
assoc_itf_count = 2;
}
#endif
uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, (uint16_t) (desc_end-p_desc));
TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
// uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, (uint16_t)
// (desc_end-p_desc)); TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
// Find driver for this interface
for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
usbh_class_driver_t const * driver = get_driver(drv_id);
if (driver && driver->open(dev->bus_info.rhport, dev_addr, desc_itf, drv_len) ) {
// open successfully
TU_LOG_USBH(" %s opened\r\n", driver->name);
const uint16_t remaining_len = (uint16_t)(desc_end - p_desc);
uint8_t drv_id;
for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
const usbh_class_driver_t *driver = get_driver(drv_id);
if (driver) {
const uint16_t drv_len = driver->open(dev->bus_info.rhport, dev_addr, desc_itf, remaining_len);
if ((sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len)) {
// open successfully
TU_LOG_USBH(" %s opened\r\n", driver->name);
// bind (associated) interfaces to found driver
for(uint8_t i=0; i<assoc_itf_count; i++) {
uint8_t const itf_num = desc_itf->bInterfaceNumber+i;
// bind found driver to all interfaces and endpoint within drv_len
tu_bind_driver_to_ep_itf(drv_id, dev->ep2drv, dev->itf2drv, CFG_TUH_INTERFACE_MAX, p_desc, drv_len);
// Interface number must not be used already
TU_ASSERT( TUSB_INDEX_INVALID_8 == dev->itf2drv[itf_num] );
dev->itf2drv[itf_num] = drv_id;
p_desc += drv_len; // next Interface
break; // exit driver find loop
}
// bind all endpoints to found driver
tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id);
break; // exit driver find loop
}
if (drv_id == TOTAL_DRIVER_COUNT - 1) {
TU_LOG_USBH("[%u:%u] Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
dev->bus_info.rhport, dev_addr, desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
}
}
// next Interface or IAD descriptor
p_desc += drv_len;
// no driver found
if (drv_id == TOTAL_DRIVER_COUNT) {
p_desc = tu_desc_next(p_desc); // skip this interface
TU_LOG_USBH("[%u:%u] Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
dev->bus_info.rhport, dev_addr, desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass,
desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
}
}
return true;

View File

@ -44,16 +44,15 @@
//--------------------------------------------------------------------+
// Class Driver API
//--------------------------------------------------------------------+
typedef struct {
char const* name;
bool (* const init )(void);
bool (* const deinit )(void);
bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void (* const close )(uint8_t dev_addr);
} usbh_class_driver_t;
const char *name;
bool (*const init)(void);
bool (*const deinit)(void);
uint16_t (*const open)(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
bool (*const set_config)(uint8_t dev_addr, uint8_t itf_num);
bool (*const xfer_cb)(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void (*const close)(uint8_t dev_addr);
} usbh_class_driver_t;
// Invoked when initializing host stack to get additional class drivers.
// Can be implemented by application to extend/overwrite class driver support.

View File

@ -2,7 +2,6 @@
import ctypes
import argparse
import click
import pandas as pd
# hex value for register: guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4

View File

@ -821,7 +821,7 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
}
}
#if CFG_TUSB_DEBUG
#if CFG_TUSB_DEBUG && 0
TU_ATTR_ALWAYS_INLINE static inline void print_hcint(uint32_t hcint) {
const char* str[] = {
"XFRC", "HALTED", "AHBERR", "STALL",

View File

@ -241,13 +241,14 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
return ret;
}
bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed, bool is_host) {
uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep);
#if CFG_TUSB_DEBUG
bool tu_edpt_validate(const tusb_desc_endpoint_t *desc_ep, tusb_speed_t speed) {
const uint16_t max_packet_size = tu_edpt_packet_size(desc_ep);
TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size);
switch (desc_ep->bmAttributes.xfer) {
case TUSB_XFER_ISOCHRONOUS: {
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023);
const uint16_t spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023);
TU_ASSERT(max_packet_size <= spec_size);
break;
}
@ -258,16 +259,7 @@ bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed, b
TU_ASSERT(max_packet_size == 512);
} else {
// Bulk fullspeed can only be 8, 16, 32, 64
if (is_host && max_packet_size == 512) {
// HACK: while in host mode, some device incorrectly always report 512 regardless of link speed
// overwrite descriptor to force 64
TU_LOG1(" WARN: EP max packet size is 512 in fullspeed, force to 64\r\n");
tusb_desc_endpoint_t* hacked_ep = (tusb_desc_endpoint_t*) (uintptr_t) desc_ep;
hacked_ep->wMaxPacketSize = tu_htole16(64);
} else {
TU_ASSERT(max_packet_size == 8 || max_packet_size == 16 ||
max_packet_size == 32 || max_packet_size == 64);
}
TU_ASSERT(max_packet_size == 8 || max_packet_size == 16 || max_packet_size == 32 || max_packet_size == 64);
}
break;
@ -283,61 +275,46 @@ bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed, b
return true;
}
#endif
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len,
uint8_t driver_id) {
uint8_t const* p_desc = (uint8_t const*) desc_itf;
uint8_t const* desc_end = p_desc + desc_len;
bool tu_bind_driver_to_ep_itf(uint8_t driver_id, uint8_t ep2drv[][2], uint8_t itf2drv[], uint8_t itf_max,
const uint8_t *p_desc, uint16_t desc_len) {
const uint8_t *desc_end = p_desc + desc_len;
while (tu_desc_in_bounds(p_desc, desc_end)) {
const uint8_t desc_type = tu_desc_type(p_desc);
while (p_desc < desc_end) {
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress;
TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id);
ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
if (desc_type == TUSB_DESC_ENDPOINT) {
const uint8_t ep_addr = ((const tusb_desc_endpoint_t *)p_desc)->bEndpointAddress;
const uint8_t ep_num = tu_edpt_number(ep_addr);
const uint8_t ep_dir = tu_edpt_dir(ep_addr);
ep2drv[ep_num][ep_dir] = driver_id;
} else if (desc_type == TUSB_DESC_INTERFACE) {
const tusb_desc_interface_t *desc_itf = (const tusb_desc_interface_t *)p_desc;
if (desc_itf->bAlternateSetting == 0) {
TU_ASSERT(desc_itf->bInterfaceNumber < itf_max);
itf2drv[desc_itf->bInterfaceNumber] = driver_id;
}
}
p_desc = tu_desc_next(p_desc);
}
}
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) {
uint8_t const* p_desc = (uint8_t const*) desc_itf;
uint16_t len = 0;
while ((itf_count--) > 0) {
// Next on interface desc
len += tu_desc_len(desc_itf);
p_desc = tu_desc_next(p_desc);
while (len < max_len) {
if (tu_desc_len(p_desc) == 0) {
// Escape infinite loop
break;
}
// return on IAD regardless of itf count
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
return len;
}
if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0) {
break;
}
len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
}
return len;
return true;
}
//--------------------------------------------------------------------+
// Endpoint Stream Helper for both Host and Device stack
//--------------------------------------------------------------------+
bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) {
bool tu_edpt_stream_init(tu_edpt_stream_t *s, bool is_host, bool is_tx, bool overwritable, void *ff_buf,
uint16_t ff_bufsize, uint8_t *ep_buf, uint16_t ep_bufsize) {
(void) is_tx;
#if CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED == 0 // FIFO is required
if (ff_buf == NULL || ff_bufsize == 0) {
return false;
}
#endif
s->is_host = is_host;
tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable);
@ -354,58 +331,46 @@ bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool ove
return true;
}
bool tu_edpt_stream_deinit(tu_edpt_stream_t *s) {
(void)s;
#if OSAL_MUTEX_REQUIRED
if (s->ff.mutex_wr) {
osal_mutex_delete(s->ff.mutex_wr);
}
if (s->ff.mutex_rd) {
osal_mutex_delete(s->ff.mutex_rd);
}
#endif
return true;
}
TU_ATTR_ALWAYS_INLINE static inline bool stream_claim(uint8_t hwid, tu_edpt_stream_t* s) {
static bool stream_claim(tu_edpt_stream_t *s) {
TU_VERIFY(s->ep_addr != 0); // must be opened
if (s->is_host) {
#if CFG_TUH_ENABLED
return usbh_edpt_claim(hwid, s->ep_addr);
#endif
return usbh_edpt_claim(s->hwid, s->ep_addr);
#endif
} else {
#if CFG_TUD_ENABLED
return usbd_edpt_claim(hwid, s->ep_addr);
#endif
return usbd_edpt_claim(s->hwid, s->ep_addr);
#endif
}
return false;
}
TU_ATTR_ALWAYS_INLINE static inline bool stream_xfer(uint8_t hwid, tu_edpt_stream_t* s, uint16_t count) {
static bool stream_xfer(tu_edpt_stream_t *s, uint16_t count) {
if (s->is_host) {
#if CFG_TUH_ENABLED
return usbh_edpt_xfer(hwid, s->ep_addr, count ? s->ep_buf : NULL, count);
#endif
return usbh_edpt_xfer(s->hwid, s->ep_addr, count ? s->ep_buf : NULL, count);
#endif
} else {
#if CFG_TUD_ENABLED
if (s->ep_buf == NULL) {
return usbd_edpt_xfer_fifo(hwid, s->ep_addr, &s->ff, count, false);
return usbd_edpt_xfer_fifo(s->hwid, s->ep_addr, &s->ff, count, false);
} else {
return usbd_edpt_xfer(hwid, s->ep_addr, count ? s->ep_buf : NULL, count, false);
return usbd_edpt_xfer(s->hwid, s->ep_addr, count ? s->ep_buf : NULL, count, false);
}
#endif
}
return false;
}
TU_ATTR_ALWAYS_INLINE static inline bool stream_release(uint8_t hwid, tu_edpt_stream_t* s) {
static bool stream_release(tu_edpt_stream_t *s) {
if (s->is_host) {
#if CFG_TUH_ENABLED
return usbh_edpt_release(hwid, s->ep_addr);
#endif
return usbh_edpt_release(s->hwid, s->ep_addr);
#endif
} else {
#if CFG_TUD_ENABLED
return usbd_edpt_release(hwid, s->ep_addr);
#endif
return usbd_edpt_release(s->hwid, s->ep_addr);
#endif
}
return false;
}
@ -413,19 +378,18 @@ TU_ATTR_ALWAYS_INLINE static inline bool stream_release(uint8_t hwid, tu_edpt_st
//--------------------------------------------------------------------+
// Stream Write
//--------------------------------------------------------------------+
bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint32_t last_xferred_bytes) {
bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t *s, uint32_t last_xferred_bytes) {
// ZLP condition: no pending data, last transferred bytes is multiple of packet size
const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
TU_VERIFY(tu_fifo_empty(&s->ff) && last_xferred_bytes > 0 && (0 == (last_xferred_bytes & (mps - 1))));
TU_VERIFY(stream_claim(hwid, s));
TU_ASSERT(stream_xfer(hwid, s, 0));
TU_VERIFY(tu_fifo_empty(&s->ff) && last_xferred_bytes > 0 && (0 == (last_xferred_bytes & (s->mps - 1))));
TU_VERIFY(stream_claim(s));
TU_ASSERT(stream_xfer(s, 0));
return true;
}
uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t *s) {
const uint16_t ff_count = tu_fifo_count(&s->ff);
TU_VERIFY(ff_count > 0, 0); // skip if no data
TU_VERIFY(stream_claim(hwid, s), 0);
TU_VERIFY(stream_claim(s), 0);
// Pull data from FIFO -> EP buf
uint16_t count;
@ -436,22 +400,23 @@ uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
}
if (count > 0) {
TU_ASSERT(stream_xfer(hwid, s, count), 0);
TU_ASSERT(stream_xfer(s, count), 0);
return count;
} else {
// Release endpoint since we don't make any transfer
// Note: data is dropped if terminal is not connected
stream_release(hwid, s);
stream_release(s);
return 0;
}
}
uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t *s, const void *buffer, uint32_t bufsize) {
TU_VERIFY(bufsize > 0); // TODO support ZLP
uint32_t tu_edpt_stream_write(tu_edpt_stream_t *s, const void *buffer, uint32_t bufsize) {
TU_VERIFY(bufsize > 0);
#if CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED
if (0 == tu_fifo_depth(&s->ff)) {
// non-fifo mode
TU_VERIFY(stream_claim(hwid, s), 0);
TU_VERIFY(stream_claim(s), 0);
uint32_t xact_len;
if (s->ep_buf != NULL) {
// using ep buf
@ -461,80 +426,89 @@ uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t *s, const void *buf
// using hwfifo
xact_len = bufsize;
}
TU_ASSERT(stream_xfer(hwid, s, (uint16_t) xact_len), 0);
TU_ASSERT(stream_xfer(s, (uint16_t)xact_len), 0);
return xact_len;
} else {
} else
#endif
{
const uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize);
// flush if fifo has more than packet size or
// in rare case: fifo depth is configured too small (which never reach packet size)
const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
if ((tu_fifo_count(&s->ff) >= mps) || (tu_fifo_depth(&s->ff) < mps)) {
tu_edpt_stream_write_xfer(hwid, s);
if ((tu_fifo_count(&s->ff) >= s->mps) || (tu_fifo_depth(&s->ff) < s->mps)) {
tu_edpt_stream_write_xfer(s);
}
return ret;
}
}
uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s) {
if (tu_fifo_depth(&s->ff) > 0) {
return (uint32_t) tu_fifo_remaining(&s->ff);
} else {
uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t *s) {
#if CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED
if (0 == tu_fifo_depth(&s->ff)) {
// non-fifo mode
TU_VERIFY(s->ep_addr > 0); // must be opened
bool is_busy = true;
if (s->is_host) {
#if CFG_TUH_ENABLED
is_busy = usbh_edpt_busy(hwid, s->ep_addr);
#endif
is_busy = usbh_edpt_busy(s->hwid, s->ep_addr);
#endif
} else {
#if CFG_TUD_ENABLED
is_busy = usbd_edpt_busy(hwid, s->ep_addr);
#endif
is_busy = usbd_edpt_busy(s->hwid, s->ep_addr);
#endif
}
return is_busy ? 0 : s->ep_bufsize;
} else
#endif
{
return (uint32_t)tu_fifo_remaining(&s->ff);
}
}
//--------------------------------------------------------------------+
// Stream Read
//--------------------------------------------------------------------+
uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t *s) {
#if CFG_TUSB_EDPT_STREAM_NO_FIFO_ENABLED
if (0 == tu_fifo_depth(&s->ff)) {
// non-fifo mode: RX need ep buffer
TU_VERIFY(s->ep_buf != NULL, 0);
TU_VERIFY(stream_claim(hwid, s), 0);
TU_ASSERT(stream_xfer(hwid, s, s->ep_bufsize), 0);
TU_VERIFY(stream_claim(s), 0);
TU_ASSERT(stream_xfer(s, s->ep_bufsize), 0);
return s->ep_bufsize;
} else {
const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
} else
#endif
{
uint16_t available = tu_fifo_remaining(&s->ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
// TODO Actually we can still carry out the transfer, keeping count of received bytes
// and slowly move it to the FIFO when read().
// This pre-check reduces endpoint claiming
TU_VERIFY(available >= mps);
TU_VERIFY(stream_claim(hwid, s), 0);
TU_VERIFY(available >= s->mps);
TU_VERIFY(stream_claim(s), 0);
available = tu_fifo_remaining(&s->ff); // re-get available since fifo can be changed
if (available >= mps) {
if (available >= s->mps) {
// multiple of packet size limit by ep bufsize
uint16_t count = (uint16_t) (available & ~(mps - 1));
count = tu_min16(count, s->ep_bufsize);
TU_ASSERT(stream_xfer(hwid, s, count), 0);
uint16_t count = (uint16_t) (available & ~(s->mps - 1));
if (s->ep_buf != NULL) {
count = tu_min16(count, s->ep_bufsize);
}
TU_ASSERT(stream_xfer(s, count), 0);
return count;
} else {
// Release endpoint since we don't make any transfer
stream_release(hwid, s);
stream_release(s);
return 0;
}
}
}
uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) {
uint32_t tu_edpt_stream_read(tu_edpt_stream_t *s, void *buffer, uint32_t bufsize) {
const uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize);
tu_edpt_stream_read_xfer(hwid, s);
tu_edpt_stream_read_xfer(s);
return num_read;
}

View File

@ -31,7 +31,7 @@
// Version is release as major.minor.revision eg 1.0.0
#define TUSB_VERSION_MAJOR 0
#define TUSB_VERSION_MINOR 20
#define TUSB_VERSION_REVISION 0
#define TUSB_VERSION_REVISION 1
#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
#define TUSB_VERSION_STRING TU_XSTRING(TUSB_VERSION_MAJOR) "." TU_XSTRING(TUSB_VERSION_MINOR) "." TU_XSTRING(TUSB_VERSION_REVISION)
@ -309,7 +309,7 @@
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 1
#endif
#if CFG_TUD_DWC2_SLAVE_ENABLE && !CFG_TUH_DWC2_DMA_ENABLE
#if CFG_TUH_DWC2_SLAVE_ENABLE && !CFG_TUH_DWC2_DMA_ENABLE
#define CFG_TUH_EDPT_DEDICATED_HWFIFO 1
#endif
#endif
@ -321,10 +321,18 @@
#ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT
#define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0
#endif
#define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT
#endif
// CI_HS support FIFO transfer if endpoint buffer is 4k aligned and size is multiple of 4k, also DCACHE is disabled
#ifndef CFG_TUD_CI_HS_EPBUF_4K_ALIGNED
#define CFG_TUD_CI_HS_EPBUF_4K_ALIGNED 0
#endif
#if CFG_TUD_CI_HS_EPBUF_4K_ALIGNED && !CFG_TUD_MEM_DCACHE_ENABLE
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 1
#endif
//------------- pio-usb -------------//
// Enable PIO-USB software host controller
#ifndef CFG_TUH_RPI_PIO_USB
@ -335,11 +343,27 @@
#define CFG_TUD_RPI_PIO_USB 0
#endif
// MAX3421 Host controller option
//------------ MAX3421 -------------//
// Enable MAX3421 USB host controller
#ifndef CFG_TUH_MAX3421
#define CFG_TUH_MAX3421 0
#endif
//------------ FSDEV --------------//
#if defined(TUP_USBIP_FSDEV)
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 1
#endif
//------------ MUSB --------------//
#if defined(TUP_USBIP_MUSB)
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 0 // need testing to enable
#endif
//------------ RUSB2 --------------//
#if defined(TUP_USBIP_RUSB2)
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 0 // need testing to enable
#endif
//--------------------------------------------------------------------
// RootHub Mode detection
//--------------------------------------------------------------------

View File

@ -52,7 +52,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
provider.ConsumeIntegralInRange<size_t>(0, Size));
fuzz_init(callback_data.data(), callback_data.size());
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
for (int i = 0; i < FUZZ_ITERATIONS; i++) {
if (provider.remaining_bytes() == 0) {

View File

@ -46,8 +46,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
std::vector<uint8_t> callback_data = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, Size));
fuzz_init(callback_data.data(), callback_data.size());
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
for (int i = 0; i < FUZZ_ITERATIONS; i++) {
if (provider.remaining_bytes() == 0) {

View File

@ -53,7 +53,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
provider.ConsumeIntegralInRange<size_t>(0, Size));
fuzz_init(callback_data.data(), callback_data.size());
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
for (int i = 0; i < FUZZ_ITERATIONS; i++) {
if (provider.remaining_bytes() == 0) {

View File

@ -662,7 +662,7 @@ def test_example(board, f1, example):
print(f'Flashing {fw_name}.elf')
# flash firmware. It may fail randomly, retry a few times
max_rety = 1
max_rety = 3
start_s = time.time()
for i in range(max_rety):
ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name)

View File

@ -5,6 +5,7 @@ import os
import sys
import time
import subprocess
import shlex
from pathlib import Path
from multiprocessing import Pool
@ -23,15 +24,36 @@ build_separator = '-' * 95
build_status = [STATUS_OK, STATUS_FAILED, STATUS_SKIPPED]
verbose = False
clean_build = False
parallel_jobs = os.cpu_count()
# CI board control lists (used when running under CI)
ci_skip_boards = {
'rp2040': [
'adafruit_feather_rp2040_usb_host',
'adafruit_fruit_jam',
'adafruit_metro_rp2350',
'feather_rp2040_max3421',
'pico_sdk',
'raspberry_pi_pico_w',
],
}
ci_preferred_boards = {
'stm32h7': ['stm32h743eval'],
}
# -----------------------------
# Helper
# -----------------------------
def run_cmd(cmd):
#print(cmd)
r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
title = f'Command Error: {cmd}'
if isinstance(cmd, str):
raise TypeError("run_cmd expects a list/tuple of args, not a string")
args = cmd
cmd_display = " ".join(args)
r = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
title = f'Command Error: {cmd_display}'
if r.returncode != 0:
# print build output if failed
if os.getenv('GITHUB_ACTIONS'):
@ -42,7 +64,7 @@ def run_cmd(cmd):
print(title)
print(r.stdout.decode("utf-8"))
elif verbose:
print(cmd)
print(cmd_display)
print(r.stdout.decode("utf-8"))
return r
@ -87,10 +109,10 @@ def cmake_board(board, build_args, build_flags_on):
start_time = time.monotonic()
build_dir = f'cmake-build/cmake-build-{board}'
build_flags = ''
build_flags = []
if len(build_flags_on) > 0:
build_flags = ' '.join(f'-D{flag}=1' for flag in build_flags_on)
build_flags = f'-DCFLAGS_CLI="{build_flags}"'
cli_flags = ' '.join(f'-D{flag}=1' for flag in build_flags_on)
build_flags.append(f'-DCFLAGS_CLI={cli_flags}')
build_dir += '-f1_' + '_'.join(build_flags_on)
family = find_family(board)
@ -101,27 +123,26 @@ def cmake_board(board, build_args, build_flags_on):
if build_utils.skip_example(example, board):
ret[2] += 1
else:
rcmd = run_cmd(f'idf.py -C examples/{example} -B {build_dir}/{example} -G Ninja '
f'-DBOARD={board} {build_flags} build')
rcmd = run_cmd([
'idf.py', '-C', f'examples/{example}', '-B', f'{build_dir}/{example}', '-GNinja',
f'-DBOARD={board}', *build_flags, 'build'
])
ret[0 if rcmd.returncode == 0 else 1] += 1
else:
rcmd = run_cmd(f'cmake examples -B {build_dir} -G Ninja -DBOARD={board} -DCMAKE_BUILD_TYPE=MinSizeRel '
f'{build_args} {build_flags}')
rcmd = run_cmd(['cmake', 'examples', '-B', build_dir, '-GNinja',
f'-DBOARD={board}', '-DCMAKE_BUILD_TYPE=MinSizeRel', '-DLINKERMAP_OPTION=-q -f tinyusb/src',
*build_args, *build_flags])
if rcmd.returncode == 0:
cmd = f"cmake --build {build_dir}"
njobs = parallel_jobs
# circleci docker return $nproc as 36 core, limit parallel according to resource class.
# Required for IAR, also prevent crashed/killed by docker
if os.getenv('CIRCLECI'):
resource_class = { 'small': 1, 'medium': 2, 'medium+': 3, 'large': 4 }
for rc in resource_class:
if rc in os.getenv('CIRCLE_JOB'):
njobs = resource_class[rc]
break
cmd += f' --parallel {njobs}'
if clean_build:
run_cmd(["cmake", "--build", build_dir, '--target', 'clean'])
cmd = ["cmake", "--build", build_dir, '--parallel', str(parallel_jobs)]
rcmd = run_cmd(cmd)
ret[0 if rcmd.returncode == 0 else 1] += 1
if rcmd.returncode == 0:
ret[0] += 1
run_cmd(["cmake", "--build", build_dir, '--target', 'tinyusb_metrics'])
# print(rcmd.stdout.decode("utf-8"))
else:
ret[1] += 1
example = 'all'
print_build_result(board, example, 0 if ret[1] == 0 else 1, time.monotonic() - start_time)
@ -138,12 +159,12 @@ def make_one_example(example, board, make_option):
r = 2
else:
start_time = time.monotonic()
# skip -j for circleci
if not os.getenv('CIRCLECI'):
make_option += ' -j'
make_cmd = f"make -C examples/{example} BOARD={board} {make_option}"
# run_cmd(f"{make_cmd} clean")
build_result = run_cmd(f"{make_cmd} all")
make_args = ["make", "-C", f"examples/{example}", f"BOARD={board}", '-j', str(parallel_jobs)]
if make_option:
make_args += shlex.split(make_option)
if clean_build:
run_cmd(make_args + ["clean"])
build_result = run_cmd(make_args + ['all'])
r = 0 if build_result.returncode == 0 else 1
print_build_result(board, example, r, time.monotonic() - start_time)
@ -180,7 +201,7 @@ def build_boards_list(boards, build_defines, build_system, build_flags_on):
for b in boards:
r = [0, 0, 0]
if build_system == 'cmake':
build_args = ' '.join(f'-D{d}' for d in build_defines)
build_args = [f'-D{d}' for d in build_defines]
r = cmake_board(b, build_args, build_flags_on)
elif build_system == 'make':
build_args = ' '.join(f'{d}' for d in build_defines)
@ -191,29 +212,42 @@ def build_boards_list(boards, build_defines, build_system, build_flags_on):
return ret
def build_family(family, build_defines, build_system, build_flags_on, one_per_family, boards):
skip_ci = ['pico_sdk']
def get_family_boards(family, one_random, one_first):
"""Get list of boards for a family.
Args:
family: Family name
one_random: If True, return only one random board
one_first: If True, return only the first board (alphabetical)
Returns:
List of board names
"""
skip_list = []
preferred_list = []
if os.getenv('GITHUB_ACTIONS') or os.getenv('CIRCLECI'):
skip_ci_file = Path(f"hw/bsp/{family}/skip_ci.txt")
if skip_ci_file.exists():
skip_ci = skip_ci_file.read_text().split()
skip_list = ci_skip_boards.get(family, [])
preferred_list = ci_preferred_boards.get(family, [])
all_boards = []
for entry in os.scandir(f"hw/bsp/{family}/boards"):
if entry.is_dir() and not entry.name in skip_ci:
if entry.is_dir() and entry.name not in skip_list:
all_boards.append(entry.name)
if not all_boards:
print(f"No boards found for family '{family}'")
return []
all_boards.sort()
ret = [0, 0, 0]
# If only-one flag is set, select one random board
if one_per_family:
for b in boards:
# skip if -b already specify one in this family
if find_family(b) == family:
return ret
all_boards = [random.choice(all_boards)]
# If only-one flags are set, honor select list first, then pick first or random
if one_first or one_random:
if preferred_list:
return [preferred_list[0]]
if one_first:
return [all_boards[0]]
if one_random:
return [random.choice(all_boards)]
ret = build_boards_list(all_boards, build_defines, build_system, build_flags_on)
return ret
return all_boards
# -----------------------------
@ -221,16 +255,21 @@ def build_family(family, build_defines, build_system, build_flags_on, one_per_fa
# -----------------------------
def main():
global verbose
global clean_build
global parallel_jobs
parser = argparse.ArgumentParser()
parser.add_argument('families', nargs='*', default=[], help='Families to build')
parser.add_argument('-b', '--board', action='append', default=[], help='Boards to build')
parser.add_argument('-c', '--clean', action='store_true', default=False, help='Clean before build')
parser.add_argument('-t', '--toolchain', default='gcc', help='Toolchain to use, default is gcc')
parser.add_argument('-s', '--build-system', default='cmake', help='Build system to use, default is cmake')
parser.add_argument('-D', '--define-symbol', action='append', default=[], help='Define to pass to build system')
parser.add_argument('-f1', '--build-flags-on', action='append', default=[], help='Build flag to pass to build system')
parser.add_argument('-1', '--one-per-family', action='store_true', default=False, help='Build only one random board inside a family')
parser.add_argument('--one-random', action='store_true', default=False,
help='Build only one random board of each specified family')
parser.add_argument('--one-first', action='store_true', default=False,
help='Build only the first board (alphabetical) of each specified family')
parser.add_argument('-j', '--jobs', type=int, default=os.cpu_count(), help='Number of jobs to run in parallel')
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
@ -241,8 +280,10 @@ def main():
build_system = args.build_system
build_defines = args.define_symbol
build_flags_on = args.build_flags_on
one_per_family = args.one_per_family
one_random = args.one_random
one_first = args.one_first
verbose = args.verbose
clean_build = args.clean
parallel_jobs = args.jobs
build_defines.append(f'TOOLCHAIN={toolchain}')
@ -254,9 +295,8 @@ def main():
print(build_separator)
print(build_format.format('Board', 'Example', '\033[39mResult\033[0m', 'Time'))
total_time = time.monotonic()
result = [0, 0, 0]
# build families
# get all families
all_families = []
if 'all' in families:
for entry in os.scandir("hw/bsp"):
@ -266,23 +306,19 @@ def main():
all_families = list(families)
all_families.sort()
# succeeded, failed, skipped
# get boards from families and append to boards list
all_boards = list(boards)
for f in all_families:
r = build_family(f, build_defines, build_system, build_flags_on, one_per_family, boards)
result[0] += r[0]
result[1] += r[1]
result[2] += r[2]
all_boards.extend(get_family_boards(f, one_random, one_first))
# build boards
r = build_boards_list(boards, build_defines, build_system, build_flags_on)
result[0] += r[0]
result[1] += r[1]
result[2] += r[2]
# build all boards
result = build_boards_list(all_boards, build_defines, build_system, build_flags_on)
total_time = time.monotonic() - total_time
print(build_separator)
print(f"Build Summary: {result[0]} {STATUS_OK}, {result[1]} {STATUS_FAILED} and took {total_time:.2f}s")
print(build_separator)
return result[1]

View File

@ -14,6 +14,9 @@ deps_mandatory = {
'lib/lwip': ['https://github.com/lwip-tcpip/lwip.git',
'159e31b689577dbf69cf0683bbaffbd71fa5ee10',
'all'],
'tools/linkermap': ['https://github.com/hathach/linkermap.git',
'8e1f440fa15c567aceb5aa0d14f6d18c329cc67f',
'all'],
'tools/uf2': ['https://github.com/microsoft/uf2.git',
'c594542b2faa01cc33a2b97c9fbebc38549df80a',
'all'],

667
tools/metrics.py Normal file
View File

@ -0,0 +1,667 @@
#!/usr/bin/env python3
"""Calculate average sizes from bloaty CSV or TinyUSB metrics JSON outputs."""
import argparse
import csv
import glob
import io
import json
import os
import sys
from collections import defaultdict
def expand_files(file_patterns):
"""Expand file patterns (globs) to list of files.
Args:
file_patterns: List of file paths or glob patterns
Returns:
List of expanded file paths
"""
expanded = []
for pattern in file_patterns:
if '*' in pattern or '?' in pattern:
expanded.extend(glob.glob(pattern))
else:
expanded.append(pattern)
return expanded
def parse_bloaty_csv(csv_text, filters=None):
"""Parse bloaty CSV text and return normalized JSON data structure."""
filters = filters or []
reader = csv.DictReader(io.StringIO(csv_text))
size_by_unit = defaultdict(int)
symbols_by_unit: dict[str, defaultdict[str, int]] = defaultdict(lambda: defaultdict(int))
sections_by_unit: dict[str, defaultdict[str, int]] = defaultdict(lambda: defaultdict(int))
for row in reader:
compile_unit = row.get("compileunits") or row.get("compileunit") or row.get("path")
if compile_unit is None:
continue
if str(compile_unit).upper() == "TOTAL":
continue
if filters and not any(filt in compile_unit for filt in filters):
continue
try:
vmsize = int(row.get("vmsize", 0))
except ValueError:
continue
size_by_unit[compile_unit] += vmsize
symbol_name = row.get("symbols", "")
if symbol_name:
symbols_by_unit[compile_unit][symbol_name] += vmsize
section_name = row.get("sections") or row.get("section")
if section_name and vmsize:
sections_by_unit[compile_unit][section_name] += vmsize
files = []
for unit_path, total_size in size_by_unit.items():
symbols = [
{"name": sym, "size": sz}
for sym, sz in sorted(symbols_by_unit[unit_path].items(), key=lambda x: x[1], reverse=True)
]
sections = {sec: sz for sec, sz in sections_by_unit[unit_path].items() if sz}
files.append(
{
"file": os.path.basename(unit_path) or unit_path,
"path": unit_path,
"size": total_size,
"total": total_size,
"symbols": symbols,
"sections": sections,
}
)
total_all = sum(size_by_unit.values())
return {"files": files, "TOTAL": total_all}
def combine_files(input_files, filters=None):
"""Combine multiple metrics inputs (bloaty CSV or metrics JSON) into a single data set."""
filters = filters or []
all_json_data = {"file_list": [], "data": []}
for fin in input_files:
if not os.path.exists(fin):
print(f"Warning: {fin} not found, skipping", file=sys.stderr)
continue
try:
if fin.endswith(".json"):
with open(fin, "r", encoding="utf-8") as f:
json_data = json.load(f)
if filters:
json_data["files"] = [
f
for f in json_data.get("files", [])
if f.get("path") and any(filt in f["path"] for filt in filters)
]
elif fin.endswith(".csv"):
with open(fin, "r", encoding="utf-8") as f:
csv_text = f.read()
json_data = parse_bloaty_csv(csv_text, filters)
else:
if fin.endswith(".elf"):
print(f"Warning: {fin} is an ELF; please run bloaty with --csv output first. Skipping.",
file=sys.stderr)
else:
print(f"Warning: {fin} is not a supported CSV or JSON metrics input. Skipping.",
file=sys.stderr)
continue
# Drop any fake TOTAL entries that slipped in as files
json_data["files"] = [
f for f in json_data.get("files", [])
if str(f.get("file", "")).upper() != "TOTAL"
]
all_json_data["file_list"].append(fin)
all_json_data["data"].append(json_data)
except Exception as e: # pragma: no cover - defensive
print(f"Warning: Failed to analyze {fin}: {e}", file=sys.stderr)
continue
return all_json_data
def compute_avg(all_json_data):
"""Compute average sizes from combined json_data.
Args:
all_json_data: Dictionary with file_list and data from combine_files()
Returns:
json_average: Dictionary with averaged size data
"""
if not all_json_data["data"]:
return None
# Merge files with the same 'file' value and compute averages
file_accumulator = {} # key: file name, value: {"sizes": [sizes], "totals": [totals], "symbols": {name: [sizes]}, "sections": {name: [sizes]}}
for json_data in all_json_data["data"]:
for f in json_data.get("files", []):
fname = f["file"]
if fname not in file_accumulator:
file_accumulator[fname] = {
"sizes": [],
"totals": [],
"path": f.get("path"),
"symbols": defaultdict(list),
"sections": defaultdict(list),
}
size_val = f.get("size", f.get("total", 0))
file_accumulator[fname]["sizes"].append(size_val)
file_accumulator[fname]["totals"].append(f.get("total", size_val))
for sym in f.get("symbols", []):
name = sym.get("name")
if name is None:
continue
file_accumulator[fname]["symbols"][name].append(sym.get("size", 0))
sections_map = f.get("sections") or {}
for sname, ssize in sections_map.items():
file_accumulator[fname]["sections"][sname].append(ssize)
# Build json_average with averaged values
files_average = []
for fname, data in file_accumulator.items():
avg_size = round(sum(data["sizes"]) / len(data["sizes"])) if data["sizes"] else 0
symbols_avg = []
for sym_name, sizes in data["symbols"].items():
if not sizes:
continue
symbols_avg.append({"name": sym_name, "size": round(sum(sizes) / len(sizes))})
symbols_avg.sort(key=lambda x: x["size"], reverse=True)
sections_avg = {
sec_name: round(sum(sizes) / len(sizes))
for sec_name, sizes in data["sections"].items()
if sizes
}
files_average.append(
{
"file": fname,
"path": data["path"],
"size": avg_size,
"symbols": symbols_avg,
"sections": sections_avg,
}
)
totals_list = [d.get("TOTAL") for d in all_json_data["data"] if isinstance(d.get("TOTAL"), (int, float))]
total_size = round(sum(totals_list) / len(totals_list)) if totals_list else (
sum(f["size"] for f in files_average) or 1)
for f in files_average:
f["percent"] = (f["size"] / total_size) * 100 if total_size else 0
for sym in f["symbols"]:
sym["percent"] = (sym["size"] / f["size"]) * 100 if f["size"] else 0
json_average = {
"file_list": all_json_data["file_list"],
"TOTAL": total_size,
"files": files_average,
}
return json_average
def compare_files(base_file, new_file, filters=None):
"""Compare two CSV or JSON inputs and generate difference report."""
filters = filters or []
base_avg = compute_avg(combine_files([base_file], filters))
new_avg = compute_avg(combine_files([new_file], filters))
if not base_avg or not new_avg:
return None
base_files = {f["file"]: f for f in base_avg["files"]}
new_files = {f["file"]: f for f in new_avg["files"]}
all_file_names = set(base_files.keys()) | set(new_files.keys())
comparison_files = []
for fname in sorted(all_file_names):
b = base_files.get(fname, {})
n = new_files.get(fname, {})
b_size = b.get("size", 0)
n_size = n.get("size", 0)
base_sections = b.get("sections") or {}
new_sections = n.get("sections") or {}
# Symbol diffs
b_syms = {s["name"]: s for s in b.get("symbols", [])}
n_syms = {s["name"]: s for s in n.get("symbols", [])}
all_syms = set(b_syms.keys()) | set(n_syms.keys())
symbols = []
for sym in all_syms:
sb = b_syms.get(sym, {}).get("size", 0)
sn = n_syms.get(sym, {}).get("size", 0)
symbols.append({"name": sym, "base": sb, "new": sn, "diff": sn - sb})
symbols.sort(key=lambda x: abs(x["diff"]), reverse=True)
comparison_files.append({
"file": fname,
"size": {"base": b_size, "new": n_size, "diff": n_size - b_size},
"symbols": symbols,
"sections": {
name: {
"base": base_sections.get(name, 0),
"new": new_sections.get(name, 0),
"diff": new_sections.get(name, 0) - base_sections.get(name, 0),
}
for name in sorted(set(base_sections) | set(new_sections))
},
})
total = {
"base": base_avg.get("TOTAL", 0),
"new": new_avg.get("TOTAL", 0),
"diff": new_avg.get("TOTAL", 0) - base_avg.get("TOTAL", 0),
}
return {
"base_file": base_file,
"new_file": new_file,
"total": total,
"files": comparison_files,
}
def get_sort_key(sort_order):
"""Get sort key function based on sort order.
Args:
sort_order: One of 'size-', 'size+', 'name-', 'name+'
Returns:
Tuple of (key_func, reverse)
"""
def _size_val(entry):
if isinstance(entry.get('total'), int):
return entry.get('total', 0)
if isinstance(entry.get('total'), dict):
return entry['total'].get('new', 0)
return entry.get('size', 0)
if sort_order == 'size-':
return _size_val, True
elif sort_order == 'size+':
return _size_val, False
elif sort_order == 'name-':
return lambda x: x.get('file', ''), True
else: # name+
return lambda x: x.get('file', ''), False
def format_diff(base, new, diff):
"""Format a diff value with percentage."""
if diff == 0:
return f"{new}"
if base == 0 or new == 0:
return f"{base}{new}"
pct = (diff / base) * 100
sign = "+" if diff > 0 else ""
return f"{base}{new} ({sign}{diff}, {sign}{pct:.1f}%)"
def write_json_output(json_data, path):
"""Write JSON output with indentation."""
with open(path, "w", encoding="utf-8") as outf:
json.dump(json_data, outf, indent=2)
def render_combine_table(json_data, sort_order='name+'):
"""Render averaged sizes as markdown table lines (no title)."""
files = json_data.get("files", [])
if not files:
return ["No entries."]
key_func, reverse = get_sort_key(sort_order)
files_sorted = sorted(files, key=key_func, reverse=reverse)
total_size = json_data.get("TOTAL") or sum(f.get("size", 0) for f in files_sorted)
pct_strings = [
f"{(f.get('percent') if f.get('percent') is not None else (f.get('size', 0) / total_size * 100 if total_size else 0)):.1f}%"
for f in files_sorted]
pct_width = 6
size_width = max(len("size"), *(len(str(f.get("size", 0))) for f in files_sorted), len(str(total_size)))
file_width = max(len("File"), *(len(f.get("file", "")) for f in files_sorted), len("TOTAL"))
# Build section totals on the fly from file data
sections_global = defaultdict(int)
for f in files_sorted:
for name, size in (f.get("sections") or {}).items():
sections_global[name] += size
# Display sections in reverse alphabetical order for stable column layout
section_names = sorted(sections_global.keys(), reverse=True)
section_widths = {}
for name in section_names:
max_val = max((f.get("sections", {}).get(name, 0) for f in files_sorted), default=0)
section_widths[name] = max(len(name), len(str(max_val)), 1)
if not section_names:
header = f"| {'File':<{file_width}} | {'size':>{size_width}} | {'%':>{pct_width}} |"
separator = f"| :{'-' * (file_width - 1)} | {'-' * (size_width - 1)}: | {'-' * (pct_width - 1)}: |"
else:
header_parts = [f"| {'File':<{file_width}} |"]
sep_parts = [f"| :{'-' * (file_width - 1)} |"]
for name in section_names:
header_parts.append(f" {name:>{section_widths[name]}} |")
sep_parts.append(f" {'-' * (section_widths[name] - 1)}: |")
header_parts.append(f" {'size':>{size_width}} | {'%':>{pct_width}} |")
sep_parts.append(f" {'-' * (size_width - 1)}: | {'-' * (pct_width - 1)}: |")
header = "".join(header_parts)
separator = "".join(sep_parts)
lines = [header, separator]
for f, pct_str in zip(files_sorted, pct_strings):
size_val = f.get("size", 0)
parts = [f"| {f.get('file', ''):<{file_width}} |"]
if section_names:
sections_map = f.get("sections") or {}
for name in section_names:
parts.append(f" {sections_map.get(name, 0):>{section_widths[name]}} |")
parts.append(f" {size_val:>{size_width}} | {pct_str:>{pct_width}} |")
lines.append("".join(parts))
total_parts = [f"| {'TOTAL':<{file_width}} |"]
if section_names:
for name in section_names:
total_parts.append(f" {sections_global.get(name, 0):>{section_widths[name]}} |")
total_parts.append(f" {total_size:>{size_width}} | {'100.0%':>{pct_width}} |")
lines.append("".join(total_parts))
return lines
def write_combine_markdown(json_data, path, sort_order='name+', title="TinyUSB Average Code Size Metrics"):
"""Write averaged size data to a markdown file."""
md_lines = [f"# {title}", ""]
md_lines.extend(render_combine_table(json_data, sort_order))
md_lines.append("")
if json_data.get("file_list"):
md_lines.extend(["<details>", "<summary>Input files</summary>", ""])
md_lines.extend([f"- {mf}" for mf in json_data["file_list"]])
md_lines.extend(["", "</details>", ""])
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(md_lines))
def write_compare_markdown(comparison, path, sort_order='size'):
"""Write comparison data to markdown file."""
md_lines = [
"# Size Difference Report",
"",
"Because TinyUSB code size varies by port and configuration, the metrics below represent the averaged totals across all example builds.",
"",
"Note: If there is no change, only one value is shown.",
"",
]
significant, minor, unchanged = _split_by_significance(comparison["files"], sort_order)
def render(title, rows, collapsed=False):
if collapsed:
md_lines.append(f"<details><summary>{title}</summary>")
md_lines.append("")
else:
md_lines.append(f"## {title}")
md_lines.extend(render_compare_table(_build_rows(rows, sort_order), include_sum=True))
md_lines.append("")
if collapsed:
md_lines.append("</details>")
md_lines.append("")
render("Changes >1% in size", significant)
render("Changes <1% in size", minor)
render("No changes", unchanged, collapsed=True)
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(md_lines))
def print_compare_summary(comparison, sort_order='name+'):
"""Print diff report to stdout in table form."""
files = comparison["files"]
rows = _build_rows(files, sort_order)
lines = render_compare_table(rows, include_sum=True)
for line in lines:
print(line)
def _build_rows(files, sort_order):
"""Sort files and prepare printable fields."""
def sort_key(file_row):
if sort_order == 'size-':
return abs(file_row["size"]["diff"])
if sort_order in ('size', 'size+'):
return abs(file_row["size"]["diff"])
if sort_order == 'name-':
return file_row['file']
return file_row['file']
reverse = sort_order in ('size-', 'name-')
files_sorted = sorted(files, key=sort_key, reverse=reverse)
rows = []
for f in files_sorted:
sd = f["size"]
diff_val = sd['new'] - sd['base']
if sd['base'] == 0:
pct_str = "n/a"
else:
pct_val = (diff_val / sd['base']) * 100
pct_str = f"{pct_val:+.1f}%"
rows.append({
"file": f['file'],
"base": sd['base'],
"new": sd['new'],
"diff": diff_val,
"pct": pct_str,
"sections": f.get("sections", {}),
})
return rows
def _split_by_significance(files, sort_order):
"""Split files into >1% changes, <1% changes, and no changes."""
def is_significant(file_row):
base = file_row["size"]["base"]
diff = abs(file_row["size"]["diff"])
if base == 0:
return diff != 0
return (diff / base) * 100 > 1.0
rows_sorted = sorted(
files,
key=lambda f: abs(f["size"]["diff"]) if sort_order.startswith("size") else f["file"],
reverse=sort_order in ('size-', 'name-'),
)
significant = []
minor = []
unchanged = []
for f in rows_sorted:
if f["size"]["diff"] == 0:
unchanged.append(f)
else:
(significant if is_significant(f) else minor).append(f)
return significant, minor, unchanged
def render_compare_table(rows, include_sum):
"""Return markdown table lines for given rows."""
if not rows:
return ["No entries.", ""]
# collect section columns (reverse alpha)
section_names = sorted(
{name for r in rows for name in (r.get("sections") or {})},
reverse=True,
)
def fmt_abs(val_old, val_new):
diff = val_new - val_old
if diff == 0:
return f"{val_new}"
sign = "+" if diff > 0 else ""
return f"{val_old}{val_new} ({sign}{diff})"
sum_base = sum(r["base"] for r in rows)
sum_new = sum(r["new"] for r in rows)
total_diff = sum_new - sum_base
total_pct = "n/a" if sum_base == 0 else f"{(total_diff / sum_base) * 100:+.1f}%"
file_width = max(len("file"), *(len(r["file"]) for r in rows), len("TOTAL"))
size_width = max(
len("size"),
*(len(fmt_abs(r["base"], r["new"])) for r in rows),
len(fmt_abs(sum_base, sum_new)),
)
pct_width = max(len("% diff"), *(len(r["pct"]) for r in rows), len(total_pct))
section_widths = {}
for name in section_names:
max_val_len = 0
for r in rows:
sec_entry = (r.get("sections") or {}).get(name, {"base": 0, "new": 0})
max_val_len = max(max_val_len, len(fmt_abs(sec_entry.get("base", 0), sec_entry.get("new", 0))))
section_widths[name] = max(len(name), max_val_len, 1)
header_parts = [f"| {'file':<{file_width}} |"]
sep_parts = [f"| :{'-' * (file_width - 1)} |"]
for name in section_names:
header_parts.append(f" {name:>{section_widths[name]}} |")
sep_parts.append(f" {'-' * (section_widths[name] - 1)}: |")
header_parts.append(f" {'size':>{size_width}} | {'% diff':>{pct_width}} |")
sep_parts.append(f" {'-' * (size_width - 1)}: | {'-' * (pct_width - 1)}: |")
header = "".join(header_parts)
separator = "".join(sep_parts)
lines = [header, separator]
for r in rows:
parts = [f"| {r['file']:<{file_width}} |"]
sections_map = r.get("sections") or {}
for name in section_names:
sec_entry = sections_map.get(name, {"base": 0, "new": 0})
parts.append(f" {fmt_abs(sec_entry.get('base', 0), sec_entry.get('new', 0)):>{section_widths[name]}} |")
parts.append(f" {fmt_abs(r['base'], r['new']):>{size_width}} | {r['pct']:>{pct_width}} |")
lines.append("".join(parts))
if include_sum:
total_parts = [f"| {'TOTAL':<{file_width}} |"]
for name in section_names:
total_base = sum((r.get("sections") or {}).get(name, {}).get("base", 0) for r in rows)
total_new = sum((r.get("sections") or {}).get(name, {}).get("new", 0) for r in rows)
total_parts.append(f" {fmt_abs(total_base, total_new):>{section_widths[name]}} |")
total_parts.append(f" {fmt_abs(sum_base, sum_new):>{size_width}} | {total_pct:>{pct_width}} |")
lines.append("".join(total_parts))
return lines
def cmd_combine(args):
"""Handle combine subcommand."""
input_files = expand_files(args.files)
all_json_data = combine_files(input_files, args.filters)
json_average = compute_avg(all_json_data)
if json_average is None:
print("No valid map files found", file=sys.stderr)
sys.exit(1)
if not args.quiet:
for line in render_combine_table(json_average, sort_order=args.sort):
print(line)
if args.json_out:
write_json_output(json_average, args.out + '.json')
if args.markdown_out:
write_combine_markdown(json_average, args.out + '.md', sort_order=args.sort,
title="TinyUSB Average Code Size Metrics")
def cmd_compare(args):
"""Handle compare subcommand."""
comparison = compare_files(args.base, args.new, args.filters)
if comparison is None:
print("Failed to compare files", file=sys.stderr)
sys.exit(1)
if not args.quiet:
print_compare_summary(comparison, args.sort)
if args.markdown_out:
write_compare_markdown(comparison, args.out + '.md', args.sort)
if not args.quiet:
print(f"Comparison written to {args.out}.md")
def main(argv=None):
parser = argparse.ArgumentParser(description='Code size metrics tool')
subparsers = parser.add_subparsers(dest='command', required=True, help='Available commands')
# Combine subcommand
combine_parser = subparsers.add_parser('combine', help='Combine and average bloaty CSV outputs or metrics JSON files')
combine_parser.add_argument('files', nargs='+',
help='Path to bloaty CSV output or TinyUSB metrics JSON file(s) (including linkermap-generated) or glob pattern(s)')
combine_parser.add_argument('-f', '--filter', dest='filters', action='append', default=[],
help='Only include compile units whose path contains this substring (can be repeated)')
combine_parser.add_argument('-o', '--out', dest='out', default='metrics',
help='Output path basename for JSON and Markdown files (default: metrics)')
combine_parser.add_argument('-j', '--json', dest='json_out', action='store_true',
help='Write JSON output file')
combine_parser.add_argument('-m', '--markdown', dest='markdown_out', action='store_true',
help='Write Markdown output file')
combine_parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
help='Suppress summary output')
combine_parser.add_argument('-S', '--sort', dest='sort', default='size-',
choices=['size', 'size-', 'size+', 'name', 'name-', 'name+'],
help='Sort order: size/size- (descending), size+ (ascending), name/name+ (ascending), name- (descending). Default: size-')
# Compare subcommand
compare_parser = subparsers.add_parser('compare', help='Compare two metrics inputs (bloaty CSV or metrics JSON)')
compare_parser.add_argument('base', help='Base CSV/metrics JSON file')
compare_parser.add_argument('new', help='New CSV/metrics JSON file')
compare_parser.add_argument('-f', '--filter', dest='filters', action='append', default=[],
help='Only include compile units whose path contains this substring (can be repeated)')
compare_parser.add_argument('-o', '--out', dest='out', default='metrics_compare',
help='Output path basename for Markdown/JSON files (default: metrics_compare)')
compare_parser.add_argument('-m', '--markdown', dest='markdown_out', action='store_true',
help='Write Markdown output file')
compare_parser.add_argument('-S', '--sort', dest='sort', default='name+',
choices=['size', 'size-', 'size+', 'name', 'name-', 'name+'],
help='Sort order: size/size- (descending), size+ (ascending), name/name+ (ascending), name- (descending). Default: name+')
compare_parser.add_argument('-q', '--quiet', dest='quiet', action='store_true',
help='Suppress stdout summary output')
args = parser.parse_args(argv)
if args.command == 'combine':
cmd_combine(args)
elif args.command == 'compare':
cmd_compare(args)
if __name__ == '__main__':
main()