summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-22 15:36:37 +0700
committerhathach <[email protected]>2026-06-22 15:36:37 +0700
commit706e4a5daaf8bbb55f0a7d00b69d4bc3c4cd70eb (patch)
tree4b9d46e8b6397ba050d7fba159a67efc0d511eca
parent8abaf22bb6f2b63910cf35fb70cd223d0e4b30cd (diff)
parent299c0a55629691f6bbded895a4be377633e815ab (diff)
Merge remote-tracking branch 'origin/master' into add-ch58x-usbfs
-rw-r--r--.github/workflows/build.yml47
-rw-r--r--.github/workflows/claude-code-review.yml5
-rw-r--r--.github/workflows/metrics_comment.yml39
-rw-r--r--.github/workflows/pr_comment.yml131
-rw-r--r--examples/device/audio_test_freertos/sdkconfig.defaults3
-rw-r--r--examples/device/cdc_uac2/src/uac2_app.c124
-rw-r--r--examples/device/midi2_device/src/usb_descriptors.c4
-rw-r--r--examples/device/uac2_headset/src/main.c124
-rw-r--r--examples/device/uac2_speaker_fb/src/main.c124
-rw-r--r--examples/device/usbtmc/src/usb_descriptors.c14
-rw-r--r--hw/bsp/stm32f1/family.c10
-rw-r--r--hw/bsp/stm32f3/family.c7
-rw-r--r--hw/bsp/stm32g4/family.c10
-rw-r--r--src/class/audio/audio.h32
-rw-r--r--src/class/net/ncm_device.c15
-rw-r--r--src/class/net/net_device.h4
-rw-r--r--src/class/video/video_device.c2
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h22
-rwxr-xr-xtest/hil/hil_test.py9
-rw-r--r--test/hil/tinyusb.json3
-rw-r--r--tools/metrics.py4
21 files changed, 404 insertions, 329 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3f6458285..c8c597e50 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -171,10 +171,6 @@ jobs:
mv metrics_compare.md $COMPARE_FILE
gh release upload $CURR_TAG metrics.json $COMPARE_FILE
- - 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@v7
@@ -183,7 +179,6 @@ jobs:
path: |
metrics_compare.md
metrics.json
- pr_number.txt
- name: Post Code Metrics as PR Comment
if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false)
@@ -420,45 +415,3 @@ jobs:
path: hil_report.md
if-no-files-found: ignore
overwrite: true
-
- # ---------------------------------------
- # Combine HIL results from the rigs into a single sticky PR comment (one table per rig)
- # ---------------------------------------
- hil-report:
- needs: [ hil-tinyusb, hil-hfp-iar ]
- if: |
- always() &&
- (needs.hil-tinyusb.result != 'skipped' || needs.hil-hfp-iar.result != 'skipped') &&
- github.event_name == 'pull_request' &&
- github.repository_owner == 'hathach' &&
- github.event.pull_request.head.repo.fork == false
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
- steps:
- - name: Download HIL reports
- uses: actions/download-artifact@v5
- with:
- pattern: hil-report-*
- path: hil-reports
-
- - name: Combine rig reports (one table per rig)
- run: |
- {
- echo "## Hardware-in-the-loop (HIL) Test Report"
- echo
- for d in hil-reports/hil-report-*; do
- [ -d "$d" ] || continue
- echo "### ${d#hil-reports/hil-report-}"
- echo
- cat "$d/hil_report.md" 2>/dev/null || echo "_no report produced_"
- echo
- done
- } > hil_combined.md
- cat hil_combined.md
-
- - name: Post HIL report as sticky PR comment
- uses: marocchino/sticky-pull-request-comment@v2
- with:
- header: hil-report
- path: hil_combined.md
diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml
index 60cc4db4b..59019616f 100644
--- a/.github/workflows/claude-code-review.yml
+++ b/.github/workflows/claude-code-review.yml
@@ -53,5 +53,8 @@ jobs:
# TEMPORARY: expose the full Claude transcript in the Actions log for
# debugging. Revert to remove once done.
show_full_output: true
- claude_args: '--max-turns 50 --model claude-opus-4-8'
+ claude_args: |
+ --max-turns 50
+ --model claude-opus-4-8
+ --effort max
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
diff --git a/.github/workflows/metrics_comment.yml b/.github/workflows/metrics_comment.yml
deleted file mode 100644
index 5d250211f..000000000
--- a/.github/workflows/metrics_comment.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-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:
- actions: read
- pull-requests: write
- steps:
- - name: Download Artifacts
- uses: actions/download-artifact@v5
- 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 }}
diff --git a/.github/workflows/pr_comment.yml b/.github/workflows/pr_comment.yml
new file mode 100644
index 000000000..4d50817b4
--- /dev/null
+++ b/.github/workflows/pr_comment.yml
@@ -0,0 +1,131 @@
+name: PR Comment
+
+on:
+ workflow_run:
+ workflows: ["Build"]
+ types:
+ - completed
+
+jobs:
+ # Resolve the PR number from trusted workflow_run metadata, NOT from build artifacts: a forked PR
+ # controls its own Build run and could plant any number, which the privileged jobs below would
+ # then post to. Same-repo PRs populate workflow_run.pull_requests; for forks it is empty, so look
+ # the PR up by the trusted head SHA.
+ pr_number:
+ if: github.event.workflow_run.event == 'pull_request'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: read
+ outputs:
+ number: ${{ steps.resolve.outputs.number }}
+ steps:
+ - name: Resolve PR number
+ id: resolve
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
+ HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
+ HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }}
+ PRS_JSON: ${{ toJSON(github.event.workflow_run.pull_requests) }}
+ run: |
+ # Every lookup is best-effort: on any miss the number stays empty and the comment jobs
+ # below simply skip (never a failed check).
+ # Same-repo PRs: workflow_run.pull_requests is populated.
+ num=$(printf '%s' "$PRS_JSON" | jq -r '.[0].number // empty')
+ # Fork PRs: pull_requests is empty. Find the open PR by its trusted head ref and confirm
+ # its head SHA matches the built commit.
+ if [ -z "$num" ] && [ -n "$HEAD_BRANCH" ] && [ -n "$HEAD_REPO" ]; then
+ num=$(gh api --method GET "repos/$REPO/pulls" \
+ -f state=open -f head="${HEAD_REPO%%/*}:$HEAD_BRANCH" \
+ --jq '[.[] | select(.head.sha == env.HEAD_SHA)][0].number // empty' 2>/dev/null || true)
+ fi
+ echo "number=$num" >> "$GITHUB_OUTPUT"
+
+ metrics-comment:
+ needs: pr_number
+ if: >
+ github.event.workflow_run.conclusion == 'success' &&
+ needs.pr_number.outputs.number != ''
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ pull-requests: write
+ steps:
+ - name: Download Artifacts
+ uses: actions/download-artifact@v5
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ name: metrics-comment
+ # Best-effort: docs-only PRs skip code-metrics, so the artifact may be absent.
+ continue-on-error: true
+
+ - name: Post Code Metrics as PR Comment
+ if: hashFiles('metrics_compare.md') != ''
+ uses: marocchino/sticky-pull-request-comment@v2
+ with:
+ header: code-metrics
+ path: metrics_compare.md
+ number: ${{ needs.pr_number.outputs.number }}
+
+ # ---------------------------------------
+ # Combine the rigs' HIL reports into one sticky PR comment (one table per rig).
+ # Runs here (workflow_run / base-repo context) rather than in build.yml so it also works on
+ # forked PRs, whose build-side GITHUB_TOKEN is read-only and cannot post comments. Posts even
+ # on build/HIL failure (when the report matters most); skips only on cancellation.
+ # ---------------------------------------
+ hil-comment:
+ needs: pr_number
+ if: >
+ github.event.workflow_run.conclusion != 'cancelled' &&
+ needs.pr_number.outputs.number != ''
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ pull-requests: write
+ steps:
+ - name: Download HIL reports
+ uses: actions/download-artifact@v5
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ pattern: hil-report-*
+ path: hil-reports
+ continue-on-error: true
+
+ - name: Combine rig reports (one table per rig)
+ id: combine
+ run: |
+ shopt -s nullglob
+ dirs=(hil-reports/hil-report-*)
+ if [ ${#dirs[@]} -eq 0 ]; then
+ echo "No HIL reports found"
+ exit 0
+ fi
+ {
+ echo "## Hardware-in-the-loop (HIL) Test Report"
+ echo
+ for d in "${dirs[@]}"; do
+ [ -d "$d" ] || continue
+ echo "### ${d#hil-reports/hil-report-}"
+ echo
+ cat "$d/hil_report.md" 2>/dev/null || echo "_no report produced_"
+ echo
+ done
+ } > hil_combined.md
+ # Fork PRs can influence report content and this job posts in base-repo context, so
+ # neutralize @-mentions (insert a zero-width space) to prevent notification abuse.
+ zwsp=$(printf '\342\200\213')
+ sed -i -E "s/@([A-Za-z0-9_-])/@${zwsp}\1/g" hil_combined.md
+ cat hil_combined.md
+ echo "found=true" >> "$GITHUB_OUTPUT"
+
+ - name: Post HIL report as sticky PR comment
+ if: steps.combine.outputs.found == 'true'
+ uses: marocchino/sticky-pull-request-comment@v2
+ with:
+ header: hil-report
+ path: hil_combined.md
+ number: ${{ needs.pr_number.outputs.number }}
diff --git a/examples/device/audio_test_freertos/sdkconfig.defaults b/examples/device/audio_test_freertos/sdkconfig.defaults
index 83871619e..6e7a1cf52 100644
--- a/examples/device/audio_test_freertos/sdkconfig.defaults
+++ b/examples/device/audio_test_freertos/sdkconfig.defaults
@@ -1,3 +1,6 @@
CONFIG_IDF_CMAKE=y
+# 1000 Hz tick: the UAC iso IN endpoint must be serviced every 1 ms frame;
+# ESP-IDF's default 100 Hz starves the audio task -> host capture fails (arecord EIO).
+CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c
index 6e9d1d9e3..a504c3b57 100644
--- a/examples/device/cdc_uac2/src/uac2_app.c
+++ b/examples/device/cdc_uac2/src/uac2_app.c
@@ -82,20 +82,20 @@ void audio_task(void) {
}
// Helper for clock get requests
-static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_t const *request)
+static bool tud_audio_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request)
{
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ)
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ)
{
- if (request->bRequest == AUDIO20_CS_REQ_CUR)
+ if (p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
audio20_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf));
}
- else if (request->bRequest == AUDIO20_CS_REQ_RANGE)
+ else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE)
{
audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
@@ -110,32 +110,33 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
}
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
}
}
- else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO20_CS_REQ_CUR)
+ else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID &&
+ p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
audio20_control_cur_1_t cur_valid = { .bCur = 1 };
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid));
}
- TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
// Helper for clock set requests
-static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf)
+static bool tud_audio_clock_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf)
{
(void)rhport;
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
- TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ)
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
+
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ)
{
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *)buf)->bCur;
@@ -145,80 +146,83 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_
}
else
{
- TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
}
// Helper for feature unit get requests
-static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request)
+static bool tud_audio_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request)
{
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+ uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
- if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR)
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
- audio20_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] };
- TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1));
+ audio20_control_cur_1_t mute1 = { .bCur = mute[channel_num] };
+ TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1));
}
- else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME)
+ else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME)
{
- if (request->bRequest == AUDIO20_CS_REQ_RANGE)
+ if (p_request->bRequest == AUDIO20_CS_REQ_RANGE)
{
audio20_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = tu_htole16(1),
.subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) }
};
- TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
+ TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num,
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol));
}
- else if (request->bRequest == AUDIO20_CS_REQ_CUR)
+ else if (p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
- audio20_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) };
- TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol));
+ audio20_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[channel_num]) };
+ TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol));
}
}
- TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
// Helper for feature unit set requests
-static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf)
+static bool tud_audio_feature_unit_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf)
{
(void)rhport;
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
- TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+ uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
+
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE)
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE)
{
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *)buf)->bCur;
+ mute[channel_num] = ((audio20_control_cur_1_t const *)buf)->bCur;
- TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
+ TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]);
return true;
}
- else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME)
+ else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME)
{
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *)buf)->bCur;
+ volume[channel_num] = ((audio20_control_cur_2_t const *)buf)->bCur;
- TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
+ TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256);
return true;
}
else
{
- TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
}
@@ -229,32 +233,32 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_r
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
- audio20_control_request_t const *request = (audio20_control_request_t const *)p_request;
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_CLOCK) {
- return tud_audio_clock_get_request(rhport, request);
+ if (entity_id == UAC2_ENTITY_CLOCK) {
+ return tud_audio_clock_get_request(rhport, p_request);
}
- if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) {
- return tud_audio_feature_unit_get_request(rhport, request);
+ if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT) {
+ return tud_audio_feature_unit_get_request(rhport, p_request);
} else {
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
}
return false;
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
- audio20_control_request_t const *request = (audio20_control_request_t const *)p_request;
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) {
- return tud_audio_feature_unit_set_request(rhport, request, buf);
+ if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT) {
+ return tud_audio_feature_unit_set_request(rhport, p_request, buf);
}
- if (request->bEntityID == UAC2_ENTITY_CLOCK) {
- return tud_audio_clock_set_request(rhport, request, buf);
+ if (entity_id == UAC2_ENTITY_CLOCK) {
+ return tud_audio_clock_set_request(rhport, p_request, buf);
}
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
return false;
}
diff --git a/examples/device/midi2_device/src/usb_descriptors.c b/examples/device/midi2_device/src/usb_descriptors.c
index 19a43f2d8..f035f9c48 100644
--- a/examples/device/midi2_device/src/usb_descriptors.c
+++ b/examples/device/midi2_device/src/usb_descriptors.c
@@ -70,7 +70,11 @@ enum {
// Endpoint addresses
#define EPNUM_MIDI2_OUT 0x01
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+#define EPNUM_MIDI2_IN 0x82
+#else
#define EPNUM_MIDI2_IN 0x81
+#endif
static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c
index 779e927bc..c30b31f67 100644
--- a/examples/device/uac2_headset/src/main.c
+++ b/examples/device/uac2_headset/src/main.c
@@ -319,16 +319,16 @@ static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const
#if TUD_OPT_HIGH_SPEED
// Helper for clock get requests
-static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
+static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
- if (request->bRequest == AUDIO20_CS_REQ_CUR) {
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
+ if (p_request->bRequest == AUDIO20_CS_REQ_CUR) {
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
- } else if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf));
+ } else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) {
audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
@@ -340,28 +340,29 @@ static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
}
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
}
- } else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO20_CS_REQ_CUR) {
+ } else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID &&
+ p_request->bRequest == AUDIO20_CS_REQ_CUR) {
audio20_control_cur_1_t cur_valid = {.bCur = 1};
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid));
}
- TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
// Helper for clock set requests
-static bool audio20_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
+static bool audio20_clock_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) {
(void) rhport;
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
- TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t));
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
+
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
@@ -369,93 +370,96 @@ static bool audio20_clock_set_request(uint8_t rhport, audio20_control_request_t
return true;
} else {
- TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
}
// Helper for feature unit get requests
-static bool audio20_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
+static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+ uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
- if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) {
- audio20_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
- TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
- } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
- if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]};
+ TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1));
+ } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) {
+ if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) {
audio20_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = tu_htole16(1),
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
- TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
+ TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num,
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
- } else if (request->bRequest == AUDIO20_CS_REQ_CUR) {
- audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
- TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol));
+ } else if (p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[channel_num])};
+ TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol));
}
}
- TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
// Helper for feature unit set requests
-static bool audio20_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) {
+static bool audio20_feature_unit_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) {
(void) rhport;
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
- TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+ uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
+
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) {
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t));
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *) buf)->bCur;
+ mute[channel_num] = ((audio20_control_cur_1_t const *) buf)->bCur;
- TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
+ TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]);
return true;
- } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t));
+ } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *) buf)->bCur;
+ volume[channel_num] = ((audio20_control_cur_2_t const *) buf)->bCur;
- TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
+ TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256);
return true;
} else {
- TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
}
static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
- audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return audio20_clock_get_request(rhport, request);
- if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return audio20_feature_unit_get_request(rhport, request);
+ if (entity_id == UAC2_ENTITY_CLOCK)
+ return audio20_clock_get_request(rhport, p_request);
+ if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT)
+ return audio20_feature_unit_get_request(rhport, p_request);
else {
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
}
return false;
}
static bool audio20_set_req_entity(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
- audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return audio20_feature_unit_set_request(rhport, request, buf);
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return audio20_clock_set_request(rhport, request, buf);
+ if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT)
+ return audio20_feature_unit_set_request(rhport, p_request, buf);
+ if (entity_id == UAC2_ENTITY_CLOCK)
+ return audio20_clock_set_request(rhport, p_request, buf);
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
return false;
}
diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c
index 402642162..7e4d27f1c 100644
--- a/examples/device/uac2_speaker_fb/src/main.c
+++ b/examples/device/uac2_speaker_fb/src/main.c
@@ -315,16 +315,16 @@ const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
-static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
+static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
- if (request->bRequest == AUDIO20_CS_REQ_CUR) {
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
+ if (p_request->bRequest == AUDIO20_CS_REQ_CUR) {
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
- } else if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf));
+ } else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) {
audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
@@ -336,25 +336,26 @@ static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
}
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
}
- } else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO20_CS_REQ_CUR) {
+ } else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID &&
+ p_request->bRequest == AUDIO20_CS_REQ_CUR) {
audio20_control_cur_1_t cur_valid = {.bCur = 1};
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid));
}
- TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
-static bool audio20_clock_set_request(audio20_control_request_t const *request, uint8_t const *buf) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
- TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
+static bool audio20_clock_set_request(tusb_control_request_t const *p_request, uint8_t const *buf) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t));
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
+
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
@@ -362,89 +363,92 @@ static bool audio20_clock_set_request(audio20_control_request_t const *request,
return true;
} else {
- TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
}
-static bool audio20_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
+static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+ uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
- if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) {
- audio20_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
- TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
- } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
- if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]};
+ TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1));
+ } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) {
+ if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) {
audio20_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = tu_htole16(1),
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
- TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
+ TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num,
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
- } else if (request->bRequest == AUDIO20_CS_REQ_CUR) {
- audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
- TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
- return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol));
+ } else if (p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[channel_num])};
+ TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol));
}
}
- TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
-static bool audio20_feature_unit_set_request(audio20_control_request_t const *request, uint8_t const *buf) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
- TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
+static bool audio20_feature_unit_set_request(tusb_control_request_t const *p_request, uint8_t const *buf) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+ uint8_t const channel_num = TU_U16_LOW(p_request->wValue);
+
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) {
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t));
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *) buf)->bCur;
+ mute[channel_num] = ((audio20_control_cur_1_t const *) buf)->bCur;
- TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
+ TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]);
return true;
- } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
- TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t));
+ } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *) buf)->bCur;
+ volume[channel_num] = ((audio20_control_cur_2_t const *) buf)->bCur;
- TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
+ TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256);
return true;
} else {
- TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n",
+ ctrl_sel, p_request->bRequest);
return false;
}
}
static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
- audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return audio20_clock_get_request(rhport, request);
- if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
- return audio20_feature_unit_get_request(rhport, request);
+ if (entity_id == UAC2_ENTITY_CLOCK)
+ return audio20_clock_get_request(rhport, p_request);
+ if (entity_id == UAC2_ENTITY_FEATURE_UNIT)
+ return audio20_feature_unit_get_request(rhport, p_request);
else {
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
}
return false;
}
static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint8_t *buf) {
- audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
- return audio20_feature_unit_set_request(request, buf);
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return audio20_clock_set_request(request, buf);
+ if (entity_id == UAC2_ENTITY_FEATURE_UNIT)
+ return audio20_feature_unit_set_request(p_request, buf);
+ if (entity_id == UAC2_ENTITY_CLOCK)
+ return audio20_clock_set_request(p_request, buf);
TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
return false;
}
diff --git a/examples/device/usbtmc/src/usb_descriptors.c b/examples/device/usbtmc/src/usb_descriptors.c
index 16bd176f8..ecdcef834 100644
--- a/examples/device/usbtmc/src/usb_descriptors.c
+++ b/examples/device/usbtmc/src/usb_descriptors.c
@@ -79,16 +79,26 @@ uint8_t const * tud_descriptor_device_cb(void)
#if defined(CFG_TUD_USBTMC)
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+# define EPNUM_USBTMC_OUT 0x01
+# define EPNUM_USBTMC_IN 0x82
+# define EPNUM_USBTMC_INT 0x83
+#else
+# define EPNUM_USBTMC_OUT 0x01
+# define EPNUM_USBTMC_IN 0x81
+# define EPNUM_USBTMC_INT 0x82
+#endif
+
# define TUD_USBTMC_DESC_MAIN(_itfnum,_bNumEndpoints, _bulkMaxPacketLength) \
TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, /*_stridx = */ 4u, TUD_USBTMC_PROTOCOL_USB488), \
- TUD_USBTMC_BULK_DESCRIPTORS(/* OUT = */0x01, /* IN = */ 0x81, /* packet size = */_bulkMaxPacketLength)
+ TUD_USBTMC_BULK_DESCRIPTORS(EPNUM_USBTMC_OUT, EPNUM_USBTMC_IN, /* packet size = */_bulkMaxPacketLength)
#if CFG_TUD_USBTMC_ENABLE_INT_EP
// USBTMC Interrupt xfer always has length of 2, but we use epMaxSize=8 for
// compatibility with mcus that only allow 8, 16, 32 or 64 for FS endpoints
# define TUD_USBTMC_DESC(_itfnum, _bulkMaxPacketLength) \
TUD_USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 3, _bulkMaxPacketLength), \
- TUD_USBTMC_INT_DESCRIPTOR(/* INT ep # */ 0x82, /* epMaxSize = */ 8, /* bInterval = */16u )
+ TUD_USBTMC_INT_DESCRIPTOR(EPNUM_USBTMC_INT, /* epMaxSize = */ 8, /* bInterval = */16u )
# define TUD_USBTMC_DESC_LEN (TUD_USBTMC_IF_DESCRIPTOR_LEN + TUD_USBTMC_BULK_DESCRIPTORS_LEN + TUD_USBTMC_INT_DESCRIPTOR_LEN)
#else
diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c
index abde44d21..67427da1f 100644
--- a/hw/bsp/stm32f1/family.c
+++ b/hw/bsp/stm32f1/family.c
@@ -63,9 +63,11 @@ void USB_LP_IRQHandler(void) {
tud_int_handler(0);
}
-void USBWakeUp_IRQHandler(void) {
- tud_int_handler(0);
-}
+// USB wakeup EXTI IRQ is not enabled by the fsdev driver (see fsdev_stm32.h);
+// restore when STOP-mode wakeup is implemented.
+//void USBWakeUp_IRQHandler(void) {
+// tud_int_handler(0);
+//}
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
@@ -128,7 +130,7 @@ void board_init(void) {
// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
- NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
+ //NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
#endif
// LED
diff --git a/hw/bsp/stm32f3/family.c b/hw/bsp/stm32f3/family.c
index 35e1852e8..bddf224d2 100644
--- a/hw/bsp/stm32f3/family.c
+++ b/hw/bsp/stm32f3/family.c
@@ -76,9 +76,10 @@ void USB_LP_IRQHandler(void) {
// USB wakeup interrupt (Channel 76): Triggered by the wakeup event from the USB
// Suspend mode.
-void USBWakeUp_RMP_IRQHandler(void) {
- tud_int_handler(0);
-}
+// Not enabled by the fsdev driver (see fsdev_stm32.h); restore for STOP-mode wakeup.
+//void USBWakeUp_RMP_IRQHandler(void) {
+// tud_int_handler(0);
+//}
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
diff --git a/hw/bsp/stm32g4/family.c b/hw/bsp/stm32g4/family.c
index 433f74e2a..cf7d4329b 100644
--- a/hw/bsp/stm32g4/family.c
+++ b/hw/bsp/stm32g4/family.c
@@ -61,9 +61,11 @@ void USB_LP_IRQHandler(void) {
tud_int_handler(0);
}
-void USBWakeUp_IRQHandler(void) {
- tud_int_handler(0);
-}
+// USB wakeup EXTI IRQ is not enabled by the fsdev driver (see fsdev_stm32.h);
+// restore when STOP-mode wakeup is implemented.
+//void USBWakeUp_IRQHandler(void) {
+// tud_int_handler(0);
+//}
// USB PD
void UCPD1_IRQHandler(void) {
@@ -99,7 +101,7 @@ void board_init(void) {
// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
NVIC_SetPriority(USB_HP_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(USB_LP_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
- NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
+ //NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
#endif
GPIO_InitTypeDef GPIO_InitStruct;
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index cff38cc22..428391bb2 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -490,10 +490,19 @@ typedef struct TU_ATTR_PACKED {
uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_ENDPOINT.
uint8_t bEndpointAddress;///< The address of the endpoint on the USB device described by this descriptor.
struct TU_ATTR_PACKED {
+#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE)
uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
uint8_t usage : 2; // Data, Feedback, Implicit feedback
uint8_t : 2;
+#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE)
+ uint8_t : 2;
+ uint8_t usage : 2; // Data, Feedback, Implicit feedback
+ uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
+ uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
+#else
+ #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE"
+#endif
} bmAttributes;
uint16_t wMaxPacketSize; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected.
uint8_t bInterval; ///< Interval for polling endpoint for data transfers.
@@ -1177,29 +1186,6 @@ typedef struct TU_ATTR_PACKED {
uint16_t wLockDelay; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
} audio20_desc_cs_as_iso_data_ep_t;
-// 5.2.2 Control Request Layout
-typedef struct TU_ATTR_PACKED {
- union {
- struct TU_ATTR_PACKED {
- uint8_t recipient : 5;///< Recipient type tusb_request_recipient_t.
- uint8_t type : 2; ///< Request type tusb_request_type_t.
- uint8_t direction : 1;///< Direction type. tusb_dir_t
- } bmRequestType_bit;
-
- uint8_t bmRequestType;
- };
-
- uint8_t bRequest;///< Request type audio_cs_req_t
- uint8_t bChannelNumber;
- uint8_t bControlSelector;
- union {
- uint8_t bInterface;
- uint8_t bEndpoint;
- };
- uint8_t bEntityID;
- uint16_t wLength;
-} audio20_control_request_t;
-
//// 5.2.3 Control Request Parameter Block Layout
// 5.2.3.1 1-byte Control CUR Parameter Block
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index a78e472c2..e5f441300 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -152,6 +152,14 @@ TU_ATTR_WEAK void tud_network_set_packet_filter_cb(uint16_t packet_filter) {
(void) packet_filter;
}
+TU_ATTR_WEAK bool tud_network_default_link_state_cb(void) {
+ #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
+ return CFG_TUD_NCM_DEFAULT_LINK_UP;
+ #else
+ return true;
+ #endif
+}
+
/**
* This is the NTB parameter structure
*
@@ -852,12 +860,7 @@ void netd_init(void) {
for (int i = 0; i < RECV_NTB_N; ++i) {
ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
}
- // Default link state - can be configured via CFG_TUD_NCM_DEFAULT_LINK_UP
- #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
- ncm_interface.link_is_up = CFG_TUD_NCM_DEFAULT_LINK_UP;
- #else
- ncm_interface.link_is_up = true; // Default to link up if not set.
- #endif
+ ncm_interface.link_is_up = tud_network_default_link_state_cb();
} // netd_init
/**
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index 332df09b3..1ad069d92 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -106,6 +106,10 @@ extern uint8_t tud_network_mac_address[6];
// Optional callback: informs the application about host requested packet filter bits
void tud_network_set_packet_filter_cb(uint16_t packet_filter);
+// Optional callback: called during netd_init() to get the initial link state.
+// Override to return the actual physical link state instead of the compile-time default.
+bool tud_network_default_link_state_cb(void);
+
// Set the network link state (up/down) and notify the host
void tud_network_link_state(uint8_t rhport, bool is_up);
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index bbcfe45d5..e31ab4194 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -865,7 +865,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
/* FS must be less than or equal to max packet size */
TU_VERIFY (tu_edpt_packet_size(ep) >= max_size);
#ifdef TUP_DCD_EDPT_ISO_ALLOC
- usbd_edpt_iso_activate(rhport, ep);
+ TU_ASSERT(usbd_edpt_iso_activate(rhport, ep));
#else
TU_ASSERT(usbd_edpt_open(rhport, ep));
#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
index b15c95302..93cdac808 100644
--- a/src/portable/st/stm32_fsdev/fsdev_stm32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -168,14 +168,18 @@
#define FSDEV_USE_SBUF_ISO 0
#endif
-//--------------------------------------------------------------------+
-//
-//--------------------------------------------------------------------+
-
+// STM32L1 calls it USB_FS_WKUP_IRQn; alias so the commented USBWakeUp_IRQn below
+// can be uncommented as-is.
#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn)
#define USBWakeUp_IRQn USB_FS_WKUP_IRQn
#endif
+// USB interrupt vectors to enable in NVIC. The EXTI-line USB wakeup interrupt
+// (USBWakeUp_IRQn, and USBWakeUp_RMP_IRQn on F3) is left commented out: resume is
+// handled in-band via ISTR.WKUP in the USB_LP/HP ISR; the EXTI line is only needed to
+// wake the core from STOP mode, which this driver does not implement (it never arms or
+// clears that EXTI line, so enabling its NVIC vector can only spuriously fire/freeze).
+// TODO: uncomment USBWakeUp_IRQn (+ arm/clear its EXTI line) when adding STOP-mode wakeup.
static const IRQn_Type fsdev_irq[] = {
#if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4, OPT_MCU_STM32U5)
USB_IRQn,
@@ -192,15 +196,15 @@ static const IRQn_Type fsdev_irq[] = {
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
USB_HP_CAN1_TX_IRQn,
USB_LP_CAN1_RX0_IRQn,
- USBWakeUp_IRQn,
+ //USBWakeUp_IRQn,
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
USB_HP_CAN_TX_IRQn,
USB_LP_CAN_RX0_IRQn,
- USBWakeUp_IRQn,
+ //USBWakeUp_IRQn,
#elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
USB_HP_IRQn,
USB_LP_IRQn,
- USBWakeUp_IRQn,
+ //USBWakeUp_IRQn,
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
USB_HP_IRQn,
USB_LP_IRQn,
@@ -223,7 +227,7 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) {
if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
NVIC_EnableIRQ(USB_HP_IRQn);
NVIC_EnableIRQ(USB_LP_IRQn);
- NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
+ //NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
} else
#endif
{
@@ -243,7 +247,7 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) {
if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
NVIC_DisableIRQ(USB_HP_IRQn);
NVIC_DisableIRQ(USB_LP_IRQn);
- NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+ //NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
} else
#endif
{
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index ec6c7c448..07375c1ad 100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -22,7 +22,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-# Host setup:
+# Host setup (required: a missing tool fails its test rather than skipping it):
# - System packages: sudo apt install mtools libmtp9 alsa-utils iperf
# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun)
# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64
@@ -51,7 +51,6 @@ import serial
import subprocess
import json
import glob
-import shutil
from multiprocessing import Pool, Lock
from multiprocessing import TimeoutError as MpTimeoutError
import hashlib
@@ -1380,10 +1379,6 @@ def test_device_audio_test_freertos(board):
if os.name == 'nt':
return 'skipped'
- arecord = shutil.which('arecord')
- if arecord is None:
- return 'skipped'
-
pcm = None
timeout = ENUM_TIMEOUT
while timeout > 0:
@@ -1397,7 +1392,7 @@ def test_device_audio_test_freertos(board):
raw_path = f'/tmp/tinyusb_audio_{uid}.raw'
cmd = [
- arecord,
+ 'arecord',
'-D', pcm,
'-q',
'-f', 'S16_LE',
diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json
index 71d92aae1..2ea910c4c 100644
--- a/test/hil/tinyusb.json
+++ b/test/hil/tinyusb.json
@@ -132,6 +132,7 @@
"device": true,
"host": false,
"dual": true,
+ "skip": ["device/audio_test_freertos"],
"dev_attached": [
{
"vid_pid": "067b_2303",
@@ -139,7 +140,7 @@
"is_cdc": true
}
],
- "comment": "pl23x"
+ "comment": "pl23x; audio_test_freertos skipped: samd51 iso-IN capture fails (arecord EIO)"
},
"flasher": {
"name": "jlink",
diff --git a/tools/metrics.py b/tools/metrics.py
index d716cb2a5..0e29fc1ab 100644
--- a/tools/metrics.py
+++ b/tools/metrics.py
@@ -384,9 +384,9 @@ def render_combine_table(json_data, sort_order='name+'):
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 = [f"## {title}", "", "<details><summary>Size table</summary>", ""]
md_lines.extend(render_combine_table(json_data, sort_order))
- md_lines.append("")
+ md_lines.extend(["", "</details>", ""])
if json_data.get("file_list"):
md_lines.extend(["<details>", "<summary>Input files</summary>", ""])