mirror of
https://github.com/hathach/tinyusb.git
synced 2026-03-24 00:14:54 +00:00
Merge branch 'refs/heads/master' into audio_open
This commit is contained in:
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
144
.github/workflows/build.yml
vendored
144
.github/workflows/build.yml
vendored
@ -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:
|
||||
|
||||
34
.github/workflows/build_util.yml
vendored
34
.github/workflows/build_util.yml
vendored
@ -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
|
||||
|
||||
24
.github/workflows/ci_set_matrix.py
vendored
24
.github/workflows/ci_set_matrix.py
vendored
@ -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
38
.github/workflows/metrics_comment.yml
vendored
Normal 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
5
.gitignore
vendored
@ -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
1
.idea/cmake.xml
generated
@ -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="-DCFG_TUD_DWC2_DMA_ENABLE=1"" />
|
||||
<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" />
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 ()
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 ()
|
||||
|
||||
@ -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 ()
|
||||
|
||||
@ -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 ()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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_ */
|
||||
|
||||
@ -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
|
||||
@ -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_ */
|
||||
|
||||
/** @} */
|
||||
@ -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;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
37
src/class/vendor/vendor_device.c
vendored
37
src/class/vendor/vendor_device.c
vendored
@ -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
|
||||
}
|
||||
|
||||
7
src/class/vendor/vendor_device.h
vendored
7
src/class/vendor/vendor_device.h
vendored
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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_ */
|
||||
|
||||
/// @}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
101
src/host/usbh.c
101
src/host/usbh.c
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
import ctypes
|
||||
import argparse
|
||||
import click
|
||||
import pandas as pd
|
||||
|
||||
# hex value for register: guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
|
||||
|
||||
@ -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",
|
||||
|
||||
218
src/tusb.c
218
src/tusb.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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)
|
||||
|
||||
158
tools/build.py
158
tools/build.py
@ -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]
|
||||
|
||||
|
||||
|
||||
@ -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
667
tools/metrics.py
Normal 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()
|
||||
Reference in New Issue
Block a user