summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-06-22 21:30:58 +0200
committerHiFiPhile <[email protected]>2026-06-22 21:30:58 +0200
commit693cdce08e14833f26f4e8a1f26e4fd546be4c35 (patch)
tree7667d2223dc32d9f21b5b60ab200e64ed46e3e20 /examples
parent41e9eaa65a935136085d78ec4b99c81ff991b560 (diff)
parentcd3561bf158afd5a5718904b8139a338d1e3b67c (diff)
Merge remote-tracking branch 'tinyusb/master' into pr-osal-spin-deinit
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'examples')
-rw-r--r--examples/CMakeLists.txt27
-rw-r--r--examples/build_system/cmake/cpu/cortex-m55.cmake2
-rw-r--r--examples/build_system/cmake/cpu/ft32.cmake13
-rw-r--r--examples/build_system/cmake/cpu/rv32imac-ilp32.cmake4
-rw-r--r--examples/build_system/cmake/cpu/rx610.cmake12
-rw-r--r--examples/build_system/cmake/cpu/rx64m.cmake12
-rw-r--r--examples/build_system/cmake/toolchain/arm_clang.cmake1
-rw-r--r--examples/build_system/cmake/toolchain/arm_iar.cmake15
-rw-r--r--examples/build_system/cmake/toolchain/common.cmake48
-rw-r--r--examples/build_system/cmake/toolchain/cstat_sel_checks.txt247
-rw-r--r--examples/build_system/cmake/toolchain/ft32_gcc.cmake22
-rw-r--r--examples/build_system/cmake/toolchain/rx_gcc.cmake26
-rw-r--r--examples/build_system/make/cpu/arm1176jzf-s.mk4
-rw-r--r--examples/build_system/make/cpu/arm926ej-s.mk4
-rw-r--r--examples/build_system/make/cpu/cortex-a53.mk7
-rw-r--r--examples/build_system/make/cpu/cortex-a72.mk7
-rw-r--r--examples/build_system/make/cpu/cortex-m0.mk5
-rw-r--r--examples/build_system/make/cpu/cortex-m0plus.mk5
-rw-r--r--examples/build_system/make/cpu/cortex-m23.mk5
-rw-r--r--examples/build_system/make/cpu/cortex-m3.mk5
-rw-r--r--examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk7
-rw-r--r--examples/build_system/make/cpu/cortex-m33.mk9
-rw-r--r--examples/build_system/make/cpu/cortex-m4-nofpu.mk4
-rw-r--r--examples/build_system/make/cpu/cortex-m4.mk4
-rw-r--r--examples/build_system/make/cpu/cortex-m55.mk9
-rw-r--r--examples/build_system/make/cpu/cortex-m7-fpsp.mk9
-rw-r--r--examples/build_system/make/cpu/cortex-m7.mk9
-rw-r--r--examples/build_system/make/cpu/cortex-m85.mk9
-rw-r--r--examples/build_system/make/cpu/msp430.mk2
-rw-r--r--examples/build_system/make/cpu/rv32i-ilp32.mk2
-rw-r--r--examples/build_system/make/cpu/rv32imac-ilp32.mk7
-rw-r--r--examples/build_system/make/make.mk190
-rw-r--r--examples/build_system/make/rules.mk195
-rw-r--r--examples/build_system/make/toolchain/arm_iar.mk13
-rw-r--r--examples/build_system/make/toolchain/gcc_common.mk3
-rw-r--r--examples/build_system/make/toolchain/gcc_rules.mk11
-rw-r--r--examples/build_system/make/toolchain/iar_rules.mk44
-rw-r--r--examples/build_system/make/toolchain/riscv_gcc.mk2
-rw-r--r--examples/device/CMakeLists.txt66
-rw-r--r--examples/device/audio_4_channel_mic/CMakeLists.txt17
-rw-r--r--examples/device/audio_4_channel_mic/Makefile8
-rw-r--r--examples/device/audio_4_channel_mic/skip.txt1
-rw-r--r--examples/device/audio_4_channel_mic/src/main.c57
-rwxr-xr-xexamples/device/audio_4_channel_mic/src/plot_audio_samples.py49
-rw-r--r--examples/device/audio_4_channel_mic/src/tusb_config.h12
-rw-r--r--examples/device/audio_4_channel_mic/src/usb_descriptors.c32
-rw-r--r--examples/device/audio_4_channel_mic_freertos/CMakeLists.txt20
-rw-r--r--examples/device/audio_4_channel_mic_freertos/Makefile8
-rw-r--r--examples/device/audio_4_channel_mic_freertos/skip.txt7
-rw-r--r--examples/device/audio_4_channel_mic_freertos/src/main.c44
-rwxr-xr-xexamples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py50
-rw-r--r--examples/device/audio_4_channel_mic_freertos/src/tusb_config.h12
-rw-r--r--examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c24
-rw-r--r--examples/device/audio_test/CMakeLists.txt15
-rw-r--r--examples/device/audio_test/Makefile8
-rw-r--r--examples/device/audio_test/skip.txt1
-rw-r--r--examples/device/audio_test/src/main.c56
-rwxr-xr-xexamples/device/audio_test/src/plot_audio_samples.py50
-rw-r--r--examples/device/audio_test/src/tusb_config.h11
-rw-r--r--examples/device/audio_test/src/usb_descriptors.c28
-rw-r--r--examples/device/audio_test_freertos/CMakeLists.txt18
-rw-r--r--examples/device/audio_test_freertos/Makefile8
-rw-r--r--examples/device/audio_test_freertos/sdkconfig.defaults3
-rw-r--r--examples/device/audio_test_freertos/skip.txt7
-rw-r--r--examples/device/audio_test_freertos/src/main.c46
-rwxr-xr-xexamples/device/audio_test_freertos/src/plot_audio_samples.py50
-rw-r--r--examples/device/audio_test_freertos/src/tusb_config.h11
-rw-r--r--examples/device/audio_test_freertos/src/usb_descriptors.c24
-rw-r--r--examples/device/audio_test_multi_rate/CMakeLists.txt15
-rw-r--r--examples/device/audio_test_multi_rate/Makefile8
-rw-r--r--examples/device/audio_test_multi_rate/skip.txt1
-rw-r--r--examples/device/audio_test_multi_rate/src/main.c377
-rwxr-xr-xexamples/device/audio_test_multi_rate/src/plot_audio_samples.py50
-rw-r--r--examples/device/audio_test_multi_rate/src/tusb_config.h29
-rw-r--r--examples/device/audio_test_multi_rate/src/usb_descriptors.c109
-rw-r--r--examples/device/audio_test_multi_rate/src/usb_descriptors.h78
-rw-r--r--examples/device/board_test/CMakeLists.txt12
-rw-r--r--examples/device/board_test/Makefile8
-rw-r--r--examples/device/board_test/src/main.c134
-rw-r--r--examples/device/board_test/src/tusb_config.h6
-rw-r--r--examples/device/cdc_dual_ports/CMakeLists.txt15
-rw-r--r--examples/device/cdc_dual_ports/Makefile8
-rw-r--r--examples/device/cdc_dual_ports/src/main.c12
-rw-r--r--examples/device/cdc_dual_ports/src/tusb_config.h12
-rw-r--r--examples/device/cdc_dual_ports/src/usb_descriptors.c45
-rw-r--r--examples/device/cdc_msc/CMakeLists.txt10
-rw-r--r--examples/device/cdc_msc/Makefile8
-rw-r--r--examples/device/cdc_msc/prj.conf1
-rw-r--r--examples/device/cdc_msc/src/main.c53
-rw-r--r--examples/device/cdc_msc/src/msc_disk.c124
-rw-r--r--examples/device/cdc_msc/src/tusb_config.h12
-rw-r--r--examples/device/cdc_msc/src/usb_descriptors.c40
-rw-r--r--examples/device/cdc_msc_freertos/CMakeLists.txt18
-rw-r--r--examples/device/cdc_msc_freertos/Makefile8
-rw-r--r--examples/device/cdc_msc_freertos/skip.txt7
-rw-r--r--examples/device/cdc_msc_freertos/src/main.c6
-rw-r--r--examples/device/cdc_msc_freertos/src/msc_disk.c52
-rw-r--r--examples/device/cdc_msc_freertos/src/tusb_config.h12
-rw-r--r--examples/device/cdc_msc_freertos/src/usb_descriptors.c40
-rw-r--r--examples/device/cdc_msc_throughput/CMakeLists.txt35
-rw-r--r--examples/device/cdc_msc_throughput/CMakePresets.json6
-rw-r--r--examples/device/cdc_msc_throughput/Makefile11
-rw-r--r--examples/device/cdc_msc_throughput/src/main.c151
-rw-r--r--examples/device/cdc_msc_throughput/src/tusb_config.h103
-rw-r--r--examples/device/cdc_msc_throughput/src/usb_descriptors.c186
-rw-r--r--examples/device/cdc_uac2/CMakeLists.txt17
-rw-r--r--examples/device/cdc_uac2/Makefile8
-rw-r--r--examples/device/cdc_uac2/skip.txt1
-rw-r--r--examples/device/cdc_uac2/src/cdc_app.c19
-rw-r--r--examples/device/cdc_uac2/src/common.h3
-rw-r--r--examples/device/cdc_uac2/src/main.c5
-rw-r--r--examples/device/cdc_uac2/src/tusb_config.h25
-rw-r--r--examples/device/cdc_uac2/src/uac2_app.c164
-rw-r--r--examples/device/cdc_uac2/src/usb_descriptors.c48
-rw-r--r--examples/device/cdc_uac2/src/usb_descriptors.h128
-rw-r--r--examples/device/dfu/CMakeLists.txt15
-rw-r--r--examples/device/dfu/Makefile8
-rw-r--r--examples/device/dfu/skip.txt1
-rw-r--r--examples/device/dfu/src/main.c99
-rw-r--r--examples/device/dfu/src/usb_descriptors.c193
-rw-r--r--examples/device/dfu_runtime/CMakeLists.txt15
-rw-r--r--examples/device/dfu_runtime/Makefile8
-rw-r--r--examples/device/dfu_runtime/skip.txt1
-rw-r--r--examples/device/dfu_runtime/src/main.c4
-rw-r--r--examples/device/dfu_runtime/src/usb_descriptors.c201
-rw-r--r--examples/device/dynamic_configuration/CMakeLists.txt15
-rw-r--r--examples/device/dynamic_configuration/Makefile8
-rw-r--r--examples/device/dynamic_configuration/src/main.c56
-rw-r--r--examples/device/dynamic_configuration/src/msc_disk.c63
-rw-r--r--examples/device/dynamic_configuration/src/tusb_config.h6
-rw-r--r--examples/device/dynamic_configuration/src/usb_descriptors.c18
-rw-r--r--examples/device/hid_boot_interface/CMakeLists.txt15
-rw-r--r--examples/device/hid_boot_interface/Makefile8
-rw-r--r--examples/device/hid_boot_interface/skip.txt1
-rw-r--r--examples/device/hid_boot_interface/src/main.c149
-rw-r--r--examples/device/hid_boot_interface/src/tusb_config.h6
-rw-r--r--examples/device/hid_boot_interface/src/usb_descriptors.c118
-rw-r--r--examples/device/hid_composite/CMakeLists.txt15
-rw-r--r--examples/device/hid_composite/Makefile8
-rw-r--r--examples/device/hid_composite/skip.txt1
-rw-r--r--examples/device/hid_composite/src/main.c242
-rw-r--r--examples/device/hid_composite/src/tusb_config.h6
-rw-r--r--examples/device/hid_composite/src/usb_descriptors.c14
-rw-r--r--examples/device/hid_composite_freertos/CMakeLists.txt18
-rw-r--r--examples/device/hid_composite_freertos/Makefile8
-rw-r--r--examples/device/hid_composite_freertos/skip.txt6
-rw-r--r--examples/device/hid_composite_freertos/src/main.c2
-rw-r--r--examples/device/hid_composite_freertos/src/tusb_config.h6
-rw-r--r--examples/device/hid_composite_freertos/src/usb_descriptors.c14
-rw-r--r--examples/device/hid_generic_inout/CMakeLists.txt15
-rw-r--r--examples/device/hid_generic_inout/Makefile8
-rw-r--r--examples/device/hid_generic_inout/skip.txt1
-rw-r--r--examples/device/hid_generic_inout/src/main.c2
-rw-r--r--examples/device/hid_generic_inout/src/tusb_config.h6
-rw-r--r--examples/device/hid_generic_inout/src/usb_descriptors.c22
-rw-r--r--examples/device/hid_multiple_interface/CMakeLists.txt15
-rw-r--r--examples/device/hid_multiple_interface/Makefile8
-rw-r--r--examples/device/hid_multiple_interface/skip.txt1
-rw-r--r--examples/device/hid_multiple_interface/src/main.c4
-rw-r--r--examples/device/hid_multiple_interface/src/tusb_config.h6
-rw-r--r--examples/device/hid_multiple_interface/src/usb_descriptors.c10
-rw-r--r--examples/device/midi2_device/CMakeLists.txt35
-rw-r--r--examples/device/midi2_device/CMakePresets.json6
-rw-r--r--examples/device/midi2_device/Makefile16
-rw-r--r--examples/device/midi2_device/README.md59
-rw-r--r--examples/device/midi2_device/skip.txt1
-rw-r--r--examples/device/midi2_device/src/main.c718
-rw-r--r--examples/device/midi2_device/src/tusb_config.h88
-rw-r--r--examples/device/midi2_device/src/usb_descriptors.c146
-rw-r--r--examples/device/midi_test/CMakeLists.txt15
-rw-r--r--examples/device/midi_test/Makefile8
-rw-r--r--examples/device/midi_test/skip.txt1
-rw-r--r--examples/device/midi_test/src/main.c4
-rw-r--r--examples/device/midi_test/src/tusb_config.h6
-rw-r--r--examples/device/midi_test/src/usb_descriptors.c16
-rw-r--r--examples/device/midi_test_freertos/CMakeLists.txt15
-rw-r--r--examples/device/midi_test_freertos/Makefile8
-rw-r--r--examples/device/midi_test_freertos/skip.txt6
-rw-r--r--examples/device/midi_test_freertos/src/main.c2
-rw-r--r--examples/device/midi_test_freertos/src/tusb_config.h6
-rw-r--r--examples/device/midi_test_freertos/src/usb_descriptors.c16
-rw-r--r--examples/device/msc_dual_lun/CMakeLists.txt9
-rw-r--r--examples/device/msc_dual_lun/Makefile8
-rw-r--r--examples/device/msc_dual_lun/prj.conf1
-rw-r--r--examples/device/msc_dual_lun/src/main.c158
-rw-r--r--examples/device/msc_dual_lun/src/msc_disk_dual.c10
-rw-r--r--examples/device/msc_dual_lun/src/tusb_config.h6
-rw-r--r--examples/device/msc_dual_lun/src/usb_descriptors.c26
-rw-r--r--examples/device/mtp/CMakeLists.txt9
-rw-r--r--examples/device/mtp/Makefile8
-rw-r--r--examples/device/mtp/src/main.c2
-rw-r--r--examples/device/mtp/src/mtp_fs_example.c162
-rw-r--r--examples/device/mtp/src/tinyusb_logo_png.h4
-rw-r--r--examples/device/mtp/src/tusb_config.h7
-rw-r--r--examples/device/mtp/src/usb_descriptors.c18
-rw-r--r--examples/device/net_lwip_webserver/CMakeLists.txt22
-rw-r--r--examples/device/net_lwip_webserver/Makefile9
-rw-r--r--examples/device/net_lwip_webserver/skip.txt4
-rw-r--r--examples/device/net_lwip_webserver/src/arch/cc.h6
-rw-r--r--examples/device/net_lwip_webserver/src/lwipopts.h24
-rw-r--r--examples/device/net_lwip_webserver/src/main.c56
-rw-r--r--examples/device/net_lwip_webserver/src/tusb_config.h48
-rw-r--r--examples/device/net_lwip_webserver/src/usb_descriptors.c402
-rw-r--r--examples/device/printer_to_cdc/CMakeLists.txt29
-rw-r--r--examples/device/printer_to_cdc/CMakePresets.json6
-rw-r--r--examples/device/printer_to_cdc/Makefile11
-rw-r--r--examples/device/printer_to_cdc/README.md80
-rw-r--r--examples/device/printer_to_cdc/src/main.c117
-rw-r--r--examples/device/printer_to_cdc/src/tusb_config.h120
-rw-r--r--examples/device/printer_to_cdc/src/usb_descriptors.c217
-rw-r--r--examples/device/printer_to_cdc/src/usb_descriptors.h37
-rw-r--r--examples/device/uac2_headset/CMakeLists.txt15
-rw-r--r--examples/device/uac2_headset/Makefile8
-rw-r--r--examples/device/uac2_headset/skip.txt1
-rw-r--r--examples/device/uac2_headset/src/main.c404
-rw-r--r--examples/device/uac2_headset/src/tusb_config.h51
-rw-r--r--examples/device/uac2_headset/src/usb_descriptors.c118
-rw-r--r--examples/device/uac2_headset/src/usb_descriptors.h233
-rw-r--r--examples/device/uac2_speaker_fb/CMakeLists.txt16
-rw-r--r--examples/device/uac2_speaker_fb/Makefile8
-rw-r--r--examples/device/uac2_speaker_fb/skip.txt1
-rwxr-xr-xexamples/device/uac2_speaker_fb/src/audio_debug.py13
-rw-r--r--examples/device/uac2_speaker_fb/src/common_types.h2
-rw-r--r--examples/device/uac2_speaker_fb/src/main.c460
-rw-r--r--examples/device/uac2_speaker_fb/src/quirk_os_guessing.c90
-rw-r--r--examples/device/uac2_speaker_fb/src/quirk_os_guessing.h75
-rw-r--r--examples/device/uac2_speaker_fb/src/tusb_config.h53
-rw-r--r--examples/device/uac2_speaker_fb/src/usb_descriptors.c191
-rw-r--r--examples/device/uac2_speaker_fb/src/usb_descriptors.h152
-rw-r--r--examples/device/usbtmc/CMakeLists.txt15
-rw-r--r--examples/device/usbtmc/Makefile8
-rw-r--r--examples/device/usbtmc/src/main.c7
-rw-r--r--examples/device/usbtmc/src/usb_descriptors.c49
-rw-r--r--examples/device/usbtmc/src/usbtmc_app.c9
-rw-r--r--examples/device/video_capture/CMakeLists.txt17
-rw-r--r--examples/device/video_capture/Makefile8
-rw-r--r--examples/device/video_capture/skip.txt2
-rw-r--r--examples/device/video_capture/src/main.c89
-rw-r--r--examples/device/video_capture/src/tusb_config.h7
-rw-r--r--examples/device/video_capture/src/usb_descriptors.c14
-rw-r--r--examples/device/video_capture_2ch/CMakeLists.txt17
-rw-r--r--examples/device/video_capture_2ch/Makefile8
-rw-r--r--examples/device/video_capture_2ch/skip.txt2
-rw-r--r--examples/device/video_capture_2ch/src/main.c17
-rw-r--r--examples/device/video_capture_2ch/src/tusb_config.h6
-rw-r--r--examples/device/video_capture_2ch/src/usb_descriptors.c14
-rw-r--r--examples/device/webusb_serial/CMakeLists.txt15
-rw-r--r--examples/device/webusb_serial/Makefile8
-rw-r--r--examples/device/webusb_serial/skip.txt1
-rw-r--r--examples/device/webusb_serial/src/main.c60
-rw-r--r--examples/device/webusb_serial/src/tusb_config.h6
-rw-r--r--examples/device/webusb_serial/src/usb_descriptors.c32
-rw-r--r--examples/dual/CMakeLists.txt11
-rw-r--r--examples/dual/dynamic_switch/CMakeLists.txt30
-rw-r--r--examples/dual/dynamic_switch/CMakePresets.json6
-rw-r--r--examples/dual/dynamic_switch/Makefile16
-rw-r--r--examples/dual/dynamic_switch/README.md60
-rw-r--r--examples/dual/dynamic_switch/only.txt13
-rw-r--r--examples/dual/dynamic_switch/src/CMakeLists.txt4
-rw-r--r--examples/dual/dynamic_switch/src/main.c501
-rw-r--r--examples/dual/dynamic_switch/src/tusb_config.h160
-rw-r--r--examples/dual/dynamic_switch/src/usb_descriptors.c199
-rw-r--r--examples/dual/host_hid_to_device_cdc/CMakeLists.txt17
-rw-r--r--examples/dual/host_hid_to_device_cdc/Makefile10
-rw-r--r--examples/dual/host_hid_to_device_cdc/src/main.c4
-rw-r--r--examples/dual/host_hid_to_device_cdc/src/tusb_config.h6
-rw-r--r--examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c16
-rw-r--r--examples/dual/host_info_to_device_cdc/CMakeLists.txt22
-rw-r--r--examples/dual/host_info_to_device_cdc/Makefile10
-rw-r--r--examples/dual/host_info_to_device_cdc/only.txt1
-rw-r--r--examples/dual/host_info_to_device_cdc/src/CMakeLists.txt4
-rw-r--r--examples/dual/host_info_to_device_cdc/src/main.c161
-rw-r--r--examples/dual/host_info_to_device_cdc/src/tusb_config.h8
-rw-r--r--examples/dual/host_info_to_device_cdc/src/usb_descriptors.c16
-rw-r--r--examples/host/CMakeLists.txt22
-rw-r--r--examples/host/bare_api/CMakeLists.txt15
-rw-r--r--examples/host/bare_api/Makefile8
-rw-r--r--examples/host/bare_api/only.txt22
-rw-r--r--examples/host/bare_api/skip.txt1
-rw-r--r--examples/host/bare_api/src/main.c69
-rw-r--r--examples/host/cdc_msc_hid/CMakeLists.txt15
-rw-r--r--examples/host/cdc_msc_hid/Makefile8
-rw-r--r--examples/host/cdc_msc_hid/only.txt22
-rw-r--r--examples/host/cdc_msc_hid/skip.txt1
-rw-r--r--examples/host/cdc_msc_hid/src/app.h34
-rw-r--r--examples/host/cdc_msc_hid/src/cdc_app.c79
-rw-r--r--examples/host/cdc_msc_hid/src/hid_app.c2
-rw-r--r--examples/host/cdc_msc_hid/src/main.c5
-rw-r--r--examples/host/cdc_msc_hid/src/msc_app.c7
-rw-r--r--examples/host/cdc_msc_hid/src/tusb_config.h11
-rw-r--r--examples/host/cdc_msc_hid_freertos/CMakeLists.txt15
-rw-r--r--examples/host/cdc_msc_hid_freertos/Makefile8
-rw-r--r--examples/host/cdc_msc_hid_freertos/only.txt19
-rw-r--r--examples/host/cdc_msc_hid_freertos/skip.txt4
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/app.h35
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/cdc_app.c19
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/hid_app.c4
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/main.c6
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/msc_app.c9
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/tusb_config.h6
-rw-r--r--examples/host/device_info/CMakeLists.txt15
-rw-r--r--examples/host/device_info/Makefile8
-rw-r--r--examples/host/device_info/only.txt22
-rw-r--r--examples/host/device_info/skip.txt1
-rw-r--r--examples/host/device_info/src/main.c83
-rw-r--r--examples/host/hid_controller/CMakeLists.txt15
-rw-r--r--examples/host/hid_controller/Makefile8
-rw-r--r--examples/host/hid_controller/only.txt20
-rw-r--r--examples/host/hid_controller/skip.txt1
-rw-r--r--examples/host/hid_controller/src/app.h33
-rw-r--r--examples/host/hid_controller/src/hid_app.c3
-rw-r--r--examples/host/hid_controller/src/main.c22
-rw-r--r--examples/host/hid_controller/src/tusb_config.h6
-rw-r--r--examples/host/midi2_host/CMakeLists.txt29
-rw-r--r--examples/host/midi2_host/CMakePresets.json6
-rw-r--r--examples/host/midi2_host/Makefile13
-rw-r--r--examples/host/midi2_host/only.txt34
-rw-r--r--examples/host/midi2_host/skip.txt1
-rw-r--r--examples/host/midi2_host/src/main.c163
-rw-r--r--examples/host/midi2_host/src/tusb_config.h106
-rw-r--r--examples/host/midi_rx/CMakeLists.txt15
-rw-r--r--examples/host/midi_rx/Makefile8
-rw-r--r--examples/host/midi_rx/only.txt20
-rw-r--r--examples/host/midi_rx/skip.txt1
-rw-r--r--examples/host/midi_rx/src/main.c4
-rw-r--r--examples/host/msc_file_explorer/CMakeLists.txt31
-rw-r--r--examples/host/msc_file_explorer/Makefile9
-rw-r--r--examples/host/msc_file_explorer/README.md105
-rw-r--r--examples/host/msc_file_explorer/only.txt22
-rw-r--r--examples/host/msc_file_explorer/skip.txt1
-rw-r--r--examples/host/msc_file_explorer/src/ffconf.h313
-rw-r--r--examples/host/msc_file_explorer/src/main.c38
-rw-r--r--examples/host/msc_file_explorer/src/msc_app.c558
-rw-r--r--examples/host/msc_file_explorer/src/msc_app.h1
-rw-r--r--examples/host/msc_file_explorer/src/tusb_config.h6
-rw-r--r--examples/host/msc_file_explorer_freertos/CMakeLists.txt42
-rw-r--r--examples/host/msc_file_explorer_freertos/CMakePresets.json6
-rw-r--r--examples/host/msc_file_explorer_freertos/Makefile27
-rw-r--r--examples/host/msc_file_explorer_freertos/README.md105
-rw-r--r--examples/host/msc_file_explorer_freertos/only.txt28
-rw-r--r--examples/host/msc_file_explorer_freertos/skip.txt4
-rw-r--r--examples/host/msc_file_explorer_freertos/src/CMakeLists.txt13
-rw-r--r--examples/host/msc_file_explorer_freertos/src/ffconf.h313
-rw-r--r--examples/host/msc_file_explorer_freertos/src/main.c158
-rw-r--r--examples/host/msc_file_explorer_freertos/src/msc_app.c709
-rw-r--r--examples/host/msc_file_explorer_freertos/src/msc_app.h34
-rw-r--r--examples/host/msc_file_explorer_freertos/src/tusb_config.h126
-rw-r--r--examples/typec/power_delivery/CMakeLists.txt15
-rw-r--r--examples/typec/power_delivery/Makefile12
-rw-r--r--examples/typec/power_delivery/src/main.c2
-rw-r--r--examples/typec/power_delivery/src/tusb_config.h6
351 files changed, 11405 insertions, 4136 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index d34c6ed5d..7669290a8 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,11 +1,28 @@
cmake_minimum_required(VERSION 3.20)
-#set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
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
+ )
diff --git a/examples/build_system/cmake/cpu/cortex-m55.cmake b/examples/build_system/cmake/cpu/cortex-m55.cmake
index a7a57957c..d5f6fa74a 100644
--- a/examples/build_system/cmake/cpu/cortex-m55.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m55.cmake
@@ -13,6 +13,7 @@ elseif (TOOLCHAIN STREQUAL "clang")
--target=arm-none-eabi
-mcpu=cortex-m55
-mfpu=fpv5-d16
+ -mcmse
)
set(FREERTOS_PORT GCC_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
@@ -20,6 +21,7 @@ elseif (TOOLCHAIN STREQUAL "iar")
set(TOOLCHAIN_COMMON_FLAGS
--cpu cortex-m55
--fpu VFPv5_D16
+ --cmse
)
set(FREERTOS_PORT IAR_ARM_CM55_NTZ_NONSECURE CACHE INTERNAL "")
diff --git a/examples/build_system/cmake/cpu/ft32.cmake b/examples/build_system/cmake/cpu/ft32.cmake
new file mode 100644
index 000000000..13153e555
--- /dev/null
+++ b/examples/build_system/cmake/cpu/ft32.cmake
@@ -0,0 +1,13 @@
+if (TOOLCHAIN STREQUAL "gcc")
+ set(TOOLCHAIN_COMMON_FLAGS
+ -fvar-tracking
+ -fvar-tracking-assignments
+ )
+
+elseif (TOOLCHAIN STREQUAL "clang")
+ message(FATAL_ERROR "Clang is not supported for this target")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+ message(FATAL_ERROR "IAR is not supported for this target")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake b/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake
index 584d90519..8c2538cee 100644
--- a/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake
+++ b/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake
@@ -1,13 +1,13 @@
if (TOOLCHAIN STREQUAL "gcc")
set(TOOLCHAIN_COMMON_FLAGS
- -march=rv32imac_zicsr
+ -march=rv32imac_zicsr_zifencei
-mabi=ilp32
)
set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
elseif (TOOLCHAIN STREQUAL "clang")
set(TOOLCHAIN_COMMON_FLAGS
- -march=rv32imac_zicsr
+ -march=rv32imac_zicsr_zifencei
-mabi=ilp32
)
set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
diff --git a/examples/build_system/cmake/cpu/rx610.cmake b/examples/build_system/cmake/cpu/rx610.cmake
new file mode 100644
index 000000000..6f535275d
--- /dev/null
+++ b/examples/build_system/cmake/cpu/rx610.cmake
@@ -0,0 +1,12 @@
+if (NOT DEFINED TOOLCHAIN OR TOOLCHAIN STREQUAL "gcc")
+ set(TOOLCHAIN_COMMON_FLAGS
+ -mcpu=rx610
+ -misa=v1
+ -mlittle-endian-data
+ -fshort-enums
+ )
+ set(FREERTOS_PORT GCC_RX600 CACHE INTERNAL "")
+
+else ()
+ message(FATAL_ERROR "Toolchain ${TOOLCHAIN} is not supported for RX")
+endif ()
diff --git a/examples/build_system/cmake/cpu/rx64m.cmake b/examples/build_system/cmake/cpu/rx64m.cmake
new file mode 100644
index 000000000..2b106ea00
--- /dev/null
+++ b/examples/build_system/cmake/cpu/rx64m.cmake
@@ -0,0 +1,12 @@
+if (NOT DEFINED TOOLCHAIN OR TOOLCHAIN STREQUAL "gcc")
+ set(TOOLCHAIN_COMMON_FLAGS
+ -mcpu=rx64m
+ -misa=v2
+ -mlittle-endian-data
+ -fshort-enums
+ )
+ set(FREERTOS_PORT GCC_RX600 CACHE INTERNAL "")
+
+else ()
+ message(FATAL_ERROR "Toolchain ${TOOLCHAIN} is not supported for RX")
+endif ()
diff --git a/examples/build_system/cmake/toolchain/arm_clang.cmake b/examples/build_system/cmake/toolchain/arm_clang.cmake
index fe3c2b453..e5ca82fab 100644
--- a/examples/build_system/cmake/toolchain/arm_clang.cmake
+++ b/examples/build_system/cmake/toolchain/arm_clang.cmake
@@ -7,6 +7,7 @@ if (NOT DEFINED CMAKE_CXX_COMPILER)
endif ()
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
find_program(CMAKE_SIZE llvm-size)
find_program(CMAKE_OBJCOPY llvm-objcopy)
find_program(CMAKE_OBJDUMP llvm-objdump)
diff --git a/examples/build_system/cmake/toolchain/arm_iar.cmake b/examples/build_system/cmake/toolchain/arm_iar.cmake
index 083815715..67d100bbc 100644
--- a/examples/build_system/cmake/toolchain/arm_iar.cmake
+++ b/examples/build_system/cmake/toolchain/arm_iar.cmake
@@ -14,4 +14,19 @@ find_program(CMAKE_SIZE size)
find_program(CMAKE_OBJCOPY ielftool)
find_program(CMAKE_OBJDUMP iefdumparm)
+find_program(CMAKE_IAR_CSTAT icstat)
+find_program(CMAKE_IAR_CHECKS ichecks)
+find_program(CMAKE_IAR_REPORT ireport)
+
+if (IAR_CSTAT)
+cmake_minimum_required(VERSION 4.1)
+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
+ )
+endif ()
+
include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
diff --git a/examples/build_system/cmake/toolchain/common.cmake b/examples/build_system/cmake/toolchain/common.cmake
index 4c181137b..e610a349b 100644
--- a/examples/build_system/cmake/toolchain/common.cmake
+++ b/examples/build_system/cmake/toolchain/common.cmake
@@ -20,46 +20,32 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_CPU}.cmake)
# ----------------------------------------------------------------------------
# Compile flags
# ----------------------------------------------------------------------------
-if (TOOLCHAIN STREQUAL "gcc")
+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
+# -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_COMMON_FLAGS)
- list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
- --diag_suppress=Li065
- )
-
-elseif (TOOLCHAIN STREQUAL "clang")
- list(APPEND TOOLCHAIN_COMMON_FLAGS
- -fdata-sections
- -ffunction-sections
- -fno-strict-aliasing
- )
- list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
- -Wl,--print-memory-usage
- -Wl,--gc-sections
- -Wl,--cref
- )
+ 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 ()
-# 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})
diff --git a/examples/build_system/cmake/toolchain/cstat_sel_checks.txt b/examples/build_system/cmake/toolchain/cstat_sel_checks.txt
new file mode 100644
index 000000000..b7efba4ad
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/cstat_sel_checks.txt
@@ -0,0 +1,247 @@
+# IAR C-STAT Checks Manifest Handler V2.7.5.562
+#
+MISRAC2012-Dir-4.3
+MISRAC2012-Dir-4.7_c
+MISRAC2012-Dir-4.10
+MISRAC2012-Dir-4.11_a
+MISRAC2012-Dir-4.11_b
+MISRAC2012-Dir-4.11_c
+MISRAC2012-Dir-4.11_d
+MISRAC2012-Dir-4.11_e
+MISRAC2012-Dir-4.11_f
+MISRAC2012-Dir-4.11_g
+MISRAC2012-Dir-4.11_h
+MISRAC2012-Dir-4.11_i
+MISRAC2012-Dir-4.12
+MISRAC2012-Dir-4.14_a
+MISRAC2012-Dir-4.14_b
+MISRAC2012-Dir-4.14_c
+MISRAC2012-Dir-4.14_d
+MISRAC2012-Dir-4.14_e
+MISRAC2012-Dir-4.14_f
+MISRAC2012-Dir-4.14_g
+MISRAC2012-Dir-4.14_h
+MISRAC2012-Dir-4.14_i
+MISRAC2012-Dir-4.14_j
+MISRAC2012-Dir-4.14_l
+MISRAC2012-Dir-4.14_m
+MISRAC2012-Dir-4.15
+MISRAC2012-Rule-1.3_a
+MISRAC2012-Rule-1.3_b
+MISRAC2012-Rule-1.3_c
+MISRAC2012-Rule-1.3_d
+MISRAC2012-Rule-1.3_e
+MISRAC2012-Rule-1.3_f
+MISRAC2012-Rule-1.3_g
+MISRAC2012-Rule-1.3_h
+MISRAC2012-Rule-1.3_i
+MISRAC2012-Rule-1.3_j
+MISRAC2012-Rule-1.3_k
+MISRAC2012-Rule-1.3_l
+MISRAC2012-Rule-1.3_m
+MISRAC2012-Rule-1.3_n
+MISRAC2012-Rule-1.3_o
+MISRAC2012-Rule-1.3_p
+MISRAC2012-Rule-1.3_q
+MISRAC2012-Rule-1.3_r
+MISRAC2012-Rule-1.3_s
+MISRAC2012-Rule-1.3_t
+MISRAC2012-Rule-1.3_u
+MISRAC2012-Rule-1.3_v
+MISRAC2012-Rule-1.4
+MISRAC2012-Rule-1.5_b
+MISRAC2012-Rule-1.5_c
+MISRAC2012-Rule-1.5_d
+MISRAC2012-Rule-1.5_e
+MISRAC2012-Rule-1.5_f
+MISRAC2012-Rule-1.5_g
+MISRAC2012-Rule-2.1_a
+MISRAC2012-Rule-2.1_b
+MISRAC2012-Rule-2.2_a
+MISRAC2012-Rule-2.2_b
+MISRAC2012-Rule-2.2_c
+MISRAC2012-Rule-3.1
+MISRAC2012-Rule-3.2
+MISRAC2012-Rule-5.1
+MISRAC2012-Rule-5.2_c89
+MISRAC2012-Rule-5.2_c99
+MISRAC2012-Rule-5.3_c89
+MISRAC2012-Rule-5.3_c99
+MISRAC2012-Rule-5.4_c89
+MISRAC2012-Rule-5.4_c99
+MISRAC2012-Rule-5.5_c89
+MISRAC2012-Rule-5.5_c99
+MISRAC2012-Rule-5.6
+MISRAC2012-Rule-5.7
+MISRAC2012-Rule-5.8
+MISRAC2012-Rule-6.1
+MISRAC2012-Rule-6.2
+MISRAC2012-Rule-6.3
+MISRAC2012-Rule-7.1
+MISRAC2012-Rule-7.2
+MISRAC2012-Rule-7.3
+MISRAC2012-Rule-7.4_a
+MISRAC2012-Rule-7.4_b
+MISRAC2012-Rule-7.5
+MISRAC2012-Rule-7.6
+MISRAC2012-Rule-8.1
+MISRAC2012-Rule-8.2_a
+MISRAC2012-Rule-8.2_b
+MISRAC2012-Rule-8.3
+MISRAC2012-Rule-8.4
+MISRAC2012-Rule-8.5_a
+MISRAC2012-Rule-8.5_b
+MISRAC2012-Rule-8.10
+MISRAC2012-Rule-8.12
+MISRAC2012-Rule-8.14
+MISRAC2012-Rule-8.15
+MISRAC2012-Rule-9.1_a
+MISRAC2012-Rule-9.1_b
+MISRAC2012-Rule-9.1_d
+MISRAC2012-Rule-9.1_e
+MISRAC2012-Rule-9.2
+MISRAC2012-Rule-9.3
+MISRAC2012-Rule-9.4
+MISRAC2012-Rule-9.5_a
+MISRAC2012-Rule-9.5_b
+MISRAC2012-Rule-9.6
+MISRAC2012-Rule-9.7
+MISRAC2012-Rule-10.1_R2
+MISRAC2012-Rule-10.1_R3
+MISRAC2012-Rule-10.1_R4
+MISRAC2012-Rule-10.1_R5
+MISRAC2012-Rule-10.1_R6
+MISRAC2012-Rule-10.1_R7
+MISRAC2012-Rule-10.1_R8
+MISRAC2012-Rule-10.1_R10
+MISRAC2012-Rule-10.2
+MISRAC2012-Rule-10.3
+MISRAC2012-Rule-10.4_a
+MISRAC2012-Rule-10.4_b
+MISRAC2012-Rule-10.6
+MISRAC2012-Rule-10.7
+MISRAC2012-Rule-10.8
+MISRAC2012-Rule-11.1
+MISRAC2012-Rule-11.2
+MISRAC2012-Rule-11.3
+MISRAC2012-Rule-11.6
+MISRAC2012-Rule-11.7
+MISRAC2012-Rule-11.8
+MISRAC2012-Rule-11.9
+MISRAC2012-Rule-11.10
+MISRAC2012-Rule-12.2
+MISRAC2012-Rule-12.5
+MISRAC2012-Rule-12.6
+MISRAC2012-Rule-13.1
+MISRAC2012-Rule-13.2_a
+MISRAC2012-Rule-13.2_b
+MISRAC2012-Rule-13.2_c
+MISRAC2012-Rule-13.5
+MISRAC2012-Rule-13.6
+MISRAC2012-Rule-14.1_a
+MISRAC2012-Rule-14.1_b
+MISRAC2012-Rule-14.2
+MISRAC2012-Rule-14.3_a
+MISRAC2012-Rule-14.3_b
+MISRAC2012-Rule-14.4_a
+MISRAC2012-Rule-14.4_b
+MISRAC2012-Rule-14.4_c
+MISRAC2012-Rule-14.4_d
+MISRAC2012-Rule-15.2
+MISRAC2012-Rule-15.3
+MISRAC2012-Rule-15.6_a
+MISRAC2012-Rule-15.6_b
+MISRAC2012-Rule-15.6_c
+MISRAC2012-Rule-15.6_d
+MISRAC2012-Rule-15.6_e
+MISRAC2012-Rule-15.7
+MISRAC2012-Rule-16.1
+MISRAC2012-Rule-16.2
+MISRAC2012-Rule-16.3
+MISRAC2012-Rule-16.4
+MISRAC2012-Rule-16.5
+MISRAC2012-Rule-16.6
+MISRAC2012-Rule-16.7
+MISRAC2012-Rule-17.1
+MISRAC2012-Rule-17.2_a
+MISRAC2012-Rule-17.2_b
+MISRAC2012-Rule-17.3
+MISRAC2012-Rule-17.4
+MISRAC2012-Rule-17.5
+MISRAC2012-Rule-17.6
+MISRAC2012-Rule-17.7
+MISRAC2012-Rule-17.13
+MISRAC2012-Rule-18.1_a
+MISRAC2012-Rule-18.1_b
+MISRAC2012-Rule-18.1_c
+MISRAC2012-Rule-18.1_d
+MISRAC2012-Rule-18.2
+MISRAC2012-Rule-18.3
+MISRAC2012-Rule-18.4
+MISRAC2012-Rule-18.6_a
+MISRAC2012-Rule-18.6_b
+MISRAC2012-Rule-18.6_c
+MISRAC2012-Rule-18.6_d
+MISRAC2012-Rule-18.7
+MISRAC2012-Rule-18.8
+MISRAC2012-Rule-18.9
+MISRAC2012-Rule-18.10
+MISRAC2012-Rule-19.1
+MISRAC2012-Rule-20.2
+MISRAC2012-Rule-20.4_c89
+MISRAC2012-Rule-20.4_c99
+MISRAC2012-Rule-20.6_a
+MISRAC2012-Rule-20.6_b
+MISRAC2012-Rule-20.7
+MISRAC2012-Rule-21.1
+MISRAC2012-Rule-21.2
+MISRAC2012-Rule-21.3
+MISRAC2012-Rule-21.4
+MISRAC2012-Rule-21.5
+MISRAC2012-Rule-21.6
+MISRAC2012-Rule-21.7
+MISRAC2012-Rule-21.8
+MISRAC2012-Rule-21.9
+MISRAC2012-Rule-21.10
+MISRAC2012-Rule-21.12_a
+MISRAC2012-Rule-21.12_b
+MISRAC2012-Rule-21.12_c
+MISRAC2012-Rule-21.13
+MISRAC2012-Rule-21.14
+MISRAC2012-Rule-21.15
+MISRAC2012-Rule-21.16
+MISRAC2012-Rule-21.17_a
+MISRAC2012-Rule-21.17_b
+MISRAC2012-Rule-21.17_c
+MISRAC2012-Rule-21.17_d
+MISRAC2012-Rule-21.17_e
+MISRAC2012-Rule-21.17_f
+MISRAC2012-Rule-21.18_a
+MISRAC2012-Rule-21.18_b
+MISRAC2012-Rule-21.19_a
+MISRAC2012-Rule-21.19_b
+MISRAC2012-Rule-21.20
+MISRAC2012-Rule-21.21
+MISRAC2012-Rule-21.22
+MISRAC2012-Rule-21.23
+MISRAC2012-Rule-21.24
+MISRAC2012-Rule-21.25
+MISRAC2012-Rule-22.1_a
+MISRAC2012-Rule-22.1_b
+MISRAC2012-Rule-22.2_a
+MISRAC2012-Rule-22.2_b
+MISRAC2012-Rule-22.2_c
+MISRAC2012-Rule-22.3
+MISRAC2012-Rule-22.4
+MISRAC2012-Rule-22.5_a
+MISRAC2012-Rule-22.5_b
+MISRAC2012-Rule-22.6
+MISRAC2012-Rule-22.7_a
+MISRAC2012-Rule-22.7_b
+MISRAC2012-Rule-22.8
+MISRAC2012-Rule-22.9
+MISRAC2012-Rule-22.10
+MISRAC2012-Rule-23.2
+MISRAC2012-Rule-23.4
+MISRAC2012-Rule-23.6
+MISRAC2012-Rule-23.8
diff --git a/examples/build_system/cmake/toolchain/ft32_gcc.cmake b/examples/build_system/cmake/toolchain/ft32_gcc.cmake
new file mode 100644
index 000000000..edf8048ab
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/ft32_gcc.cmake
@@ -0,0 +1,22 @@
+if (NOT DEFINED CMAKE_C_COMPILER)
+ set(CMAKE_C_COMPILER "ft32-elf-gcc")
+endif ()
+
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+ set(CMAKE_CXX_COMPILER "ft32-elf-g++")
+endif ()
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
+find_program(CMAKE_SIZE ft32-elf-size)
+find_program(CMAKE_OBJCOPY ft32-elf-objcopy)
+find_program(CMAKE_OBJDUMP ft32-elf-objdump)
+
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
+
+get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
+if (IS_IN_TRY_COMPILE)
+ set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
+ set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
+ cmake_print_variables(CMAKE_C_LINK_FLAGS)
+endif ()
diff --git a/examples/build_system/cmake/toolchain/rx_gcc.cmake b/examples/build_system/cmake/toolchain/rx_gcc.cmake
new file mode 100644
index 000000000..3f8e3662e
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/rx_gcc.cmake
@@ -0,0 +1,26 @@
+# Cross Compiler for RX
+if (NOT DEFINED CROSS_COMPILE)
+ set(CROSS_COMPILE "rx-elf-")
+endif ()
+
+if (NOT DEFINED CMAKE_C_COMPILER)
+ set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
+endif ()
+
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+ set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++)
+endif ()
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+find_program(CMAKE_SIZE ${CROSS_COMPILE}size)
+find_program(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy)
+find_program(CMAKE_OBJDUMP ${CROSS_COMPILE}objdump)
+
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
+
+get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
+if (IS_IN_TRY_COMPILE)
+ set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
+ set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
+ cmake_print_variables(CMAKE_C_LINK_FLAGS)
+endif ()
diff --git a/examples/build_system/make/cpu/arm1176jzf-s.mk b/examples/build_system/make/cpu/arm1176jzf-s.mk
index 022ccf7ad..c0cef8784 100644
--- a/examples/build_system/make/cpu/arm1176jzf-s.mk
+++ b/examples/build_system/make/cpu/arm1176jzf-s.mk
@@ -2,8 +2,4 @@ ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=arm1176jzf-s \
-else ifeq ($(TOOLCHAIN),iar)
- #CFLAGS += --cpu cortex-a53
- #ASFLAGS += --cpu cortex-a53
-
endif
diff --git a/examples/build_system/make/cpu/arm926ej-s.mk b/examples/build_system/make/cpu/arm926ej-s.mk
index 5b84f514f..e0558eca7 100644
--- a/examples/build_system/make/cpu/arm926ej-s.mk
+++ b/examples/build_system/make/cpu/arm926ej-s.mk
@@ -2,8 +2,4 @@ ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=arm926ej-s \
-else ifeq ($(TOOLCHAIN),iar)
- #CFLAGS += --cpu cortex-a53
- #ASFLAGS += --cpu cortex-a53
-
endif
diff --git a/examples/build_system/make/cpu/cortex-a53.mk b/examples/build_system/make/cpu/cortex-a53.mk
index 42e522ecf..20ed8f0cc 100644
--- a/examples/build_system/make/cpu/cortex-a53.mk
+++ b/examples/build_system/make/cpu/cortex-a53.mk
@@ -2,11 +2,4 @@ ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=cortex-a53 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-a53 \
-
- ASFLAGS += \
- --cpu cortex-a53 \
-
endif
diff --git a/examples/build_system/make/cpu/cortex-a72.mk b/examples/build_system/make/cpu/cortex-a72.mk
index 1b3d8da4a..b2b3d9181 100644
--- a/examples/build_system/make/cpu/cortex-a72.mk
+++ b/examples/build_system/make/cpu/cortex-a72.mk
@@ -2,11 +2,4 @@ ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
-mcpu=cortex-a72 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-a72 \
-
- ASFLAGS += \
- --cpu cortex-a72 \
-
endif
diff --git a/examples/build_system/make/cpu/cortex-m0.mk b/examples/build_system/make/cpu/cortex-m0.mk
index c2c33a2ee..1d618982e 100644
--- a/examples/build_system/make/cpu/cortex-m0.mk
+++ b/examples/build_system/make/cpu/cortex-m0.mk
@@ -9,11 +9,6 @@ else ifeq ($(TOOLCHAIN),clang)
--target=arm-none-eabi \
-mcpu=cortex-m0 \
-else ifeq ($(TOOLCHAIN),iar)
- # IAR Flags
- CFLAGS += --cpu cortex-m0
- ASFLAGS += --cpu cortex-m0
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m0plus.mk b/examples/build_system/make/cpu/cortex-m0plus.mk
index fe8feb227..029ce09d1 100644
--- a/examples/build_system/make/cpu/cortex-m0plus.mk
+++ b/examples/build_system/make/cpu/cortex-m0plus.mk
@@ -9,11 +9,6 @@ else ifeq ($(TOOLCHAIN),clang)
--target=arm-none-eabi \
-mcpu=cortex-m0plus \
-else ifeq ($(TOOLCHAIN),iar)
- # IAR Flags
- CFLAGS += --cpu cortex-m0+
- ASFLAGS += --cpu cortex-m0+
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m23.mk b/examples/build_system/make/cpu/cortex-m23.mk
index 7ab758352..b92e24401 100644
--- a/examples/build_system/make/cpu/cortex-m23.mk
+++ b/examples/build_system/make/cpu/cortex-m23.mk
@@ -9,11 +9,6 @@ else ifeq ($(TOOLCHAIN),clang)
--target=arm-none-eabi \
-mcpu=cortex-m23 \
-else ifeq ($(TOOLCHAIN),iar)
- # IAR Flags
- CFLAGS += --cpu cortex-m23
- ASFLAGS += --cpu cortex-m23
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m3.mk b/examples/build_system/make/cpu/cortex-m3.mk
index b6325313f..b58f0203b 100644
--- a/examples/build_system/make/cpu/cortex-m3.mk
+++ b/examples/build_system/make/cpu/cortex-m3.mk
@@ -9,11 +9,6 @@ else ifeq ($(TOOLCHAIN),clang)
--target=arm-none-eabi \
-mcpu=cortex-m3 \
-else ifeq ($(TOOLCHAIN),iar)
- # IAR Flags
- CFLAGS += --cpu cortex-m3
- ASFLAGS += --cpu cortex-m3
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk b/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk
index 405053dd0..858a721fd 100644
--- a/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk
+++ b/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk
@@ -10,13 +10,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m33 \
-mfpu=softvp \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-m33+nodsp \
-
- ASFLAGS += \
- --cpu cortex-m33+nodsp \
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m33.mk b/examples/build_system/make/cpu/cortex-m33.mk
index 47b0eaecd..5699fef7b 100644
--- a/examples/build_system/make/cpu/cortex-m33.mk
+++ b/examples/build_system/make/cpu/cortex-m33.mk
@@ -11,15 +11,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m33 \
-mfpu=fpv5-sp-d16 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-m33 \
- --fpu VFPv5-SP \
-
- ASFLAGS += \
- --cpu cortex-m33 \
- --fpu VFPv5-SP \
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m4-nofpu.mk b/examples/build_system/make/cpu/cortex-m4-nofpu.mk
index ac2916005..62ab21e31 100644
--- a/examples/build_system/make/cpu/cortex-m4-nofpu.mk
+++ b/examples/build_system/make/cpu/cortex-m4-nofpu.mk
@@ -9,10 +9,6 @@ else ifeq ($(TOOLCHAIN),clang)
--target=arm-none-eabi \
-mcpu=cortex-m4
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += --cpu cortex-m4 --fpu none
- ASFLAGS += --cpu cortex-m4 --fpu none
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m4.mk b/examples/build_system/make/cpu/cortex-m4.mk
index 57d6e126d..e50fe00ef 100644
--- a/examples/build_system/make/cpu/cortex-m4.mk
+++ b/examples/build_system/make/cpu/cortex-m4.mk
@@ -11,10 +11,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m4 \
-mfpu=fpv4-sp-d16 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += --cpu cortex-m4 --fpu VFPv4-SP
- ASFLAGS += --cpu cortex-m4 --fpu VFPv4-SP
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m55.mk b/examples/build_system/make/cpu/cortex-m55.mk
index de627caed..94cca1a5e 100644
--- a/examples/build_system/make/cpu/cortex-m55.mk
+++ b/examples/build_system/make/cpu/cortex-m55.mk
@@ -12,15 +12,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m55 \
-mfpu=fpv5-d16 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-m55 \
- --fpu VFPv5_D16 \
-
- ASFLAGS += \
- --cpu cortex-m55 \
- --fpu VFPv5_D16 \
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m7-fpsp.mk b/examples/build_system/make/cpu/cortex-m7-fpsp.mk
index cd42c6fb8..e79ff6d73 100644
--- a/examples/build_system/make/cpu/cortex-m7-fpsp.mk
+++ b/examples/build_system/make/cpu/cortex-m7-fpsp.mk
@@ -11,15 +11,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m7 \
-mfpu=fpv5-sp-d16 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-m7 \
- --fpu VFPv5_sp \
-
- ASFLAGS += \
- --cpu cortex-m7 \
- --fpu VFPv5_sp \
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m7.mk b/examples/build_system/make/cpu/cortex-m7.mk
index 3e6116179..3bd5c1155 100644
--- a/examples/build_system/make/cpu/cortex-m7.mk
+++ b/examples/build_system/make/cpu/cortex-m7.mk
@@ -11,15 +11,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m7 \
-mfpu=fpv5-d16 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-m7 \
- --fpu VFPv5_D16 \
-
- ASFLAGS += \
- --cpu cortex-m7 \
- --fpu VFPv5_D16 \
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/cortex-m85.mk b/examples/build_system/make/cpu/cortex-m85.mk
index 75e8f3aaf..d66e26418 100644
--- a/examples/build_system/make/cpu/cortex-m85.mk
+++ b/examples/build_system/make/cpu/cortex-m85.mk
@@ -11,15 +11,6 @@ else ifeq ($(TOOLCHAIN),clang)
-mcpu=cortex-m85 \
-mfpu=fpv5-d16 \
-else ifeq ($(TOOLCHAIN),iar)
- CFLAGS += \
- --cpu cortex-m85 \
- --fpu VFPv5_D16 \
-
- ASFLAGS += \
- --cpu cortex-m85 \
- --fpu VFPv5_D16 \
-
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/msp430.mk b/examples/build_system/make/cpu/msp430.mk
index 6daa2c38d..83a35140f 100644
--- a/examples/build_system/make/cpu/msp430.mk
+++ b/examples/build_system/make/cpu/msp430.mk
@@ -2,8 +2,6 @@ ifeq ($(TOOLCHAIN),gcc)
# nothing to add
else ifeq ($(TOOLCHAIN),clang)
# nothing to add
-else ifeq ($(TOOLCHAIN),iar)
- # nothing to add
else
$(error "TOOLCHAIN is not supported")
endif
diff --git a/examples/build_system/make/cpu/rv32i-ilp32.mk b/examples/build_system/make/cpu/rv32i-ilp32.mk
index af764afc5..6b2306008 100644
--- a/examples/build_system/make/cpu/rv32i-ilp32.mk
+++ b/examples/build_system/make/cpu/rv32i-ilp32.mk
@@ -8,8 +8,6 @@ else ifeq ($(TOOLCHAIN),clang)
-march=rv32i_zicsr \
-mabi=ilp32 \
-else ifeq ($(TOOLCHAIN),iar)
- $(error not support)
endif
# For freeRTOS port source
diff --git a/examples/build_system/make/cpu/rv32imac-ilp32.mk b/examples/build_system/make/cpu/rv32imac-ilp32.mk
index 19c322ebc..e4a3ad24f 100644
--- a/examples/build_system/make/cpu/rv32imac-ilp32.mk
+++ b/examples/build_system/make/cpu/rv32imac-ilp32.mk
@@ -1,16 +1,13 @@
ifeq ($(TOOLCHAIN),gcc)
CFLAGS += \
- -march=rv32imac_zicsr \
+ -march=rv32imac_zicsr_zifencei \
-mabi=ilp32 \
else ifeq ($(TOOLCHAIN),clang)
CFLAGS += \
- -march=rv32imac_zicsr \
+ -march=rv32imac_zicsr_zifencei \
-mabi=ilp32 \
-else ifeq ($(TOOLCHAIN),iar)
- $(error not support)
-
endif
# For freeRTOS port source
diff --git a/examples/build_system/make/make.mk b/examples/build_system/make/make.mk
deleted file mode 100644
index 4f5d3242e..000000000
--- a/examples/build_system/make/make.mk
+++ /dev/null
@@ -1,190 +0,0 @@
-# ---------------------------------------
-# Common make definition for all examples
-# ---------------------------------------
-
-# upper helper function
-to_upper = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$(subst -,_,$(1))))))))))))))))))))))))))))
-
-#-------------------------------------------------------------
-# Toolchain
-# Can be changed via TOOLCHAIN=gcc|iar or CC=arm-none-eabi-gcc|iccarm|clang
-#-------------------------------------------------------------
-ifneq (,$(findstring clang,$(CC)))
- TOOLCHAIN = clang
-else ifneq (,$(findstring iccarm,$(CC)))
- TOOLCHAIN = iar
-else ifneq (,$(findstring gcc,$(CC)))
- TOOLCHAIN = gcc
-endif
-
-# Default to GCC
-ifndef TOOLCHAIN
- TOOLCHAIN = gcc
-endif
-
-#-------------- TOP and CURRENT_PATH ------------
-
-# Set TOP to be the path to get from the current directory (where make was invoked) to the top of the tree.
-# $(lastword $(MAKEFILE_LIST)) returns the name of this makefile relative to where make was invoked.
-THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
-
-# strip off /examples/build_system/make to get for example ../../..
-# and Set TOP to an absolute path
-TOP = $(abspath $(subst make.mk,../../..,$(THIS_MAKEFILE)))
-
-# Set CURRENT_PATH to the relative path from TOP to the current directory, ie examples/device/cdc_msc_freertos
-CURRENT_PATH = $(subst $(TOP)/,,$(abspath .))
-
-#-------------- Linux/Windows ------------
-
-# Detect whether shell style is windows or not
-# https://stackoverflow.com/questions/714100/os-detecting-makefile/52062069#52062069
-ifeq '$(findstring ;,$(PATH))' ';'
-# PATH contains semicolon - so we're definitely on Windows.
-CMDEXE := 1
-
-# makefile shell commands should use syntax for DOS CMD, not unix sh
-# Force DOS command shell on Windows.
-SHELL := cmd.exe
-endif
-
-ifeq ($(CMDEXE),1)
- CP = copy
- RM = del
- MKDIR = mkdir
- PYTHON = python
-else
- CP = cp
- RM = rm
- MKDIR = mkdir
- PYTHON = python3
-endif
-
-
-# Build directory
-BUILD := _build/$(BOARD)
-
-PROJECT := $(notdir $(CURDIR))
-BIN := $(TOP)/_bin/$(BOARD)/$(notdir $(CURDIR))
-
-#-------------------------------------------------------------
-# Board / Family
-#-------------------------------------------------------------
-
-# Board without family
-ifneq ($(wildcard $(TOP)/hw/bsp/$(BOARD)/board.mk),)
- BOARD_PATH := hw/bsp/$(BOARD)
- FAMILY :=
-endif
-
-# Board within family
-ifeq ($(BOARD_PATH),)
- BOARD_PATH := $(subst $(TOP)/,,$(wildcard $(TOP)/hw/bsp/*/boards/$(BOARD)))
- FAMILY := $(word 3, $(subst /, ,$(BOARD_PATH)))
- FAMILY_PATH = hw/bsp/$(FAMILY)
-endif
-
-ifeq ($(BOARD_PATH),)
- $(info You must provide a BOARD parameter with 'BOARD=')
- $(error Invalid BOARD specified)
-endif
-
-ifeq ($(FAMILY),)
- include $(TOP)/hw/bsp/$(BOARD)/board.mk
-else
- # Include Family and Board specific defs
- include $(TOP)/$(FAMILY_PATH)/family.mk
- SRC_C += $(subst $(TOP)/,,$(wildcard $(TOP)/$(FAMILY_PATH)/*.c))
-endif
-
-#-------------------------------------------------------------
-# Source files and compiler flags
-#-------------------------------------------------------------
-# tinyusb makefile
-include $(TOP)/src/tinyusb.mk
-SRC_C += $(TINYUSB_SRC_C)
-
-# Include all source C in family & board folder
-SRC_C += hw/bsp/board.c
-SRC_C += $(subst $(TOP)/,,$(wildcard $(TOP)/$(BOARD_PATH)/*.c))
-
-INC += \
- $(TOP)/$(FAMILY_PATH) \
- $(TOP)/src \
-
-BOARD_UPPER = $(call to_upper,$(BOARD))
-CFLAGS += -DBOARD_$(BOARD_UPPER)
-
-ifdef CFLAGS_CLI
- CFLAGS += $(CFLAGS_CLI)
-endif
-
-# use max3421 as host controller
-ifeq (${MAX3421_HOST},1)
- SRC_C += src/portable/analog/max3421/hcd_max3421.c
- CFLAGS += -DCFG_TUH_MAX3421=1
-endif
-
-# Log level is mapped to TUSB DEBUG option
-ifneq ($(LOG),)
- CFLAGS += -DCFG_TUSB_DEBUG=$(LOG)
-endif
-
-# Logger: default is uart, can be set to rtt or swo
-ifeq ($(LOGGER),rtt)
- CFLAGS += -DLOGGER_RTT
- #CFLAGS += -DSEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
- INC += $(TOP)/lib/SEGGER_RTT/RTT
- SRC_C += lib/SEGGER_RTT/RTT/SEGGER_RTT.c
-endif
-ifeq ($(LOGGER),swo)
- CFLAGS += -DLOGGER_SWO
-else
- CFLAGS += -DLOGGER_UART
-endif
-
-# CPU specific flags
-ifdef CPU_CORE
- include ${TOP}/examples/build_system/make/cpu/$(CPU_CORE).mk
-endif
-
-# toolchain specific
-include ${TOP}/examples/build_system/make/toolchain/arm_$(TOOLCHAIN).mk
-
-#---------------------- FreeRTOS -----------------------
-FREERTOS_SRC = lib/FreeRTOS-Kernel
-FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC)
-
-ifeq ($(RTOS),freertos)
- SRC_C += \
- $(FREERTOS_SRC)/list.c \
- $(FREERTOS_SRC)/queue.c \
- $(FREERTOS_SRC)/tasks.c \
- $(FREERTOS_SRC)/timers.c \
- $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c))
-
- SRC_S += $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s))
- INC += \
- $(TOP)/hw/bsp/$(FAMILY)/FreeRTOSConfig \
- $(TOP)/$(FREERTOS_SRC)/include \
- $(TOP)/$(FREERTOS_PORTABLE_SRC)
-
- CFLAGS += -DCFG_TUSB_OS=OPT_OS_FREERTOS
-
- # Suppress FreeRTOSConfig.h warnings
- CFLAGS_GCC += -Wno-error=redundant-decls
-
- # Suppress FreeRTOS source warnings
- CFLAGS_GCC += -Wno-error=cast-qual
-
- # FreeRTOS (lto + Os) linker issue
- LDFLAGS_GCC += -Wl,--undefined=vTaskSwitchContext
-endif
-
-#---------------- Helper ----------------
-check_defined = \
- $(strip $(foreach 1,$1, \
- $(call __check_defined,$1,$(strip $(value 2)))))
-__check_defined = \
- $(if $(value $1),, \
- $(error Undefined make flag: $1$(if $2, ($2))))
diff --git a/examples/build_system/make/rules.mk b/examples/build_system/make/rules.mk
deleted file mode 100644
index 86de17b6c..000000000
--- a/examples/build_system/make/rules.mk
+++ /dev/null
@@ -1,195 +0,0 @@
-# ---------------------------------------
-# Common make rules for all examples
-# ---------------------------------------
-
-# Set all as default goal
-.DEFAULT_GOAL := all
-
-# ---------------- GNU Make Start -----------------------
-# ESP32-Sx and RP2040 has its own CMake build system
-ifeq (,$(findstring $(FAMILY),espressif rp2040))
-
-# ---------------------------------------
-# Rules
-# ---------------------------------------
-
-all: $(BUILD)/$(PROJECT).bin $(BUILD)/$(PROJECT).hex size
-
-uf2: $(BUILD)/$(PROJECT).uf2
-
-# We set vpath to point to the top of the tree so that the source files
-# can be located. By following this scheme, it allows a single build rule
-# to be used to compile all .c files.
-vpath %.c . $(TOP)
-vpath %.s . $(TOP)
-vpath %.S . $(TOP)
-
-include ${TOP}/examples/build_system/make/toolchain/$(TOOLCHAIN)_rules.mk
-
-# ---------------------------------------
-# Compiler Flags
-# ---------------------------------------
-
-CFLAGS += $(addprefix -I,$(INC))
-
-# Verbose mode
-ifeq ("$(V)","1")
-$(info CFLAGS $(CFLAGS) ) $(info )
-$(info LDFLAGS $(LDFLAGS)) $(info )
-$(info ASFLAGS $(ASFLAGS)) $(info )
-endif
-
-
-OBJ_DIRS = $(sort $(dir $(OBJ)))
-$(OBJ): | $(OBJ_DIRS)
-$(OBJ_DIRS):
-ifeq ($(CMDEXE),1)
- -@$(MKDIR) $(subst /,\,$@)
-else
- @$(MKDIR) -p $@
-endif
-
-# UF2 generation, iMXRT need to strip to text only before conversion
-ifneq ($(FAMILY),imxrt)
-$(BUILD)/$(PROJECT).uf2: $(BUILD)/$(PROJECT).hex
- @echo CREATE $@
- $(PYTHON) $(TOP)/tools/uf2/utils/uf2conv.py -f $(UF2_FAMILY_ID) -c -o $@ $^
-endif
-
-copy-artifact: $(BUILD)/$(PROJECT).bin $(BUILD)/$(PROJECT).hex $(BUILD)/$(PROJECT).uf2
-
-endif
-# ---------------- GNU Make End -----------------------
-
-.PHONY: clean
-clean:
-ifeq ($(CMDEXE),1)
- rd /S /Q $(subst /,\,$(BUILD))
-else
- $(RM) -rf $(BUILD)
-endif
-
-# get depenecies
-.PHONY: get-deps
-get-deps:
- $(PYTHON) $(TOP)/tools/get_deps.py ${FAMILY}
-
-.PHONY: size
-size: $(BUILD)/$(PROJECT).elf
- -@echo ''
- @$(SIZE) $<
- -@echo ''
-
-# linkermap must be install previously at https://github.com/hathach/linkermap
-linkermap: $(BUILD)/$(PROJECT).elf
- @linkermap -v $<.map
-
-# ---------------------------------------
-# Flash Targets
-# ---------------------------------------
-
-# --------------- Jlink -----------------
-ifeq ($(OS),Windows_NT)
- JLINKEXE = JLink.exe
-else
- JLINKEXE = JLinkExe
-endif
-
-# Jlink Interface
-JLINK_IF ?= swd
-
-# Jlink script
-$(BUILD)/$(BOARD).jlink: $(BUILD)/$(PROJECT).hex
- @echo halt > $@
- @echo loadfile $^ >> $@
- @echo r >> $@
- @echo go >> $@
- @echo exit >> $@
-
-# Flash using jlink
-flash-jlink: $(BUILD)/$(BOARD).jlink
- $(JLINKEXE) -device $(JLINK_DEVICE) -if $(JLINK_IF) -JTAGConf -1,-1 -speed auto -CommandFile $<
-
-# --------------- stm32 cube programmer -----------------
-# Flash STM32 MCU using stlink with STM32 Cube Programmer CLI
-flash-stlink: $(BUILD)/$(PROJECT).elf
- STM32_Programmer_CLI --connect port=swd --write $< --go
-
-# --------------- xfel -----------------
-$(BUILD)/$(PROJECT)-sunxi.bin: $(BUILD)/$(PROJECT).bin
- $(PYTHON) $(TOP)/tools/mksunxi.py $< $@
-
-flash-xfel: $(BUILD)/$(PROJECT)-sunxi.bin
- xfel spinor write 0 $<
- xfel reset
-
-# --------------- pyocd -----------------
-PYOCD_OPTION ?=
-flash-pyocd: $(BUILD)/$(PROJECT).hex
- pyocd flash -t $(PYOCD_TARGET) $(PYOCD_OPTION) $<
- #pyocd reset -t $(PYOCD_TARGET)
-
-# --------------- openocd -----------------
-OPENOCD_OPTION ?=
-flash-openocd: $(BUILD)/$(PROJECT).elf
- openocd $(OPENOCD_OPTION) -c "program $< verify reset exit"
-
-# --------------- openocd-wch -----------------
-# wch-linke is not supported yet in official openOCD yet. We need to either use
-# 1. download openocd as part of mounriver studio http://www.mounriver.com/download or
-# 2. compiled from https://github.com/hathach/riscv-openocd-wch or
-# https://github.com/dragonlock2/miscboards/blob/main/wch/SDK/riscv-openocd.tar.xz
-# with ./configure --disable-werror --enable-wlinke --enable-ch347=no
-OPENOCD_WCH ?= /home/${USER}/app/riscv-openocd-wch/src/openocd
-OPENOCD_WCH_OPTION ?=
-flash-openocd-wch: $(BUILD)/$(PROJECT).elf
- $(OPENOCD_WCH) $(OPENOCD_WCH_OPTION) -c init -c halt -c "flash write_image $<" -c reset -c exit
-
-# --------------- wlink-rs -----------------
-# flash with https://github.com/ch32-rs/wlink
-WLINK_RS ?= wlink
-flash-wlink-rs: $(BUILD)/$(PROJECT).elf
- $(WLINK_RS) flash $<
-
-# --------------- dfu-util -----------------
-DFU_UTIL_OPTION ?= -a 0
-flash-dfu-util: $(BUILD)/$(PROJECT).bin
- dfu-util -R $(DFU_UTIL_OPTION) -D $<
-
-# --------------- Black Magic -----------------
-# This symlink is created by https://github.com/blacksphere/blackmagic/blob/master/driver/99-blackmagic.rules
-BMP ?= /dev/ttyBmpGdb
-
-flash-bmp: $(BUILD)/$(PROJECT).elf
- $(GDB) --batch -ex 'target extended-remote $(BMP)' -ex 'monitor swdp_scan' -ex 'attach 1' -ex load $<
-
-debug-bmp: $(BUILD)/$(PROJECT).elf
- $(GDB) -ex 'target extended-remote $(BMP)' -ex 'monitor swdp_scan' -ex 'attach 1' $<
-
-# --------------- TI Uniflash -----------------
-DSLITE ?= dslite.sh
-flash-uniflash: $(BUILD)/$(PROJECT).hex
- ${DSLITE} ${UNIFLASH_OPTION} -f $<
-
-#-------------- Artifacts --------------
-
-# Create binary directory
-$(BIN):
-ifeq ($(CMDEXE),1)
- @$(MKDIR) $(subst /,\,$@)
-else
- @$(MKDIR) -p $@
-endif
-
-# Copy binaries .elf, .bin, .hex, .uf2 to BIN for upload
-# due to large size of combined artifacts, only uf2 is uploaded for now
-copy-artifact: $(BIN)
- @$(CP) $(BUILD)/$(PROJECT).uf2 $(BIN)
- #@$(CP) $(BUILD)/$(PROJECT).bin $(BIN)
- #@$(CP) $(BUILD)/$(PROJECT).hex $(BIN)
- #@$(CP) $(BUILD)/$(PROJECT).elf $(BIN)
-
-# Print out the value of a make variable.
-# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile
-print-%:
- @echo $* = $($*)
diff --git a/examples/build_system/make/toolchain/arm_iar.mk b/examples/build_system/make/toolchain/arm_iar.mk
deleted file mode 100644
index 17967b41a..000000000
--- a/examples/build_system/make/toolchain/arm_iar.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-# makefile for arm iar toolchain
-
-CC = iccarm
-AS = iasmarm
-LD = ilinkarm
-OBJCOPY = ielftool --silent
-SIZE = size
-
-# Enable extension mode (gcc compatible)
-CFLAGS += -e --debug --silent
-
-# silent mode
-ASFLAGS += -S $(addprefix -I,$(INC))
diff --git a/examples/build_system/make/toolchain/gcc_common.mk b/examples/build_system/make/toolchain/gcc_common.mk
index 0cbb6774d..42fd01183 100644
--- a/examples/build_system/make/toolchain/gcc_common.mk
+++ b/examples/build_system/make/toolchain/gcc_common.mk
@@ -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
diff --git a/examples/build_system/make/toolchain/gcc_rules.mk b/examples/build_system/make/toolchain/gcc_rules.mk
index fc5225503..3463cad48 100644
--- a/examples/build_system/make/toolchain/gcc_rules.mk
+++ b/examples/build_system/make/toolchain/gcc_rules.mk
@@ -1,5 +1,3 @@
-SRC_S += $(SRC_S_GCC)
-
# Assembly files can be name with upper case .S, convert it to .s
SRC_S := $(SRC_S:.S=.s)
@@ -9,7 +7,7 @@ SRC_S := $(SRC_S:.S=.s)
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=_asm.o))
OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
-CFLAGS += $(CFLAGS_GCC) -MD
+CFLAGS += -MD
# LTO makes it difficult to analyze map file for optimizing size purpose
# We will run this option in ci
@@ -25,18 +23,13 @@ ifeq ($(TOOLCHAIN),clang)
CFLAGS += $(CFLAGS_CLANG)
LDFLAGS += $(CFLAGS) $(LDFLAGS_CLANG)
else
-LDFLAGS += $(CFLAGS) $(LDFLAGS_GCC)
+LDFLAGS += $(CFLAGS)
endif
-# TODO should be removed after all examples are updated
ifdef LD_FILE
LDFLAGS += -Wl,-T,$(TOP)/$(LD_FILE)
endif
-ifdef LD_FILE_GCC
-LDFLAGS += -Wl,-T,$(TOP)/$(LD_FILE_GCC)
-endif
-
ASFLAGS += $(CFLAGS)
# libc
diff --git a/examples/build_system/make/toolchain/iar_rules.mk b/examples/build_system/make/toolchain/iar_rules.mk
deleted file mode 100644
index 2c066f6da..000000000
--- a/examples/build_system/make/toolchain/iar_rules.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-SRC_S += $(SRC_S_IAR)
-
-# Assembly files can be name with upper case .S, convert it to .s
-SRC_S := $(SRC_S:.S=.s)
-
-# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
-# assembly file should be placed first in linking order
-# '_asm' suffix is added to object of assembly file
-OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=_asm.o))
-OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
-
-# Linker script
-LDFLAGS += --config $(TOP)/$(LD_FILE_IAR)
-
-# ---------------------------------------
-# Rules
-# ---------------------------------------
-
-# Compile .c file
-$(BUILD)/obj/%.o: %.c
- @echo CC $(notdir $@)
- @$(CC) $(CFLAGS) -c -o $@ $<
-
-# ASM sources lower case .s
-$(BUILD)/obj/%_asm.o: %.s
- @echo AS $(notdir $@)
- @$(AS) $(ASFLAGS) -c -o $@ $<
-
-# ASM sources upper case .S
-$(BUILD)/obj/%_asm.o: %.S
- @echo AS $(notdir $@)
- @$(AS) $(ASFLAGS) -c -o $@ $<
-
-$(BUILD)/$(PROJECT).bin: $(BUILD)/$(PROJECT).elf
- @echo CREATE $@
- @$(OBJCOPY) --bin $^ $@
-
-$(BUILD)/$(PROJECT).hex: $(BUILD)/$(PROJECT).elf
- @echo CREATE $@
- @$(OBJCOPY) --ihex $^ $@
-
-$(BUILD)/$(PROJECT).elf: $(OBJ)
- @echo LINK $@
- @$(LD) -o $@ $(LDFLAGS) $^
diff --git a/examples/build_system/make/toolchain/riscv_gcc.mk b/examples/build_system/make/toolchain/riscv_gcc.mk
index 843aff38c..b5de12a83 100644
--- a/examples/build_system/make/toolchain/riscv_gcc.mk
+++ b/examples/build_system/make/toolchain/riscv_gcc.mk
@@ -1,7 +1,7 @@
# makefile for arm gcc toolchain
# Can be set by family, default to ARM GCC
-CROSS_COMPILE ?= riscv-none-embed-
+CROSS_COMPILE ?= riscv-none-elf-
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt
index eb625ea51..1432b36bb 100644
--- a/examples/device/CMakeLists.txt
+++ b/examples/device/CMakeLists.txt
@@ -6,31 +6,41 @@ 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_msc_throughput
+ 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
+ midi2_device
+ msc_dual_lun
+ mtp
+ net_lwip_webserver
+ printer_to_cdc
+ uac2_headset
+ uac2_speaker_fb
+ usbtmc
+ video_capture
+ video_capture_2ch
+ webusb_serial
+ )
+
+foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+endforeach ()
diff --git a/examples/device/audio_4_channel_mic/CMakeLists.txt b/examples/device/audio_4_channel_mic/CMakeLists.txt
index c8086ae46..5d7ffa4fc 100644
--- a/examples/device/audio_4_channel_mic/CMakeLists.txt
+++ b/examples/device/audio_4_channel_mic/CMakeLists.txt
@@ -2,37 +2,34 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(audio_4_channel_mic C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Add libm for GCC
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
- target_link_libraries(${PROJECT} PUBLIC m)
+ target_link_libraries(${PROJECT_NAME} PUBLIC m)
endif()
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/audio_4_channel_mic/Makefile b/examples/device/audio_4_channel_mic/Makefile
index 2c825bbf7..31e2c6f44 100644
--- a/examples/device/audio_4_channel_mic/Makefile
+++ b/examples/device/audio_4_channel_mic/Makefile
@@ -1,14 +1,14 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c \
src/usb_descriptors.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/audio_4_channel_mic/skip.txt b/examples/device/audio_4_channel_mic/skip.txt
index 157605df1..3ca433c08 100644
--- a/examples/device/audio_4_channel_mic/skip.txt
+++ b/examples/device/audio_4_channel_mic/skip.txt
@@ -3,3 +3,4 @@ mcu:SAME5X
mcu:SAMG
family:broadcom_64bit
family:espressif
+mcu:CH583
diff --git a/examples/device/audio_4_channel_mic/src/main.c b/examples/device/audio_4_channel_mic/src/main.c
index 3e0f03a20..c9c6dd46c 100644
--- a/examples/device/audio_4_channel_mic/src/main.c
+++ b/examples/device/audio_4_channel_mic/src/main.c
@@ -66,8 +66,8 @@ uint32_t sampFreq;
uint8_t clkValid;
// Range states
-audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
-audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
+audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
+audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX * CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000];
@@ -155,8 +155,10 @@ void tud_resume_cb(void) {
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
- if (start_ms == curr_ms) return;// not enough time
+ uint32_t curr_ms = tusb_time_millis_api();
+ if (start_ms == curr_ms) {
+ return; // not enough time
+ }
start_ms = curr_ms;
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
}
@@ -171,7 +173,7 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_req
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -191,7 +193,7 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_re
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -218,25 +220,25 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
(void) itf;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
+ mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
+ volume[channelNum] = (uint16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
@@ -297,13 +299,13 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Input terminal (Microphone input)
if (entityID == 1) {
switch (ctrlSel) {
- case AUDIO_TE_CTRL_CONNECTOR: {
+ case AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
- audio_desc_channel_cluster_t ret;
+ audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
- ret.bmChannelConfig = (audio_channel_config_t) 0;
+ ret.bmChannelConfig = (audio20_channel_config_t) 0;
ret.iChannelNames = 0;
TU_LOG2(" Get terminal connector\r\n");
@@ -321,24 +323,23 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
- audio_control_range_2_n_t(1)
- ret;
+ audio20_control_range_2_n_t(1) ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90;// -90 dB
@@ -364,15 +365,15 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Clock Source unit
if (entityID == 4) {
switch (ctrlSel) {
- case AUDIO_CS_CTRL_SAM_FREQ:
+ case AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
// Buffered control transfer is needed for IN flow control to work
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
@@ -383,7 +384,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
break;
- case AUDIO_CS_CTRL_CLK_VALID:
+ case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
@@ -407,7 +408,9 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return;// not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/audio_4_channel_mic/src/plot_audio_samples.py b/examples/device/audio_4_channel_mic/src/plot_audio_samples.py
index 4d61e7f5e..618745934 100755
--- a/examples/device/audio_4_channel_mic/src/plot_audio_samples.py
+++ b/examples/device/audio_4_channel_mic/src/plot_audio_samples.py
@@ -4,6 +4,51 @@ import matplotlib.pyplot as plt
import numpy as np
import platform
+
+def find_windows_input_device(name_hint, channels, preferred_apis=None):
+ """Pick a Windows input device index from query_devices() output."""
+ preferred_apis = preferred_apis or []
+ name_hint = name_hint.lower()
+ candidates = []
+
+ for index in range(len(sd.query_devices())):
+ device_info = sd.query_devices(index)
+ max_input_channels = int(device_info.get('max_input_channels', 0))
+
+ if max_input_channels < channels:
+ continue
+
+ device_name = str(device_info.get('name', '')).lower()
+ if name_hint not in device_name:
+ continue
+
+ score = 0
+
+ # Prefer exact channel matches to avoid selecting a different stream layout.
+ if max_input_channels == channels:
+ score += 100
+ else:
+ score += 10
+
+ hostapi_index = int(device_info.get('hostapi', -1))
+ api_name = ''
+ if hostapi_index >= 0:
+ api_name = str(sd.query_hostapis(hostapi_index)).lower()
+
+ for priority, api_hint in enumerate(preferred_apis):
+ if api_hint in api_name:
+ score += 50 - priority
+ break
+
+ candidates.append((score, index))
+
+ if not candidates:
+ raise ValueError(
+ 'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
+ )
+
+ return max(candidates, key=lambda item: item[0])[1]
+
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
@@ -14,8 +59,8 @@ if __name__ == '__main__':
duration = 1 # Duration of recording
if platform.system() == 'Windows':
- # WDM-KS is needed since there are more than one MicNode device APIs (at least in Windows)
- device = 'Microphone (MicNode_4_Ch), Windows WASAPI'
+ # Match by substring to support names like "Microphone (2- MicNode_4_Ch)".
+ device = find_windows_input_device('micnode_4_ch', channels=4, preferred_apis=['wasapi', 'wdm-ks', 'mme'])
elif platform.system() == 'Darwin':
device = 'MicNode_4_Ch'
else:
diff --git a/examples/device/audio_4_channel_mic/src/tusb_config.h b/examples/device/audio_4_channel_mic/src/tusb_config.h
index 0ee3ba2d0..085f9d168 100644
--- a/examples/device/audio_4_channel_mic/src/tusb_config.h
+++ b/examples/device/audio_4_channel_mic/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -105,14 +105,10 @@ extern "C" {
// Have a look into audio_device.h for all configurations
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_FOUR_CH_DESC_LEN
-
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
-
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
-#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
@@ -123,4 +119,4 @@ extern "C" {
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_4_channel_mic/src/usb_descriptors.c b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
index 728a5f9ce..6b9a9bbae 100644
--- a/examples/device/audio_4_channel_mic/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -80,7 +80,7 @@ enum
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_FOUR_CH_DESC_LEN)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO20_MIC_FOUR_CH_DESC_LEN)
#if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@@ -91,6 +91,14 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X, OPT_MCU_CH32V307)
+ // Only EP3 is available for ISO
+ #define EPNUM_AUDIO 0x03
+
#else
#define EPNUM_AUDIO 0x01
#endif
@@ -101,7 +109,7 @@ uint8_t const desc_configuration[] =
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
- TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
+ TUD_AUDIO20_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -126,7 +134,7 @@ enum {
};
// array of pointer to string descriptors
-char const* string_desc_arr [] = {
+static char const* string_desc_arr [] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"PaniRCorp", // 1: Manufacturer
"MicNode_4_Ch", // 2: Product
@@ -156,18 +164,22 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if ( chr_count > max_count ) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
- _desc_str[1 + i] = str[i];
+ _desc_str[1 + i] = (uint16_t) str[i];
}
break;
}
diff --git a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
index c50d4fef7..66ef19fbc 100644
--- a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
+++ b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
@@ -2,37 +2,39 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
-project(${PROJECT} C CXX ASM)
+project(audio_4_channel_mic_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Add libm for GCC
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
- target_link_libraries(${PROJECT} PUBLIC m)
+ target_link_libraries(${PROJECT_NAME} PUBLIC m)
endif()
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} freertos)
+family_configure_device_example(${PROJECT_NAME} freertos)
diff --git a/examples/device/audio_4_channel_mic_freertos/Makefile b/examples/device/audio_4_channel_mic_freertos/Makefile
index bd625b345..3c421af74 100644
--- a/examples/device/audio_4_channel_mic_freertos/Makefile
+++ b/examples/device/audio_4_channel_mic_freertos/Makefile
@@ -1,15 +1,15 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/audio_4_channel_mic_freertos/skip.txt b/examples/device/audio_4_channel_mic_freertos/skip.txt
index 30cd46e7e..1fd6b4b8a 100644
--- a/examples/device/audio_4_channel_mic_freertos/skip.txt
+++ b/examples/device/audio_4_channel_mic_freertos/skip.txt
@@ -1,15 +1,16 @@
+mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
+mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
-mcu:RP2040
+mcu:FT90X
mcu:SAMD11
-mcu:SAMX7X
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
mcu:STM32L0
@@ -17,3 +18,5 @@ board:lpcxpresso11u37
board:lpcxpresso1347
family:broadcom_32bit
family:broadcom_64bit
+family:nuc121_125
+family:hpmicro
diff --git a/examples/device/audio_4_channel_mic_freertos/src/main.c b/examples/device/audio_4_channel_mic_freertos/src/main.c
index 96eca0be9..eac66a4ef 100644
--- a/examples/device/audio_4_channel_mic_freertos/src/main.c
+++ b/examples/device/audio_4_channel_mic_freertos/src/main.c
@@ -102,8 +102,8 @@ uint32_t sampFreq;
uint8_t clkValid;
// Range states
-audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
-audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
+audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
+audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX * CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000];
@@ -245,7 +245,7 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_req
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -265,7 +265,7 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_re
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -292,25 +292,25 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
(void) itf;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
+ mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[channelNum] = ((audio_control_cur_2_t *) pBuff)->bCur;
+ volume[channelNum] = (uint16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG1(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
@@ -368,9 +368,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Input terminal (Microphone input)
if (entityID == 1) {
switch (ctrlSel) {
- case AUDIO_TE_CTRL_CONNECTOR: {
+ case AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
- audio_desc_channel_cluster_t ret;
+ audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
@@ -392,23 +392,23 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG1(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
- audio_control_range_2_n_t(1) ret;
+ audio20_control_range_2_n_t(1) ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90;// -90 dB
@@ -434,15 +434,15 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Clock Source unit
if (entityID == 4) {
switch (ctrlSel) {
- case AUDIO_CS_CTRL_SAM_FREQ:
+ case AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Sample Freq.\r\n");
// Buffered control transfer is needed for IN flow control to work
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
@@ -453,7 +453,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
break;
- case AUDIO_CS_CTRL_CLK_VALID:
+ case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG1(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
diff --git a/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py b/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py
index 4d5ca28d6..3b3cd0d83 100755
--- a/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py
+++ b/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py
@@ -4,6 +4,52 @@ import matplotlib.pyplot as plt
import numpy as np
import platform
+
+def find_windows_input_device(name_hint, channels, preferred_apis=None):
+ """Pick a Windows input device index from query_devices() output."""
+ preferred_apis = preferred_apis or []
+ name_hint = name_hint.lower()
+ candidates = []
+
+ for index in range(len(sd.query_devices())):
+ device_info = sd.query_devices(index)
+ max_input_channels = int(device_info.get('max_input_channels', 0))
+
+ if max_input_channels < channels:
+ continue
+
+ device_name = str(device_info.get('name', '')).lower()
+ if name_hint not in device_name:
+ continue
+
+ score = 0
+
+ # Prefer exact channel matches to avoid selecting a different stream layout.
+ if max_input_channels == channels:
+ score += 100
+ else:
+ score += 10
+
+ hostapi_index = int(device_info.get('hostapi', -1))
+ api_name = ''
+ if hostapi_index >= 0:
+ hostapi_info = sd.query_hostapis(hostapi_index)
+ api_name = str(hostapi_info.get('name', '')).lower()
+
+ for priority, api_hint in enumerate(preferred_apis):
+ if api_hint in api_name:
+ score += 50 - priority
+ break
+
+ candidates.append((score, index))
+
+ if not candidates:
+ raise ValueError(
+ 'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
+ )
+
+ return max(candidates, key=lambda item: item[0])[1]
+
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
@@ -14,8 +60,8 @@ if __name__ == '__main__':
duration = 100e-3 # Duration of recording
if platform.system() == 'Windows':
- # WDM-KS is needed since there are more than one MicNode device APIs (at least in Windows)
- device = 'Microphone (MicNode_4_Ch), Windows WDM-KS'
+ # Match by substring to support names like "Microphone (2- MicNode_4_Ch)".
+ device = find_windows_input_device('micnode_4_ch', channels=4, preferred_apis=['wdm-ks', 'wasapi', 'mme'])
elif platform.system() == 'Darwin':
device = 'MicNode_4_Ch'
else:
diff --git a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
index d973be2af..74729695f 100644
--- a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
+++ b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -111,14 +111,10 @@ extern "C" {
// Have a look into audio_device.h for all configurations
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_FOUR_CH_DESC_LEN
-
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
-
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
-#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
@@ -129,4 +125,4 @@ extern "C" {
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
index 728a5f9ce..216cd062a 100644
--- a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -80,7 +80,7 @@ enum
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_FOUR_CH_DESC_LEN)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO20_MIC_FOUR_CH_DESC_LEN)
#if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
@@ -101,7 +105,7 @@ uint8_t const desc_configuration[] =
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
- TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
+ TUD_AUDIO20_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -156,14 +160,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if ( chr_count > max_count ) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
diff --git a/examples/device/audio_test/CMakeLists.txt b/examples/device/audio_test/CMakeLists.txt
index 6a7e68c3d..3382530c6 100644
--- a/examples/device/audio_test/CMakeLists.txt
+++ b/examples/device/audio_test/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(audio_test C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/audio_test/Makefile b/examples/device/audio_test/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/audio_test/Makefile
+++ b/examples/device/audio_test/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/audio_test/skip.txt b/examples/device/audio_test/skip.txt
index 65b137814..42394bb11 100644
--- a/examples/device/audio_test/skip.txt
+++ b/examples/device/audio_test/skip.txt
@@ -2,3 +2,4 @@ mcu:SAMD11
mcu:SAME5X
mcu:SAMG
family:espressif
+mcu:CH583
diff --git a/examples/device/audio_test/src/main.c b/examples/device/audio_test/src/main.c
index 5b3beec24..876a41d06 100644
--- a/examples/device/audio_test/src/main.c
+++ b/examples/device/audio_test/src/main.c
@@ -63,8 +63,8 @@ uint32_t sampFreq;
uint8_t clkValid;
// Range states
-audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
-audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
+audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
+audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2];
@@ -138,8 +138,10 @@ void tud_resume_cb(void) {
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
- if (start_ms == curr_ms) return;// not enough time
+ uint32_t curr_ms = tusb_time_millis_api();
+ if (start_ms == curr_ms) {
+ return; // not enough time
+ }
start_ms = curr_ms;
for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) {
test_buffer_audio[cnt] = startVal++;
@@ -157,7 +159,7 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_req
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -177,7 +179,7 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_re
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -204,25 +206,25 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
(void) itf;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
+ mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
+ volume[channelNum] = (uint16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
@@ -283,13 +285,13 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Input terminal (Microphone input)
if (entityID == 1) {
switch (ctrlSel) {
- case AUDIO_TE_CTRL_CONNECTOR: {
+ case AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
- audio_desc_channel_cluster_t ret;
+ audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
- ret.bmChannelConfig = (audio_channel_config_t) 0;
+ ret.bmChannelConfig = (audio20_channel_config_t) 0;
ret.iChannelNames = 0;
TU_LOG2(" Get terminal connector\r\n");
@@ -307,23 +309,23 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
- audio_control_range_2_n_t(1)
+ audio20_control_range_2_n_t(1)
ret;
ret.wNumSubRanges = 1;
@@ -350,14 +352,14 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Clock Source unit
if (entityID == 4) {
switch (ctrlSel) {
- case AUDIO_CS_CTRL_SAM_FREQ:
+ case AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
@@ -368,7 +370,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
break;
- case AUDIO_CS_CTRL_CLK_VALID:
+ case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
@@ -400,7 +402,9 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return;// not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/audio_test/src/plot_audio_samples.py b/examples/device/audio_test/src/plot_audio_samples.py
index 2be8948ea..af01b7b3e 100755
--- a/examples/device/audio_test/src/plot_audio_samples.py
+++ b/examples/device/audio_test/src/plot_audio_samples.py
@@ -5,6 +5,52 @@ import numpy as np
import platform
import csv
+
+def find_windows_input_device(name_hint, channels, preferred_apis=None):
+ """Pick a Windows input device index from query_devices() output."""
+ preferred_apis = preferred_apis or []
+ name_hint = name_hint.lower()
+ candidates = []
+
+ for index in range(len(sd.query_devices())):
+ device_info = sd.query_devices(index)
+ max_input_channels = int(device_info.get('max_input_channels', 0))
+
+ if max_input_channels < channels:
+ continue
+
+ device_name = str(device_info.get('name', '')).lower()
+ if name_hint not in device_name:
+ continue
+
+ score = 0
+
+ # Prefer exact channel matches (for example 1ch source over a 4ch source).
+ if max_input_channels == channels:
+ score += 100
+ else:
+ score += 10
+
+ hostapi_index = int(device_info.get('hostapi', -1))
+ api_name = ''
+ if hostapi_index >= 0:
+ hostapi_info = sd.query_hostapis(hostapi_index)
+ api_name = str(hostapi_info.get('name', '')).lower()
+
+ for priority, api_hint in enumerate(preferred_apis):
+ if api_hint in api_name:
+ score += 50 - priority
+ break
+
+ candidates.append((score, index))
+
+ if not candidates:
+ raise ValueError(
+ 'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
+ )
+
+ return max(candidates, key=lambda item: item[0])[1]
+
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
@@ -15,8 +61,8 @@ if __name__ == '__main__':
duration = 3 # Duration of recording
if platform.system() == 'Windows':
- # MME is needed since there are more than one MicNode device APIs (at least in Windows)
- device = 'Microphone (MicNode), Windows WASAPI'
+ # Match by substring to support names like "Microphone (2- MicNode)".
+ device = find_windows_input_device('micnode', channels=1, preferred_apis=['wasapi', 'mme', 'wdm-ks'])
elif platform.system() == 'Darwin':
device = 'MicNode'
else:
diff --git a/examples/device/audio_test/src/tusb_config.h b/examples/device/audio_test/src/tusb_config.h
index 10bf53809..e311d892f 100644
--- a/examples/device/audio_test/src/tusb_config.h
+++ b/examples/device/audio_test/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -108,13 +108,10 @@ extern "C" {
// Have a look into audio_device.h for all configurations
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer
-
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
-#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
@@ -122,4 +119,4 @@ extern "C" {
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_test/src/usb_descriptors.c b/examples/device/audio_test/src/usb_descriptors.c
index 9864377f6..cea4eb8d1 100644
--- a/examples/device/audio_test/src/usb_descriptors.c
+++ b/examples/device/audio_test/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -80,7 +80,7 @@ enum
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_DESC_LEN)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO20_MIC_ONE_CH_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@@ -91,6 +91,14 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X, OPT_MCU_CH32V307)
+ // Only EP3 is available for ISO
+ #define EPNUM_AUDIO 0x03
+
#else
#define EPNUM_AUDIO 0x01
#endif
@@ -101,7 +109,7 @@ uint8_t const desc_configuration[] =
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
- TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
+ TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -158,14 +166,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
diff --git a/examples/device/audio_test_freertos/CMakeLists.txt b/examples/device/audio_test_freertos/CMakeLists.txt
index 6ce9e72fe..a39e56822 100644
--- a/examples/device/audio_test_freertos/CMakeLists.txt
+++ b/examples/device/audio_test_freertos/CMakeLists.txt
@@ -2,32 +2,34 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
-project(${PROJECT} C CXX ASM)
+project(audio_test_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} freertos)
+family_configure_device_example(${PROJECT_NAME} freertos)
diff --git a/examples/device/audio_test_freertos/Makefile b/examples/device/audio_test_freertos/Makefile
index bd625b345..3c421af74 100644
--- a/examples/device/audio_test_freertos/Makefile
+++ b/examples/device/audio_test_freertos/Makefile
@@ -1,15 +1,15 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
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/audio_test_freertos/skip.txt b/examples/device/audio_test_freertos/skip.txt
index 1f3d4281a..660bacd25 100644
--- a/examples/device/audio_test_freertos/skip.txt
+++ b/examples/device/audio_test_freertos/skip.txt
@@ -1,17 +1,20 @@
+mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
+mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
-mcu:RP2040
+mcu:FT90X
mcu:SAMD11
-mcu:SAMX7X
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
family:broadcom_32bit
family:broadcom_64bit
board:stm32l0538disco
+family:nuc121_125
+family:hpmicro
diff --git a/examples/device/audio_test_freertos/src/main.c b/examples/device/audio_test_freertos/src/main.c
index 1eab5dab8..cf2fb74d1 100644
--- a/examples/device/audio_test_freertos/src/main.c
+++ b/examples/device/audio_test_freertos/src/main.c
@@ -100,8 +100,8 @@ uint32_t sampFreq;
uint8_t clkValid;
// Range states
-audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
-audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
+audio20_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
+audio20_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2];
@@ -231,7 +231,7 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_req
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -251,7 +251,7 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_re
(void) pBuff;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
@@ -278,25 +278,25 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
(void) itf;
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
+ mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
+ volume[channelNum] = (uint16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG1(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
@@ -354,13 +354,13 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Input terminal (Microphone input)
if (entityID == 1) {
switch (ctrlSel) {
- case AUDIO_TE_CTRL_CONNECTOR: {
+ case AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
- audio_desc_channel_cluster_t ret;
+ audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
- ret.bmChannelConfig = (audio_channel_config_t) 0;
+ ret.bmChannelConfig = (audio20_channel_config_t) 0;
ret.iChannelNames = 0;
TU_LOG1(" Get terminal connector\r\n");
@@ -378,23 +378,23 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Feature unit
if (entityID == 2) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG1(" Get Mute of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
- audio_control_range_2_n_t(1)
+ audio20_control_range_2_n_t(1)
ret;
ret.wNumSubRanges = 1;
@@ -421,14 +421,14 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Clock Source unit
if (entityID == 4) {
switch (ctrlSel) {
- case AUDIO_CS_CTRL_SAM_FREQ:
+ case AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG1(" Get Sample Freq.\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG1(" Get Sample Freq. range\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
@@ -439,7 +439,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
break;
- case AUDIO_CS_CTRL_CLK_VALID:
+ case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG1(" Get Sample Freq. valid\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
diff --git a/examples/device/audio_test_freertos/src/plot_audio_samples.py b/examples/device/audio_test_freertos/src/plot_audio_samples.py
index b6d8e824b..b6a916be4 100755
--- a/examples/device/audio_test_freertos/src/plot_audio_samples.py
+++ b/examples/device/audio_test_freertos/src/plot_audio_samples.py
@@ -4,6 +4,52 @@ import matplotlib.pyplot as plt
import numpy as np
import platform
+
+def find_windows_input_device(name_hint, channels, preferred_apis=None):
+ """Pick a Windows input device index from query_devices() output."""
+ preferred_apis = preferred_apis or []
+ name_hint = name_hint.lower()
+ candidates = []
+
+ for index in range(len(sd.query_devices())):
+ device_info = sd.query_devices(index)
+ max_input_channels = int(device_info.get('max_input_channels', 0))
+
+ if max_input_channels < channels:
+ continue
+
+ device_name = str(device_info.get('name', '')).lower()
+ if name_hint not in device_name:
+ continue
+
+ score = 0
+
+ # Prefer exact channel matches (for example 1ch source over a 4ch source).
+ if max_input_channels == channels:
+ score += 100
+ else:
+ score += 10
+
+ hostapi_index = int(device_info.get('hostapi', -1))
+ api_name = ''
+ if hostapi_index >= 0:
+ hostapi_info = sd.query_hostapis(hostapi_index)
+ api_name = str(hostapi_info.get('name', '')).lower()
+
+ for priority, api_hint in enumerate(preferred_apis):
+ if api_hint in api_name:
+ score += 50 - priority
+ break
+
+ candidates.append((score, index))
+
+ if not candidates:
+ raise ValueError(
+ 'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
+ )
+
+ return max(candidates, key=lambda item: item[0])[1]
+
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
@@ -14,8 +60,8 @@ if __name__ == '__main__':
duration = 3 # Duration of recording
if platform.system() == 'Windows':
- # MME is needed since there are more than one MicNode device APIs (at least in Windows)
- device = 'Microphone (MicNode), Windows WASAPI'
+ # Match by substring to support names like "Microphone (2- MicNode)".
+ device = find_windows_input_device('micnode', channels=1, preferred_apis=['wasapi', 'mme', 'wdm-ks'])
elif platform.system() == 'Darwin':
device = 'MicNode'
else:
diff --git a/examples/device/audio_test_freertos/src/tusb_config.h b/examples/device/audio_test_freertos/src/tusb_config.h
index c9dc50082..81f0c11f4 100644
--- a/examples/device/audio_test_freertos/src/tusb_config.h
+++ b/examples/device/audio_test_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -114,13 +114,10 @@ extern "C" {
// Have a look into audio_device.h for all configurations
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer
-
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
-#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
@@ -128,4 +125,4 @@ extern "C" {
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_test_freertos/src/usb_descriptors.c b/examples/device/audio_test_freertos/src/usb_descriptors.c
index 9864377f6..37ebf84d3 100644
--- a/examples/device/audio_test_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_test_freertos/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -80,7 +80,7 @@ enum
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_DESC_LEN)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO20_MIC_ONE_CH_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
@@ -101,7 +105,7 @@ uint8_t const desc_configuration[] =
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
- TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
+ TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -158,14 +162,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
diff --git a/examples/device/audio_test_multi_rate/CMakeLists.txt b/examples/device/audio_test_multi_rate/CMakeLists.txt
index 6a7e68c3d..c17831cda 100644
--- a/examples/device/audio_test_multi_rate/CMakeLists.txt
+++ b/examples/device/audio_test_multi_rate/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(audio_test_multi_rate C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/audio_test_multi_rate/Makefile b/examples/device/audio_test_multi_rate/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/audio_test_multi_rate/Makefile
+++ b/examples/device/audio_test_multi_rate/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/audio_test_multi_rate/skip.txt b/examples/device/audio_test_multi_rate/skip.txt
index 65b137814..42394bb11 100644
--- a/examples/device/audio_test_multi_rate/skip.txt
+++ b/examples/device/audio_test_multi_rate/skip.txt
@@ -2,3 +2,4 @@ mcu:SAMD11
mcu:SAME5X
mcu:SAMG
family:espressif
+mcu:CH583
diff --git a/examples/device/audio_test_multi_rate/src/main.c b/examples/device/audio_test_multi_rate/src/main.c
index 9d467991e..a86beb415 100644
--- a/examples/device/audio_test_multi_rate/src/main.c
+++ b/examples/device/audio_test_multi_rate/src/main.c
@@ -60,7 +60,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
-uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
+int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
uint32_t sampFreq;
uint8_t bytesPerSample;
uint8_t clkValid;
@@ -80,9 +80,6 @@ static const uint8_t bytesPerSampleAltList[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] =
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX,
};
-audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
-
-
// Audio test data
CFG_TUD_MEM_ALIGN uint8_t test_buffer_audio[(TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
uint16_t startVal = 0;
@@ -111,9 +108,6 @@ int main(void) {
led_blinking_task();
audio_task();
}
-
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -152,8 +146,10 @@ void tud_resume_cb(void) {
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
- if (start_ms == curr_ms) return;// not enough time
+ uint32_t curr_ms = tusb_time_millis_api();
+ if (start_ms == curr_ms) {
+ return; // not enough time
+ }
start_ms = curr_ms;
// 16bit
if (bytesPerSample == 2) {
@@ -176,91 +172,204 @@ void audio_task(void) {
// Application Callback API Implementations
//--------------------------------------------------------------------+
-// Invoked when set interface is called, typically on start/stop streaming or format change
-bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
- (void) rhport;
- //uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+//--------------------------------------------------------------------+
+// UAC1 Helper Functions
+//--------------------------------------------------------------------+
- // Clear buffer when streaming format is changed
- if (alt != 0) {
- bytesPerSample = bytesPerSampleAltList[alt - 1];
+static bool audio10_set_req_ep(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ // Request uses 3 bytes
+ TU_VERIFY(p_request->wLength == 3);
+
+ sampFreq = tu_unaligned_read32(pBuff) & 0x00FFFFFF;
+
+ TU_LOG2("EP set current freq: %" PRIu32 "\r\n", sampFreq);
+
+ return true;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
}
- return true;
+
+ return false;
}
-// Invoked when audio class specific set request received for an EP
-bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
- (void) rhport;
- (void) pBuff;
+static bool audio10_get_req_ep(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- // We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_GET_CUR) {
+ TU_LOG2("EP get current freq\r\n");
+
+ uint8_t freq[3];
+ freq[0] = (uint8_t) (sampFreq & 0xFF);
+ freq[1] = (uint8_t) ((sampFreq >> 8) & 0xFF);
+ freq[2] = (uint8_t) ((sampFreq >> 16) & 0xFF);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, freq, sizeof(freq));
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
- // Page 91 in UAC2 specification
+static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- uint8_t ep = TU_U16_LOW(p_request->wIndex);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
- (void) channelNum;
- (void) ctrlSel;
- (void) ep;
+ // If request is for our feature unit (ID defined in usbd.h)
+ if (entityID == 0x02) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength ==1);
- return false;// Yet not implemented
-}
+ mute[channelNum] = pBuff[0];
-// Invoked when audio class specific set request received for an interface
-bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
- (void) rhport;
- (void) pBuff;
+ TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
+ return true;
- // We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ default:
+ return false; // not supported
+ }
- // Page 91 in UAC2 specification
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 2);
+
+ volume[channelNum] = (int16_t)tu_unaligned_read16(pBuff) / 256;
+
+ TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our feature unit (ID defined in usbd.h)
+ if (entityID == 0x02) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
+ // There does not exist a range parameter block for mute
+ TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
- (void) channelNum;
- (void) ctrlSel;
- (void) itf;
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_GET_CUR:
+ TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
+ {
+ int16_t vol = (int16_t) volume[channelNum];
+ vol = vol * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
+ }
- return false;// Yet not implemented
+ case AUDIO10_CS_REQ_GET_MIN:
+ TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum);
+ {
+ int16_t min = -90; // -90 dB
+ min = min * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
+ }
+
+ case AUDIO10_CS_REQ_GET_MAX:
+ TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum);
+ {
+ int16_t max = 30; // +30 dB
+ max = max * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
+ }
+
+ case AUDIO10_CS_REQ_GET_RES:
+ TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum);
+ {
+ int16_t res = 1; // 1 dB
+ res = res * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
+ }
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ 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 *pBuff) {
- (void) rhport;
+//--------------------------------------------------------------------+
+// UAC2 Helper Functions
+//--------------------------------------------------------------------+
+
+#if TUD_OPT_HIGH_SPEED
- // Page 91 in UAC2 specification
+static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- uint8_t itf = TU_U16_LOW(p_request->wIndex);
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
- (void) itf;
-
// We do not support any set range requests here, only current value requests
- TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
// If request is for our feature unit
if (entityID == UAC2_ENTITY_FEATURE_UNIT) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Request uses format layout 1
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
+ mute[channelNum] = ((audio20_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
// Request uses format layout 2
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
+ volume[channelNum] = (int16_t) ((audio20_control_cur_2_t *) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
@@ -275,10 +384,10 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Clock Source unit
if (entityID == UAC2_ENTITY_CLOCK) {
switch (ctrlSel) {
- case AUDIO_CS_CTRL_SAM_FREQ:
- TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_4_t));
+ case AUDIO20_CS_CTRL_SAM_FREQ:
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
- sampFreq = (uint32_t) ((audio_control_cur_4_t *) pBuff)->bCur;
+ sampFreq = (uint32_t) ((audio20_control_cur_4_t *) pBuff)->bCur;
TU_LOG2("Clock set current freq: %" PRIu32 "\r\n", sampFreq);
@@ -292,59 +401,20 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
}
- return false;// Yet not implemented
-}
-
-// Invoked when audio class specific get request received for an EP
-bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
- (void) rhport;
-
- // Page 91 in UAC2 specification
- uint8_t channelNum = TU_U16_LOW(p_request->wValue);
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- uint8_t ep = TU_U16_LOW(p_request->wIndex);
-
- (void) channelNum;
- (void) ctrlSel;
- (void) ep;
-
- // return tud_control_xfer(rhport, p_request, &tmp, 1);
-
- return false;// Yet not implemented
-}
-
-// Invoked when audio class specific get request received for an interface
-bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
- (void) rhport;
-
- // Page 91 in UAC2 specification
- uint8_t channelNum = TU_U16_LOW(p_request->wValue);
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- uint8_t itf = TU_U16_LOW(p_request->wIndex);
-
- (void) channelNum;
- (void) ctrlSel;
- (void) itf;
-
- return false;// Yet not implemented
+ return false;
}
-// 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) {
- (void) rhport;
-
- // Page 91 in UAC2 specification
+static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since we have only one audio function implemented, we do not need the itf value
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == UAC2_ENTITY_INPUT_TERMINAL) {
switch (ctrlSel) {
- case AUDIO_TE_CTRL_CONNECTOR: {
+ case AUDIO20_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
- audio_desc_channel_cluster_t ret;
+ audio20_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
@@ -366,23 +436,23 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Feature unit
if (entityID == UAC2_ENTITY_FEATURE_UNIT) {
switch (ctrlSel) {
- case AUDIO_FU_CTRL_MUTE:
+ case AUDIO20_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
- case AUDIO_FU_CTRL_VOLUME:
+ case AUDIO20_FU_CTRL_VOLUME:
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
- case AUDIO_CS_REQ_RANGE:
+ case AUDIO20_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
- audio_control_range_2_n_t(1)
+ audio20_control_range_2_n_t(1)
ret;
ret.wNumSubRanges = 1;
@@ -409,16 +479,16 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
// Clock Source unit
if (entityID == UAC2_ENTITY_CLOCK) {
switch (ctrlSel) {
- case AUDIO_CS_CTRL_SAM_FREQ:
+ case AUDIO20_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch (p_request->bRequest) {
- case AUDIO_CS_REQ_CUR:
+ case AUDIO20_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
- case AUDIO_CS_REQ_RANGE: {
+ case AUDIO20_CS_REQ_RANGE: {
TU_LOG2(" Get Sample Freq. range\r\n");
- audio_control_range_4_n_t(N_sampleRates) rangef =
+ audio20_control_range_4_n_t(N_sampleRates) rangef =
{
.wNumSubRanges = tu_htole16(N_sampleRates)};
TU_LOG1("Clock get %d freq ranges\r\n", N_sampleRates);
@@ -437,7 +507,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
break;
- case AUDIO_CS_CTRL_CLK_VALID:
+ case AUDIO20_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
@@ -449,7 +519,82 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
}
- TU_LOG2(" Unsupported entity: %d\r\n", entityID);
+ return false;
+}
+
+#endif // TUD_OPT_HIGH_SPEED
+
+//--------------------------------------------------------------------+
+// Main Callback Functions
+//--------------------------------------------------------------------+
+
+// Invoked when set interface is called, typically on start/stop streaming or format change
+bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+ //uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
+ uint8_t const alt = tu_u16_low(p_request->wValue);
+
+ // Clear buffer when streaming format is changed
+ if (alt != 0) {
+ bytesPerSample = bytesPerSampleAltList[alt - 1];
+ }
+ return true;
+}
+
+// Invoked when audio class specific set request received for an EP
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) pBuff;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_ep(p_request, pBuff);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an EP
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_ep(rhport, p_request);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// 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 *pBuff) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_entity(p_request, pBuff);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_set_req_entity(p_request, pBuff);
+#endif
+ }
+
+ return false;// Yet not implemented
+}
+
+// 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) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_entity(rhport, p_request);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_get_req_entity(rhport, p_request);
+#endif
+ }
+
return false;// Yet not implemented
}
@@ -469,7 +614,9 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return;// not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/audio_test_multi_rate/src/plot_audio_samples.py b/examples/device/audio_test_multi_rate/src/plot_audio_samples.py
index 1f33a003e..f35cfa311 100755
--- a/examples/device/audio_test_multi_rate/src/plot_audio_samples.py
+++ b/examples/device/audio_test_multi_rate/src/plot_audio_samples.py
@@ -5,6 +5,52 @@ import numpy as np
import platform
import csv
+
+def find_windows_input_device(name_hint, channels, preferred_apis=None):
+ """Pick a Windows input device index from query_devices() output."""
+ preferred_apis = preferred_apis or []
+ name_hint = name_hint.lower()
+ candidates = []
+
+ for index in range(len(sd.query_devices())):
+ device_info = sd.query_devices(index)
+ max_input_channels = int(device_info.get('max_input_channels', 0))
+
+ if max_input_channels < channels:
+ continue
+
+ device_name = str(device_info.get('name', '')).lower()
+ if name_hint not in device_name:
+ continue
+
+ score = 0
+
+ # Prefer exact channel matches (for example 1ch source over a 4ch source).
+ if max_input_channels == channels:
+ score += 100
+ else:
+ score += 10
+
+ hostapi_index = int(device_info.get('hostapi', -1))
+ api_name = ''
+ if hostapi_index >= 0:
+ hostapi_info = sd.query_hostapis(hostapi_index)
+ api_name = str(hostapi_info.get('name', '')).lower()
+
+ for priority, api_hint in enumerate(preferred_apis):
+ if api_hint in api_name:
+ score += 50 - priority
+ break
+
+ candidates.append((score, index))
+
+ if not candidates:
+ raise ValueError(
+ 'No input device matching hint="{}" with at least {} channels'.format(name_hint, channels)
+ )
+
+ return max(candidates, key=lambda item: item[0])[1]
+
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
@@ -15,8 +61,8 @@ if __name__ == '__main__':
duration = 100e-3 # Duration of recording
if platform.system() == 'Windows':
- # MME is needed since there are more than one MicNode device APIs (at least in Windows)
- device = 'Microphone (MicNode) MME'
+ # Match by substring to support names like "Microphone (2- MicNode)".
+ device = find_windows_input_device('micnode', channels=1, preferred_apis=['mme', 'wasapi', 'wdm-ks'])
elif platform.system() == 'Darwin':
device = 'MicNode'
else:
diff --git a/examples/device/audio_test_multi_rate/src/tusb_config.h b/examples/device/audio_test_multi_rate/src/tusb_config.h
index b48c0a0be..3587ae089 100644
--- a/examples/device/audio_test_multi_rate/src/tusb_config.h
+++ b/examples/device/audio_test_multi_rate/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -116,26 +116,31 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX 2
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX 16
-// 24bit in 32bit slots
+// 24bit in 32bit slots (UAC2 only)
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 4
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX 24
// Have a look into audio_device.h for all configurations
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_2_FORMAT_DESC_LEN
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer
+#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
+#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
-#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
-#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
+// UAC1 (Full-Speed) Endpoint size calculation
+#define CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(false, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+// UAC2 (High-Speed) Endpoint size calculation
+#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
-#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
-#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example write FIFO every 1ms, so it should be 8 times larger for HS device
+// Maximum EP IN size for all AS alternate settings used
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, TU_MAX(CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN))
+
+// Tx flow control needs buffer size >= 4* EP size to work correctly
+// Example write FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, TU_MAX(32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, 32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN))
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_test_multi_rate/src/usb_descriptors.c b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
index f50e70a25..b1f60dd10 100644
--- a/examples/device/audio_test_multi_rate/src/usb_descriptors.c
+++ b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
@@ -34,15 +34,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
@@ -67,23 +66,19 @@ tusb_desc_device_t const desc_device =
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
+uint8_t const * tud_descriptor_device_cb(void) {
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
-enum
-{
+enum {
ITF_NUM_AUDIO_CONTROL = 0,
ITF_NUM_AUDIO_STREAMING,
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_2_FORMAT_DESC_LEN)
-
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
@@ -93,28 +88,93 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X, OPT_MCU_CH32V307)
+ // Only EP3 is available for ISO
+ #define EPNUM_AUDIO 0x03
+
#else
#define EPNUM_AUDIO 0x01
#endif
-uint8_t const desc_configuration[] =
-{
+#define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_MIC_ONE_CH_DESC_LEN(3))
+
+uint8_t const desc_uac1_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC1_TOTAL_LEN, 0x00, 100),
+
+ // Interface number, string index, EP Out & EP In address, EP size
+ TUD_AUDIO10_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ 2, /*_nBitsUsedPerSample*/ 16, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, 32000, 48000, 96000)
+};
+
+TU_VERIFY_STATIC(sizeof(desc_uac1_configuration) == CONFIG_UAC1_TOTAL_LEN, "Incorrect size");
+
+#if TUD_OPT_HIGH_SPEED
+#define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_MIC_ONE_CH_2_FORMAT_DESC_LEN)
+
+uint8_t const desc2_uac2_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC2_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
- TUD_AUDIO_MIC_ONE_CH_2_FORMAT_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_epin*/ 0x80 | EPNUM_AUDIO)
+ TUD_AUDIO20_MIC_ONE_CH_2_FORMAT_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_epin*/ 0x80 | EPNUM_AUDIO)
};
-TU_VERIFY_STATIC(sizeof(desc_configuration) == CONFIG_TOTAL_LEN, "Incorrect size");
+TU_VERIFY_STATIC(sizeof(desc2_uac2_configuration) == CONFIG_UAC2_TOTAL_LEN, "Incorrect size");
+
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = 0x0200,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
+};
+
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;// for multiple configurations
+
+ // if link speed is high return fullspeed config, and vice versa
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_uac1_configuration : desc2_uac2_configuration;
+}
+
+#endif// highspeed
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
(void) index; // for multiple configurations
- return desc_configuration;
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ if(tud_speed_get() == TUSB_SPEED_FULL) {
+ return desc_uac1_configuration;
+ } else {
+ return desc2_uac2_configuration;
+ }
+#else
+ return desc_uac1_configuration;
+#endif
}
//--------------------------------------------------------------------+
@@ -130,8 +190,7 @@ enum {
};
// array of pointer to string descriptors
-char const* string_desc_arr [] =
-{
+char const* string_desc_arr [] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"PaniRCorp", // 1: Manufacturer
"MicNode", // 2: Product
@@ -162,14 +221,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
diff --git a/examples/device/audio_test_multi_rate/src/usb_descriptors.h b/examples/device/audio_test_multi_rate/src/usb_descriptors.h
index 8381e31f5..948a7f4ae 100644
--- a/examples/device/audio_test_multi_rate/src/usb_descriptors.h
+++ b/examples/device/audio_test_multi_rate/src/usb_descriptors.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _USB_DESCRIPTORS_H_
-#define _USB_DESCRIPTORS_H_
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
// #include "tusb.h"
@@ -35,68 +35,68 @@
#define UAC2_ENTITY_FEATURE_UNIT 0x02
-#define TUD_AUDIO_MIC_ONE_CH_2_FORMAT_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
- + TUD_AUDIO_DESC_STD_AC_LEN\
- + TUD_AUDIO_DESC_CS_AC_LEN\
- + TUD_AUDIO_DESC_CLK_SRC_LEN\
- + TUD_AUDIO_DESC_INPUT_TERM_LEN\
- + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
- + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\
+#define TUD_AUDIO20_MIC_ONE_CH_2_FORMAT_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1)\
/* Interface 1, Alternate 0 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
/* Interface 1, Alternate 1 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 1, Alternate 2 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN)
-#define TUD_AUDIO_MIC_ONE_CH_2_FORMAT_DESCRIPTOR(_itfnum, _stridx, _epin) \
+#define TUD_AUDIO20_MIC_ONE_CH_2_FORMAT_DESCRIPTOR(_itfnum, _stridx, _epin) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
- TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
- TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
/* Clock Source Descriptor(4.7.2.1) */\
- TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS | AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_VAL_POS, /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ AUDIO20_CTRL_RW << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS | AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_VAL_POS, /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
/* Input Terminal Descriptor(4.7.2.4) */\
- TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\
/* Output Terminal Descriptor(4.7.2.5) */\
- TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC2_ENTITY_INPUT_TERMINAL, /*_srcid*/ UAC2_ENTITY_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC2_ENTITY_INPUT_TERMINAL, /*_srcid*/ UAC2_ENTITY_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
- TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ UAC2_ENTITY_FEATURE_UNIT, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ UAC2_ENTITY_FEATURE_UNIT, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
/* Interface 1, Alternate 2 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN, /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
#endif
diff --git a/examples/device/board_test/CMakeLists.txt b/examples/device/board_test/CMakeLists.txt
index 9a604a732..f14d72c08 100644
--- a/examples/device/board_test/CMakeLists.txt
+++ b/examples/device/board_test/CMakeLists.txt
@@ -2,13 +2,15 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
-project(${PROJECT} C CXX ASM)
+project(board_test C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
@@ -18,7 +20,7 @@ endif()
if (RTOS STREQUAL zephyr)
set(EXE_NAME app)
else()
- set(EXE_NAME ${PROJECT})
+ set(EXE_NAME ${PROJECT_NAME})
add_executable(${EXE_NAME})
endif()
diff --git a/examples/device/board_test/Makefile b/examples/device/board_test/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/board_test/Makefile
+++ b/examples/device/board_test/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/board_test/src/main.c b/examples/device/board_test/src/main.c
index d91a8760e..96dc1bd30 100644
--- a/examples/device/board_test/src/main.c
+++ b/examples/device/board_test/src/main.c
@@ -39,11 +39,48 @@ enum {
#define HELLO_STR "Hello from TinyUSB\r\n"
+// board test example does not use both device and host stack
+#if CFG_TUSB_OS != OPT_OS_NONE
+uint32_t tusb_time_millis_api(void) {
+ return osal_time_millis();
+}
+
+void tusb_time_delay_ms_api(uint32_t ms) {
+ osal_task_delay(ms);
+}
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+// CI_BUILD (defined for all CI builds, see hw/bsp/family_support.cmake) skips the
+// blink/echo loop below: after HIL tests, this firmware is flashed to park the
+// board in a quiet, low-power idle state (no USB, LED, or UART activity).
+#ifdef CI_BUILD
int main(void) {
- board_init();
- board_led_write(true);
+ while (1) {
+ #if defined(ESP_PLATFORM)
+ vTaskDelay(portMAX_DELAY);
+ #endif
+ }
+}
+
+#else
+// Task parameter type: ULONG for ThreadX, void* for FreeRTOS and noos
+#if CFG_TUSB_OS == OPT_OS_THREADX
+ #define RTOS_PARAM ULONG
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+ #define RTOS_PARAM void*
+ static void freertos_init(void);
+#else
+ #define RTOS_PARAM void*
+#endif
+static void board_test_loop(RTOS_PARAM param) {
+ (void) param;
uint32_t start_ms = 0;
+ (void) start_ms;
bool led_state = false;
while (1) {
@@ -58,24 +95,93 @@ int main(void) {
}
// Blink and print every interval ms
- if (!(board_millis() - start_ms < interval_ms)) {
- start_ms = board_millis();
-
- if (ch < 0) {
- // skip if echoing
- printf(HELLO_STR);
+ #if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(interval_ms / portTICK_PERIOD_MS);
+ #elif CFG_TUSB_OS == OPT_OS_THREADX
+ tx_thread_sleep(_osal_ms2tick(interval_ms));
+ #else
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
+ continue; // not enough time
+ }
+ #endif
+ start_ms = tusb_time_millis_api();
- #ifndef LOGGER_UART
- board_uart_write(HELLO_STR, strlen(HELLO_STR));
- #endif
- }
+ if (ch < 0) {
+ // skip if echoing
+ printf(HELLO_STR);
- board_led_write(led_state);
- led_state = 1 - led_state; // toggle
+ #ifndef LOGGER_UART
+ board_uart_write(HELLO_STR, sizeof(HELLO_STR)-1);
+ #endif
}
+
+ board_led_write(led_state);
+ led_state = !led_state; // toggle
}
}
+int main(void) {
+ board_init();
+ board_led_write(true);
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ freertos_init();
+#elif CFG_TUSB_OS == OPT_OS_THREADX
+ tx_kernel_enter();
+#else
+ board_test_loop(NULL);
+#endif
+
+ return 0;
+}
+
+//--------------------------------------------------------------------+
+// FreeRTOS
+//--------------------------------------------------------------------+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+
+#ifdef ESP_PLATFORM
+#define MAIN_STACK_SIZE 4096
+#else
+#define MAIN_STACK_SIZE 512
+#endif
+
+#if configSUPPORT_STATIC_ALLOCATION
+static StackType_t _main_stack[MAIN_STACK_SIZE];
+static StaticTask_t _main_taskdef;
+#endif
+
+static void freertos_init(void) {
+ #if configSUPPORT_STATIC_ALLOCATION
+ xTaskCreateStatic(board_test_loop, "main", MAIN_STACK_SIZE, NULL, 1, _main_stack, &_main_taskdef);
+ #else
+ xTaskCreate(board_test_loop, "main", MAIN_STACK_SIZE, NULL, 1, NULL);
+ #endif
+
+ #ifndef ESP_PLATFORM
+ vTaskStartScheduler();
+ #endif
+}
+
+//--------------------------------------------------------------------+
+// ThreadX
+//--------------------------------------------------------------------+
+#elif CFG_TUSB_OS == OPT_OS_THREADX
+
+#define MAIN_TASK_STACK_SIZE 1024
+static TX_THREAD _main_thread;
+static ULONG _main_thread_stack[MAIN_TASK_STACK_SIZE / sizeof(ULONG)];
+
+void tx_application_define(void *first_unused_memory) {
+ (void) first_unused_memory;
+ static CHAR main_thread_name[] = "main";
+ tx_thread_create(&_main_thread, main_thread_name, board_test_loop, 0,
+ _main_thread_stack, MAIN_TASK_STACK_SIZE,
+ 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
+}
+#endif
+#endif // CI_BUILD
+
#ifdef ESP_PLATFORM
void app_main(void) {
main();
diff --git a/examples/device/board_test/src/tusb_config.h b/examples/device/board_test/src/tusb_config.h
index 81829d450..97ec65f66 100644
--- a/examples/device/board_test/src/tusb_config.h
+++ b/examples/device/board_test/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -74,4 +74,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_dual_ports/CMakeLists.txt b/examples/device/cdc_dual_ports/CMakeLists.txt
index 6a7e68c3d..697906b83 100644
--- a/examples/device/cdc_dual_ports/CMakeLists.txt
+++ b/examples/device/cdc_dual_ports/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_dual_ports C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/cdc_dual_ports/Makefile b/examples/device/cdc_dual_ports/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/cdc_dual_ports/Makefile
+++ b/examples/device/cdc_dual_ports/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_dual_ports/src/main.c b/examples/device/cdc_dual_ports/src/main.c
index 8fe003f21..6f918218d 100644
--- a/examples/device/cdc_dual_ports/src/main.c
+++ b/examples/device/cdc_dual_ports/src/main.c
@@ -75,10 +75,14 @@ static void echo_serial_port(uint8_t itf, uint8_t buf[], uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
if (itf == 0) {
// echo back 1st port as lower case
- if (isupper(buf[i])) buf[i] += case_diff;
+ if (isupper(buf[i])) {
+ buf[i] += case_diff;
+ }
} else {
// echo back 2nd port as upper case
- if (islower(buf[i])) buf[i] -= case_diff;
+ if (islower(buf[i])) {
+ buf[i] -= case_diff;
+ }
}
tud_cdc_n_write_char(itf, buf[i]);
@@ -153,7 +157,9 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/cdc_dual_ports/src/tusb_config.h b/examples/device/cdc_dual_ports/src/tusb_config.h
index 7f7df3909..633a3faea 100644
--- a/examples/device/cdc_dual_ports/src/tusb_config.h
+++ b/examples/device/cdc_dual_ports/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -103,11 +103,13 @@
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-// CDC Endpoint transfer buffer size, more is faster
-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_dual_ports/src/usb_descriptors.c b/examples/device/cdc_dual_ports/src/usb_descriptors.c
index bbcb479f5..adfd8cf9d 100644
--- a/examples/device/cdc_dual_ports/src/usb_descriptors.c
+++ b/examples/device/cdc_dual_ports/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -106,16 +106,27 @@ enum {
#define EPNUM_CDC_1_OUT 0x05
#define EPNUM_CDC_1_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_0_NOTIF 0x81
- #define EPNUM_CDC_0_OUT 0x02
- #define EPNUM_CDC_0_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_0_NOTIF 0x81
+ #define EPNUM_CDC_0_OUT 0x08
+ #define EPNUM_CDC_0_IN 0x89
- #define EPNUM_CDC_1_NOTIF 0x84
- #define EPNUM_CDC_1_OUT 0x05
- #define EPNUM_CDC_1_IN 0x86
+ #define EPNUM_CDC_1_NOTIF 0x82
+ #define EPNUM_CDC_1_OUT 0x0A
+ #define EPNUM_CDC_1_IN 0x8B
+ #else
+ #define EPNUM_CDC_0_NOTIF 0x81
+ #define EPNUM_CDC_0_OUT 0x02
+ #define EPNUM_CDC_0_IN 0x83
+
+ #define EPNUM_CDC_1_NOTIF 0x84
+ #define EPNUM_CDC_1_OUT 0x05
+ #define EPNUM_CDC_1_IN 0x86
+ #endif
#else
#define EPNUM_CDC_0_NOTIF 0x81
@@ -127,7 +138,7 @@ enum {
#define EPNUM_CDC_1_IN 0x84
#endif
-uint8_t const desc_fs_configuration[] = {
+static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -140,7 +151,7 @@ uint8_t const desc_fs_configuration[] = {
#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -152,9 +163,9 @@ uint8_t const desc_hs_configuration[] = {
};
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_MISC,
@@ -213,7 +224,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] = {
+static char const *string_desc_arr[] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
diff --git a/examples/device/cdc_msc/CMakeLists.txt b/examples/device/cdc_msc/CMakeLists.txt
index b07f92342..293b497a7 100644
--- a/examples/device/cdc_msc/CMakeLists.txt
+++ b/examples/device/cdc_msc/CMakeLists.txt
@@ -1,15 +1,11 @@
cmake_minimum_required(VERSION 3.20)
-#set_property(GLOBAL PROPERTY USE_FOLDERS ON)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_msc C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
@@ -19,7 +15,7 @@ endif()
if (RTOS STREQUAL zephyr)
set(EXE_NAME app)
else()
- set(EXE_NAME ${PROJECT})
+ set(EXE_NAME ${PROJECT_NAME})
add_executable(${EXE_NAME})
endif()
diff --git a/examples/device/cdc_msc/Makefile b/examples/device/cdc_msc/Makefile
index 0c2e37180..de50d118f 100644
--- a/examples/device/cdc_msc/Makefile
+++ b/examples/device/cdc_msc/Makefile
@@ -1,8 +1,8 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
@@ -10,6 +10,6 @@ EXAMPLE_SOURCE += \
src/msc_disk.c \
src/usb_descriptors.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_msc/prj.conf b/examples/device/cdc_msc/prj.conf
index 2f5139d9d..9e86a118d 100644
--- a/examples/device/cdc_msc/prj.conf
+++ b/examples/device/cdc_msc/prj.conf
@@ -3,4 +3,3 @@ CONFIG_FPU=y
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_NRFX_POWER=y
-CONFIG_NRFX_UARTE0=y
diff --git a/examples/device/cdc_msc/src/main.c b/examples/device/cdc_msc/src/main.c
index 4e7aa989e..b00a0e3a9 100644
--- a/examples/device/cdc_msc/src/main.c
+++ b/examples/device/cdc_msc/src/main.c
@@ -37,11 +37,12 @@
*/
enum {
BLINK_NOT_MOUNTED = 250,
- BLINK_MOUNTED = 1000,
- BLINK_SUSPENDED = 2500,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+static bool blink_enable = true;
void led_blinking_task(void);
void cdc_task(void);
@@ -51,10 +52,7 @@ int main(void) {
board_init();
// init device stack on configured roothub port
- tusb_rhport_init_t dev_init = {
- .role = TUSB_ROLE_DEVICE,
- .speed = TUSB_SPEED_AUTO
- };
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
@@ -85,7 +83,7 @@ void tud_umount_cb(void) {
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en) {
- (void) remote_wakeup_en;
+ (void)remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
@@ -106,9 +104,9 @@ void cdc_task(void) {
// connected and there are data available
if (tud_cdc_available()) {
// read data
- char buf[64];
+ char buf[64];
uint32_t count = tud_cdc_read(buf, sizeof(buf));
- (void) count;
+ (void)count;
// Echo back
// Note: Skip echo by commenting out write() and write_flush()
@@ -119,11 +117,14 @@ void cdc_task(void) {
}
// Press on-board button to send Uart status notification
+ static cdc_notify_uart_state_t uart_state = {.value = 0};
+
static uint32_t btn_prev = 0;
- static cdc_notify_uart_state_t uart_state = { .value = 0 };
- const uint32_t btn = board_button_read();
- if (!btn_prev && btn) {
+ const uint32_t btn = board_button_read();
+
+ if ((btn_prev == 0u) && (btn != 0u)) {
uart_state.dsr ^= 1;
+ uart_state.dcd ^= 1;
tud_cdc_notify_uart_state(&uart_state);
}
btn_prev = btn;
@@ -132,33 +133,39 @@ void cdc_task(void) {
// Invoked when cdc when line state changed e.g connected/disconnected
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
- (void) itf;
- (void) rts;
+ (void)itf;
+ (void)rts;
- // TODO set some indicator
if (dtr) {
// Terminal connected
+ blink_enable = false;
+ board_led_write(true);
} else {
// Terminal disconnected
+ blink_enable = true;
}
}
// Invoked when CDC interface received data from host
void tud_cdc_rx_cb(uint8_t itf) {
- (void) itf;
+ (void)itf;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void) {
- static uint32_t start_ms = 0;
- static bool led_state = false;
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
- // Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
- start_ms += blink_interval_ms;
+ if (blink_enable) {
+ // Blink every interval ms
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
+ start_ms += blink_interval_ms;
- board_led_write(led_state);
- led_state = 1 - led_state; // toggle
+ board_led_write(led_state);
+ led_state = !led_state;
+ }
}
diff --git a/examples/device/cdc_msc/src/msc_disk.c b/examples/device/cdc_msc/src/msc_disk.c
index 96f9f19ec..017acd039 100644
--- a/examples/device/cdc_msc/src/msc_disk.c
+++ b/examples/device/cdc_msc/src/msc_disk.c
@@ -45,6 +45,7 @@ enum {
DISK_BLOCK_SIZE = 512
};
+static
#ifdef CFG_EXAMPLE_MSC_READONLY
const
#endif
@@ -57,63 +58,64 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
// drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
// filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
// FAT magic code at offset 510-511
-{
- 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
- 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U',
- 'S', 'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+ {
+ 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
+ 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U',
+ 'S', 'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
- // Zero up to 2 last bytes of FAT magic code
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // Zero up to 2 last bytes of FAT magic code
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA},
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
+ },
//------------- Block1: FAT12 Table -------------//
-{
- 0xF8, 0xFF, 0xFF, 0xFF, 0x0F// // first 2 entries must be F8FF, third entry is cluster end of readme file
+ {
+ 0xF8, 0xFF, 0xFF, 0xFF, 0x0F// first 2 entries must be F8FF, third entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
-{
- // first entry is volume label
- 'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // second entry is readme file
- 'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6, 0x52, 0x6D,
- 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
- sizeof(README_CONTENTS) - 1, 0x00, 0x00, 0x00// readme's files size (4 Bytes)
+ {
+ // first entry is volume label
+ 'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // second entry is readme file
+ 'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6, 0x52, 0x6D,
+ 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
+ sizeof(README_CONTENTS) - 1, 0x00, 0x00, 0x00// readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
- README_CONTENTS
+ {README_CONTENTS}
};
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
@@ -126,9 +128,9 @@ uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uin
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
- memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
- memcpy(inquiry_resp->product_id, pid, strlen(pid));
- memcpy(inquiry_resp->product_rev, rev, strlen(rev));
+ (void) strncpy((char*) inquiry_resp->vendor_id, vid, 8);
+ (void) strncpy((char*) inquiry_resp->product_id, pid, 16);
+ (void) strncpy((char*) inquiry_resp->product_rev, rev, 4);
return sizeof(scsi_inquiry_resp_t); // 36 bytes
}
@@ -141,8 +143,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) {
// RAM disk is ready until ejected
if (ejected) {
// Additional Sense 3A-00 is NOT_FOUND
- tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
- return false;
+ return tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
}
return true;
@@ -152,7 +153,6 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) {
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
(void) lun;
-
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
@@ -192,7 +192,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buff
}
uint8_t const *addr = msc_disk[lba] + offset;
- memcpy(buffer, addr, bufsize);
+ (void) memcpy(buffer, addr, bufsize);
return (int32_t) bufsize;
}
@@ -213,11 +213,13 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *
(void) lun;
// out of ramdisk
- if (lba >= DISK_BLOCK_NUM) return -1;
+ if (lba >= DISK_BLOCK_NUM) {
+ return -1;
+ }
#ifndef CFG_EXAMPLE_MSC_READONLY
uint8_t *addr = msc_disk[lba] + offset;
- memcpy(addr, buffer, bufsize);
+ (void) memcpy(addr, buffer, bufsize);
#else
(void) lba;
(void) offset;
@@ -231,17 +233,17 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
+ (void) lun;
+ (void) scsi_cmd;
(void) buffer;
(void) bufsize;
- switch (scsi_cmd[0]) {
- default:
- // Set Sense = Invalid Command Operation
- tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ // currently no other commands are supported
- // negative means error -> tinyusb could stall and/or response with failed status
- return -1;
- }
+ // Set Sense = Invalid Command Operation
+ (void) tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+ return -1; // stall/failed command request;
}
#endif
diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h
index 811d464e9..f0709d8fb 100644
--- a/examples/device/cdc_msc/src/tusb_config.h
+++ b/examples/device/cdc_msc/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -103,8 +103,10 @@
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-// CDC Endpoint transfer buffer size, more is faster
-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 512
@@ -113,4 +115,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c
index 597a6b1e6..5dc80dee3 100644
--- a/examples/device/cdc_msc/src/usb_descriptors.c
+++ b/examples/device/cdc_msc/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -102,15 +102,25 @@ enum {
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
- #define EPNUM_MSC_OUT 0x04
- #define EPNUM_MSC_IN 0x85
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
@@ -125,7 +135,7 @@ enum {
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
// full speed configuration
-uint8_t const desc_fs_configuration[] = {
+static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -140,7 +150,7 @@ uint8_t const desc_fs_configuration[] = {
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -152,10 +162,10 @@ uint8_t const desc_hs_configuration[] = {
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
@@ -223,7 +233,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] = {
+static char const *string_desc_arr[] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
diff --git a/examples/device/cdc_msc_freertos/CMakeLists.txt b/examples/device/cdc_msc_freertos/CMakeLists.txt
index f7636a07a..1eafd529a 100644
--- a/examples/device/cdc_msc_freertos/CMakeLists.txt
+++ b/examples/device/cdc_msc_freertos/CMakeLists.txt
@@ -2,33 +2,35 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
-project(${PROJECT} C CXX ASM)
+project(cdc_msc_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} freertos)
+family_configure_device_example(${PROJECT_NAME} freertos)
diff --git a/examples/device/cdc_msc_freertos/Makefile b/examples/device/cdc_msc_freertos/Makefile
index 10cff57a0..dbab13395 100644
--- a/examples/device/cdc_msc_freertos/Makefile
+++ b/examples/device/cdc_msc_freertos/Makefile
@@ -1,9 +1,9 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
@@ -11,6 +11,6 @@ EXAMPLE_SOURCE = \
src/msc_disk.c \
src/usb_descriptors.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_msc_freertos/skip.txt b/examples/device/cdc_msc_freertos/skip.txt
index b73a6d8dd..48781de84 100644
--- a/examples/device/cdc_msc_freertos/skip.txt
+++ b/examples/device/cdc_msc_freertos/skip.txt
@@ -1,17 +1,20 @@
+mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
+mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
-mcu:RP2040
+mcu:FT90X
mcu:SAMD11
-mcu:SAMX7X
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
mcu:STM32L0
family:broadcom_32bit
family:broadcom_64bit
+family:nuc121_125
+family:hpmicro
diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c
index 69f2435ba..4a920c90b 100644
--- a/examples/device/cdc_msc_freertos/src/main.c
+++ b/examples/device/cdc_msc_freertos/src/main.c
@@ -34,10 +34,10 @@
#define USBD_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled
- #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #define USBD_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
-#define CDC_STACK_SIZE configMINIMAL_STACK_SIZE
+#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
//--------------------------------------------------------------------+
@@ -88,7 +88,7 @@ int main(void) {
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
- xTaskCreate(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
+ xTaskCreate(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
#endif
#ifndef ESP_PLATFORM
diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c
index 849712e6a..ab551c288 100644
--- a/examples/device/cdc_msc_freertos/src/msc_disk.c
+++ b/examples/device/cdc_msc_freertos/src/msc_disk.c
@@ -76,6 +76,7 @@ enum {
DISK_BLOCK_SIZE = 512
};
+static
#ifdef CFG_EXAMPLE_MSC_READONLY
const
#endif
@@ -167,7 +168,7 @@ static void io_task(void *params) {
while (1) {
if (xQueueReceive(io_queue, &io_ops, portMAX_DELAY)) {
uint8_t* addr = (uint8_t*) (uintptr_t) (msc_disk[io_ops.lba] + io_ops.offset);
- int32_t nbytes = io_ops.bufsize;
+ int32_t nbytes = (int32_t) io_ops.bufsize;
if (io_ops.is_read) {
memcpy(io_ops.buffer, addr, io_ops.bufsize);
} else {
@@ -198,9 +199,9 @@ uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t* inquiry_resp, uin
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
- memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
- memcpy(inquiry_resp->product_id, pid, strlen(pid));
- memcpy(inquiry_resp->product_rev, rev, strlen(rev));
+ strncpy((char*) inquiry_resp->vendor_id, vid, 8);
+ strncpy((char*) inquiry_resp->product_id, pid, 16);
+ strncpy((char*) inquiry_resp->product_rev, rev, 4);
return sizeof(scsi_inquiry_resp_t); // 36 bytes
}
@@ -299,13 +300,11 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
return TUD_MSC_RET_ERROR;
}
- #ifdef CFG_EXAMPLE_MSC_READONLY
+#ifdef CFG_EXAMPLE_MSC_READONLY
(void) lun;
(void) buffer;
return bufsize;
- #endif
-
- #if CFG_EXAMPLE_MSC_ASYNC_IO
+#elif CFG_EXAMPLE_MSC_ASYNC_IO
io_ops_t io_ops = {.is_read = false, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize};
// Send IO operation to IO task
@@ -318,43 +317,24 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS);
return bufsize;
- #endif
+#endif
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
- // read10 & write10 has their own callback and MUST not be handled here
-
- void const *response = NULL;
- int32_t resplen = 0;
-
- // most scsi handled is input
- bool in_xfer = true;
-
- switch (scsi_cmd[0]) {
- default:
- // Set Sense = Invalid Command Operation
- tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
-
- // negative means error -> tinyusb could stall and/or response with failed status
- resplen = -1;
- break;
- }
+ (void) lun;
+ (void) scsi_cmd;
+ (void) buffer;
+ (void) bufsize;
- // return resplen must not larger than bufsize
- if (resplen > bufsize) { resplen = bufsize; }
+ // currently no other commands are supported
- if (response && (resplen > 0)) {
- if (in_xfer) {
- memcpy(buffer, response, (size_t) resplen);
- } else {
- // SCSI output
- }
- }
+ // Set Sense = Invalid Command Operation
+ (void) tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
- return (int32_t) resplen;
+ return -1; // stall/failed command request;
}
#endif
diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h
index 9cc3a18d1..a78f2ea05 100644
--- a/examples/device/cdc_msc_freertos/src/tusb_config.h
+++ b/examples/device/cdc_msc_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -110,8 +110,10 @@
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-// CDC Endpoint transfer buffer size, more is faster
-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 512
@@ -120,4 +122,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_msc_freertos/src/usb_descriptors.c b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
index cb440c209..f5b015051 100644
--- a/examples/device/cdc_msc_freertos/src/usb_descriptors.c
+++ b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -102,15 +102,25 @@ enum {
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
- #define EPNUM_MSC_OUT 0x04
- #define EPNUM_MSC_IN 0x85
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
@@ -124,7 +134,7 @@ enum {
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
-uint8_t const desc_fs_configuration[] =
+static uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -140,7 +150,7 @@ uint8_t const desc_fs_configuration[] =
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
-uint8_t const desc_hs_configuration[] =
+static uint8_t const desc_hs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -153,10 +163,10 @@ uint8_t const desc_hs_configuration[] =
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier =
+static tusb_desc_device_qualifier_t const desc_device_qualifier =
{
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
@@ -225,7 +235,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] = {
+static char const *string_desc_arr[] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
diff --git a/examples/device/cdc_msc_throughput/CMakeLists.txt b/examples/device/cdc_msc_throughput/CMakeLists.txt
new file mode 100644
index 000000000..69c1caa6a
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(cdc_msc_throughput C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+if (RTOS STREQUAL zephyr)
+ set(EXE_NAME app)
+else()
+ set(EXE_NAME ${PROJECT_NAME})
+ add_executable(${EXE_NAME})
+endif()
+
+# Example source
+target_sources(${EXE_NAME} PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${EXE_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${EXE_NAME} ${RTOS})
diff --git a/examples/device/cdc_msc_throughput/CMakePresets.json b/examples/device/cdc_msc_throughput/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/device/cdc_msc_throughput/Makefile b/examples/device/cdc_msc_throughput/Makefile
new file mode 100644
index 000000000..035e90308
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/Makefile
@@ -0,0 +1,11 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_msc_throughput/src/main.c b/examples/device/cdc_msc_throughput/src/main.c
new file mode 100644
index 000000000..116cbe13f
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/main.c
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+// cdc_msc_throughput: minimal CDC+MSC device aimed at measuring pure USB bulk throughput.
+// MSC read/write callbacks don't touch any backing storage - write discards the
+// data and read only zero-fills the low LBAs the host scans during enumeration
+// (partition table, GPT header). Higher LBAs return whatever is already in the
+// transfer buffer, so `dd` numbers reflect the USB/driver ceiling, not any
+// simulated storage or per-byte memset cost.
+// CDC path drains RX in tud_cdc_rx_cb and sources TX from a static filler in the
+// main loop so `dd` can target /dev/ttyACMx in either direction.
+
+static void cdc_throughput_task(void);
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+int main(void) {
+ board_init();
+
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+
+ board_init_after_tusb();
+
+ while (1) {
+ tud_task();
+ cdc_throughput_task();
+ }
+}
+
+//--------------------------------------------------------------------+
+// CDC callbacks + tasks
+//--------------------------------------------------------------------+
+void tud_cdc_rx_cb(uint8_t itf) {
+ (void) itf;
+ tud_cdc_read_flush(); // Drain RX
+}
+
+static void cdc_throughput_task(void) {
+ if (!tud_cdc_connected()) return;
+
+ // Source TX: fill whatever write room is free.
+ static uint8_t const filler[CFG_TUD_CDC_TX_EPSIZE] = {0};
+ uint32_t room = tud_cdc_write_available();
+ while (room > 0) {
+ uint32_t n = tud_cdc_write(filler, tu_min32(room, sizeof(filler)));
+ if (n == 0) {
+ break;
+ }
+ room -= n;
+ }
+ tud_cdc_write_flush();
+}
+
+//--------------------------------------------------------------------+
+// MSC callbacks
+//--------------------------------------------------------------------+
+
+// 1 GiB logical capacity so `dd` can run long enough for stable numbers.
+// No real backing store - block content is synthesised on read, discarded on write.
+enum {
+ DISK_BLOCK_SIZE = 512,
+ DISK_BLOCK_COUNT = 0x00200000u, // 2 Mi blocks = 1 GiB
+ // Kernel probes partition-table / filesystem-superblock locations near the
+ // start of the disk during enumeration. Zero-fill only this head range so the
+ // block layer sees "no partition, no filesystem" and leaves us alone; higher
+ // LBAs skip the memset so `dd` measures pure USB/driver throughput.
+ DISK_ZEROFILL_LBA = 64, // 32 KiB
+};
+
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
+ (void) lun;
+ const char vid[] = "TinyUSB";
+ const char pid[] = "Mass Storage";
+ const char rev[] = "1.0";
+ (void) strncpy((char*) vendor_id, vid, 8);
+ (void) strncpy((char*) product_id, pid, 16);
+ (void) strncpy((char*) product_rev, rev, 4);
+}
+
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
+ (void) lun;
+ return true;
+}
+
+void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
+ (void) lun;
+ *block_count = DISK_BLOCK_COUNT;
+ *block_size = DISK_BLOCK_SIZE;
+}
+
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
+ (void) lun; (void) power_condition; (void) start; (void) load_eject;
+ return true;
+}
+
+bool tud_msc_is_writable_cb(uint8_t lun) {
+ (void) lun;
+ return true;
+}
+
+// READ10: zero-fill only the head range the kernel inspects, skip memset everywhere
+// else so we measure the USB / driver path rather than memset cost.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
+ (void) lun; (void) offset;
+ if (lba < DISK_ZEROFILL_LBA) {
+ memset(buffer, 0, bufsize);
+ } else {
+ (void) buffer;
+ }
+ return (int32_t) bufsize;
+}
+
+// WRITE10: discard the received data entirely - this is the pure USB-speed test.
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
+ (void) lun; (void) lba; (void) offset; (void) buffer;
+ return (int32_t) bufsize;
+}
+
+// Unknown SCSI commands: stall with Invalid Command sense.
+int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
+ (void) scsi_cmd; (void) buffer; (void) bufsize;
+ tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ return -1;
+}
diff --git a/examples/device/cdc_msc_throughput/src/tusb_config.h b/examples/device/cdc_msc_throughput/src/tusb_config.h
new file mode 100644
index 000000000..6c8655719
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/tusb_config.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// Board Specific Configuration
+//--------------------------------------------------------------------
+
+#ifndef BOARD_TUD_RHPORT
+ #define BOARD_TUD_RHPORT 0
+#endif
+
+#ifndef BOARD_TUD_MAX_SPEED
+ #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUSB_MCU
+ #error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+ #define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+ #define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+#ifndef CFG_TUSB_MEM_SECTION
+ #define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+ #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+ #define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC 1
+#define CFG_TUD_MSC 1
+
+// Large MSC bulk buffer: host transfers big CBW payloads (e.g. dd bs=1M does 64KiB
+// chunks). A 4K per-bulk-IO buffer lets the class driver amortise the per-CBW
+// overhead across many USB packets, approximating the maximum USB bulk throughput.
+#define CFG_TUD_MSC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 4096 : 1024)
+
+// #define CFG_TUD_CDC_TX_PERSISTENT 1
+
+// CDC throughput: size for HS; tinyusb will auto-scale for FS via TUD_OPT_HIGH_SPEED.
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 2*512 : 2*64)
+#define CFG_TUD_CDC_TX_EPSIZE CFG_TUD_CDC_RX_EPSIZE
+
+#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 2*512 : 2*64)
+#define CFG_TUD_CDC_TX_BUFSIZE CFG_TUD_CDC_RX_BUFSIZE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_msc_throughput/src/usb_descriptors.c b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
new file mode 100644
index 000000000..3b0ff6e17
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#define USB_PID (0x4000 | ((CFG_TUD_CDC) ? (1 << 0) : 0) | ((CFG_TUD_MSC) ? (1 << 1) : 0))
+#define USB_VID 0xCafe
+#define USB_BCD 0x0200
+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+
+ // IAD required for composite CDC + MSC
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01,
+};
+
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
+}
+
+enum {
+ ITF_NUM_CDC = 0,
+ ITF_NUM_CDC_DATA,
+ ITF_NUM_MSC,
+ ITF_NUM_TOTAL,
+};
+
+// Place bulk endpoints on EP>=8 for MAX32690 class parts (bigger FIFO, DPB-capable).
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
+#else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+ #define EPNUM_MSC_OUT 0x03
+ #define EPNUM_MSC_IN 0x83
+#endif
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
+
+static uint8_t const desc_fs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+ TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
+};
+
+#if TUD_OPT_HIGH_SPEED
+static uint8_t const desc_hs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+ TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
+};
+
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = USB_BCD,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00,
+};
+
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;
+ memcpy(desc_other_speed_config,
+ (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+ CONFIG_TOTAL_LEN);
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+ return desc_other_speed_config;
+}
+#endif
+
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index;
+#if TUD_OPT_HIGH_SPEED
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
+ return desc_fs_configuration;
+#endif
+}
+
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+};
+
+static char const *string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 },
+ "TinyUSB",
+ "Throughput",
+ NULL,
+ "TinyUSB CDC",
+ "TinyUSB MSC",
+};
+
+static uint16_t _desc_str[32 + 1];
+
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch (index) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL;
+ const char *str = string_desc_arr[index];
+ chr_count = strlen(str);
+ size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1;
+ if (chr_count > max_count) chr_count = max_count;
+ for (size_t i = 0; i < chr_count; i++) _desc_str[1 + i] = str[i];
+ break;
+ }
+
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ return _desc_str;
+}
diff --git a/examples/device/cdc_uac2/CMakeLists.txt b/examples/device/cdc_uac2/CMakeLists.txt
index c8c797637..fb14dc184 100644
--- a/examples/device/cdc_uac2/CMakeLists.txt
+++ b/examples/device/cdc_uac2/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_uac2 C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/uac2_app.c
@@ -26,13 +23,13 @@ target_sources(${PROJECT} PUBLIC
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example... see the corresponding function
# in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
# Uncomment me to enable UART based debugging
-# pico_enable_stdio_uart(${PROJECT} 1)
+# pico_enable_stdio_uart(${PROJECT_NAME} 1)
diff --git a/examples/device/cdc_uac2/Makefile b/examples/device/cdc_uac2/Makefile
index 21dcdb0b2..6276be8d0 100644
--- a/examples/device/cdc_uac2/Makefile
+++ b/examples/device/cdc_uac2/Makefile
@@ -1,8 +1,8 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
@@ -11,6 +11,6 @@ EXAMPLE_SOURCE += \
src/uac2_app.c \
src/usb_descriptors.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_uac2/skip.txt b/examples/device/cdc_uac2/skip.txt
index a2a76af0e..db1d5b80b 100644
--- a/examples/device/cdc_uac2/skip.txt
+++ b/examples/device/cdc_uac2/skip.txt
@@ -6,3 +6,4 @@ mcu:SAME5X
mcu:SAMG
board:stm32l052dap52
family:espressif
+mcu:CH583
diff --git a/examples/device/cdc_uac2/src/cdc_app.c b/examples/device/cdc_uac2/src/cdc_app.c
index 2166c1d6b..6d18a0e69 100644
--- a/examples/device/cdc_uac2/src/cdc_app.c
+++ b/examples/device/cdc_uac2/src/cdc_app.c
@@ -29,33 +29,26 @@
#include "common.h"
// Invoked when cdc when line state changed e.g connected/disconnected
-void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
-{
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
(void) itf;
(void) rts;
- if (dtr)
- {
+ if (dtr) {
// Terminal connected
- }
- else
- {
+ } else {
// Terminal disconnected
}
}
// Invoked when CDC interface received data from host
-void tud_cdc_rx_cb(uint8_t itf)
-{
+void tud_cdc_rx_cb(uint8_t itf) {
uint8_t buf[64];
uint32_t count;
// connected() check for DTR bit
// Most but not all terminal client set this when making connection
- if (tud_cdc_connected())
- {
- if (tud_cdc_available()) // data is available
- {
+ if (tud_cdc_connected()) {
+ if (tud_cdc_available() > 0) {
count = tud_cdc_n_read(itf, buf, sizeof(buf));
(void) count;
diff --git a/examples/device/cdc_uac2/src/common.h b/examples/device/cdc_uac2/src/common.h
index f281024c7..ff8b7a953 100644
--- a/examples/device/cdc_uac2/src/common.h
+++ b/examples/device/cdc_uac2/src/common.h
@@ -31,4 +31,7 @@ enum
VOLUME_CTRL_SILENCE = 0x8000,
};
+void led_blinking_task(void);
+void audio_task(void);
+
#endif
diff --git a/examples/device/cdc_uac2/src/main.c b/examples/device/cdc_uac2/src/main.c
index bc87f6e3c..cb7b3a142 100644
--- a/examples/device/cdc_uac2/src/main.c
+++ b/examples/device/cdc_uac2/src/main.c
@@ -38,9 +38,6 @@ extern uint32_t blink_interval_ms;
#include "pico/stdlib.h"
#endif
-void led_blinking_task(void);
-void audio_task(void);
-
/*------------- MAIN -------------*/
int main(void)
{
@@ -68,8 +65,6 @@ int main(void)
// printf("Hello, world!\r\n");
#endif
}
-
- return 0;
}
//--------------------------------------------------------------------+
diff --git a/examples/device/cdc_uac2/src/tusb_config.h b/examples/device/cdc_uac2/src/tusb_config.h
index 2e744f8d2..358ff6747 100644
--- a/examples/device/cdc_uac2/src/tusb_config.h
+++ b/examples/device/cdc_uac2/src/tusb_config.h
@@ -24,8 +24,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -105,8 +105,6 @@ extern "C" {
// AUDIO CLASS DRIVER CONFIGURATION
//--------------------------------------------------------------------
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN
-
// How many formats are used, need to adjust USB descriptor if changed
#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2
@@ -142,8 +140,8 @@ extern "C" {
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
@@ -151,24 +149,23 @@ extern "C" {
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
+#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
+#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
-// Size of control request buffer
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
-
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-// CDC Endpoint transfer buffer size, more is faster
-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c
index a1a0dd73d..a504c3b57 100644
--- a/examples/device/cdc_uac2/src/uac2_app.c
+++ b/examples/device/cdc_uac2/src/uac2_app.c
@@ -66,8 +66,10 @@ uint8_t current_resolution;
// In a real application, this would be replaced with actual I2S send/receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
- if (start_ms == curr_ms) return;// not enough time
+ uint32_t curr_ms = tusb_time_millis_api();
+ if (start_ms == curr_ms) {
+ return; // not enough time
+ }
start_ms = curr_ms;
// When new data arrived, copy data from speaker buffer, to microphone buffer
// and send it over
@@ -80,22 +82,22 @@ void audio_task(void) {
}
// Helper for clock get requests
-static bool tud_audio_clock_get_request(uint8_t rhport, audio_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 == AUDIO_CS_CTRL_SAM_FREQ)
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ)
{
- if (request->bRequest == AUDIO_CS_REQ_CUR)
+ if (p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
- audio_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));
+ audio20_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf));
}
- else if (request->bRequest == AUDIO_CS_REQ_RANGE)
+ else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE)
{
- audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
+ audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)
};
@@ -108,34 +110,35 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_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 == AUDIO_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO_CS_REQ_CUR)
+ else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID &&
+ p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
- audio_control_cur_1_t cur_valid = { .bCur = 1 };
+ 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, audio_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 == AUDIO_CS_REQ_CUR);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ)
{
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
- current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
+ current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *)buf)->bCur;
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
@@ -143,80 +146,83 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
}
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, audio_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 == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR)
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
- audio_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 == AUDIO_FU_CTRL_VOLUME)
+ else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME)
{
- if (request->bRequest == AUDIO_CS_REQ_RANGE)
+ if (p_request->bRequest == AUDIO20_CS_REQ_RANGE)
{
- audio_control_range_2_n_t(1) range_vol = {
+ 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 == AUDIO_CS_REQ_CUR)
+ else if (p_request->bRequest == AUDIO20_CS_REQ_CUR)
{
- audio_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, audio_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 == AUDIO_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 == AUDIO_FU_CTRL_MUTE)
+ if (ctrl_sel == AUDIO20_FU_CTRL_MUTE)
{
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t));
- mute[request->bChannelNumber] = ((audio_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 == AUDIO_FU_CTRL_VOLUME)
+ else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME)
{
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t));
- volume[request->bChannelNumber] = ((audio_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;
}
}
@@ -226,33 +232,33 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
//--------------------------------------------------------------------+
// 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)
-{
- audio_control_request_t const *request = (audio_control_request_t const *)p_request;
+bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_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 (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return tud_audio_feature_unit_get_request(rhport, request);
- else
- {
+ if (entity_id == UAC2_ENTITY_CLOCK) {
+ return tud_audio_clock_get_request(rhport, p_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)
-{
- audio_control_request_t const *request = (audio_control_request_t const *)p_request;
+bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
+ 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 (request->bEntityID == UAC2_ENTITY_CLOCK)
- return tud_audio_clock_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 (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;
}
@@ -261,8 +267,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const
{
(void)rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
// Audio streaming stop
@@ -275,8 +281,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
@@ -301,7 +307,9 @@ void led_blinking_task(void)
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return;
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return;
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c
index da55bdb5a..fdffc761e 100644
--- a/examples/device/cdc_uac2/src/usb_descriptors.c
+++ b/examples/device/cdc_uac2/src/usb_descriptors.c
@@ -35,14 +35,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -97,15 +97,25 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_IN 0x01
- #define EPNUM_AUDIO_OUT 0x02
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put CDC bulk on EP>=8 and audio iso on EP10/11 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO_OUT 0x0A
+ #define EPNUM_AUDIO_IN 0x0B
- #define EPNUM_CDC_NOTIF 0x83
- #define EPNUM_CDC_OUT 0x04
- #define EPNUM_CDC_IN 0x85
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #else
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x04
+ #define EPNUM_CDC_IN 0x85
+ #endif
#else
#define EPNUM_AUDIO_IN 0x01
@@ -116,7 +126,7 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_CDC_IN 0x84
#endif
-uint8_t const desc_fs_configuration[] =
+static uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -132,7 +142,7 @@ uint8_t const desc_fs_configuration[] =
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -144,10 +154,10 @@ uint8_t const desc_hs_configuration[] = {
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0100,
@@ -215,7 +225,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
@@ -248,14 +258,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
diff --git a/examples/device/cdc_uac2/src/usb_descriptors.h b/examples/device/cdc_uac2/src/usb_descriptors.h
index 736feeefe..139384d3e 100644
--- a/examples/device/cdc_uac2/src/usb_descriptors.h
+++ b/examples/device/cdc_uac2/src/usb_descriptors.h
@@ -24,8 +24,8 @@
*
*/
-#ifndef _USB_DESCRIPTORS_H_
-#define _USB_DESCRIPTORS_H_
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
// #include "tusb.h"
@@ -49,110 +49,110 @@ enum
ITF_NUM_TOTAL
};
-#define TUD_AUDIO_HEADSET_STEREO_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
- + TUD_AUDIO_DESC_STD_AC_LEN\
- + TUD_AUDIO_DESC_CS_AC_LEN\
- + TUD_AUDIO_DESC_CLK_SRC_LEN\
- + TUD_AUDIO_DESC_INPUT_TERM_LEN\
- + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN\
- + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
- + TUD_AUDIO_DESC_INPUT_TERM_LEN\
- + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
+#define TUD_AUDIO_HEADSET_STEREO_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
/* Interface 1, Alternate 0 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
/* Interface 1, Alternate 0 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 1, Alternate 2 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 2, Alternate 0 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
/* Interface 2, Alternate 1 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 2, Alternate 2 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN)
#define TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(_stridx, _epout, _epin) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitfs*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ 3, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_IAD(/*_firstitfs*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ 3, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
- TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
- TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
/* Clock Source Descriptor(4.7.2.1) */\
- TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \
/* Input Terminal Descriptor(4.7.2.4) */\
- TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
- TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS)),\
/* Output Terminal Descriptor(4.7.2.5) */\
- TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Input Terminal Descriptor(4.7.2.4) */\
- TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Output Terminal Descriptor(4.7.2.5) */\
- TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x05),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x05),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
/* Interface 1, Alternate 2 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x04),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x04),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 2, Alternate 1 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
/* Interface 2, Alternate 2 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
#endif
diff --git a/examples/device/dfu/CMakeLists.txt b/examples/device/dfu/CMakeLists.txt
index 3da8ee3df..61d169460 100644
--- a/examples/device/dfu/CMakeLists.txt
+++ b/examples/device/dfu/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(dfu C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/dfu/Makefile b/examples/device/dfu/Makefile
index 52a24cdb0..9e1eab4a2 100644
--- a/examples/device/dfu/Makefile
+++ b/examples/device/dfu/Makefile
@@ -1,14 +1,14 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/dfu/skip.txt b/examples/device/dfu/skip.txt
index 9dde06c30..ccff857ac 100644
--- a/examples/device/dfu/skip.txt
+++ b/examples/device/dfu/skip.txt
@@ -1,3 +1,2 @@
-mcu:TM4C123
mcu:BCM2835
family:espressif
diff --git a/examples/device/dfu/src/main.c b/examples/device/dfu/src/main.c
index c0c848837..fb3c22630 100644
--- a/examples/device/dfu/src/main.c
+++ b/examples/device/dfu/src/main.c
@@ -23,7 +23,7 @@
*
*/
- /*
+/*
* After device is enumerated in dfu mode run the following commands
*
* To transfer firmware from host to device (best to test with text file)
@@ -48,21 +48,18 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
-const char* upload_image[2]=
-{
- "Hello world from TinyUSB DFU! - Partition 0",
- "Hello world from TinyUSB DFU! - Partition 1"
-};
+const char *upload_image[2] = {"Hello world from TinyUSB DFU! - Partition 0",
+ "Hello world from TinyUSB DFU! - Partition 1"};
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
-enum {
+enum {
BLINK_NOT_MOUNTED = 250,
- BLINK_MOUNTED = 1000,
- BLINK_SUSPENDED = 2500,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
@@ -70,21 +67,16 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
void led_blinking_task(void);
/*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
board_init();
// init device stack on configured roothub port
- tusb_rhport_init_t dev_init = {
- .role = TUSB_ROLE_DEVICE,
- .speed = TUSB_SPEED_AUTO
- };
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
- while (1)
- {
+ while (1) {
tud_task(); // tinyusb device task
led_blinking_task();
}
@@ -95,29 +87,25 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
- (void) remote_wakeup_en;
+void tud_suspend_cb(bool remote_wakeup_en) {
+ (void)remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -129,19 +117,17 @@ void tud_resume_cb(void)
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
// During this period, USB host won't try to communicate with us.
-uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
-{
- if ( state == DFU_DNBUSY )
- {
+uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) {
+ if (state == DFU_DNBUSY) {
// For this example
// - Atl0 Flash is fast : 1 ms
// - Alt1 EEPROM is slow: 100 ms
return (alt == 0) ? 1 : 100;
- }
- else if (state == DFU_MANIFEST)
- {
+ } else if (state == DFU_MANIFEST) {
// since we don't buffer entire image and do any flashing in manifest stage
return 0;
+ } else {
+ // nothing to do
}
return 0;
@@ -150,15 +136,13 @@ uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
// This callback could be returned before flashing op is complete (async).
// Once finished flashing, application must call tud_dfu_finish_flashing()
-void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, uint16_t length)
-{
- (void) alt;
- (void) block_num;
+void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, const uint8_t *data, uint16_t length) {
+ (void)alt;
+ (void)block_num;
//printf("\r\nReceived Alt %u BlockNum %u of length %u\r\n", alt, wBlockNum, length);
- for(uint16_t i=0; i<length; i++)
- {
+ for (uint16_t i = 0; i < length; i++) {
printf("%c", data[i]);
}
@@ -169,9 +153,8 @@ void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, u
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
// Application can do checksum, or actual flashing if buffered entire image previously.
// Once finished flashing, application must call tud_dfu_finish_flashing()
-void tud_dfu_manifest_cb(uint8_t alt)
-{
- (void) alt;
+void tud_dfu_manifest_cb(uint8_t alt) {
+ (void)alt;
printf("Download completed, enter manifestation\r\n");
// flashing op for manifest is complete without error
@@ -182,27 +165,28 @@ void tud_dfu_manifest_cb(uint8_t alt)
// Invoked when received DFU_UPLOAD request
// Application must populate data with up to length bytes and
// Return the number of written bytes
-uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length)
-{
- (void) block_num;
- (void) length;
+uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t *data, uint16_t length) {
+ (void)block_num;
+ (void)length;
- uint16_t const xfer_len = (uint16_t) strlen(upload_image[alt]);
+ if (block_num != 0u) {
+ return 0; // for this example we only support single block upload
+ }
+
+ const uint16_t xfer_len = tu_min16((uint16_t)strlen(upload_image[alt]), length);
memcpy(data, upload_image[alt], xfer_len);
return xfer_len;
}
// Invoked when the Host has terminated a download or upload transfer
-void tud_dfu_abort_cb(uint8_t alt)
-{
- (void) alt;
+void tud_dfu_abort_cb(uint8_t alt) {
+ (void)alt;
printf("Host aborted transfer\r\n");
}
// Invoked when a DFU_DETACH request is received
-void tud_dfu_detach_cb(void)
-{
+void tud_dfu_detach_cb(void) {
printf("Host detach, we should probably reboot\r\n");
}
@@ -210,13 +194,14 @@ void tud_dfu_detach_cb(void)
// BLINKING TASK + Indicator pulse
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
- static uint32_t start_ms = 0;
- static bool led_state = false;
+void led_blinking_task(void) {
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/dfu/src/usb_descriptors.c b/examples/device/dfu/src/usb_descriptors.c
index fd469aaf2..e4291be2d 100644
--- a/examples/device/dfu/src/usb_descriptors.c
+++ b/examples/device/dfu/src/usb_descriptors.c
@@ -25,7 +25,6 @@
#include "bsp/board_api.h"
#include "tusb.h"
-#include "class/dfu/dfu_device.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
@@ -33,49 +32,45 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0200,
+static const tusb_desc_device_t desc_device =
+ {.bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x0201,
- #if CFG_TUD_CDC
- // Use Interface Association Descriptor (IAD) for CDC
- // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
- .bDeviceClass = TUSB_CLASS_MISC,
- .bDeviceSubClass = MISC_SUBCLASS_COMMON,
- .bDeviceProtocol = MISC_PROTOCOL_IAD,
- #else
- .bDeviceClass = 0x00,
- .bDeviceSubClass = 0x00,
- .bDeviceProtocol = 0x00,
- #endif
+#if CFG_TUD_CDC
+ // Use Interface Association Descriptor (IAD) for CDC
+ // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+#else
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+#endif
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .idVendor = 0xCafe,
- .idProduct = USB_PID,
- .bcdDevice = 0x0100,
+ .idVendor = 0xCafe,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
- .iManufacturer = 0x01,
- .iProduct = 0x02,
- .iSerialNumber = 0x03,
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
- .bNumConfigurations = 0x01
-};
+ .bNumConfigurations = 0x01};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
- return (uint8_t const *) &desc_device;
+const uint8_t *tud_descriptor_device_cb(void) {
+ return (const uint8_t *)&desc_device;
}
//--------------------------------------------------------------------+
@@ -83,20 +78,17 @@ uint8_t const * tud_descriptor_device_cb(void)
//--------------------------------------------------------------------+
// Number of Alternate Interface (each for 1 flash partition)
-#define ALT_COUNT 2
+#define ALT_COUNT 2
-enum
-{
+enum {
ITF_NUM_DFU_MODE,
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_DESC_LEN(ALT_COUNT))
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_DESC_LEN(ALT_COUNT))
+#define FUNC_ATTRS (DFU_ATTR_CAN_UPLOAD | DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
-#define FUNC_ATTRS (DFU_ATTR_CAN_UPLOAD | DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
-
-uint8_t const desc_configuration[] =
-{
+uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -107,13 +99,91 @@ uint8_t const desc_configuration[] =
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
- (void) index; // for multiple configurations
+const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
+ (void)index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
+// BOS Descriptor
+//--------------------------------------------------------------------+
+
+/* Microsoft OS 2.0 registry property descriptor
+ Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
+ device should create DeviceInterfaceGUIDs. It can be done by driver and
+ in case of real PnP solution device should expose MS "Microsoft OS 2.0
+ registry property descriptor". Such descriptor can insert any record
+ into Windows registry per device/configuration/interface. In our case it
+ will insert "DeviceInterfaceGUIDs" multistring property.
+ GUID is freshly generated and should be OK to use.
+ https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
+ (Section Microsoft OS compatibility descriptors)
+*/
+
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+#define MS_OS_20_DESC_LEN 0xA2
+#define VENDOR_REQUEST_MICROSOFT 1
+
+// BOS Descriptor is required for webUSB
+const uint8_t desc_bos[] = {
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
+
+ // Microsoft OS 2.0 descriptor
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)};
+
+const uint8_t *tud_descriptor_bos_cb(void) {
+ return desc_bos;
+}
+
+const uint8_t desc_ms_os_20[] = {
+ // Set header: length, type, windows version, total length
+ U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000),
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
+
+ // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
+ U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
+
+ // MS OS 2.0 Registry property descriptor: length, type
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
+ U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A),// wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
+ 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
+ U16_TO_U8S_LE(0x0050),// wPropertyDataLength
+ //bPropertyData: {3E7E0711-DF3B-4158-A32F-E5951B2AB9A1}.
+ '{', 0x00, '3', 0x00, 'E', 0x00, '7', 0x00, 'E', 0x00, '0', 0x00, '7', 0x00, '1', 0x00, '1', 0x00, '-', 0x00,
+ 'D', 0x00, 'F', 0x00, '3', 0x00, 'B', 0x00, '-', 0x00, '4', 0x00, '1', 0x00, '5', 0x00, '8', 0x00, '-', 0x00,
+ 'A', 0x00, '3', 0x00, '2', 0x00, 'F', 0x00, '-', 0x00, 'E', 0x00, '5', 0x00, '9', 0x00, '5', 0x00, '1', 0x00,
+ 'B', 0x00, '2', 0x00, 'A', 0x00, 'B', 0x00, '9', 0x00, 'A', 0x00, '1', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) {
+ // nothing to with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
+
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {
+ if (request->bRequest == VENDOR_REQUEST_MICROSOFT) {
+ if (request->wIndex == 7) {
+ return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, MS_OS_20_DESC_LEN);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ // stall unknown request
+ return false;
+}
+
+//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
@@ -126,25 +196,24 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
-{
- (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
- "TinyUSB", // 1: Manufacturer
- "TinyUSB Device", // 2: Product
- NULL, // 3: Serials will use unique ID if possible
- "FLASH", // 4: DFU Partition 1
- "EEPROM", // 5: DFU Partition 2
+static const char *string_desc_arr[] = {
+ (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB Device", // 2: Product
+ NULL, // 3: Serials will use unique ID if possible
+ "FLASH", // 4: DFU Partition 1
+ "EEPROM", // 5: DFU Partition 2
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
- (void) langid;
+const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void)langid;
size_t chr_count;
- switch ( index ) {
+ switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
@@ -158,24 +227,28 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
- chr_count = strlen(str);
- size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ chr_count = strlen(str);
+ const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
- for ( size_t i = 0; i < chr_count; i++ ) {
+ for (size_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
- _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
diff --git a/examples/device/dfu_runtime/CMakeLists.txt b/examples/device/dfu_runtime/CMakeLists.txt
index 3da8ee3df..8f6df9f75 100644
--- a/examples/device/dfu_runtime/CMakeLists.txt
+++ b/examples/device/dfu_runtime/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(dfu_runtime C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/dfu_runtime/Makefile b/examples/device/dfu_runtime/Makefile
index 1b4d398cf..1a4b428dc 100644
--- a/examples/device/dfu_runtime/Makefile
+++ b/examples/device/dfu_runtime/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/dfu_runtime/skip.txt b/examples/device/dfu_runtime/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/dfu_runtime/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/dfu_runtime/src/main.c b/examples/device/dfu_runtime/src/main.c
index 37cb80093..6f412e8de 100644
--- a/examples/device/dfu_runtime/src/main.c
+++ b/examples/device/dfu_runtime/src/main.c
@@ -132,7 +132,9 @@ void led_blinking_task(void)
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/dfu_runtime/src/usb_descriptors.c b/examples/device/dfu_runtime/src/usb_descriptors.c
index 7ac53d255..8fa078da2 100644
--- a/examples/device/dfu_runtime/src/usb_descriptors.c
+++ b/examples/device/dfu_runtime/src/usb_descriptors.c
@@ -25,7 +25,6 @@
#include "bsp/board_api.h"
#include "tusb.h"
-#include "class/dfu/dfu_rt_device.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
@@ -33,48 +32,45 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0200,
+static const tusb_desc_device_t desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x0201,
- #if CFG_TUD_CDC
- // Use Interface Association Descriptor (IAD) for CDC
- // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
- .bDeviceClass = TUSB_CLASS_MISC,
- .bDeviceSubClass = MISC_SUBCLASS_COMMON,
- .bDeviceProtocol = MISC_PROTOCOL_IAD,
- #else
- .bDeviceClass = 0x00,
- .bDeviceSubClass = 0x00,
- .bDeviceProtocol = 0x00,
- #endif
+#if CFG_TUD_CDC
+ // Use Interface Association Descriptor (IAD) for CDC
+ // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+#else
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+#endif
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .idVendor = 0xCafe,
- .idProduct = USB_PID,
- .bcdDevice = 0x0100,
+ .idVendor = 0xCafe,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
- .iManufacturer = 0x01,
- .iProduct = 0x02,
- .iSerialNumber = 0x03,
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
- .bNumConfigurations = 0x01
+ .bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
+uint8_t const *tud_descriptor_device_cb(void) {
return (uint8_t const *) &desc_device;
}
@@ -82,16 +78,14 @@ uint8_t const * tud_descriptor_device_cb(void)
// Configuration Descriptor
//--------------------------------------------------------------------+
-enum
-{
+enum {
ITF_NUM_DFU_RT,
ITF_NUM_TOTAL
};
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_RT_DESC_LEN)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_DFU_RT_DESC_LEN)
-uint8_t const desc_configuration[] =
-{
+uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -103,13 +97,109 @@ uint8_t const desc_configuration[] =
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
- (void) index; // for multiple configurations
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index;// for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
+// BOS Descriptor
+//--------------------------------------------------------------------+
+
+/* Microsoft OS 2.0 registry property descriptor
+ Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
+ device should create DeviceInterfaceGUIDs. It can be done by driver and
+ in case of real PnP solution device should expose MS "Microsoft OS 2.0
+ registry property descriptor". Such descriptor can insert any record
+ into Windows registry per device/configuration/interface. In our case it
+ will insert "DeviceInterfaceGUIDs" multistring property.
+ GUID is freshly generated and should be OK to use.
+ https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
+ (Section Microsoft OS compatibility descriptors)
+*/
+
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+#define MS_OS_20_DESC_LEN 0xA2
+#define VENDOR_REQUEST_MICROSOFT 1
+
+// BOS Descriptor is required for webUSB
+const uint8_t desc_bos[] = {
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
+
+ // Microsoft OS 2.0 descriptor
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)};
+
+const uint8_t *tud_descriptor_bos_cb(void) {
+ return desc_bos;
+}
+
+uint8_t const desc_ms_os_20[] = {
+ // Set header: length, type, windows version, total length
+ U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
+
+ // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
+ U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sub-compatible
+
+ // MS OS 2.0 Registry property descriptor: length, type
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
+ U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A),// wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
+ 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
+ U16_TO_U8S_LE(0x0050),// wPropertyDataLength
+ //bPropertyData: {F7CC2C68-3B14-4D72-B876-1A981AD2C9E5}.
+ '{', 0x00, 'F', 0x00, '7', 0x00, 'C', 0x00, 'C', 0x00, '2', 0x00, 'C', 0x00, '6', 0x00, '8', 0x00, '-', 0x00,
+ '3', 0x00, 'B', 0x00, '1', 0x00, '4', 0x00, '-', 0x00, '4', 0x00, 'D', 0x00, '7', 0x00, '2', 0x00, '-', 0x00,
+ 'B', 0x00, '8', 0x00, '7', 0x00, '6', 0x00, '-', 0x00, '1', 0x00, 'A', 0x00, '9', 0x00, '8', 0x00, '1', 0x00,
+ 'A', 0x00, 'D', 0x00, '2', 0x00, 'C', 0x00, '9', 0x00, 'E', 0x00, '5', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
+ // nothing to with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
+
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {
+ if (request->bRequest == VENDOR_REQUEST_MICROSOFT) {
+ if (request->wIndex == 7) {
+ return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, MS_OS_20_DESC_LEN);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ switch (request->bmRequestType_bit.type) {
+ case TUSB_REQ_TYPE_VENDOR:
+ switch (request->bRequest) {
+ case VENDOR_REQUEST_MICROSOFT:
+ if (request->wIndex == 7) {
+ return tud_control_xfer(rhport, request, (void *) (uintptr_t) desc_ms_os_20, MS_OS_20_DESC_LEN);
+ } else {
+ return false;
+ }
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // stall unknown request
+ return false;
+}
+
+//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
@@ -122,24 +212,23 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
-{
- (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
- "TinyUSB", // 1: Manufacturer
- "TinyUSB Device", // 2: Product
- NULL, // 3: Serials will use unique ID if possible
- "TinyUSB DFU runtime", // 4: DFU runtime
+static char const *string_desc_arr[] = {
+ (const char[]){0x09, 0x04},// 0: is supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB Device", // 2: Product
+ NULL, // 3: Serials will use unique ID if possible
+ "TinyUSB DFU runtime", // 4: DFU runtime
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
- (void) langid;
+const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void)langid;
size_t chr_count;
- switch ( index ) {
+ switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
@@ -153,24 +242,28 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
- chr_count = strlen(str);
- size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ chr_count = strlen(str);
+ const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
- for ( size_t i = 0; i < chr_count; i++ ) {
+ for (size_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
- _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
diff --git a/examples/device/dynamic_configuration/CMakeLists.txt b/examples/device/dynamic_configuration/CMakeLists.txt
index 8a62d6ba2..4a1b67a69 100644
--- a/examples/device/dynamic_configuration/CMakeLists.txt
+++ b/examples/device/dynamic_configuration/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(dynamic_configuration C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/dynamic_configuration/Makefile b/examples/device/dynamic_configuration/Makefile
index 1b4d398cf..1a4b428dc 100644
--- a/examples/device/dynamic_configuration/Makefile
+++ b/examples/device/dynamic_configuration/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/dynamic_configuration/src/main.c b/examples/device/dynamic_configuration/src/main.c
index 258cfcd02..8ebb42f9a 100644
--- a/examples/device/dynamic_configuration/src/main.c
+++ b/examples/device/dynamic_configuration/src/main.c
@@ -109,23 +109,18 @@ void tud_resume_cb(void)
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
-void cdc_task(void)
-{
- if ( tud_cdc_connected() )
- {
- // connected and there are data available
- if ( tud_cdc_available() )
- {
+void cdc_task(void) {
+ if (tud_cdc_connected()) {
+ // connected and there are data available read and echo back
+ if (tud_cdc_available()) {
uint8_t buf[64];
-
- // read and echo back
uint32_t count = tud_cdc_read(buf, sizeof(buf));
- for(uint32_t i=0; i<count; i++)
- {
+ for (uint32_t i = 0; i < count; i++) {
tud_cdc_write_char(buf[i]);
-
- if ( buf[i] == '\r' ) tud_cdc_write_char('\n');
+ if (buf[i] == '\r') {
+ tud_cdc_write_char('\n');
+ }
}
tud_cdc_write_flush();
@@ -134,23 +129,18 @@ void cdc_task(void)
}
// Invoked when cdc when line state changed e.g connected/disconnected
-void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
-{
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
(void) itf;
// connected
- if ( dtr && rts )
- {
+ if (dtr && rts) {
// print initial message when connected
tud_cdc_write_str("\r\nTinyUSB CDC MSC device example\r\n");
}
}
// Invoked when CDC interface received data from host
-void tud_cdc_rx_cb(uint8_t itf)
-{
- (void) itf;
-}
+void tud_cdc_rx_cb(uint8_t itf) { (void) itf; }
//--------------------------------------------------------------------+
// MIDI Task
@@ -167,8 +157,7 @@ static const uint8_t note_sequence[] =
56,61,64,68,74,78,81,86,90,93,98,102
};
-void midi_task(void)
-{
+void midi_task(void) {
static uint32_t start_ms = 0;
uint8_t const cable_num = 0; // MIDI jack associated with USB endpoint
@@ -181,7 +170,9 @@ void midi_task(void)
while( tud_midi_available() ) tud_midi_packet_read(packet);
// send note every 1000 ms
- if (board_millis() - start_ms < 286) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < 286) {
+ return; // not enough time
+ }
start_ms += 286;
// Previous positions in the note sequence.
@@ -189,7 +180,9 @@ void midi_task(void)
// If we currently are at position 0, set the
// previous position to the last note in the sequence.
- if (previous < 0) previous = sizeof(note_sequence) - 1;
+ if (previous < 0) {
+ previous = sizeof(note_sequence) - 1;
+ }
// Send Note On for current position at full velocity (127) on channel 1.
uint8_t note_on[3] = { 0x90 | channel, note_sequence[note_pos], 127 };
@@ -203,21 +196,24 @@ void midi_task(void)
note_pos++;
// If we are at the end of the sequence, start over.
- if (note_pos >= sizeof(note_sequence)) note_pos = 0;
+ if (note_pos >= sizeof(note_sequence)) {
+ note_pos = 0;
+ }
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return;// not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
- led_state = 1 - led_state; // toggle
+ led_state = 1 - led_state;// toggle
}
diff --git a/examples/device/dynamic_configuration/src/msc_disk.c b/examples/device/dynamic_configuration/src/msc_disk.c
index ebc86e260..b545e4652 100644
--- a/examples/device/dynamic_configuration/src/msc_disk.c
+++ b/examples/device/dynamic_configuration/src/msc_disk.c
@@ -43,6 +43,7 @@ enum
DISK_BLOCK_SIZE = 512
};
+static
#ifdef CFG_EXAMPLE_MSC_READONLY
const
#endif
@@ -126,9 +127,9 @@ uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uin
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
- memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
- memcpy(inquiry_resp->product_id, pid, strlen(pid));
- memcpy(inquiry_resp->product_rev, rev, strlen(rev));
+ strncpy((char*) inquiry_resp->vendor_id, vid, 8);
+ strncpy((char*) inquiry_resp->product_id, pid, 16);
+ strncpy((char*) inquiry_resp->product_rev, rev, 4);
return sizeof(scsi_inquiry_resp_t); // 36 bytes
}
@@ -176,12 +177,13 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
-int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
-{
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
(void) lun;
// out of ramdisk
- if ( lba >= DISK_BLOCK_NUM ) return -1;
+ if ( lba >= DISK_BLOCK_NUM ) {
+ return -1;
+ }
uint8_t const* addr = msc_disk[lba] + offset;
memcpy(buffer, addr, bufsize);
@@ -191,12 +193,13 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
-int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
-{
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
(void) lun;
// out of ramdisk
- if ( lba >= DISK_BLOCK_NUM ) return -1;
+ if ( lba >= DISK_BLOCK_NUM ) {
+ return -1;
+ }
#ifndef CFG_EXAMPLE_MSC_READONLY
uint8_t* addr = msc_disk[lba] + offset;
@@ -211,42 +214,18 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
-int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
-{
- // read10 & write10 has their own callback and MUST not be handled here
-
- void const* response = NULL;
- int32_t resplen = 0;
-
- // most scsi handled is input
- bool in_xfer = true;
-
- switch (scsi_cmd[0])
- {
- default:
- // Set Sense = Invalid Command Operation
- tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
-
- // negative means error -> tinyusb could stall and/or response with failed status
- resplen = -1;
- break;
- }
+int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
+ (void) lun;
+ (void) scsi_cmd;
+ (void) buffer;
+ (void) bufsize;
- // return resplen must not larger than bufsize
- if ( resplen > bufsize ) resplen = bufsize;
+ // currently no other commands are supported
- if ( response && (resplen > 0) )
- {
- if(in_xfer)
- {
- memcpy(buffer, response, (size_t) resplen);
- }else
- {
- // SCSI output
- }
- }
+ // Set Sense = Invalid Command Operation
+ (void) tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
- return resplen;
+ return -1; // stall/failed command request;
}
#endif
diff --git a/examples/device/dynamic_configuration/src/tusb_config.h b/examples/device/dynamic_configuration/src/tusb_config.h
index b9b3878cc..7309d97c9 100644
--- a/examples/device/dynamic_configuration/src/tusb_config.h
+++ b/examples/device/dynamic_configuration/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -112,4 +112,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/dynamic_configuration/src/usb_descriptors.c b/examples/device/dynamic_configuration/src/usb_descriptors.c
index 0a2049288..c4049414f 100644
--- a/examples/device/dynamic_configuration/src/usb_descriptors.c
+++ b/examples/device/dynamic_configuration/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
// Configuration mode
// 0 : enumerated as CDC/MIDI. Board button is not pressed when enumerating
@@ -132,7 +132,7 @@ enum
#define EPNUM_1_MSC_OUT 0x02
#define EPNUM_1_MSC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_0_CDC_NOTIF 0x81
@@ -202,7 +202,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
@@ -232,14 +232,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
diff --git a/examples/device/hid_boot_interface/CMakeLists.txt b/examples/device/hid_boot_interface/CMakeLists.txt
index 3da8ee3df..2fee46517 100644
--- a/examples/device/hid_boot_interface/CMakeLists.txt
+++ b/examples/device/hid_boot_interface/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(hid_boot_interface C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/hid_boot_interface/Makefile b/examples/device/hid_boot_interface/Makefile
index 52a24cdb0..9e1eab4a2 100644
--- a/examples/device/hid_boot_interface/Makefile
+++ b/examples/device/hid_boot_interface/Makefile
@@ -1,14 +1,14 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/hid_boot_interface/skip.txt b/examples/device/hid_boot_interface/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/hid_boot_interface/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/hid_boot_interface/src/main.c b/examples/device/hid_boot_interface/src/main.c
index 45712cede..7f2153ae9 100644
--- a/examples/device/hid_boot_interface/src/main.c
+++ b/examples/device/hid_boot_interface/src/main.c
@@ -23,8 +23,8 @@
*
*/
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -40,10 +40,10 @@
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
-enum {
+enum {
BLINK_NOT_MOUNTED = 250,
- BLINK_MOUNTED = 1000,
- BLINK_SUSPENDED = 2500,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
@@ -52,28 +52,21 @@ void led_blinking_task(void);
void hid_task(void);
/*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
board_init();
// init device stack on configured roothub port
- tusb_rhport_init_t dev_init = {
- .role = TUSB_ROLE_DEVICE,
- .speed = TUSB_SPEED_AUTO
- };
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
- while (1)
- {
+ while (1) {
tud_task(); // tinyusb device task
led_blinking_task();
hid_task();
}
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -81,29 +74,25 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
- (void) remote_wakeup_en;
+void tud_suspend_cb(bool remote_wakeup_en) {
+ (void)remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -113,59 +102,54 @@ void tud_resume_cb(void)
// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
-void hid_task(void)
-{
+void hid_task(void) {
// Poll every 10ms
- const uint32_t interval_ms = 10;
- static uint32_t start_ms = 0;
+ const uint32_t interval_ms = 10;
+ static uint32_t start_ms = 0;
- if ( board_millis() - start_ms < interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
+ return; // not enough time
+ }
start_ms += interval_ms;
uint32_t const btn = board_button_read();
- if ( tud_suspended() && btn )
- {
+ if (tud_suspended() && btn) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
- }
- else
- {
+ } else {
// keyboard interface
- if ( tud_hid_n_ready(ITF_NUM_KEYBOARD) )
- {
+ if (tud_hid_n_ready(ITF_NUM_KEYBOARD)) {
// used to avoid send multiple consecutive zero report for keyboard
static bool has_keyboard_key = false;
uint8_t const report_id = 0;
uint8_t const modifier = 0;
- if ( btn )
- {
- uint8_t keycode[6] = { 0 };
- keycode[0] = HID_KEY_ARROW_RIGHT;
+ if (btn) {
+ uint8_t keycode[6] = {0};
+ keycode[0] = HID_KEY_ARROW_RIGHT;
tud_hid_n_keyboard_report(ITF_NUM_KEYBOARD, report_id, modifier, keycode);
has_keyboard_key = true;
- }else
- {
+ } else {
// send empty key report if previously has key pressed
- if (has_keyboard_key) tud_hid_n_keyboard_report(ITF_NUM_KEYBOARD, report_id, modifier, NULL);
+ if (has_keyboard_key) {
+ tud_hid_n_keyboard_report(ITF_NUM_KEYBOARD, report_id, modifier, NULL);
+ }
has_keyboard_key = false;
}
}
// mouse interface
- if ( tud_hid_n_ready(ITF_NUM_MOUSE) )
- {
- if ( btn )
- {
+ if (tud_hid_n_ready(ITF_NUM_MOUSE)) {
+ if (btn) {
uint8_t const report_id = 0;
uint8_t const button_mask = 0;
- int8_t const vertical = 0;
- int8_t const horizontal = 0;
- int8_t const delta = 5;
+ int8_t const vertical = 0;
+ int8_t const horizontal = 0;
+ int8_t const delta = 5;
tud_hid_n_mouse_report(ITF_NUM_MOUSE, report_id, button_mask, delta, delta, vertical, horizontal);
}
@@ -175,10 +159,9 @@ void hid_task(void)
// Invoked when received SET_PROTOCOL request
// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
-void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol)
-{
- (void) instance;
- (void) protocol;
+void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
+ (void)instance;
+ (void)protocol;
// nothing to do since we use the same compatible boot report for both Boot and Report mode.
// TODO set a indicator for user
@@ -187,11 +170,10 @@ void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol)
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
-void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len)
-{
- (void) instance;
- (void) report;
- (void) len;
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const *report, uint16_t len) {
+ (void)instance;
+ (void)report;
+ (void)len;
// nothing to do
}
@@ -199,42 +181,40 @@ void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
-uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
-{
+uint16_t tud_hid_get_report_cb(
+ uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
// TODO not Implemented
- (void) instance;
- (void) report_id;
- (void) report_type;
- (void) buffer;
- (void) reqlen;
+ (void)instance;
+ (void)report_id;
+ (void)report_type;
+ (void)buffer;
+ (void)reqlen;
return 0;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
-void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
-{
- (void) report_id;
+void tud_hid_set_report_cb(
+ uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
+ (void)report_id;
// keyboard interface
- if (instance == ITF_NUM_KEYBOARD)
- {
+ if (instance == ITF_NUM_KEYBOARD) {
// Set keyboard LED e.g Capslock, Numlock etc...
- if (report_type == HID_REPORT_TYPE_OUTPUT)
- {
+ if (report_type == HID_REPORT_TYPE_OUTPUT) {
// bufsize should be (at least) 1
- if ( bufsize < 1 ) return;
+ if (bufsize < 1) {
+ return;
+ }
uint8_t const kbd_leds = buffer[0];
- if (kbd_leds & KEYBOARD_LED_CAPSLOCK)
- {
+ if (kbd_leds & KEYBOARD_LED_CAPSLOCK) {
// Capslock On: disable blink, turn led on
blink_interval_ms = 0;
board_led_write(true);
- }else
- {
+ } else {
// Caplocks Off: back to normal blink
board_led_write(false);
blink_interval_ms = BLINK_MOUNTED;
@@ -246,16 +226,19 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
- static uint32_t start_ms = 0;
- static bool led_state = false;
+void led_blinking_task(void) {
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
// blink is disabled
- if (!blink_interval_ms) return;
+ if (!blink_interval_ms) {
+ return;
+ }
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/hid_boot_interface/src/tusb_config.h b/examples/device/hid_boot_interface/src/tusb_config.h
index 52723e09f..5d6fd62e1 100644
--- a/examples/device/hid_boot_interface/src/tusb_config.h
+++ b/examples/device/hid_boot_interface/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -104,4 +104,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/hid_boot_interface/src/usb_descriptors.c b/examples/device/hid_boot_interface/src/usb_descriptors.c
index d68ef16d9..b5c31a94a 100644
--- a/examples/device/hid_boot_interface/src/usb_descriptors.c
+++ b/examples/device/hid_boot_interface/src/usb_descriptors.c
@@ -33,60 +33,49 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0200,
- .bDeviceClass = 0x00,
- .bDeviceSubClass = 0x00,
- .bDeviceProtocol = 0x00,
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .idVendor = 0xCafe,
- .idProduct = USB_PID,
- .bcdDevice = 0x0100,
+ .idVendor = 0xCafe,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
- .iManufacturer = 0x01,
- .iProduct = 0x02,
- .iSerialNumber = 0x03,
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
- .bNumConfigurations = 0x01
-};
+ .bNumConfigurations = 0x01};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
- return (uint8_t const *) &desc_device;
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t const *)&desc_device;
}
//--------------------------------------------------------------------+
// HID Report Descriptor
//--------------------------------------------------------------------+
-uint8_t const desc_hid_keyboard_report[] =
-{
- TUD_HID_REPORT_DESC_KEYBOARD()
-};
+uint8_t const desc_hid_keyboard_report[] = {TUD_HID_REPORT_DESC_KEYBOARD()};
-uint8_t const desc_hid_mouse_report[] =
-{
- TUD_HID_REPORT_DESC_MOUSE()
-};
+uint8_t const desc_hid_mouse_report[] = {TUD_HID_REPORT_DESC_MOUSE()};
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance)
-{
+uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
return (instance == 0) ? desc_hid_keyboard_report : desc_hid_mouse_report;
}
@@ -94,36 +83,37 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance)
// Configuration Descriptor
//--------------------------------------------------------------------+
-#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + 2*TUD_HID_DESC_LEN)
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + 2 * TUD_HID_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
- // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
- // 1 Interrupt, 2 Bulk, 3 Iso, 4 Interrupt, 5 Bulk etc ...
- #define EPNUM_KEYBOARD 0x81
- #define EPNUM_MOUSE 0x84
+// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+// 1 Interrupt, 2 Bulk, 3 Iso, 4 Interrupt, 5 Bulk etc ...
+#define EPNUM_KEYBOARD 0x81
+#define EPNUM_MOUSE 0x84
#else
- #define EPNUM_KEYBOARD 0x81
- #define EPNUM_MOUSE 0x82
+#define EPNUM_KEYBOARD 0x81
+#define EPNUM_MOUSE 0x82
#endif
-uint8_t const desc_configuration[] =
-{
+uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
- TUD_HID_DESCRIPTOR(ITF_NUM_KEYBOARD, 0, HID_ITF_PROTOCOL_KEYBOARD, sizeof(desc_hid_keyboard_report), EPNUM_KEYBOARD, CFG_TUD_HID_EP_BUFSIZE, 10),
+ TUD_HID_DESCRIPTOR(
+ ITF_NUM_KEYBOARD, 0, HID_ITF_PROTOCOL_KEYBOARD, sizeof(desc_hid_keyboard_report), EPNUM_KEYBOARD,
+ CFG_TUD_HID_EP_BUFSIZE, 10),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
- TUD_HID_DESCRIPTOR(ITF_NUM_MOUSE, 0, HID_ITF_PROTOCOL_MOUSE, sizeof(desc_hid_mouse_report), EPNUM_MOUSE, CFG_TUD_HID_EP_BUFSIZE, 10)
-};
+ TUD_HID_DESCRIPTOR(
+ ITF_NUM_MOUSE, 0, HID_ITF_PROTOCOL_MOUSE, sizeof(desc_hid_mouse_report), EPNUM_MOUSE, CFG_TUD_HID_EP_BUFSIZE,
+ 10)};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
- (void) index; // for multiple configurations
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void)index; // for multiple configurations
return desc_configuration;
}
@@ -140,12 +130,11 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
-{
- (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
- "TinyUSB", // 1: Manufacturer
- "TinyUSB Device", // 2: Product
- NULL, // 3: Serials will use unique ID if possible
+static char const *string_desc_arr[] = {
+ (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB Device", // 2: Product
+ NULL, // 3: Serials will use unique ID if possible
};
static uint16_t _desc_str[32 + 1];
@@ -153,41 +142,44 @@ static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
- (void) langid;
+ (void)langid;
size_t chr_count;
- switch ( index ) {
+ switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
- case STRID_SERIAL:
- chr_count = board_usb_get_serial(_desc_str + 1, 32);
- break;
+ case STRID_SERIAL: chr_count = board_usb_get_serial(_desc_str + 1, 32); break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
+
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
- for ( size_t i = 0; i < chr_count; i++ ) {
+ for (size_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
- _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
diff --git a/examples/device/hid_composite/CMakeLists.txt b/examples/device/hid_composite/CMakeLists.txt
index 3da8ee3df..f1ddbd125 100644
--- a/examples/device/hid_composite/CMakeLists.txt
+++ b/examples/device/hid_composite/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(hid_composite C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/hid_composite/Makefile b/examples/device/hid_composite/Makefile
index 1b4d398cf..1a4b428dc 100644
--- a/examples/device/hid_composite/Makefile
+++ b/examples/device/hid_composite/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/hid_composite/skip.txt b/examples/device/hid_composite/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/hid_composite/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/hid_composite/src/main.c b/examples/device/hid_composite/src/main.c
index fa02a1abe..7c9d5af8d 100644
--- a/examples/device/hid_composite/src/main.c
+++ b/examples/device/hid_composite/src/main.c
@@ -23,8 +23,8 @@
*
*/
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -41,10 +41,10 @@
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
-enum {
+enum {
BLINK_NOT_MOUNTED = 250,
- BLINK_MOUNTED = 1000,
- BLINK_SUSPENDED = 2500,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
@@ -53,24 +53,18 @@ void led_blinking_task(void);
void hid_task(void);
/*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
board_init();
// init device stack on configured roothub port
- tusb_rhport_init_t dev_init = {
- .role = TUSB_ROLE_DEVICE,
- .speed = TUSB_SPEED_AUTO
- };
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
board_init_after_tusb();
- while (1)
- {
+ while (1) {
tud_task(); // tinyusb device task
led_blinking_task();
-
hid_task();
}
}
@@ -80,29 +74,25 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
- (void) remote_wakeup_en;
+void tud_suspend_cb(bool remote_wakeup_en) {
+ (void)remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -110,157 +100,128 @@ void tud_resume_cb(void)
// USB HID
//--------------------------------------------------------------------+
-static void send_hid_report(uint8_t report_id, uint32_t btn)
-{
+static void send_hid_report(uint8_t report_id, uint32_t btn) {
// skip if hid is not ready yet
- if ( !tud_hid_ready() ) return;
+ if (!tud_hid_ready()) {
+ return;
+ }
- switch(report_id)
- {
- case REPORT_ID_KEYBOARD:
- {
+ switch (report_id) {
+ case REPORT_ID_KEYBOARD: {
// use to avoid send multiple consecutive zero report for keyboard
static bool has_keyboard_key = false;
- if ( btn )
- {
- uint8_t keycode[6] = { 0 };
- keycode[0] = HID_KEY_A;
+ if (btn != 0u) {
+ uint8_t keycode[6] = {0};
+ keycode[0] = HID_KEY_A;
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
has_keyboard_key = true;
- }else
- {
+ } else {
// send empty key report if previously has key pressed
- if (has_keyboard_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
+ if (has_keyboard_key) {
+ tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
+ }
has_keyboard_key = false;
}
+ break;
}
- break;
- case REPORT_ID_MOUSE:
- {
+ case REPORT_ID_MOUSE: {
int8_t const delta = 5;
// no button, right + down, no scroll, no pan
tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
+ break;
}
- break;
- case REPORT_ID_CONSUMER_CONTROL:
- {
+ case REPORT_ID_CONSUMER_CONTROL: {
// use to avoid send multiple consecutive zero report
static bool has_consumer_key = false;
- if ( btn )
- {
+ if (btn != 0u) {
// volume down
uint16_t volume_down = HID_USAGE_CONSUMER_VOLUME_DECREMENT;
tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &volume_down, 2);
has_consumer_key = true;
- }else
- {
+ } else {
// send empty key report (release key) if previously has key pressed
uint16_t empty_key = 0;
- if (has_consumer_key) tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &empty_key, 2);
+ if (has_consumer_key) {
+ tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &empty_key, 2);
+ }
has_consumer_key = false;
}
+ break;
}
- break;
- case REPORT_ID_GAMEPAD:
- {
+ case REPORT_ID_GAMEPAD: {
// use to avoid send multiple consecutive zero report for keyboard
static bool has_gamepad_key = false;
- hid_gamepad_report_t report =
- {
- .x = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,
- .hat = 0, .buttons = 0
- };
+ hid_gamepad_report_t report = {.x = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0, .hat = 0, .buttons = 0};
- if ( btn )
- {
- report.hat = GAMEPAD_HAT_UP;
+ if (btn != 0u) {
+ report.hat = GAMEPAD_HAT_UP;
report.buttons = GAMEPAD_BUTTON_A;
tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
has_gamepad_key = true;
- }else
- {
- report.hat = GAMEPAD_HAT_CENTERED;
+ } else {
+ report.hat = GAMEPAD_HAT_CENTERED;
report.buttons = 0;
- if (has_gamepad_key) tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
+ if (has_gamepad_key) {
+ tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
+ }
has_gamepad_key = false;
}
+ break;
}
- break;
- default: break;
- }
-}
-
-/* use this to send stylus touch signal through USB. */
-static void send_stylus_touch(uint16_t x, uint16_t y, bool state)
-{
- // skip if hid is not ready yet
- if ( !tud_hid_ready() ) return;
-
- static bool has_stylus_pen = false;
- hid_stylus_report_t report =
- {
- .attr = 0,
- .x = 0,
- .y = 0
- };
+ case REPORT_ID_STYLUS_PEN: {
+ static bool touch_state = false;
+ hid_stylus_report_t report = {.attr = 0, .x = 0, .y = 0};
- report.x = x;
- report.y = y;
-
- if (state)
- {
- report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE;
- tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
+ if (btn != 0u) {
+ report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE;
+ report.x = 100;
+ report.y = 100;
+ tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
+ touch_state = true;
+ } else {
+ report.attr = 0;
+ if (touch_state) {
+ tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
+ }
+ touch_state = false;
+ }
+ break;
+ }
- has_stylus_pen = true;
- }else
- {
- report.attr = 0;
- if (has_stylus_pen) tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
- has_stylus_pen = false;
+ default: break; // unknown report id
}
-
}
// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
-void hid_task(void)
-{
+void hid_task(void) {
// Poll every 10ms
- const uint32_t interval_ms = 10;
- static uint32_t start_ms = 0;
- static uint32_t touch_ms = 0;
- static bool touch_state = false;
+ const uint32_t interval_ms = 10;
+ static uint32_t start_ms = 0;
- if (board_millis() - touch_ms < 100) {
- touch_ms = board_millis();
- send_stylus_touch(0, 0, touch_state = !touch_state);
- return;
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
+ return; // not enough time
}
-
- if ( board_millis() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
uint32_t const btn = board_button_read();
// Remote wakeup
- if ( tud_suspended() && btn )
- {
+ if (tud_suspended() && btn != 0u) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
- }else
- {
+ } else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
send_hid_report(REPORT_ID_KEYBOARD, btn);
}
@@ -269,15 +230,13 @@ void hid_task(void)
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
-void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len)
-{
- (void) instance;
- (void) len;
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const *report, uint16_t len) {
+ (void)instance;
+ (void)len;
uint8_t next_report_id = report[0] + 1u;
- if (next_report_id < REPORT_ID_COUNT)
- {
+ if (next_report_id < REPORT_ID_COUNT) {
send_hid_report(next_report_id, board_button_read());
}
}
@@ -285,41 +244,39 @@ void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
-uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
-{
+uint16_t tud_hid_get_report_cb(
+ uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
// TODO not Implemented
- (void) instance;
- (void) report_id;
- (void) report_type;
- (void) buffer;
- (void) reqlen;
+ (void)instance;
+ (void)report_id;
+ (void)report_type;
+ (void)buffer;
+ (void)reqlen;
return 0;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
-void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
-{
- (void) instance;
+void tud_hid_set_report_cb(
+ uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
+ (void)instance;
- if (report_type == HID_REPORT_TYPE_OUTPUT)
- {
+ if (report_type == HID_REPORT_TYPE_OUTPUT) {
// Set keyboard LED e.g Capslock, Numlock etc...
- if (report_id == REPORT_ID_KEYBOARD)
- {
+ if (report_id == REPORT_ID_KEYBOARD) {
// bufsize should be (at least) 1
- if ( bufsize < 1 ) return;
+ if (bufsize < 1) {
+ return;
+ }
uint8_t const kbd_leds = buffer[0];
- if (kbd_leds & KEYBOARD_LED_CAPSLOCK)
- {
+ if ((kbd_leds & KEYBOARD_LED_CAPSLOCK) != 0u) {
// Capslock On: disable blink, turn led on
blink_interval_ms = 0;
board_led_write(true);
- }else
- {
+ } else {
// Caplocks Off: back to normal blink
board_led_write(false);
blink_interval_ms = BLINK_MOUNTED;
@@ -331,16 +288,19 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
- static uint32_t start_ms = 0;
- static bool led_state = false;
+void led_blinking_task(void) {
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
// blink is disabled
- if (!blink_interval_ms) return;
+ if (0u == blink_interval_ms) {
+ return;
+ }
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/hid_composite/src/tusb_config.h b/examples/device/hid_composite/src/tusb_config.h
index 6bd32c427..895745ed2 100644
--- a/examples/device/hid_composite/src/tusb_config.h
+++ b/examples/device/hid_composite/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -104,4 +104,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/hid_composite/src/usb_descriptors.c b/examples/device/hid_composite/src/usb_descriptors.c
index ce7fbd13f..46e4b63f9 100644
--- a/examples/device/hid_composite/src/usb_descriptors.c
+++ b/examples/device/hid_composite/src/usb_descriptors.c
@@ -33,9 +33,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -43,7 +43,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -120,10 +120,10 @@ uint8_t const desc_configuration[] =
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier =
+static tusb_desc_device_qualifier_t const desc_device_qualifier =
{
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
@@ -188,7 +188,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/device/hid_composite_freertos/CMakeLists.txt b/examples/device/hid_composite_freertos/CMakeLists.txt
index 6ce9e72fe..2081a7782 100644
--- a/examples/device/hid_composite_freertos/CMakeLists.txt
+++ b/examples/device/hid_composite_freertos/CMakeLists.txt
@@ -2,32 +2,34 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
-project(${PROJECT} C CXX ASM)
+project(hid_composite_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} freertos)
+family_configure_device_example(${PROJECT_NAME} freertos)
diff --git a/examples/device/hid_composite_freertos/Makefile b/examples/device/hid_composite_freertos/Makefile
index bd625b345..3c421af74 100644
--- a/examples/device/hid_composite_freertos/Makefile
+++ b/examples/device/hid_composite_freertos/Makefile
@@ -1,15 +1,15 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
src/main.c \
src/usb_descriptors.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/hid_composite_freertos/skip.txt b/examples/device/hid_composite_freertos/skip.txt
index 650bf355b..97d8e168b 100644
--- a/examples/device/hid_composite_freertos/skip.txt
+++ b/examples/device/hid_composite_freertos/skip.txt
@@ -1,16 +1,18 @@
+mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
+mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
-mcu:RP2040
+mcu:FT90X
mcu:SAMD11
-mcu:SAMX7X
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
family:broadcom_32bit
family:broadcom_64bit
+family:hpmicro
diff --git a/examples/device/hid_composite_freertos/src/main.c b/examples/device/hid_composite_freertos/src/main.c
index 0eb13add3..391e3c42a 100644
--- a/examples/device/hid_composite_freertos/src/main.c
+++ b/examples/device/hid_composite_freertos/src/main.c
@@ -53,7 +53,7 @@
#define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
#endif
-#define HID_STACK_SZIE configMINIMAL_STACK_SIZE
+#define HID_STACK_SZIE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 2 : 1))
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
diff --git a/examples/device/hid_composite_freertos/src/tusb_config.h b/examples/device/hid_composite_freertos/src/tusb_config.h
index b28033a0c..ad067ac82 100644
--- a/examples/device/hid_composite_freertos/src/tusb_config.h
+++ b/examples/device/hid_composite_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -111,4 +111,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/hid_composite_freertos/src/usb_descriptors.c b/examples/device/hid_composite_freertos/src/usb_descriptors.c
index 3f231fecc..a745c17b5 100644
--- a/examples/device/hid_composite_freertos/src/usb_descriptors.c
+++ b/examples/device/hid_composite_freertos/src/usb_descriptors.c
@@ -33,9 +33,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -43,7 +43,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -119,10 +119,10 @@ uint8_t const desc_configuration[] =
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier =
+static tusb_desc_device_qualifier_t const desc_device_qualifier =
{
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
@@ -185,7 +185,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/device/hid_generic_inout/CMakeLists.txt b/examples/device/hid_generic_inout/CMakeLists.txt
index 3da8ee3df..b363c161b 100644
--- a/examples/device/hid_generic_inout/CMakeLists.txt
+++ b/examples/device/hid_generic_inout/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(hid_generic_inout C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/hid_generic_inout/Makefile b/examples/device/hid_generic_inout/Makefile
index 1b4d398cf..1a4b428dc 100644
--- a/examples/device/hid_generic_inout/Makefile
+++ b/examples/device/hid_generic_inout/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/hid_generic_inout/skip.txt b/examples/device/hid_generic_inout/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/hid_generic_inout/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/hid_generic_inout/src/main.c b/examples/device/hid_generic_inout/src/main.c
index 9837a47d9..2a6b91a8a 100644
--- a/examples/device/hid_generic_inout/src/main.c
+++ b/examples/device/hid_generic_inout/src/main.c
@@ -168,7 +168,7 @@ void led_blinking_task(void)
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/hid_generic_inout/src/tusb_config.h b/examples/device/hid_generic_inout/src/tusb_config.h
index 98143ac4d..58dc52afc 100644
--- a/examples/device/hid_generic_inout/src/tusb_config.h
+++ b/examples/device/hid_generic_inout/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -104,4 +104,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/hid_generic_inout/src/usb_descriptors.c b/examples/device/hid_generic_inout/src/usb_descriptors.c
index 64f6d17ae..93e718461 100644
--- a/examples/device/hid_generic_inout/src/usb_descriptors.c
+++ b/examples/device/hid_generic_inout/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -97,7 +97,15 @@ enum
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
-#define EPNUM_HID 0x01
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
+ // e.g EP1 OUT & EP1 IN cannot exist together
+ #define EPNUM_HID_OUT 0x01
+ #define EPNUM_HID_IN 0x82
+#else
+ #define EPNUM_HID_OUT 0x01
+ #define EPNUM_HID_IN 0x81
+#endif
uint8_t const desc_configuration[] =
{
@@ -105,7 +113,7 @@ uint8_t const desc_configuration[] =
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, protocol, report descriptor len, EP Out & In address, size & polling interval
- TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
+ TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID_OUT, EPNUM_HID_IN, CFG_TUD_HID_EP_BUFSIZE, 10)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -130,7 +138,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/device/hid_multiple_interface/CMakeLists.txt b/examples/device/hid_multiple_interface/CMakeLists.txt
index 3da8ee3df..f4a9895d5 100644
--- a/examples/device/hid_multiple_interface/CMakeLists.txt
+++ b/examples/device/hid_multiple_interface/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(hid_multiple_interface C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/hid_multiple_interface/Makefile b/examples/device/hid_multiple_interface/Makefile
index 1b4d398cf..1a4b428dc 100644
--- a/examples/device/hid_multiple_interface/Makefile
+++ b/examples/device/hid_multiple_interface/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/hid_multiple_interface/skip.txt b/examples/device/hid_multiple_interface/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/hid_multiple_interface/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/hid_multiple_interface/src/main.c b/examples/device/hid_multiple_interface/src/main.c
index 0bccd13c1..c71a9cbad 100644
--- a/examples/device/hid_multiple_interface/src/main.c
+++ b/examples/device/hid_multiple_interface/src/main.c
@@ -120,7 +120,7 @@ void hid_task(void)
const uint32_t interval_ms = 10;
static uint32_t start_ms = 0;
- if ( board_millis() - start_ms < interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
uint32_t const btn = board_button_read();
@@ -205,7 +205,7 @@ void led_blinking_task(void)
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/hid_multiple_interface/src/tusb_config.h b/examples/device/hid_multiple_interface/src/tusb_config.h
index 49dc962fe..e1805017d 100644
--- a/examples/device/hid_multiple_interface/src/tusb_config.h
+++ b/examples/device/hid_multiple_interface/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -104,4 +104,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/hid_multiple_interface/src/usb_descriptors.c b/examples/device/hid_multiple_interface/src/usb_descriptors.c
index 86f567e8e..cd2d93c44 100644
--- a/examples/device/hid_multiple_interface/src/usb_descriptors.c
+++ b/examples/device/hid_multiple_interface/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -146,7 +146,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/device/midi2_device/CMakeLists.txt b/examples/device/midi2_device/CMakeLists.txt
new file mode 100644
index 000000000..f1fe09db2
--- /dev/null
+++ b/examples/device/midi2_device/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(midi2_device C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${PROJECT_NAME} noos)
+
+# Suppress pre-existing warning in usbd.c (uint8_t comparison always true/false)
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ target_compile_options(${PROJECT_NAME} PRIVATE -Wno-type-limits)
+endif()
diff --git a/examples/device/midi2_device/CMakePresets.json b/examples/device/midi2_device/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/device/midi2_device/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/device/midi2_device/Makefile b/examples/device/midi2_device/Makefile
new file mode 100644
index 000000000..829d9da59
--- /dev/null
+++ b/examples/device/midi2_device/Makefile
@@ -0,0 +1,16 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+# Example source
+EXAMPLE_SOURCE += \
+ src/main.c \
+ src/usb_descriptors.c \
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+# Suppress pre-existing warning in usbd.c
+CFLAGS_GCC += -Wno-type-limits
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/midi2_device/README.md b/examples/device/midi2_device/README.md
new file mode 100644
index 000000000..1731dba57
--- /dev/null
+++ b/examples/device/midi2_device/README.md
@@ -0,0 +1,59 @@
+# MIDI 2.0 Song Sender
+
+USB MIDI 2.0 Device example that plays "Twinkle Twinkle Little Star" using
+native UMP (Universal MIDI Packet) format with full MIDI 2.0 expression.
+
+## MIDI 2.0 Features Demonstrated
+
+- 16-bit Velocity (vs 7-bit MIDI 1.0)
+- 32-bit Control Change values
+- 32-bit Pitch Bend (vs 14-bit MIDI 1.0)
+- 32-bit Channel Pressure (Aftertouch)
+- 32-bit Poly Pressure (Per-Note Aftertouch)
+- Per-Note Management (MIDI 2.0 exclusive)
+- Program Change with Bank Select
+- JR Timestamps
+
+## USB Descriptor
+
+The device exposes both USB-MIDI 1.0 (Alt Setting 0) and USB-MIDI 2.0 (Alt Setting 1)
+as required by the USB-MIDI 2.0 specification. A MIDI 2.0 capable host (e.g. Windows
+MIDI Services) will select Alt Setting 1 for native UMP transport. Legacy hosts use
+Alt Setting 0 with automatic MIDI 1.0 fallback.
+
+## Hardware
+
+- Any RP2040 board with USB (e.g. Raspberry Pi Pico)
+- LED on GPIO 25: steady = playing, slow blink = waiting for host
+
+## Building
+
+```bash
+mkdir build && cd build
+cmake -DBOARD=raspberry_pi_pico -DPICO_SDK_FETCH_FROM_GIT=on -G Ninja ..
+cmake --build .
+```
+
+## Flashing
+
+Hold BOOTSEL, connect USB, drag `midi2_device.uf2` to the RPI-RP2 drive.
+
+## Testing
+
+**Linux:**
+```bash
+aseqdump -p "MIDI 2.0 Device"
+```
+
+**Windows (MIDI 2.0 native):**
+```powershell
+midi endpoint list
+midi endpoint monitor
+```
+
+## Song Data
+
+Twinkle Twinkle Little Star in C major, 120 BPM. Six phrases with dynamic
+shaping (pp to ff crescendo and back), pitch bend vibrato on sustained notes,
+and channel/poly pressure for expression. All values use genuine MIDI 2.0
+resolution with no 7-bit equivalent.
diff --git a/examples/device/midi2_device/skip.txt b/examples/device/midi2_device/skip.txt
new file mode 100644
index 000000000..eadb6e74a
--- /dev/null
+++ b/examples/device/midi2_device/skip.txt
@@ -0,0 +1 @@
+mcu:SAMD11
diff --git a/examples/device/midi2_device/src/main.c b/examples/device/midi2_device/src/main.c
new file mode 100644
index 000000000..ce052a20b
--- /dev/null
+++ b/examples/device/midi2_device/src/main.c
@@ -0,0 +1,718 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "class/midi/midi2_device.h"
+
+//--------------------------------------------------------------------+
+// MIDI 2.0 UMP Message Type Constants (M2-104-UM, Section 4)
+//--------------------------------------------------------------------+
+// Message Type (MT) occupies bits 31-28 of Word 0
+#define UMP_MT_UTILITY 0x00000000 // 32-bit: Utility (NOOP, JR Clock, JR Timestamp)
+#define UMP_MT_SYSTEM 0x10000000 // 32-bit: System Common / Real Time
+#define UMP_MT_MIDI1_CV 0x20000000 // 32-bit: MIDI 1.0 Channel Voice
+#define UMP_MT_DATA64 0x30000000 // 64-bit: Data (SysEx 7-bit)
+#define UMP_MT_MIDI2_CV 0x40000000 // 64-bit: MIDI 2.0 Channel Voice
+#define UMP_MT_DATA128 0x50000000 // 128-bit: Data (SysEx 8-bit)
+
+// MIDI 2.0 Channel Voice status (bits 23-20 of Word 0)
+#define UMP_STATUS_NOTE_OFF 0x00800000
+#define UMP_STATUS_NOTE_ON 0x00900000
+#define UMP_STATUS_POLY_PRESSURE 0x00A00000
+#define UMP_STATUS_CC 0x00B00000
+#define UMP_STATUS_PROGRAM 0x00C00000
+#define UMP_STATUS_CHAN_PRESSURE 0x00D00000
+#define UMP_STATUS_PITCH_BEND 0x00E00000
+#define UMP_STATUS_PN_MGMT 0x00F00000 // Per-Note Management
+
+// Note Attribute Types (MIDI 2.0 spec, Section 4.2.6)
+#define UMP_ATTR_NONE 0x00
+#define UMP_ATTR_MANUFACTURER 0x01
+#define UMP_ATTR_PROFILE 0x02
+#define UMP_ATTR_PITCH_7_9 0x03 // Pitch 7.9 format
+
+//--------------------------------------------------------------------+
+// MIDI 2.0 UMP Builders - Full Spec Coverage
+//--------------------------------------------------------------------+
+
+// Helper: send a 64-bit UMP (2 words)
+static inline void ump_send_64(uint32_t w0, uint32_t w1) {
+ uint32_t words[2] = { w0, w1 };
+ tud_midi2_ump_write(words, 2);
+}
+
+// Helper: send a 32-bit UMP (1 word)
+static inline void ump_send_32(uint32_t w0) {
+ tud_midi2_ump_write(&w0, 1);
+}
+
+// -- Utility Messages (MT=0x0, 32-bit) --
+
+static inline void ump_jr_timestamp(uint16_t timestamp) {
+ // Word 0: [MT(0x0) | Group(0) | Status(0x0020) | Timestamp(16-bit)]
+ ump_send_32(UMP_MT_UTILITY | 0x00200000 | (uint32_t)timestamp);
+}
+
+// -- MIDI 2.0 Channel Voice: Note On (MT=0x4, 64-bit) --
+// Word 0: [MT(4):Group(4):Status(4):Channel(4):NoteNumber(8):AttrType(8)]
+// Word 1: [Velocity(16):Attribute(16)]
+static inline void ump_note_on(uint8_t group, uint8_t channel,
+ uint8_t pitch, uint16_t velocity,
+ uint8_t attr_type, uint16_t attr_val) {
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_NOTE_ON | ((uint32_t)(channel & 0x0F) << 16)
+ | ((uint32_t)(pitch & 0x7F) << 8)
+ | (uint32_t)(attr_type & 0xFF);
+ uint32_t w1 = ((uint32_t)(velocity & 0xFFFF) << 16)
+ | (uint32_t)(attr_val & 0xFFFF);
+ ump_send_64(w0, w1);
+}
+
+// -- MIDI 2.0 Channel Voice: Note Off (MT=0x4, 64-bit) --
+static inline void ump_note_off(uint8_t group, uint8_t channel,
+ uint8_t pitch, uint16_t velocity,
+ uint8_t attr_type, uint16_t attr_val) {
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_NOTE_OFF | ((uint32_t)(channel & 0x0F) << 16)
+ | ((uint32_t)(pitch & 0x7F) << 8)
+ | (uint32_t)(attr_type & 0xFF);
+ uint32_t w1 = ((uint32_t)(velocity & 0xFFFF) << 16)
+ | (uint32_t)(attr_val & 0xFFFF);
+ ump_send_64(w0, w1);
+}
+
+// -- MIDI 2.0 Channel Voice: Control Change (MT=0x4, 64-bit) --
+// Word 0: [MT(4):Group(4):Status(0xB):Channel(4):Index(8):Reserved(8)]
+// Word 1: [Data(32)] -- full 32-bit CC resolution (vs 7-bit MIDI 1.0)
+static inline void ump_cc(uint8_t group, uint8_t channel,
+ uint8_t index, uint32_t value) {
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_CC | ((uint32_t)(channel & 0x0F) << 16)
+ | ((uint32_t)(index & 0x7F) << 8);
+ ump_send_64(w0, value);
+}
+
+// -- MIDI 2.0 Channel Voice: Program Change (MT=0x4, 64-bit) --
+// Word 0: [MT(4):Group(4):Status(0xC):Channel(4):Reserved(8):OptionFlags(8)]
+// Word 1: [Program(8):Reserved(8):BankMSB(8):BankLSB(8)]
+// OptionFlags bit 0 = Bank Valid
+static inline void ump_program_change(uint8_t group, uint8_t channel,
+ uint8_t program,
+ bool bank_valid, uint8_t bank_msb,
+ uint8_t bank_lsb) {
+ uint8_t flags = bank_valid ? 0x01 : 0x00;
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_PROGRAM | ((uint32_t)(channel & 0x0F) << 16)
+ | (uint32_t)flags;
+ uint32_t w1 = ((uint32_t)program << 24)
+ | ((uint32_t)bank_msb << 8)
+ | (uint32_t)bank_lsb;
+ ump_send_64(w0, w1);
+}
+
+// -- MIDI 2.0 Channel Voice: Pitch Bend (MT=0x4, 64-bit) --
+// Word 0: [MT(4):Group(4):Status(0xE):Channel(4):Reserved(16)]
+// Word 1: [PitchBend(32)] -- full 32-bit (vs 14-bit MIDI 1.0!)
+// 0x80000000 = center, 0x00000000 = min, 0xFFFFFFFF = max
+static inline void ump_pitch_bend(uint8_t group, uint8_t channel,
+ uint32_t value) {
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_PITCH_BEND | ((uint32_t)(channel & 0x0F) << 16);
+ ump_send_64(w0, value);
+}
+
+// -- MIDI 2.0 Channel Voice: Channel Pressure / Aftertouch (MT=0x4, 64-bit) --
+// Word 0: [MT(4):Group(4):Status(0xD):Channel(4):Reserved(16)]
+// Word 1: [Pressure(32)] -- full 32-bit (vs 7-bit MIDI 1.0)
+static inline void ump_channel_pressure(uint8_t group, uint8_t channel,
+ uint32_t pressure) {
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_CHAN_PRESSURE | ((uint32_t)(channel & 0x0F) << 16);
+ ump_send_64(w0, pressure);
+}
+
+// -- MIDI 2.0 Channel Voice: Poly Pressure / Per-Note Aftertouch --
+// Word 0: [MT(4):Group(4):Status(0xA):Channel(4):NoteNumber(8):Reserved(8)]
+// Word 1: [Pressure(32)]
+static inline void ump_poly_pressure(uint8_t group, uint8_t channel,
+ uint8_t pitch, uint32_t pressure) {
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_POLY_PRESSURE | ((uint32_t)(channel & 0x0F) << 16)
+ | ((uint32_t)(pitch & 0x7F) << 8);
+ ump_send_64(w0, pressure);
+}
+
+// -- MIDI 2.0 Channel Voice: Per-Note Management (MT=0x4, 64-bit) --
+// Exclusive to MIDI 2.0: controls per-note behavior
+// Word 0: [MT(4):Group(4):Status(0xF):Channel(4):NoteNumber(8):Flags(8)]
+// Word 1: Reserved
+// Flags bit 1 = Reset (S), bit 0 = Detach (D)
+static inline void ump_per_note_mgmt(uint8_t group, uint8_t channel,
+ uint8_t pitch, bool detach,
+ bool reset) {
+ uint8_t flags = (reset ? 0x02 : 0x00) | (detach ? 0x01 : 0x00);
+ uint32_t w0 = UMP_MT_MIDI2_CV | ((uint32_t)(group & 0x0F) << 24)
+ | UMP_STATUS_PN_MGMT | ((uint32_t)(channel & 0x0F) << 16)
+ | ((uint32_t)(pitch & 0x7F) << 8)
+ | (uint32_t)flags;
+ ump_send_64(w0, 0x00000000);
+}
+
+//--------------------------------------------------------------------+
+// MIDI 1.0 Channel Voice Builders (UMP MT 0x2, 32-bit)
+//--------------------------------------------------------------------+
+// Used on Alt 1 when negotiated protocol is MIDI 1.0.
+// Word layout: [MT(0x2) | Group(4b) | Status(8b) | Data1(8b) | Data2(8b)]
+// Status nibbles: 0x8=NoteOff, 0x9=NoteOn, 0xA=PolyPress, 0xB=CC,
+// 0xC=ProgChg, 0xD=ChanPress, 0xE=PitchBend.
+
+static inline void ump_midi1_send(uint8_t group, uint8_t status,
+ uint8_t data1, uint8_t data2) {
+ uint32_t w = UMP_MT_MIDI1_CV
+ | ((uint32_t)(group & 0x0F) << 24)
+ | ((uint32_t)status << 16)
+ | ((uint32_t)data1 << 8)
+ | (uint32_t)data2;
+ ump_send_32(w);
+}
+
+static inline void ump_midi1_note_on(uint8_t group, uint8_t channel,
+ uint8_t note, uint8_t vel7) {
+ ump_midi1_send(group, 0x90 | (channel & 0x0F), note & 0x7F, vel7 & 0x7F);
+}
+
+static inline void ump_midi1_note_off(uint8_t group, uint8_t channel,
+ uint8_t note, uint8_t vel7) {
+ ump_midi1_send(group, 0x80 | (channel & 0x0F), note & 0x7F, vel7 & 0x7F);
+}
+
+static inline void ump_midi1_cc(uint8_t group, uint8_t channel,
+ uint8_t cc, uint8_t val7) {
+ ump_midi1_send(group, 0xB0 | (channel & 0x0F), cc & 0x7F, val7 & 0x7F);
+}
+
+static inline void ump_midi1_program(uint8_t group, uint8_t channel,
+ uint8_t program) {
+ ump_midi1_send(group, 0xC0 | (channel & 0x0F), program & 0x7F, 0);
+}
+
+static inline void ump_midi1_pitch_bend(uint8_t group, uint8_t channel,
+ uint16_t value14) {
+ // 14-bit pitch bend: LSB first, then MSB. Center = 0x2000.
+ ump_midi1_send(group, 0xE0 | (channel & 0x0F),
+ (uint8_t)(value14 & 0x7F),
+ (uint8_t)((value14 >> 7) & 0x7F));
+}
+
+static inline void ump_midi1_channel_pressure(uint8_t group, uint8_t channel,
+ uint8_t val7) {
+ ump_midi1_send(group, 0xD0 | (channel & 0x0F), val7 & 0x7F, 0);
+}
+
+static inline void ump_midi1_poly_pressure(uint8_t group, uint8_t channel,
+ uint8_t note, uint8_t val7) {
+ ump_midi1_send(group, 0xA0 | (channel & 0x0F), note & 0x7F, val7 & 0x7F);
+}
+
+//--------------------------------------------------------------------+
+// USB-MIDI 1.0 32-bit Event Packet Builders (Alt 0 transport)
+//--------------------------------------------------------------------+
+// Used on Alt 0 (USB-MIDI 1.0). Each packet is 4 raw bytes:
+// [(Cable << 4) | CIN] [Status] [Data1] [Data2]
+// CIN = Code Index Number. See USB-MIDI 1.0 spec Section 4.
+
+static inline void midi1_pkt_send(uint8_t cable, uint8_t cin,
+ uint8_t status, uint8_t data1,
+ uint8_t data2) {
+ uint8_t packet[4] = {
+ (uint8_t)(((cable & 0x0F) << 4) | (cin & 0x0F)),
+ status, data1, data2
+ };
+ (void) tud_midi2_packet_write(packet, 1);
+}
+
+static inline void midi1_pkt_note_on(uint8_t cable, uint8_t channel,
+ uint8_t note, uint8_t vel7) {
+ midi1_pkt_send(cable, 0x9, 0x90 | (channel & 0x0F),
+ note & 0x7F, vel7 & 0x7F);
+}
+
+static inline void midi1_pkt_note_off(uint8_t cable, uint8_t channel,
+ uint8_t note, uint8_t vel7) {
+ midi1_pkt_send(cable, 0x8, 0x80 | (channel & 0x0F),
+ note & 0x7F, vel7 & 0x7F);
+}
+
+static inline void midi1_pkt_cc(uint8_t cable, uint8_t channel,
+ uint8_t cc, uint8_t val7) {
+ midi1_pkt_send(cable, 0xB, 0xB0 | (channel & 0x0F),
+ cc & 0x7F, val7 & 0x7F);
+}
+
+static inline void midi1_pkt_program(uint8_t cable, uint8_t channel,
+ uint8_t program) {
+ midi1_pkt_send(cable, 0xC, 0xC0 | (channel & 0x0F),
+ program & 0x7F, 0);
+}
+
+static inline void midi1_pkt_pitch_bend(uint8_t cable, uint8_t channel,
+ uint16_t value14) {
+ midi1_pkt_send(cable, 0xE, 0xE0 | (channel & 0x0F),
+ (uint8_t)(value14 & 0x7F),
+ (uint8_t)((value14 >> 7) & 0x7F));
+}
+
+static inline void midi1_pkt_channel_pressure(uint8_t cable, uint8_t channel,
+ uint8_t val7) {
+ midi1_pkt_send(cable, 0xD, 0xD0 | (channel & 0x0F), val7 & 0x7F, 0);
+}
+
+static inline void midi1_pkt_poly_pressure(uint8_t cable, uint8_t channel,
+ uint8_t note, uint8_t val7) {
+ midi1_pkt_send(cable, 0xA, 0xA0 | (channel & 0x0F),
+ note & 0x7F, val7 & 0x7F);
+}
+
+//--------------------------------------------------------------------+
+// Scaling Helpers (MIDI 2.0 ↔ MIDI 1.0)
+//--------------------------------------------------------------------+
+
+static inline uint8_t scale_vel16_to_vel7(uint16_t v16) { return (uint8_t)(v16 >> 9); }
+static inline uint8_t scale_val32_to_val7(uint32_t v32) { return (uint8_t)(v32 >> 25); }
+static inline uint16_t scale_pb32_to_pb14(uint32_t pb32) { return (uint16_t)(pb32 >> 18); }
+
+//--------------------------------------------------------------------+
+// Dispatch Layer - Transport + Protocol Fallback
+//--------------------------------------------------------------------+
+// Follows the same idea as the UAC examples (`tud_descriptor_configuration_cb`
+// returning UAC1 or UAC2 based on bus speed): pick the path that matches the
+// state the host put us in. Here the decision is made per message because
+// MIDI 2.0 advertises both alts in a single config descriptor; the host
+// selects via SetInterface and UMP Stream protocol negotiation.
+
+static void send_note_on(uint8_t grp, uint8_t ch, uint8_t note, uint16_t vel16) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ midi1_pkt_note_on(grp, ch, note, scale_vel16_to_vel7(vel16));
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ ump_midi1_note_on(grp, ch, note, scale_vel16_to_vel7(vel16));
+ } else {
+ ump_note_on(grp, ch, note, vel16, UMP_ATTR_NONE, 0);
+ }
+}
+
+static void send_note_off(uint8_t grp, uint8_t ch, uint8_t note, uint16_t vel16) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ midi1_pkt_note_off(grp, ch, note, scale_vel16_to_vel7(vel16));
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ ump_midi1_note_off(grp, ch, note, scale_vel16_to_vel7(vel16));
+ } else {
+ ump_note_off(grp, ch, note, vel16, UMP_ATTR_NONE, 0);
+ }
+}
+
+static void send_cc(uint8_t grp, uint8_t ch, uint8_t cc, uint32_t val32) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ midi1_pkt_cc(grp, ch, cc, scale_val32_to_val7(val32));
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ ump_midi1_cc(grp, ch, cc, scale_val32_to_val7(val32));
+ } else {
+ ump_cc(grp, ch, cc, val32);
+ }
+}
+
+static void send_program_change(uint8_t grp, uint8_t ch, uint8_t program,
+ bool with_bank, uint8_t bank_msb,
+ uint8_t bank_lsb) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ if (with_bank) {
+ midi1_pkt_cc(grp, ch, 0x00, bank_msb);
+ midi1_pkt_cc(grp, ch, 0x20, bank_lsb);
+ }
+ midi1_pkt_program(grp, ch, program);
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ if (with_bank) {
+ ump_midi1_cc(grp, ch, 0x00, bank_msb);
+ ump_midi1_cc(grp, ch, 0x20, bank_lsb);
+ }
+ ump_midi1_program(grp, ch, program);
+ } else {
+ ump_program_change(grp, ch, program, with_bank, bank_msb, bank_lsb);
+ }
+}
+
+static void send_pitch_bend(uint8_t grp, uint8_t ch, uint32_t pb32) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ midi1_pkt_pitch_bend(grp, ch, scale_pb32_to_pb14(pb32));
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ ump_midi1_pitch_bend(grp, ch, scale_pb32_to_pb14(pb32));
+ } else {
+ ump_pitch_bend(grp, ch, pb32);
+ }
+}
+
+static void send_channel_pressure(uint8_t grp, uint8_t ch, uint32_t val32) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ midi1_pkt_channel_pressure(grp, ch, scale_val32_to_val7(val32));
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ ump_midi1_channel_pressure(grp, ch, scale_val32_to_val7(val32));
+ } else {
+ ump_channel_pressure(grp, ch, val32);
+ }
+}
+
+static void send_poly_pressure(uint8_t grp, uint8_t ch, uint8_t note,
+ uint32_t val32) {
+ uint8_t alt = tud_midi2_alt_setting();
+ if (alt == 0) {
+ midi1_pkt_poly_pressure(grp, ch, note, scale_val32_to_val7(val32));
+ } else if (tud_midi2_protocol() == MIDI_PROTOCOL_MIDI1) {
+ ump_midi1_poly_pressure(grp, ch, note, scale_val32_to_val7(val32));
+ } else {
+ ump_poly_pressure(grp, ch, note, val32);
+ }
+}
+
+//--------------------------------------------------------------------+
+// Song Data
+//--------------------------------------------------------------------+
+
+// Extended note event with MIDI 2.0 expression data
+typedef struct {
+ uint8_t pitch; // MIDI pitch (0-127, 0=rest)
+ uint16_t duration_ms; // Duration in ms
+ uint16_t velocity; // 16-bit velocity (MIDI 2.0)
+ uint32_t pressure; // 32-bit aftertouch (0 = none)
+ int16_t bend_cents; // Pitch bend in cents (0 = none, for vibrato/ornaments)
+} midi2_note_t;
+
+// 16-bit velocity (MIDI 2.0): values that have NO 7-bit equivalent.
+// MIDI 1.0 can only express 128 levels (0x0000, 0x0200, 0x0400 ... 0xFE00).
+// These use the full 16-bit range to prove genuine MIDI 2.0 resolution.
+#define V_PPP 0x0A3D // 2621 - between MIDI1 vel 5 and 6
+#define V_PP 0x1C71 // 7281 - between MIDI1 vel 14 and 15
+#define V_P 0x3219 // 12825 - between MIDI1 vel 24 and 25
+#define V_MP 0x4F5C // 20316 - between MIDI1 vel 39 and 40
+#define V_MF 0x6E93 // 28307 - between MIDI1 vel 55 and 56
+#define V_F 0x8DA5 // 36261 - between MIDI1 vel 70 and 71
+#define V_FF 0xAC37 // 44087 - between MIDI1 vel 85 and 86
+#define V_FFF 0xDEB8 // 57016 - between MIDI1 vel 111 and 112
+
+// Twinkle Twinkle Little Star - Traditional
+// Tempo: 120 BPM (500ms per quarter note)
+// Key: C major, 4/4
+// Demonstrates all MIDI 2.0 Channel Voice features:
+// 16-bit velocity, 32-bit CC, 32-bit pitch bend,
+// 32-bit channel pressure, per-note poly pressure,
+// per-note management, program change with bank select,
+// JR timestamps
+static const midi2_note_t song_data[] = {
+ // Phrase 1: "Twin-kle twin-kle lit-tle star" (C C G G A A G-)
+ // Crescendo pp -> mp, gentle entry
+ { .pitch = 60, .duration_ms = 500, .velocity = V_PP, .pressure = 0, .bend_cents = 0 }, // C4
+ { .pitch = 60, .duration_ms = 500, .velocity = V_P, .pressure = 0, .bend_cents = 0 }, // C4
+ { .pitch = 67, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 67, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 69, .duration_ms = 500, .velocity = V_MF, .pressure = 0x1A3D7E5F, .bend_cents = 0 }, // A4 (32-bit pressure)
+ { .pitch = 69, .duration_ms = 500, .velocity = V_MF, .pressure = 0x2B851EB9, .bend_cents = 0 }, // A4 (pressure swell)
+ { .pitch = 67, .duration_ms = 1000,.velocity = V_MF, .pressure = 0x3C6EF373, .bend_cents = 7 }, // G4 (half, bend 7 cents)
+
+ // Phrase 2: "How I won-der what you are" (F F E E D D C-)
+ // mf, sustained
+ { .pitch = 65, .duration_ms = 500, .velocity = V_MF, .pressure = 0, .bend_cents = 0 }, // F4
+ { .pitch = 65, .duration_ms = 500, .velocity = V_MF, .pressure = 0, .bend_cents = 0 }, // F4
+ { .pitch = 64, .duration_ms = 500, .velocity = V_MF, .pressure = 0x1E4C2B7A, .bend_cents = 0 }, // E4
+ { .pitch = 64, .duration_ms = 500, .velocity = V_MF, .pressure = 0x2D5A8FC1, .bend_cents = 0 }, // E4 (aftertouch swell)
+ { .pitch = 62, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // D4
+ { .pitch = 62, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // D4
+ { .pitch = 60, .duration_ms = 1000,.velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // C4 (half, resolve)
+
+ // Phrase 3: "Up a-bove the world so high" (G G F F E E D-)
+ // f, building intensity
+ { .pitch = 67, .duration_ms = 500, .velocity = V_F, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 67, .duration_ms = 500, .velocity = V_F, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 65, .duration_ms = 500, .velocity = V_F, .pressure = 0x2F8A4E13, .bend_cents = 0 }, // F4
+ { .pitch = 65, .duration_ms = 500, .velocity = V_MF, .pressure = 0x41B2C9D7, .bend_cents = 0 }, // F4 (triggers poly pressure)
+ { .pitch = 64, .duration_ms = 500, .velocity = V_MF, .pressure = 0, .bend_cents = 0 }, // E4
+ { .pitch = 64, .duration_ms = 500, .velocity = V_MF, .pressure = 0, .bend_cents = 0 }, // E4
+ { .pitch = 62, .duration_ms = 1000,.velocity = V_MF, .pressure = 0x537DC2A6, .bend_cents = 13 }, // D4 (half, vibrato 13 cents)
+
+ // Phrase 4: "Like a dia-mond in the sky" (G G F F E E D-)
+ // ff, expressive peak
+ { .pitch = 67, .duration_ms = 500, .velocity = V_FF, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 67, .duration_ms = 500, .velocity = V_FF, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 65, .duration_ms = 500, .velocity = V_F, .pressure = 0x44E7B8D2, .bend_cents = 0 }, // F4 (triggers poly pressure)
+ { .pitch = 65, .duration_ms = 500, .velocity = V_F, .pressure = 0x56A3F14B, .bend_cents = 0 }, // F4 (triggers poly pressure)
+ { .pitch = 64, .duration_ms = 500, .velocity = V_MF, .pressure = 0x2C8E1F5A, .bend_cents = 0 }, // E4
+ { .pitch = 64, .duration_ms = 500, .velocity = V_MF, .pressure = 0x1D73A4E8, .bend_cents = 0 }, // E4
+ { .pitch = 62, .duration_ms = 1000,.velocity = V_MF, .pressure = 0x63F5B17D, .bend_cents = 19 }, // D4 (half, vibrato 19 cents)
+
+ // Phrase 5: "Twin-kle twin-kle lit-tle star" (C C G G A A G-)
+ // Diminuendo mf -> mp
+ { .pitch = 60, .duration_ms = 500, .velocity = V_MF, .pressure = 0, .bend_cents = 0 }, // C4
+ { .pitch = 60, .duration_ms = 500, .velocity = V_MF, .pressure = 0, .bend_cents = 0 }, // C4
+ { .pitch = 67, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 67, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // G4
+ { .pitch = 69, .duration_ms = 500, .velocity = V_MP, .pressure = 0x1B4F6D83, .bend_cents = 0 }, // A4
+ { .pitch = 69, .duration_ms = 500, .velocity = V_P, .pressure = 0x0E29C5A1, .bend_cents = 0 }, // A4
+ { .pitch = 67, .duration_ms = 1000,.velocity = V_P, .pressure = 0x2A6D3B9E, .bend_cents = 5 }, // G4 (half, gentle bend 5 cents)
+
+ // Phrase 6: "How I won-der what you are" (F F E E D D C-)
+ // Dying away mp -> ppp
+ { .pitch = 65, .duration_ms = 500, .velocity = V_MP, .pressure = 0, .bend_cents = 0 }, // F4
+ { .pitch = 65, .duration_ms = 500, .velocity = V_P, .pressure = 0, .bend_cents = 0 }, // F4
+ { .pitch = 64, .duration_ms = 500, .velocity = V_P, .pressure = 0, .bend_cents = 0 }, // E4
+ { .pitch = 64, .duration_ms = 500, .velocity = V_PP, .pressure = 0, .bend_cents = 0 }, // E4
+ { .pitch = 62, .duration_ms = 500, .velocity = V_PP, .pressure = 0, .bend_cents = 0 }, // D4
+ { .pitch = 62, .duration_ms = 500, .velocity = V_PPP, .pressure = 0, .bend_cents = 0 }, // D4
+ { .pitch = 60, .duration_ms = 2000,.velocity = V_PPP, .pressure = 0x07A1E3C9, .bend_cents = 0 }, // C4 (fermata)
+
+ // Silence before loop
+ { .pitch = 0, .duration_ms = 1000,.velocity = 0, .pressure = 0, .bend_cents = 0 },
+
+ // End marker
+ { .pitch = 0, .duration_ms = 0, .velocity = 0, .pressure = 0, .bend_cents = 0 },
+};
+
+#define SONG_LENGTH (sizeof(song_data) / sizeof(midi2_note_t))
+
+//--------------------------------------------------------------------+
+// Song Playback State Machine
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint32_t current_note_idx;
+ uint32_t note_start_ms;
+ uint8_t active_pitch;
+ bool note_is_active;
+ bool setup_sent; // Initial setup (Program Change, CC) sent?
+ uint32_t loop_count;
+} song_state_t;
+
+static song_state_t song = { 0 };
+
+// Forward declarations
+void update_song_playback(uint32_t now_ms);
+void send_initial_setup(void);
+
+//--------------------------------------------------------------------+
+// MIDI 2.0 Device Callbacks (override weak stubs from middleware)
+//--------------------------------------------------------------------+
+
+void tud_midi2_rx_cb(uint8_t itf) {
+ // Drain the RX FIFO in a loop until empty. Leaving words in the FIFO
+ // across callbacks can prevent subsequent bulk OUT transfers from landing.
+ uint32_t words[8];
+ uint32_t n;
+ while ((n = tud_midi2_n_ump_read(itf, words, TU_ARRAY_SIZE(words))) > 0) {
+ (void) n;
+ }
+}
+
+// Reset playback state and re-send setup when host switches alt setting or
+// renegotiates the UMP Stream protocol.
+void tud_midi2_set_itf_cb(uint8_t itf, uint8_t alt) {
+ (void)itf;
+ song.setup_sent = false;
+ printf("[ALT] Host selected alt=%u\r\n", (unsigned)alt);
+}
+
+//--------------------------------------------------------------------+
+// Initial Setup - Program Change, CC, Per-Note Management
+//--------------------------------------------------------------------+
+
+void send_initial_setup(void) {
+ // JR Timestamp is UMP-only (Utility MT 0x0); skipped on Alt 0 transport.
+ if (tud_midi2_alt_setting() == 1) ump_jr_timestamp(0x0001);
+
+ send_program_change(0, 0, 0, true, 0, 0);
+ send_cc(0, 0, 7, 0xCCCCCCCC); // Volume 80%
+ send_cc(0, 0, 11, 0xFFFFFFFF); // Expression 100%
+ send_cc(0, 0, 64, 0x00000000); // Sustain off
+ send_cc(0, 0, 1, 0x20000000); // Modulation
+ send_cc(0, 0, 10, 0x80000000); // Pan center
+
+ // Per-Note Management is MIDI 2.0 exclusive (MT 0x4 status 0xF).
+ // Skipped when the active path falls back to MIDI 1.0 in any form.
+ if (tud_midi2_alt_setting() == 1 &&
+ tud_midi2_protocol() == MIDI_PROTOCOL_MIDI2) {
+ ump_per_note_mgmt(0, 0, 0, false, true);
+ }
+
+ send_pitch_bend(0, 0, 0x80000000); // Center
+ send_channel_pressure(0, 0, 0x00000000);
+
+ printf("[SETUP] Piano | Vol 80%% | alt=%u proto=%u\r\n",
+ (unsigned)tud_midi2_alt_setting(),
+ (unsigned)tud_midi2_protocol());
+}
+
+//--------------------------------------------------------------------+
+// Pitch Bend Conversion: cents to 32-bit value
+//--------------------------------------------------------------------+
+
+// Convert pitch bend in cents (-200 to +200) to 32-bit UMP value
+// Center = 0x80000000, range = +/- 2 semitones (200 cents)
+static inline uint32_t cents_to_pitch_bend(int16_t cents) {
+ if (cents == 0) return 0x80000000;
+ // Scale: 200 cents = full range (0x7FFFFFFF deviation from center)
+ int32_t offset = (int32_t)(((int64_t)cents * 0x7FFFFFFF) / 200);
+ return (uint32_t)((int32_t)0x80000000 + offset);
+}
+
+//--------------------------------------------------------------------+
+// Song Playback Logic - Full MIDI 2.0 Expression
+//--------------------------------------------------------------------+
+
+void update_song_playback(uint32_t now_ms) {
+ const midi2_note_t *current = &song_data[song.current_note_idx];
+
+ if (!song.setup_sent) {
+ send_initial_setup();
+ song.setup_sent = true;
+ song.note_start_ms = now_ms;
+ }
+
+ // Note duration elapsed: send Note Off, advance
+ if (song.note_is_active && (now_ms - song.note_start_ms) >= current->duration_ms) {
+ if (song.active_pitch > 0) {
+ if (current->bend_cents != 0) send_pitch_bend(0, 0, 0x80000000);
+ if (current->pressure > 0) send_channel_pressure(0, 0, 0x00000000);
+ send_note_off(0, 0, song.active_pitch, V_P);
+ }
+
+ song.note_is_active = false;
+ song.current_note_idx++;
+
+ if (song.current_note_idx >= SONG_LENGTH) {
+ song.current_note_idx = 0;
+ song.setup_sent = false;
+ song.loop_count++;
+ printf("\r\n=== Loop %lu ===\r\n", (unsigned long)song.loop_count);
+ }
+
+ song.note_start_ms = now_ms;
+ }
+
+ // Start next note
+ if (!song.note_is_active && song.current_note_idx < SONG_LENGTH) {
+ const midi2_note_t *next = &song_data[song.current_note_idx];
+
+ if (next->duration_ms == 0) {
+ song.current_note_idx = 0;
+ song.setup_sent = false;
+ song.loop_count++;
+ printf("\r\n=== Loop %lu ===\r\n", (unsigned long)song.loop_count);
+ return;
+ }
+
+ if (next->pitch > 0) {
+ // JR Timestamp is UMP-only; skip on Alt 0 transport.
+ if (tud_midi2_alt_setting() == 1) {
+ ump_jr_timestamp((uint16_t)(now_ms & 0xFFFF));
+ }
+ if (next->bend_cents != 0) {
+ send_pitch_bend(0, 0, cents_to_pitch_bend(next->bend_cents));
+ }
+ send_note_on(0, 0, next->pitch, next->velocity);
+ if (next->pressure > 0) {
+ send_channel_pressure(0, 0, next->pressure);
+ }
+ if (next->pressure > 0x40000000 && next->duration_ms > 500) {
+ send_poly_pressure(0, 0, next->pitch, next->pressure);
+ }
+
+ song.active_pitch = next->pitch;
+ song.note_is_active = true;
+ }
+
+ // Rest: honor duration
+ if (!song.note_is_active) {
+ song.active_pitch = 0;
+ song.note_is_active = true;
+ song.note_start_ms = now_ms;
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+
+int main(void) {
+ board_init();
+ printf("\r\n");
+ printf("===========================================\r\n");
+ printf(" TinyUSB MIDI 2.0 Device\r\n");
+ printf("===========================================\r\n");
+ printf("Tempo: 120 BPM | Song: %u notes\r\n", (unsigned)SONG_LENGTH);
+ printf("Transport + protocol fallback:\r\n");
+ printf(" Alt 0 -> USB-MIDI 1.0 32-bit Event Packets (packet_write)\r\n");
+ printf(" Alt 1 + MIDI1 -> UMP MT 0x2 MIDI 1.0 Channel Voice (ump_write)\r\n");
+ printf(" Alt 1 + MIDI2 -> UMP MT 0x4 MIDI 2.0 Channel Voice (ump_write)\r\n");
+ printf("Status: Initializing...\r\n");
+
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+
+ board_init_after_tusb();
+ board_led_write(true);
+
+ uint32_t last_report_ms = 0;
+
+ while (1) {
+ tud_task();
+
+ uint32_t now_ms = tusb_time_millis_api();
+
+ if (tud_midi2_mounted()) {
+ update_song_playback(now_ms);
+ board_led_write(song.active_pitch > 0);
+ } else {
+ board_led_write((now_ms / 500) & 1);
+ }
+
+ // Status report every 10 seconds
+ if (now_ms - last_report_ms > 10000) {
+ last_report_ms = now_ms;
+ if (tud_midi2_mounted()) {
+ printf("[%lums] Playing idx %lu/%u loop %lu\r\n",
+ (unsigned long)now_ms,
+ (unsigned long)song.current_note_idx,
+ (unsigned)SONG_LENGTH,
+ (unsigned long)song.loop_count);
+ } else {
+ printf("[%lums] Waiting for host...\r\n", (unsigned long)now_ms);
+ }
+ }
+ }
+}
diff --git a/examples/device/midi2_device/src/tusb_config.h b/examples/device/midi2_device/src/tusb_config.h
new file mode 100644
index 000000000..1ada0015f
--- /dev/null
+++ b/examples/device/midi2_device/src/tusb_config.h
@@ -0,0 +1,88 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT 0
+#endif
+
+#ifndef BOARD_TUD_MAX_SPEED
+#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_MIDI2 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/midi2_device/src/usb_descriptors.c b/examples/device/midi2_device/src/usb_descriptors.c
new file mode 100644
index 000000000..f035f9c48
--- /dev/null
+++ b/examples/device/midi2_device/src/usb_descriptors.c
@@ -0,0 +1,146 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "class/audio/audio.h"
+#include "class/midi/midi.h"
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = 0xcafe,
+ .idProduct = 0x4062, // MIDI 2.0 Device
+ .bcdDevice = 0x0100,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01
+};
+
+uint8_t const * tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor - MIDI 2.0
+//--------------------------------------------------------------------+
+
+enum {
+ ITF_NUM_MIDI2 = 0, // Audio Control interface
+ ITF_NUM_MIDI2_STREAMING, // MIDI Streaming interface (auto-created by TUD_MIDI2_DESCRIPTOR)
+ ITF_NUM_TOTAL
+};
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI2_DESC_LEN)
+
+// 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
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+ // MIDI 2.0 Interface
+ TUD_MIDI2_DESCRIPTOR(ITF_NUM_MIDI2, 0, EPNUM_MIDI2_OUT, EPNUM_MIDI2_IN, 64)
+};
+
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index;
+ return desc_fs_configuration;
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER = 1,
+ STRID_PRODUCT = 2,
+ STRID_SERIAL = 3,
+};
+
+static char const *string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 }, // 0: Language
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB MIDI 2.0", // 2: Product
+ NULL, // 3: Serial
+};
+
+static uint16_t _desc_str[32 + 1];
+
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch ( index ) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
+ return NULL;
+ }
+
+ const char *str = string_desc_arr[index];
+ chr_count = strlen(str);
+ const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1;
+ if ( chr_count > max_count ) {
+ chr_count = max_count;
+ }
+
+ for ( size_t i = 0; i < chr_count; i++ ) {
+ _desc_str[1 + i] = str[i];
+ }
+ break;
+ }
+
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ return _desc_str;
+}
diff --git a/examples/device/midi_test/CMakeLists.txt b/examples/device/midi_test/CMakeLists.txt
index 6a7e68c3d..09fbf20f0 100644
--- a/examples/device/midi_test/CMakeLists.txt
+++ b/examples/device/midi_test/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(midi_test C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/midi_test/Makefile b/examples/device/midi_test/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/midi_test/Makefile
+++ b/examples/device/midi_test/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/midi_test/skip.txt b/examples/device/midi_test/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/midi_test/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/midi_test/src/main.c b/examples/device/midi_test/src/main.c
index fd58e3021..1154c1d60 100644
--- a/examples/device/midi_test/src/main.c
+++ b/examples/device/midi_test/src/main.c
@@ -134,7 +134,7 @@ void midi_task(void)
}
// send note periodically
- if (board_millis() - start_ms < 286) {
+ if (tusb_time_millis_api() - start_ms < 286) {
return; // not enough time
}
start_ms += 286;
@@ -174,7 +174,7 @@ void led_blinking_task(void)
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/midi_test/src/tusb_config.h b/examples/device/midi_test/src/tusb_config.h
index 314dde438..f4282b2d7 100644
--- a/examples/device/midi_test/src/tusb_config.h
+++ b/examples/device/midi_test/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -105,4 +105,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/midi_test/src/usb_descriptors.c b/examples/device/midi_test/src/usb_descriptors.c
index 384742ae8..99c798ce1 100644
--- a/examples/device/midi_test/src/usb_descriptors.c
+++ b/examples/device/midi_test/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
@@ -87,7 +87,7 @@ enum {
#define EPNUM_MIDI_OUT 0x02
#define EPNUM_MIDI_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MIDI_OUT 0x01
@@ -98,7 +98,7 @@ enum {
#define EPNUM_MIDI_IN 0x81
#endif
-uint8_t const desc_fs_configuration[] = {
+static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -107,7 +107,7 @@ uint8_t const desc_fs_configuration[] = {
};
#if TUD_OPT_HIGH_SPEED
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -143,7 +143,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] = {
+static char const *string_desc_arr[] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
diff --git a/examples/device/midi_test_freertos/CMakeLists.txt b/examples/device/midi_test_freertos/CMakeLists.txt
index 6ce9e72fe..7e5d3e89e 100644
--- a/examples/device/midi_test_freertos/CMakeLists.txt
+++ b/examples/device/midi_test_freertos/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(midi_test_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example with FreeRTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} freertos)
+family_configure_device_example(${PROJECT_NAME} freertos)
diff --git a/examples/device/midi_test_freertos/Makefile b/examples/device/midi_test_freertos/Makefile
index 26cd83486..ebacfecdf 100644
--- a/examples/device/midi_test_freertos/Makefile
+++ b/examples/device/midi_test_freertos/Makefile
@@ -1,15 +1,15 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c \
src/usb_descriptors.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/midi_test_freertos/skip.txt b/examples/device/midi_test_freertos/skip.txt
index 650bf355b..97d8e168b 100644
--- a/examples/device/midi_test_freertos/skip.txt
+++ b/examples/device/midi_test_freertos/skip.txt
@@ -1,16 +1,18 @@
+mcu:CH32F20X
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
+mcu:CH583
mcu:CXD56
mcu:F1C100S
mcu:GD32VF103
mcu:MCXA15
mcu:MKL25ZXX
mcu:MSP430x5xx
-mcu:RP2040
+mcu:FT90X
mcu:SAMD11
-mcu:SAMX7X
mcu:VALENTYUSB_EPTRI
mcu:RAXXX
family:broadcom_32bit
family:broadcom_64bit
+family:hpmicro
diff --git a/examples/device/midi_test_freertos/src/main.c b/examples/device/midi_test_freertos/src/main.c
index 9dd66c526..f5267214e 100644
--- a/examples/device/midi_test_freertos/src/main.c
+++ b/examples/device/midi_test_freertos/src/main.c
@@ -48,7 +48,7 @@
#endif
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
-#define MIDI_STACK_SIZE configMINIMAL_STACK_SIZE
+#define MIDI_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 2 : 1))
// static task
#if configSUPPORT_STATIC_ALLOCATION
diff --git a/examples/device/midi_test_freertos/src/tusb_config.h b/examples/device/midi_test_freertos/src/tusb_config.h
index 0ffdc37fb..c158f5197 100644
--- a/examples/device/midi_test_freertos/src/tusb_config.h
+++ b/examples/device/midi_test_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -105,4 +105,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/midi_test_freertos/src/usb_descriptors.c b/examples/device/midi_test_freertos/src/usb_descriptors.c
index 384742ae8..99c798ce1 100644
--- a/examples/device/midi_test_freertos/src/usb_descriptors.c
+++ b/examples/device/midi_test_freertos/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
@@ -87,7 +87,7 @@ enum {
#define EPNUM_MIDI_OUT 0x02
#define EPNUM_MIDI_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MIDI_OUT 0x01
@@ -98,7 +98,7 @@ enum {
#define EPNUM_MIDI_IN 0x81
#endif
-uint8_t const desc_fs_configuration[] = {
+static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -107,7 +107,7 @@ uint8_t const desc_fs_configuration[] = {
};
#if TUD_OPT_HIGH_SPEED
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -143,7 +143,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] = {
+static char const *string_desc_arr[] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
diff --git a/examples/device/msc_dual_lun/CMakeLists.txt b/examples/device/msc_dual_lun/CMakeLists.txt
index 3955bfb49..72a55eebc 100644
--- a/examples/device/msc_dual_lun/CMakeLists.txt
+++ b/examples/device/msc_dual_lun/CMakeLists.txt
@@ -2,13 +2,10 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(msc_dual_lun C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
@@ -18,7 +15,7 @@ endif()
if (RTOS STREQUAL zephyr)
set(EXE_NAME app)
else()
- set(EXE_NAME ${PROJECT})
+ set(EXE_NAME ${PROJECT_NAME})
add_executable(${EXE_NAME})
endif()
diff --git a/examples/device/msc_dual_lun/Makefile b/examples/device/msc_dual_lun/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/msc_dual_lun/Makefile
+++ b/examples/device/msc_dual_lun/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/msc_dual_lun/prj.conf b/examples/device/msc_dual_lun/prj.conf
index 2f5139d9d..9e86a118d 100644
--- a/examples/device/msc_dual_lun/prj.conf
+++ b/examples/device/msc_dual_lun/prj.conf
@@ -3,4 +3,3 @@ CONFIG_FPU=y
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_NRFX_POWER=y
-CONFIG_NRFX_UARTE0=y
diff --git a/examples/device/msc_dual_lun/src/main.c b/examples/device/msc_dual_lun/src/main.c
index 62b1c872a..1d764f12c 100644
--- a/examples/device/msc_dual_lun/src/main.c
+++ b/examples/device/msc_dual_lun/src/main.c
@@ -31,7 +31,7 @@
#include "tusb.h"
//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF PROTYPES
+// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
/* Blink pattern
@@ -41,71 +41,177 @@
*/
enum {
BLINK_NOT_MOUNTED = 250,
- BLINK_MOUNTED = 1000,
- BLINK_SUSPENDED = 2500,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
-void led_blinking_task(void);
+// Task parameter type: ULONG for ThreadX, void* for FreeRTOS and noos
+#if CFG_TUSB_OS == OPT_OS_THREADX
+ #define RTOS_PARAM ULONG
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+ #define RTOS_PARAM void*
+ static void freertos_init(void);
+#else
+ #define RTOS_PARAM void*
+#endif
-/*------------- MAIN -------------*/
-int main(void) {
- board_init();
+void led_blinking_task(RTOS_PARAM param);
- // init device stack on configured roothub port
+//--------------------------------------------------------------------+
+// USB Device Task
+//--------------------------------------------------------------------+
+static void usb_device_init(void) {
tusb_rhport_init_t dev_init = {
- .role = TUSB_ROLE_DEVICE,
+ .role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
-
board_init_after_tusb();
+}
+
+#if CFG_TUSB_OS_HAS_SCHEDULER
+static void usb_device_task(RTOS_PARAM param) {
+ (void) param;
+ usb_device_init();
while (1) {
- tud_task(); // tinyusb device task
- led_blinking_task();
+ tud_task();
}
}
+#endif
//--------------------------------------------------------------------+
-// Device callbacks
+// Main
//--------------------------------------------------------------------+
+int main(void) {
+ board_init();
-// Invoked when device is mounted
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ freertos_init();
+
+#elif CFG_TUSB_OS == OPT_OS_THREADX
+ tx_kernel_enter();
+
+#else
+ // noos + pico-sdk: init USB then run polling loop
+ usb_device_init();
+
+ while (1) {
+ tud_task();
+ led_blinking_task(NULL);
+ }
+#endif
+}
+
+#ifdef ESP_PLATFORM
+void app_main(void) {
+ main();
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Device callbacks
+//--------------------------------------------------------------------+
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
-// Invoked when device is unmounted
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
-// Invoked when usb bus is suspended
-// remote_wakeup_en : if host allow us to perform remote wakeup
-// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
-// Invoked when usb bus is resumed
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
-// BLINKING TASK
+// Blinking Task
//--------------------------------------------------------------------+
-void led_blinking_task(void) {
+void led_blinking_task(RTOS_PARAM param) {
+ (void) param;
static uint32_t start_ms = 0;
static bool led_state = false;
- // Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
- start_ms += blink_interval_ms;
+ while (1) {
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
+#elif CFG_TUSB_OS == OPT_OS_THREADX
+ tx_thread_sleep(_osal_ms2tick(blink_interval_ms));
+#else
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
+#endif
+
+ start_ms += blink_interval_ms;
+ board_led_write(led_state);
+ led_state = 1 - led_state; // toggle
+ }
+}
+
+//--------------------------------------------------------------------+
+// FreeRTOS
+//--------------------------------------------------------------------+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+
+#ifdef ESP_PLATFORM
+#define USBD_STACK_SIZE 4096
+#else
+#define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2 * (CFG_TUSB_DEBUG ? 2 : 1))
+#endif
+#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
- board_led_write(led_state);
- led_state = 1 - led_state; // toggle
+#if configSUPPORT_STATIC_ALLOCATION
+static StackType_t _usb_device_stack[USBD_STACK_SIZE];
+static StaticTask_t _usb_device_taskdef;
+static StackType_t _blinky_stack[BLINKY_STACK_SIZE];
+static StaticTask_t _blinky_taskdef;
+#endif
+
+
+static void freertos_init(void) {
+ #if configSUPPORT_STATIC_ALLOCATION
+ xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, _usb_device_stack, &_usb_device_taskdef);
+ xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, _blinky_stack, &_blinky_taskdef);
+ #else
+ xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
+ #endif
+ #ifndef ESP_PLATFORM
+ vTaskStartScheduler();
+ #endif
+}
+
+//--------------------------------------------------------------------+
+// ThreadX
+//--------------------------------------------------------------------+
+#elif CFG_TUSB_OS == OPT_OS_THREADX
+
+#define USBD_STACK_SIZE 4096
+#define BLINKY_STACK_SIZE 512
+
+static TX_THREAD _usb_device_thread;
+static ULONG _usb_device_stack[USBD_STACK_SIZE / sizeof(ULONG)];
+static TX_THREAD _blinky_thread;
+static ULONG _blinky_stack[BLINKY_STACK_SIZE / sizeof(ULONG)];
+
+void tx_application_define(void *first_unused_memory) {
+ (void) first_unused_memory;
+ static CHAR usbd_name[] = "usbd";
+ static CHAR blinky_name[] = "blinky";
+ tx_thread_create(&_usb_device_thread, usbd_name, usb_device_task, 0,
+ _usb_device_stack, USBD_STACK_SIZE,
+ 0, 0, TX_NO_TIME_SLICE, TX_AUTO_START);
+ tx_thread_create(&_blinky_thread, blinky_name, led_blinking_task, 0,
+ _blinky_stack, BLINKY_STACK_SIZE,
+ 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
}
+
+#endif
diff --git a/examples/device/msc_dual_lun/src/msc_disk_dual.c b/examples/device/msc_dual_lun/src/msc_disk_dual.c
index 775fa047e..694da11db 100644
--- a/examples/device/msc_dual_lun/src/msc_disk_dual.c
+++ b/examples/device/msc_dual_lun/src/msc_disk_dual.c
@@ -217,9 +217,9 @@ uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uin
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
- memcpy(inquiry_resp->vendor_id, vid, strlen(vid));
- memcpy(inquiry_resp->product_id, pid, strlen(pid));
- memcpy(inquiry_resp->product_rev, rev, strlen(rev));
+ strncpy((char*) inquiry_resp->vendor_id, vid, 8);
+ strncpy((char*) inquiry_resp->product_id, pid, 16);
+ strncpy((char*) inquiry_resp->product_rev, rev, 4);
return sizeof(scsi_inquiry_resp_t); // 36 bytes
}
@@ -227,9 +227,7 @@ uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uin
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
- if ( lun == 1 && board_button_read() ) return false;
-
- return true; // RAM disk is always ready
+ return ( lun == 1 && board_button_read() ) ? false : true;
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
diff --git a/examples/device/msc_dual_lun/src/tusb_config.h b/examples/device/msc_dual_lun/src/tusb_config.h
index 9cbbbade9..4fb6816c6 100644
--- a/examples/device/msc_dual_lun/src/tusb_config.h
+++ b/examples/device/msc_dual_lun/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -104,4 +104,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/msc_dual_lun/src/usb_descriptors.c b/examples/device/msc_dual_lun/src/usb_descriptors.c
index efb9a966d..b328cf17f 100644
--- a/examples/device/msc_dual_lun/src/usb_descriptors.c
+++ b/examples/device/msc_dual_lun/src/usb_descriptors.c
@@ -32,14 +32,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -91,11 +91,17 @@ enum
#define EPNUM_MSC_OUT 0x02
#define EPNUM_MSC_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_MSC_OUT 0x01
- #define EPNUM_MSC_IN 0x82
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_MSC_OUT 0x08
+ #define EPNUM_MSC_IN 0x89
+ #else
+ #define EPNUM_MSC_OUT 0x01
+ #define EPNUM_MSC_IN 0x82
+ #endif
#else
#define EPNUM_MSC_OUT 0x01
@@ -103,7 +109,7 @@ enum
#endif
-uint8_t const desc_fs_configuration[] =
+static uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -113,7 +119,7 @@ uint8_t const desc_fs_configuration[] =
};
#if TUD_OPT_HIGH_SPEED
-uint8_t const desc_hs_configuration[] =
+static uint8_t const desc_hs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -151,7 +157,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/device/mtp/CMakeLists.txt b/examples/device/mtp/CMakeLists.txt
index e91eb8fd9..a9f2f1a90 100644
--- a/examples/device/mtp/CMakeLists.txt
+++ b/examples/device/mtp/CMakeLists.txt
@@ -2,13 +2,10 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(mtp C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
@@ -18,7 +15,7 @@ endif()
if (RTOS STREQUAL zephyr)
set(EXE_NAME app)
else()
- set(EXE_NAME ${PROJECT})
+ set(EXE_NAME ${PROJECT_NAME})
add_executable(${EXE_NAME})
endif()
diff --git a/examples/device/mtp/Makefile b/examples/device/mtp/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/mtp/Makefile
+++ b/examples/device/mtp/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/mtp/src/main.c b/examples/device/mtp/src/main.c
index 57d1535b2..6ffa435d1 100644
--- a/examples/device/mtp/src/main.c
+++ b/examples/device/mtp/src/main.c
@@ -102,7 +102,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c
index 5f56ca24d..b7d062c64 100644
--- a/examples/device/mtp/src/mtp_fs_example.c
+++ b/examples/device/mtp/src/mtp_fs_example.c
@@ -39,12 +39,15 @@
#define DEV_PROP_FRIENDLY_NAME "TinyUSB MTP"
//------------- storage info -------------//
-#define STORAGE_DESCRIPTRION { 'd', 'i', 's', 'k', 0 }
+#define STORAGE_DESCRIPTION { 'd', 'i', 's', 'k', 0 }
#define VOLUME_IDENTIFIER { 'v', 'o', 'l', 0 }
-typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION),
- TU_ARRAY_SIZE(((uint16_t[])VOLUME_IDENTIFIER))
-) storage_info_t;
+enum {
+ STORAGE_DESC_LEN = TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTION),
+ VOLUME_ID_LEN = TU_ARRAY_SIZE((uint16_t[])VOLUME_IDENTIFIER)
+};
+
+typedef MTP_STORAGE_INFO_STRUCT(STORAGE_DESC_LEN, VOLUME_ID_LEN) storage_info_t;
storage_info_t storage_info = {
#ifdef CFG_EXAMPLE_MTP_READONLY
@@ -59,11 +62,11 @@ storage_info_t storage_info = {
.free_space_in_bytes = 0, // calculated at runtime
.free_space_in_objects = 0, // calculated at runtime
.storage_description = {
- .count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t),
- .utf16 = STORAGE_DESCRIPTRION
+ .count = (TU_FIELD_SIZE(storage_info_t, storage_description)-1) / sizeof(uint16_t),
+ .utf16 = STORAGE_DESCRIPTION
},
.volume_identifier = {
- .count = (TU_FIELD_SZIE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t),
+ .count = (TU_FIELD_SIZE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t),
.utf16 = VOLUME_IDENTIFIER
}
};
@@ -111,8 +114,8 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = {
.image_bit_depth = 0,
.parent = 0,
.association_type = MTP_ASSOCIATION_UNDEFINED,
+ .size = sizeof(README_TXT_CONTENT)-1,
.data = (uint8_t*) (uintptr_t) README_TXT_CONTENT,
- .size = sizeof(README_TXT_CONTENT)-1
},
{
.name = { 't', 'i', 'n', 'y', 'u', 's', 'b', '.', 'p', 'n', 'g', 0 }, // "tinyusb.png"
@@ -123,8 +126,8 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = {
.image_bit_depth = 32,
.parent = 0,
.association_type = MTP_ASSOCIATION_UNDEFINED,
- .data = (uint8_t*) (uintptr_t) logo_bin,
- .size = logo_len,
+ .size = LOGO_LEN,
+ .data = (uint8_t*) (uintptr_t) logo_bin
}
};
@@ -140,6 +143,7 @@ static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_object_handles(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_object_info(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data);
+static int32_t fs_get_partial_object(tud_mtp_cb_data_t* cb_data);
static int32_t fs_delete_object(tud_mtp_cb_data_t* cb_data);
static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data);
static int32_t fs_send_object(tud_mtp_cb_data_t* cb_data);
@@ -161,6 +165,7 @@ fs_op_handler_dict_t fs_op_handler_dict[] = {
{ MTP_OP_GET_OBJECT_HANDLES, fs_get_object_handles },
{ MTP_OP_GET_OBJECT_INFO, fs_get_object_info },
{ MTP_OP_GET_OBJECT, fs_get_object },
+ { MTP_OP_GET_PARTIAL_OBJECT, fs_get_partial_object },
{ MTP_OP_DELETE_OBJECT, fs_delete_object },
{ MTP_OP_SEND_OBJECT_INFO, fs_send_object_info },
{ MTP_OP_SEND_OBJECT, fs_send_object },
@@ -272,11 +277,11 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) {
resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED;
} else {
resp_code = handler(cb_data);
- if (resp_code > MTP_RESP_UNDEFINED) {
- // send response if needed
- io_container->header->code = (uint16_t)resp_code;
- tud_mtp_response_send(io_container);
- }
+ }
+ if (resp_code > MTP_RESP_UNDEFINED) {
+ // send response if needed
+ io_container->header->code = (uint16_t)resp_code;
+ tud_mtp_response_send(io_container);
}
return resp_code;
@@ -299,11 +304,11 @@ int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) {
resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED;
} else {
resp_code = handler(cb_data);
- if (resp_code > MTP_RESP_UNDEFINED) {
- // send response if needed
- io_container->header->code = (uint16_t)resp_code;
- tud_mtp_response_send(io_container);
- }
+ }
+ if (resp_code > MTP_RESP_UNDEFINED) {
+ // send response if needed
+ io_container->header->code = (uint16_t)resp_code;
+ tud_mtp_response_send(io_container);
}
return 0;
@@ -320,9 +325,17 @@ int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) {
break;
}
// parameter is: storage id, parent handle, new handle
- mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID);
- mtp_container_add_uint32(resp, f->parent);
- mtp_container_add_uint32(resp, send_obj_handle);
+ (void) mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID);
+ (void) mtp_container_add_uint32(resp, f->parent);
+ (void) mtp_container_add_uint32(resp, send_obj_handle);
+ resp->header->code = MTP_RESP_OK;
+ break;
+ }
+
+ case MTP_OP_GET_PARTIAL_OBJECT: {
+ // response parameter: actual length of data sent excluding container header
+ const uint32_t len = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t);
+ (void) mtp_container_add_uint32(resp, len);
resp->header->code = MTP_RESP_OK;
break;
}
@@ -346,19 +359,22 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) {
//--------------------------------------------------------------------+
static int32_t fs_get_device_info(tud_mtp_cb_data_t* cb_data) {
// Device info is already prepared up to playback formats. Application only need to add string fields
+ int32_t resp_code = 0;
mtp_container_info_t* io_container = &cb_data->io_container;
- mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER);
- mtp_container_add_cstring(io_container, DEV_INFO_MODEL);
- mtp_container_add_cstring(io_container, DEV_INFO_VERSION);
+ (void) mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER);
+ (void) mtp_container_add_cstring(io_container, DEV_INFO_MODEL);
+ (void) mtp_container_add_cstring(io_container, DEV_INFO_VERSION);
enum { MAX_SERIAL_NCHARS = 32 };
uint16_t serial_utf16[MAX_SERIAL_NCHARS+1];
size_t nchars = board_usb_get_serial(serial_utf16, MAX_SERIAL_NCHARS);
serial_utf16[tu_min32(nchars, MAX_SERIAL_NCHARS)] = 0; // ensure null termination
- mtp_container_add_string(io_container, serial_utf16);
+ (void) mtp_container_add_string(io_container, serial_utf16);
- tud_mtp_data_send(io_container);
- return 0;
+ if (!tud_mtp_data_send(io_container)) {
+ resp_code = MTP_RESP_DEVICE_BUSY;
+ }
+ return resp_code;
}
static int32_t fs_open_close_session(tud_mtp_cb_data_t* cb_data) {
@@ -380,7 +396,7 @@ static int32_t fs_open_close_session(tud_mtp_cb_data_t* cb_data) {
static int32_t fs_get_storage_ids(tud_mtp_cb_data_t* cb_data) {
mtp_container_info_t* io_container = &cb_data->io_container;
uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID };
- mtp_container_add_auint32(io_container, 1, storage_ids);
+ (void) mtp_container_add_auint32(io_container, 1, storage_ids);
tud_mtp_data_send(io_container);
return 0;
}
@@ -391,10 +407,10 @@ static int32_t fs_get_storage_info(tud_mtp_cb_data_t* cb_data) {
const uint32_t storage_id = command->params[0];
TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1);
// update storage info with current free space
- storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES;
+ storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + LOGO_LEN + FS_MAX_CAPACITY_BYTES;
storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count();
storage_info.free_space_in_bytes = storage_info.free_space_in_objects ? FS_MAX_CAPACITY_BYTES : 0;
- mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info));
+ (void) mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info));
tud_mtp_data_send(io_container);
return 0;
}
@@ -412,10 +428,10 @@ static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data) {
case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME:
device_prop_header.datatype = MTP_DATA_TYPE_STR;
device_prop_header.get_set = MTP_MODE_GET;
- mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header));
- mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory
- mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current
- mtp_container_add_uint8(io_container, 0); // no form
+ (void) mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header));
+ (void) mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory
+ (void) mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current
+ (void) mtp_container_add_uint8(io_container, 0); // no form
tud_mtp_data_send(io_container);
break;
@@ -426,7 +442,7 @@ static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data) {
// get value
switch (dev_prop_code) {
case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME:
- mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME);
+ (void) mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME);
tud_mtp_data_send(io_container);
break;
@@ -446,20 +462,20 @@ static int32_t fs_get_object_handles(tud_mtp_cb_data_t* cb_data) {
const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root
(void)obj_format;
- if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) {
+ if (storage_id != 0xFFFFFFFFu && storage_id != SUPPORTED_STORAGE_ID) {
return MTP_RESP_INVALID_STORAGE_ID;
}
uint32_t handles[FS_MAX_FILE_COUNT] = { 0 };
- uint32_t count = 0;
- for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) {
+ uint32_t count = 0u;
+ for (uint8_t i = 0u; i < FS_MAX_FILE_COUNT; i++) {
fs_file_t* f = &fs_objects[i];
if (fs_file_exist(f) &&
- (parent_handle == f->parent || (parent_handle == 0xFFFFFFFF && f->parent == 0))) {
- handles[count++] = i + 1; // handle is index + 1
+ (parent_handle == f->parent || (parent_handle == 0xFFFFFFFFu && f->parent == 0u))) {
+ handles[count++] = (uint32_t) i + 1u; // handle is index + 1
}
}
- mtp_container_add_auint32(io_container, count, handles);
+ (void) mtp_container_add_auint32(io_container, count, handles);
tud_mtp_data_send(io_container);
return 0;
@@ -490,11 +506,11 @@ static int32_t fs_get_object_info(tud_mtp_cb_data_t* cb_data) {
.association_desc = 0,
.sequence_number = 0
};
- mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header));
- mtp_container_add_string(io_container, f->name);
- mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
- mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
- mtp_container_add_cstring(io_container, ""); // keywords, not used
+ (void) mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header));
+ (void) mtp_container_add_string(io_container, f->name);
+ (void) mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
+ (void) mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
+ (void) mtp_container_add_cstring(io_container, ""); // keywords, not used
tud_mtp_data_send(io_container);
return 0;
@@ -512,7 +528,7 @@ static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data) {
if (cb_data->phase == MTP_PHASE_COMMAND) {
// If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, data may only partially is added here
// the rest will be sent in tud_mtp_data_more_cb
- mtp_container_add_raw(io_container, f->data, f->size);
+ (void) mtp_container_add_raw(io_container, f->data, f->size);
tud_mtp_data_send(io_container);
} else if (cb_data->phase == MTP_PHASE_DATA) {
// continue sending remaining data: file contents offset is xferred byte minus header size
@@ -522,6 +538,42 @@ static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data) {
memcpy(io_container->payload, f->data + offset, xact_len);
tud_mtp_data_send(io_container);
}
+ } else {
+ // nothing to do
+ }
+
+ return 0;
+}
+
+static int32_t fs_get_partial_object(tud_mtp_cb_data_t* cb_data) {
+ const mtp_container_command_t* command = cb_data->command_container;
+ mtp_container_info_t* io_container = &cb_data->io_container;
+ const uint32_t obj_handle = command->params[0];
+ const uint32_t req_offset = command->params[1];
+ const uint32_t req_max = command->params[2];
+ const fs_file_t* f = fs_get_file(obj_handle);
+ if (f == NULL) {
+ return MTP_RESP_INVALID_OBJECT_HANDLE;
+ }
+
+ const uint32_t avail = (req_offset >= f->size) ? 0u : (f->size - req_offset);
+ const uint32_t to_send = tu_min32(avail, req_max);
+
+ if (cb_data->phase == MTP_PHASE_COMMAND) {
+ // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, data may only partially be added here
+ // the rest will be sent in tud_mtp_data_more_cb
+ (void) mtp_container_add_raw(io_container, f->data + req_offset, to_send);
+ tud_mtp_data_send(io_container);
+ } else if (cb_data->phase == MTP_PHASE_DATA) {
+ // continue sending remaining data: file contents offset is xferred byte minus header size
+ const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t);
+ const uint32_t xact_len = tu_min32(to_send - offset, io_container->payload_bytes);
+ if (xact_len > 0) {
+ memcpy(io_container->payload, f->data + offset + req_offset, xact_len);
+ tud_mtp_data_send(io_container);
+ }
+ } else {
+ // nothing to do
}
return 0;
@@ -537,21 +589,21 @@ static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data) {
if (!is_session_opened) {
return MTP_RESP_SESSION_NOT_OPEN;
}
- if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) {
+ if (storage_id != 0xFFFFFFFFu && storage_id != SUPPORTED_STORAGE_ID) {
return MTP_RESP_INVALID_STORAGE_ID;
}
if (cb_data->phase == MTP_PHASE_COMMAND) {
- tud_mtp_data_receive(io_container);
+ (void) tud_mtp_data_receive(io_container);
} else if (cb_data->phase == MTP_PHASE_DATA) {
mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload;
if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) {
return MTP_RESP_INVALID_STORAGE_ID;
}
- if (obj_info->parent_object) {
+ if (obj_info->parent_object != 0 && obj_info->parent_object != 0xFFFFFFFFu) { // not root
fs_file_t* parent = fs_get_file(obj_info->parent_object);
- if (parent == NULL || !parent->association_type) {
+ if (parent == NULL || 0u == parent->association_type) {
return MTP_RESP_INVALID_PARENT_OBJECT;
}
}
@@ -575,8 +627,10 @@ static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data) {
f->size = obj_info->object_compressed_size;
f->data = f_buf;
uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t);
- mtp_container_get_string(buf, f->name);
+ (void) mtp_container_get_string(buf, f->name);
// ignore date created/modified/keywords
+ } else {
+ // nothing to do
}
return 0;
diff --git a/examples/device/mtp/src/tinyusb_logo_png.h b/examples/device/mtp/src/tinyusb_logo_png.h
index 061fbc85a..f8a9bde4e 100644
--- a/examples/device/mtp/src/tinyusb_logo_png.h
+++ b/examples/device/mtp/src/tinyusb_logo_png.h
@@ -1,6 +1,6 @@
// convert using tools/file2carray.py
-const size_t logo_len = 2733;
-const uint8_t logo_bin[] __attribute__((aligned(16))) = {
+enum { LOGO_LEN = 2733 };
+static const uint8_t logo_bin[] __attribute__((aligned(16))) = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0xd2, 0xd6, 0x7f,
0x7f, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xa0,
diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h
index 4d166aa63..5224cd79b 100644
--- a/examples/device/mtp/src/tusb_config.h
+++ b/examples/device/mtp/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -106,6 +106,7 @@
MTP_OP_GET_OBJECT_HANDLES, \
MTP_OP_GET_OBJECT_INFO, \
MTP_OP_GET_OBJECT, \
+ MTP_OP_GET_PARTIAL_OBJECT, \
MTP_OP_DELETE_OBJECT, \
MTP_OP_SEND_OBJECT_INFO, \
MTP_OP_SEND_OBJECT, \
@@ -136,4 +137,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c
index 80345d8f8..4c840560e 100644
--- a/examples/device/mtp/src/usb_descriptors.c
+++ b/examples/device/mtp/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] MTP | VENDOR | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(MTP, 5))
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) | PID_MAP(MTP, 5))
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -94,7 +94,7 @@ enum
#define EPNUM_MTP_OUT 0x02
#define EPNUM_MTP_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MTP_EVT 0x81
@@ -121,7 +121,7 @@ const uint8_t desc_fs_configuration[] = {
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP event, EP event size, EP event polling, EP Out & EP In address, EP size
@@ -129,10 +129,10 @@ uint8_t const desc_hs_configuration[] = {
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
@@ -198,7 +198,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUsb", // 1: Manufacturer
diff --git a/examples/device/net_lwip_webserver/CMakeLists.txt b/examples/device/net_lwip_webserver/CMakeLists.txt
index 87b92f4dc..9c4349d24 100644
--- a/examples/device/net_lwip_webserver/CMakeLists.txt
+++ b/examples/device/net_lwip_webserver/CMakeLists.txt
@@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_LIST_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
# Prefer the tinyusb lwip
set(LWIP ${TOP}/lib/lwip)
@@ -14,25 +12,25 @@ if (NOT EXISTS ${LWIP}/src)
endif()
if (NOT EXISTS ${LWIP}/src)
- family_example_missing_dependency(${PROJECT} "lib/lwip")
+ family_example_missing_dependency(${PROJECT_NAME} "lib/lwip")
return()
endif()
-project(${PROJECT} C CXX ASM)
+project(net_lwip_webserver C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/main.c
${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src
${LWIP}/src/include
${LWIP}/src/include/ipv4
@@ -41,14 +39,14 @@ target_include_directories(${PROJECT} PUBLIC
)
# lib/networking sources
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${TOP}/lib/networking/dhserver.c
${TOP}/lib/networking/dnserver.c
${TOP}/lib/networking/rndis_reports.c
)
# lwip sources
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${LWIP}/src/core/altcp.c
${LWIP}/src/core/altcp_alloc.c
${LWIP}/src/core/altcp_tcp.c
@@ -86,7 +84,7 @@ target_sources(${PROJECT} PUBLIC
# due to warnings from other net source, we need to prevent error from some of the warnings options
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
- target_compile_options(${PROJECT} PUBLIC
+ target_compile_options(${PROJECT_NAME} PUBLIC
-Wno-error=null-dereference
-Wno-error=conversion
-Wno-error=sign-conversion
@@ -98,4 +96,4 @@ endif ()
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/net_lwip_webserver/Makefile b/examples/device/net_lwip_webserver/Makefile
index 4ad110dec..6002039b3 100644
--- a/examples/device/net_lwip_webserver/Makefile
+++ b/examples/device/net_lwip_webserver/Makefile
@@ -1,14 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
# suppress warning caused by lwip
-CFLAGS_GCC += \
+CFLAGS += \
-Wno-error=null-dereference \
-Wno-error=unused-parameter \
-Wno-error=unused-variable
INC += \
src \
- $(TOP)/hw \
$(TOP)/lib/lwip/src/include \
$(TOP)/lib/lwip/src/include/ipv4 \
$(TOP)/lib/lwip/src/include/lwip/apps \
@@ -16,7 +15,7 @@ INC += \
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
# lwip sources
SRC_C += \
@@ -66,4 +65,4 @@ SRC_C += \
lib/networking/dnserver.c \
lib/networking/rndis_reports.c
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt
index 6121f1f9d..5e5562087 100644
--- a/examples/device/net_lwip_webserver/skip.txt
+++ b/examples/device/net_lwip_webserver/skip.txt
@@ -19,4 +19,6 @@ board:at_start_f425
board:curiosity_nano
board:frdm_kl25z
# lpc55 has weird error 'ncm_interface' causes a section type conflict with 'ntb_parameters'
-family:lpc55
+#family:lpc55
+family:nuc126
+family:nuc100_120
diff --git a/examples/device/net_lwip_webserver/src/arch/cc.h b/examples/device/net_lwip_webserver/src/arch/cc.h
index 9f30b91cb..c3fc12dda 100644
--- a/examples/device/net_lwip_webserver/src/arch/cc.h
+++ b/examples/device/net_lwip_webserver/src/arch/cc.h
@@ -29,8 +29,8 @@
* Author: Adam Dunkels <[email protected]>
*
*/
-#ifndef __CC_H__
-#define __CC_H__
+#ifndef CC_H__
+#define CC_H__
//#include "cpu.h"
@@ -72,4 +72,4 @@ typedef int sys_prot_t;
#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0)
-#endif /* __CC_H__ */
+#endif /* CC_H__ */
diff --git a/examples/device/net_lwip_webserver/src/lwipopts.h b/examples/device/net_lwip_webserver/src/lwipopts.h
index 04949cef9..350120423 100644
--- a/examples/device/net_lwip_webserver/src/lwipopts.h
+++ b/examples/device/net_lwip_webserver/src/lwipopts.h
@@ -29,8 +29,16 @@
* Author: Simon Goldschmidt
*
*/
-#ifndef __LWIPOPTS_H__
-#define __LWIPOPTS_H__
+#ifndef LWIPOPTS_H__
+#define LWIPOPTS_H__
+
+// Pulls in tusb_option.h → tusb_config.h, which defines LWIP_HIGH_THROUGHPUT
+// based on the target MCU's SRAM tier.
+#include "tusb_option.h"
+
+#ifndef LWIP_HIGH_THROUGHPUT
+ #define LWIP_HIGH_THROUGHPUT 0
+#endif
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
#define NO_SYS 1
@@ -49,7 +57,15 @@
#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
#define TCP_SND_BUF (4 * TCP_MSS)
-#define TCP_WND (4 * TCP_MSS)
+#if LWIP_HIGH_THROUGHPUT
+ #define TCP_WND (8 * TCP_MSS)
+ #define PBUF_POOL_SIZE 8
+ // Must grow in step with TCP_SND_BUF (default MEMP_NUM_TCP_SEG=16 caps TCP_SND_BUF at 4*MSS).
+ #define MEMP_NUM_TCP_SEG 16
+#else
+ #define TCP_WND (4 * TCP_MSS)
+ #define PBUF_POOL_SIZE 4
+#endif
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
@@ -60,8 +76,6 @@
#define LWIP_SINGLE_NETIF 1
#define LWIP_NETIF_LINK_CALLBACK 1
-#define PBUF_POOL_SIZE 4
-
#define HTTPD_USE_CUSTOM_FSDATA 0
#define LWIP_MULTICAST_PING 1
diff --git a/examples/device/net_lwip_webserver/src/main.c b/examples/device/net_lwip_webserver/src/main.c
index dd9f213ae..b78fd7c00 100644
--- a/examples/device/net_lwip_webserver/src/main.c
+++ b/examples/device/net_lwip_webserver/src/main.c
@@ -58,6 +58,7 @@ try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00
#include "lwip/ethip6.h"
#include "lwip/init.h"
#include "lwip/timeouts.h"
+#include "lwip/sys.h"
#ifdef INCLUDE_IPERF
#include "lwip/apps/lwiperf.h"
@@ -66,6 +67,19 @@ try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00
#define INIT_IP4(a, b, c, d) \
{ PP_HTONL(LWIP_MAKEU32(a, b, c, d)) }
+/* Blink pattern
+ * - 250 ms : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+enum {
+ BLINK_NOT_MOUNTED = 250,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
+};
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
/* lwip context */
static struct netif netif_data;
@@ -172,7 +186,7 @@ static void init_lwip(void) {
}
/* handle any DNS requests from dns-server */
-bool dns_query_proc(const char *name, ip4_addr_t *addr) {
+static bool dns_query_proc(const char *name, ip4_addr_t *addr) {
if (0 == strcmp(name, "tiny.usb")) {
*addr = ipaddr;
return true;
@@ -217,6 +231,20 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
return pbuf_copy_partial(p, dst, p->tot_len, 0);
}
+static void led_blinking_task(void) {
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
+
+ // Blink every interval ms
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
+ start_ms += blink_interval_ms;
+
+ board_led_write(led_state);
+ led_state = 1 - led_state; // toggle
+}
+
static void handle_link_state_switch(void) {
/* Check for button press to toggle link state */
static bool last_link_state = true;
@@ -274,9 +302,31 @@ int main(void) {
tud_task();
sys_check_timeouts(); // service lwip
handle_link_state_switch();
+ led_blinking_task();
}
+}
- return 0;
+// Invoked when device is mounted
+void tud_mount_cb(void) {
+ blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void) {
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+// Invoked when usb bus is suspended
+// remote_wakeup_en : if host allow us to perform remote wakeup
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+void tud_suspend_cb(bool remote_wakeup_en) {
+ (void) remote_wakeup_en;
+ blink_interval_ms = BLINK_SUSPENDED;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void) {
+ blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
/* lwip has provision for using a mutex, when applicable */
@@ -290,5 +340,5 @@ void sys_arch_unprotect(sys_prot_t pval) {
/* lwip needs a millisecond time source, and the TinyUSB board support code has one available */
uint32_t sys_now(void) {
- return board_millis();
+ return tusb_time_millis_api();
}
diff --git a/examples/device/net_lwip_webserver/src/tusb_config.h b/examples/device/net_lwip_webserver/src/tusb_config.h
index c774f59ff..d94a87c99 100644
--- a/examples/device/net_lwip_webserver/src/tusb_config.h
+++ b/examples/device/net_lwip_webserver/src/tusb_config.h
@@ -23,15 +23,13 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
-#include "lwipopts.h"
-
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
@@ -88,36 +86,54 @@ extern "C" {
#ifndef USE_ECM
#if TU_CHECK_MCU(OPT_MCU_LPC15XX, OPT_MCU_LPC40XX, OPT_MCU_LPC51UXX, OPT_MCU_LPC54)
#define USE_ECM 1
-#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML21, OPT_MCU_SAML22)
+#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML2X)
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F1)
#define USE_ECM 1
-#elif TU_CHECK_MCU(OPT_MCU_MAX32690, OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX78002)
- #define USE_ECM 1
#else
#define USE_ECM 0
- #define INCLUDE_IPERF
#endif
#endif
+// MCU SRAM tier — drives the bigger lwIP buffers in lwipopts.h, the larger
+// NCM OUT NTB size below, and whether iperf is built. Small-RAM MCUs
+// (stm32c0/f1/wb, lpc11/13, samd11) keep modest defaults to fit.
+#ifndef LWIP_HIGH_THROUGHPUT
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002) || \
+ TU_CHECK_MCU(OPT_MCU_STM32F2, OPT_MCU_STM32F4, OPT_MCU_STM32F7) || \
+ TU_CHECK_MCU(OPT_MCU_STM32H5, OPT_MCU_STM32H7, OPT_MCU_STM32H7RS) || \
+ TU_CHECK_MCU(OPT_MCU_STM32C5, OPT_MCU_STM32U5, OPT_MCU_STM32N6) || \
+ TU_CHECK_MCU(OPT_MCU_RP2040, OPT_MCU_CH32V307) || \
+ TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX) || \
+ TU_CHECK_MCU(OPT_MCU_NRF5X)
+ #define LWIP_HIGH_THROUGHPUT 1
+ #else
+ #define LWIP_HIGH_THROUGHPUT 0
+ #endif
+#endif
+
+#if LWIP_HIGH_THROUGHPUT && !defined(INCLUDE_IPERF)
+ #define INCLUDE_IPERF
+#endif
+
//--------------------------------------------------------------------
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
//--------------------------------------------------------------------
-// Must be >> MTU
-// Can be set to 2048 without impact
-#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+// CDC-NCM 1.0 Table 6-4 defines 2048 as the minimum required NTB size
+#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 2048
-// Must be >> MTU
-// Can be set to smaller values if wNtbOutMaxDatagrams==1
-#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+#if LWIP_HIGH_THROUGHPUT
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 4096
+#else
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 2048
+#endif
// Number of NCM transfer blocks for reception side
#ifndef CFG_TUD_NCM_OUT_NTB_N
#define CFG_TUD_NCM_OUT_NTB_N 1
#endif
-// Number of NCM transfer blocks for transmission side
#ifndef CFG_TUD_NCM_IN_NTB_N
#define CFG_TUD_NCM_IN_NTB_N 1
#endif
@@ -141,4 +157,4 @@ extern "C" {
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c
index cd800f521..09090bb92 100644
--- a/examples/device/net_lwip_webserver/src/usb_descriptors.c
+++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c
@@ -24,6 +24,7 @@
*/
#include "bsp/board_api.h"
+#include "class/net/net_device.h"
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
@@ -32,163 +33,297 @@
* Auto ProductID layout's Bitmap:
* [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(ECM_RNDIS, 5) | _PID_MAP(NCM, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID \
+ (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) | \
+ PID_MAP(ECM_RNDIS, 5) | PID_MAP(NCM, 5))
// String Descriptor Index
-enum
-{
+enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_INTERFACE,
- STRID_MAC
+ STRID_MAC,
+ STRID_COUNT
};
-enum
-{
+enum {
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
ITF_NUM_TOTAL
};
-enum
-{
+enum {
#if CFG_TUD_ECM_RNDIS
CONFIG_ID_RNDIS = 0,
CONFIG_ID_ECM = 1,
#else
- CONFIG_ID_NCM = 0,
+ CONFIG_ID_NCM = 0,
#endif
CONFIG_ID_COUNT
};
-//--------------------------------------------------------------------+
-// Device Descriptors
-//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
#if CFG_TUD_NCM
- .bcdUSB = 0x0201,
+#define USB_BCD 0x0201
#else
- .bcdUSB = 0x0200,
+#define USB_BCD 0x0200
#endif
- // Use Interface Association Descriptor (IAD) device class
- .bDeviceClass = TUSB_CLASS_MISC,
- .bDeviceSubClass = MISC_SUBCLASS_COMMON,
- .bDeviceProtocol = MISC_PROTOCOL_IAD,
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+static const tusb_desc_device_t desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+ // Use Interface Association Descriptor (IAD) device class
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .idVendor = 0xCafe,
- .idProduct = USB_PID,
- .bcdDevice = 0x0101,
+ .idVendor = 0xCafe,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0101,
- .iManufacturer = STRID_MANUFACTURER,
- .iProduct = STRID_PRODUCT,
- .iSerialNumber = STRID_SERIAL,
+ .iManufacturer = STRID_MANUFACTURER,
+ .iProduct = STRID_PRODUCT,
+ .iSerialNumber = STRID_SERIAL,
- .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
+ .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
- return (uint8_t const *) &desc_device;
+const uint8_t *tud_descriptor_device_cb(void) {
+ return (const uint8_t *)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
-#define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN)
-#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
-#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_NCM_DESC_LEN)
+#define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN)
+#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
+#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_NCM_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
- // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
- // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
- #define EPNUM_NET_NOTIF 0x81
- #define EPNUM_NET_OUT 0x02
- #define EPNUM_NET_IN 0x82
+// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
+#define EPNUM_NET_NOTIF 0x81
+#define EPNUM_NET_OUT 0x02
+#define EPNUM_NET_IN 0x82
#elif CFG_TUSB_MCU == OPT_MCU_CXD56
- // CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
- // 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
- #define EPNUM_NET_NOTIF 0x83
- #define EPNUM_NET_OUT 0x02
- #define EPNUM_NET_IN 0x81
+// CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
+// 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
+#define EPNUM_NET_NOTIF 0x83
+#define EPNUM_NET_OUT 0x02
+#define EPNUM_NET_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
- // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
- // e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_NET_NOTIF 0x81
- #define EPNUM_NET_OUT 0x02
- #define EPNUM_NET_IN 0x83
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+// MCUs that don't support the same endpoint number with different direction IN and OUT defined in tusb_mcu.h
+// e.g EP1 OUT & EP1 IN cannot exist together
+
+#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+// endpoint 8,9 has FIFO of 2048 bytes
+#define EPNUM_NET_NOTIF 0x81
+#define EPNUM_NET_OUT 0x08
+#define EPNUM_NET_IN 0x89
+#else
+#define EPNUM_NET_NOTIF 0x81
+#define EPNUM_NET_OUT 0x02
+#define EPNUM_NET_IN 0x83
+#endif
#else
- #define EPNUM_NET_NOTIF 0x81
- #define EPNUM_NET_OUT 0x02
- #define EPNUM_NET_IN 0x82
+#define EPNUM_NET_NOTIF 0x81
+#define EPNUM_NET_OUT 0x02
+#define EPNUM_NET_IN 0x82
#endif
#if CFG_TUD_ECM_RNDIS
-static uint8_t const rndis_configuration[] =
-{
+// full speed configuration
+static uint8_t const rndis_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
- TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE),
+ TUD_RNDIS_DESCRIPTOR(
+ ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 64),
};
-static uint8_t const ecm_configuration[] =
-{
+static const uint8_t ecm_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
- TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
+ TUD_CDC_ECM_DESCRIPTOR(
+ ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 64, CFG_TUD_NET_MTU),
};
-#else
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
-static uint8_t const ncm_configuration[] =
-{
+// high speed configuration
+static uint8_t const rndis_hs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM+1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
+
+ // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+ TUD_RNDIS_DESCRIPTOR(
+ ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 512),
+};
+
+static const uint8_t ecm_hs_configuration[] = {
+ // Config number (index+1), interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
- TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
+ TUD_CDC_ECM_DESCRIPTOR(
+ ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 512, CFG_TUD_NET_MTU),
};
+#endif // highspeed
+
+#else
+
+// full speed configuration
+static uint8_t const ncm_fs_configuration[] = {
+ // Config number (index+1), interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
+
+ // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval, NCM capabilities.
+ TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 64, CFG_TUD_NET_MTU, 50, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_SIZE)),
+};
+
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
+
+// high speed configuration
+// bInterval: FS=50 means 50ms; HS encodes as 2^(n-1) * 125us, so 9 = 2^8 * 125us = 32ms
+static uint8_t const ncm_hs_configuration[] = {
+ // Config number (index+1), interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
+
+ // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval, NCM capabilities.
+ TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 512, CFG_TUD_NET_MTU, 9, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_SIZE)),
+};
+#endif // highspeed
#endif
-// Configuration array: RNDIS and CDC-ECM
+// NCM work with all latest OS i.e macos 10.10+, windows 10+, and Linux.
+// For older system Configuration array of RNDIS and CDC-ECM may be needed for better compatibility.
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
-static uint8_t const * const configuration_arr[2] =
-{
#if CFG_TUD_ECM_RNDIS
- [CONFIG_ID_RNDIS] = rndis_configuration,
- [CONFIG_ID_ECM ] = ecm_configuration
+
+static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = rndis_fs_configuration,
+ [CONFIG_ID_ECM] = ecm_fs_configuration
+};
+
+#if TUD_OPT_HIGH_SPEED
+static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = rndis_hs_configuration,
+ [CONFIG_ID_ECM] = ecm_hs_configuration
+};
+
+// Size array for each configuration
+static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = MAIN_CONFIG_TOTAL_LEN,
+ [CONFIG_ID_ECM] = ALT_CONFIG_TOTAL_LEN
+};
+
+// Scratch buffer for other speed configuration (sized to hold the largest config)
+#define MAX_CONFIG_TOTAL_LEN TU_MAX(MAIN_CONFIG_TOTAL_LEN, ALT_CONFIG_TOTAL_LEN)
+#endif
+
#else
- [CONFIG_ID_NCM ] = ncm_configuration
+
+static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_NCM] = ncm_fs_configuration
+};
+
+#if TUD_OPT_HIGH_SPEED
+static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_NCM] = ncm_hs_configuration
+};
+
+// Size array for each configuration
+static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_NCM] = NCM_CONFIG_TOTAL_LEN
+};
+
+// Scratch buffer for other speed configuration (sized to hold the largest config)
+#define MAX_CONFIG_TOTAL_LEN NCM_CONFIG_TOTAL_LEN
#endif
+
+#endif
+
+#if TUD_OPT_HIGH_SPEED
+static uint8_t desc_other_speed_config[MAX_CONFIG_TOTAL_LEN];
+
+// device qualifier: device descriptor fields that differ at other speed
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = USB_BCD,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = CONFIG_ID_COUNT,
+ .bReserved = 0x00
};
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SPEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ if (index >= CONFIG_ID_COUNT) return NULL;
+
+ // if link speed is high return fullspeed config, and vice versa
+ const uint8_t *const *arr = (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_fs_arr : configuration_hs_arr;
+
+ // Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
+ memcpy(desc_other_speed_config, arr[index], configuration_sz_arr[index]);
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+
+ return desc_other_speed_config;
+}
+
+#endif // highspeed
+
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
- return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
+const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
+ if (index >= CONFIG_ID_COUNT) return NULL;
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_hs_arr[index] : configuration_fs_arr[index];
+#else
+ return configuration_fs_arr[index];
+#endif
}
#if CFG_TUD_NCM
@@ -213,61 +348,62 @@ https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
(Section Microsoft OS compatibility descriptors)
*/
-#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
-#define MS_OS_20_DESC_LEN 0xB2
+#define MS_OS_20_DESC_LEN 0xB2
// BOS Descriptor is required for webUSB
-uint8_t const desc_bos[] =
-{
+const uint8_t desc_bos[] = {
// total length, number of device caps
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
// Microsoft OS 2.0 descriptor
- TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)
-};
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)};
-uint8_t const * tud_descriptor_bos_cb(void)
-{
+const uint8_t *tud_descriptor_bos_cb(void) {
return desc_bos;
}
-uint8_t const desc_ms_os_20[] =
-{
+const uint8_t desc_ms_os_20[] = {
// Set header: length, type, windows version, total length
- U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
+ U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000),
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
// Configuration subset header: length, type, configuration index, reserved, configuration total length
- U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A),
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0,
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
// Function Subset header: length, type, first interface, reserved, subset length
- U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_CDC, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08),
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_CDC, 0,
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
- U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'N', 'C', 'M', 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
+ U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'N', 'C', 'M', 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
- U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
- U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
- 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
- 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
+ U16_TO_U8S_LE(0x0007),
+ U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r',
+ 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
- //bPropertyData: {12345678-0D08-43FD-8B3E-127CA8AFFF9D}
- '{', 0x00, '1', 0x00, '2', 0x00, '3', 0x00, '4', 0x00, '5', 0x00, '6', 0x00, '7', 0x00, '8', 0x00, '-', 0x00,
- '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00,
- '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00,
- '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
-};
+ //bPropertyData: {12345678-0D08-43FD-8B3E-127CA8AFFF9D}
+ '{', 0x00, '1', 0x00, '2', 0x00, '3', 0x00, '4', 0x00, '5', 0x00, '6', 0x00, '7', 0x00, '8', 0x00, '-', 0x00, '0',
+ 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00,
+ 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00, '8', 0x00, 'A',
+ 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00};
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) {
// nothing to with DATA & ACK stage
- if (stage != CONTROL_STAGE_SETUP) return true;
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_VENDOR:
@@ -278,16 +414,18 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
uint16_t total_len;
memcpy(&total_len, desc_ms_os_20 + 8, 2);
- return tud_control_xfer(rhport, request, (void*)(uintptr_t)desc_ms_os_20, total_len);
+ return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, total_len);
} else {
return false;
}
- default: break;
+ default:
+ break; // nothing to do
}
break;
- default: break;
+ default:
+ break; // nothing to do
}
// stall unknown request
@@ -300,26 +438,24 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
//--------------------------------------------------------------------+
// array of pointer to string descriptors
-static char const* string_desc_arr [] =
-{
- [STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409)
- [STRID_MANUFACTURER] = "TinyUSB", // Manufacturer
- [STRID_PRODUCT] = "TinyUSB Device", // Product
- [STRID_SERIAL] = NULL, // Serials will use unique ID if possible
- [STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description
-
- // STRID_MAC index is handled separately
+static const char *string_desc_arr[STRID_COUNT] = {
+ [STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409)
+ [STRID_MANUFACTURER] = "TinyUSB", // Manufacturer
+ [STRID_PRODUCT] = "TinyUSB Device", // Product
+ [STRID_SERIAL] = NULL, // Serials will use unique ID if possible
+ [STRID_INTERFACE] = "TinyUSB Network Interface", // Interface Description
+ [STRID_MAC] = NULL // STRID_MAC index is handled separately
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
- (void) langid;
+const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void)langid;
unsigned int chr_count = 0;
- switch ( index ) {
+ switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
@@ -331,34 +467,40 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
case STRID_MAC:
// Convert MAC address into UTF-16
- for (unsigned i=0; i<sizeof(tud_network_mac_address); i++) {
- _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
- _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
+ for (unsigned i = 0; i < sizeof(tud_network_mac_address); i++) {
+ _desc_str[1 + chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
+ _desc_str[1 + chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
break;
- default:
+ default: {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) {
+ return NULL;
+ }
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
- size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+
+ const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+ if (chr_count > max_count) {
+ chr_count = max_count;
+ }
// Convert ASCII string into UTF-16
- for ( size_t i = 0; i < chr_count; i++ ) {
+ for (size_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
+ }
}
// first byte is length (including header), second byte is string type
- _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8 ) | (2*chr_count + 2));
+ _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
diff --git a/examples/device/printer_to_cdc/CMakeLists.txt b/examples/device/printer_to_cdc/CMakeLists.txt
new file mode 100644
index 000000000..3c8ab3653
--- /dev/null
+++ b/examples/device/printer_to_cdc/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(printer_to_cdc C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/printer_to_cdc/CMakePresets.json b/examples/device/printer_to_cdc/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/device/printer_to_cdc/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/device/printer_to_cdc/Makefile b/examples/device/printer_to_cdc/Makefile
new file mode 100644
index 000000000..1a4b428dc
--- /dev/null
+++ b/examples/device/printer_to_cdc/Makefile
@@ -0,0 +1,11 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/printer_to_cdc/README.md b/examples/device/printer_to_cdc/README.md
new file mode 100644
index 000000000..ecb9a678f
--- /dev/null
+++ b/examples/device/printer_to_cdc/README.md
@@ -0,0 +1,80 @@
+#### Printer to CDC
+
+This example demonstrates a USB composite device with a Printer class interface and a CDC serial interface. Data flows bidirectionally between the two:
+
+- Data sent to the Printer (from host) is forwarded to the CDC serial port
+- Data sent to the CDC serial port (from host) is forwarded to the Printer IN endpoint
+
+This is useful for debugging printer class communication or as a reference for implementing printer class devices.
+
+#### USB Interfaces
+
+| Interface | Class | Description |
+|-----------|-------|-------------|
+| 0 | CDC ACM | Virtual serial port |
+| 2 | Printer | USB Printer (bidirectional, protocol 2) |
+
+#### How to Test
+
+The device exposes two endpoints on the host:
+- `/dev/ttyACM0` (CDC serial port)
+- `/dev/usb/lp0` (USB printer)
+
+Note: the actual device numbers may vary depending on your system.
+
+**Prerequisites (Linux):**
+
+```bash
+# Load the USB printer kernel module if not already loaded
+sudo modprobe usblp
+
+# Check devices exist
+ls /dev/ttyACM* /dev/usb/lp*
+```
+
+**Test Printer to CDC (host writes to printer, reads from CDC):**
+
+```bash
+# Terminal 1: read from CDC
+cat /dev/ttyACM0
+
+# Terminal 2: write to printer
+echo "hello from printer" > /dev/usb/lp0
+# "hello from printer" appears in Terminal 1
+```
+
+**Test CDC to Printer (host writes to CDC, reads from printer):**
+
+```bash
+# Terminal 1: read from printer IN endpoint
+cat /dev/usb/lp0
+
+# Terminal 2: write to CDC
+echo "hello from cdc" > /dev/ttyACM0
+# "hello from cdc" appears in Terminal 1
+```
+
+**Interactive bidirectional test:**
+
+```bash
+# Terminal 1: open CDC serial port
+minicom -D /dev/ttyACM0
+
+# Terminal 2: send to printer
+echo "tinyusb print example" > /dev/usb/lp0
+# Text appears in minicom. Type in minicom to send data back through printer TX.
+```
+
+#### IEEE 1284 Device ID
+
+The device responds to GET_DEVICE_ID requests with:
+
+```
+MFG:TinyUSB;MDL:Printer to CDC;CMD:PS;CLS:PRINTER;
+```
+
+Verify with:
+
+```bash
+cat /sys/class/usbmisc/lp0/device/ieee1284_id
+```
diff --git a/examples/device/printer_to_cdc/src/main.c b/examples/device/printer_to_cdc/src/main.c
new file mode 100644
index 000000000..ffaf709b4
--- /dev/null
+++ b/examples/device/printer_to_cdc/src/main.c
@@ -0,0 +1,117 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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 example demonstrates a USB Printer + CDC composite device.
+ * Data received on the Printer interface is forwarded to the CDC serial port,
+ * and data received on the CDC serial port is forwarded back to the Printer interface.
+ *
+ * To test:
+ * 1. Flash the device
+ * 2. Open a serial terminal on the CDC port (e.g. /dev/ttyACM0)
+ * 3. Send data to the printer: echo "hello" > /dev/usb/lp0
+ * 4. The data appears on the CDC serial terminal
+ * 5. Type in the serial terminal to send data back through the printer TX
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#include "usb_descriptors.h"
+
+// -------------------------------------------------------------------+
+// Tasks
+// -------------------------------------------------------------------+
+
+// Forward data from Printer RX to CDC TX
+static void printer_to_cdc_task(void) {
+ uint32_t avail = tud_printer_read_available();
+ if (avail == 0 || !tud_cdc_write_available()) {
+ return;
+ }
+
+ uint8_t buf[64];
+ uint32_t count = tud_printer_read(buf, TU_MIN(sizeof(buf), tud_cdc_write_available()));
+ if (count > 0) {
+ tud_cdc_write(buf, count);
+ tud_cdc_write_flush();
+ }
+}
+
+// Forward data from CDC RX to Printer TX
+static void cdc_to_printer_task(void) {
+ uint32_t avail = tud_printer_write_available();
+ if (tud_cdc_available() == 0 || avail == 0) {
+ return;
+ }
+
+ uint8_t buf[64];
+ uint32_t count = tud_cdc_read(buf, TU_MIN(sizeof(buf), avail));
+ if (count > 0) {
+ tud_printer_write(buf, count);
+ tud_printer_write_flush();
+ }
+}
+
+int main(void) {
+ board_init();
+ // init device stack on configured roothub port
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+ board_init_after_tusb();
+
+ while (1) {
+ tud_task(); // tinyusb device task
+ printer_to_cdc_task(); // forward printer data to CDC
+ cdc_to_printer_task(); // forward CDC data to printer
+ }
+}
+
+//--------------------------------------------------------------------+
+// Printer callbacks
+//--------------------------------------------------------------------+
+
+void tud_printer_rx_cb(uint8_t itf) {
+ (void)itf;
+}
+
+// IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes).
+// The rest is the Device ID string using standard abbreviated keys.
+static const char printer_device_id[] =
+ "\x00\x34" // total length = 52 = 0x0034 (big-endian)
+ "MFG:TinyUSB;"
+ "MDL:Printer to CDC;"
+ "CMD:PS;"
+ "CLS:PRINTER;";
+
+TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 == 52, "device ID length mismatch");
+
+uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) {
+ (void)itf;
+ return (uint8_t const *)printer_device_id;
+}
diff --git a/examples/device/printer_to_cdc/src/tusb_config.h b/examples/device/printer_to_cdc/src/tusb_config.h
new file mode 100644
index 000000000..74b96c8f6
--- /dev/null
+++ b/examples/device/printer_to_cdc/src/tusb_config.h
@@ -0,0 +1,120 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_TUD_RHPORT
+ #define BOARD_TUD_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUD_MAX_SPEED
+ #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+ #error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+ #define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+ #define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+ #define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+ #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+ #define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_HID 0
+#define CFG_TUD_CDC 1
+#define CFG_TUD_MSC 0
+#define CFG_TUD_MIDI 0
+#define CFG_TUD_VENDOR 0
+#define CFG_TUD_PRINTER 1
+
+// CDC FIFO size of TX and RX
+#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// Printer buffer sizes
+#define CFG_TUD_PRINTER_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_PRINTER_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_PRINTER_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_PRINTER_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.c b/examples/device/printer_to_cdc/src/usb_descriptors.c
new file mode 100644
index 000000000..db7bfe97a
--- /dev/null
+++ b/examples/device/printer_to_cdc/src/usb_descriptors.c
@@ -0,0 +1,217 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#include "usb_descriptors.h"
+
+#define USB_VID 0xCafe
+#define USB_PID 0x4005
+#define USB_BCD 0x0200
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+
+ // Use Interface Association Descriptor (IAD) for CDC
+ // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01
+};
+
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+
+// Endpoint numbers
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #define EPNUM_PRINTER_OUT 0x0A
+ #define EPNUM_PRINTER_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+ #define EPNUM_PRINTER_OUT 0x04
+ #define EPNUM_PRINTER_IN 0x85
+ #endif
+#else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+ #define EPNUM_PRINTER_OUT 0x03
+ #define EPNUM_PRINTER_IN 0x83
+#endif
+
+// full speed configuration
+static uint8_t const desc_fs_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+ // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+
+ // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size
+ TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 64),
+};
+
+#if TUD_OPT_HIGH_SPEED
+// high speed configuration
+static uint8_t const desc_hs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+
+ TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 512),
+};
+
+// other speed configuration
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = USB_BCD,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
+};
+
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;
+
+ // if link speed is high return fullspeed config, and vice versa
+ memcpy(desc_other_speed_config,
+ (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+ CONFIG_TOTAL_LEN);
+
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+
+ return desc_other_speed_config;
+}
+
+#endif // TUD_OPT_HIGH_SPEED
+
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index;
+
+#if TUD_OPT_HIGH_SPEED
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
+ return desc_fs_configuration;
+#endif
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+ STRID_CDC,
+ STRID_PRINTER,
+};
+
+static char const *string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB Device", // 2: Product
+ NULL, // 3: Serial, use unique ID if possible
+ "TinyUSB CDC", // 4: CDC Interface
+ "TinyUSB Printer", // 5: Printer Interface
+};
+
+static uint16_t _desc_str[32 + 1];
+
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch (index) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; }
+
+ const char *str = string_desc_arr[index];
+
+ chr_count = strlen(str);
+ size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1;
+ if (chr_count > max_count) { chr_count = max_count; }
+
+ for (size_t i = 0; i < chr_count; i++) {
+ _desc_str[1 + i] = str[i];
+ }
+ break;
+ }
+
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ return _desc_str;
+}
diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.h b/examples/device/printer_to_cdc/src/usb_descriptors.h
new file mode 100644
index 000000000..830593b4d
--- /dev/null
+++ b/examples/device/printer_to_cdc/src/usb_descriptors.h
@@ -0,0 +1,37 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ */
+
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
+
+enum {
+ ITF_NUM_CDC,
+ ITF_NUM_CDC_DATA,
+ ITF_NUM_PRINTER,
+ ITF_NUM_TOTAL,
+};
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_PRINTER_DESC_LEN)
+
+#endif /* USB_DESCRIPTORS_H_ */
diff --git a/examples/device/uac2_headset/CMakeLists.txt b/examples/device/uac2_headset/CMakeLists.txt
index ced98a909..52b88aac0 100644
--- a/examples/device/uac2_headset/CMakeLists.txt
+++ b/examples/device/uac2_headset/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(uac2_headset C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/uac2_headset/Makefile b/examples/device/uac2_headset/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/uac2_headset/Makefile
+++ b/examples/device/uac2_headset/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/uac2_headset/skip.txt b/examples/device/uac2_headset/skip.txt
index a2a76af0e..db1d5b80b 100644
--- a/examples/device/uac2_headset/skip.txt
+++ b/examples/device/uac2_headset/skip.txt
@@ -6,3 +6,4 @@ mcu:SAME5X
mcu:SAMG
board:stm32l052dap52
family:espressif
+mcu:CH583
diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c
index 102a6eef1..c30b31f67 100644
--- a/examples/device/uac2_headset/src/main.c
+++ b/examples/device/uac2_headset/src/main.c
@@ -73,7 +73,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
-int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
+uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
// Buffer for microphone data
@@ -141,18 +141,195 @@ void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
+//--------------------------------------------------------------------+
+// Audio Callback Functions
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// UAC1 Helper Functions
+//--------------------------------------------------------------------+
+
+static bool audio10_set_req_ep(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ // Request uses 3 bytes
+ TU_VERIFY(p_request->wLength == 3);
+
+ current_sample_rate = tu_unaligned_read32(pBuff) & 0x00FFFFFF;
+
+ TU_LOG2("EP set current freq: %" PRIu32 "\r\n", current_sample_rate);
+
+ return true;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_ep(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_GET_CUR) {
+ TU_LOG2("EP get current freq\r\n");
+
+ uint8_t freq[3];
+ freq[0] = (uint8_t) (current_sample_rate & 0xFF);
+ freq[1] = (uint8_t) ((current_sample_rate >> 8) & 0xFF);
+ freq[2] = (uint8_t) ((current_sample_rate >> 16) & 0xFF);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, freq, sizeof(freq));
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
+
+static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our speaker feature unit
+ if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 1);
+
+ mute[channelNum] = pBuff[0];
+
+ TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 2);
+
+ volume[channelNum] = (int16_t)tu_unaligned_read16(pBuff) / 256;
+
+ TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our speaker feature unit
+ if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
+ // There does not exist a range parameter block for mute
+ TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
+
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_GET_CUR:
+ TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
+ {
+ int16_t vol = (int16_t) volume[channelNum];
+ vol = vol * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
+ }
+
+ case AUDIO10_CS_REQ_GET_MIN:
+ TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum);
+ {
+ int16_t min = -90; // -90 dB
+ min = min * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
+ }
+
+ case AUDIO10_CS_REQ_GET_MAX:
+ TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum);
+ {
+ int16_t max = 30; // +30 dB
+ max = max * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
+ }
+
+ case AUDIO10_CS_REQ_GET_RES:
+ TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum);
+ {
+ int16_t res = 1; // 1 dB
+ res = res * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
+ }
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// UAC2 Helper Functions
+//--------------------------------------------------------------------+
+
+#if TUD_OPT_HIGH_SPEED
+
// Helper for clock get requests
-static bool tud_audio_clock_get_request(uint8_t rhport, audio_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 == AUDIO_CS_CTRL_SAM_FREQ) {
- if (request->bRequest == AUDIO_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);
- audio_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 == AUDIO_CS_REQ_RANGE) {
- audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
+ audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
+ 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)};
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
@@ -163,128 +340,185 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_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 == AUDIO_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_control_cur_1_t cur_valid = {.bCur = 1};
+ } 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, audio_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 == AUDIO_CS_REQ_CUR);
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
+
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
- current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
+ current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
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 tud_audio_feature_unit_get_request(uint8_t rhport, audio_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 == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_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 == AUDIO_FU_CTRL_VOLUME) {
- if (request->bRequest == AUDIO_CS_REQ_RANGE) {
- audio_control_range_2_n_t(1) range_vol = {
+ 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 == AUDIO_CS_REQ_CUR) {
- audio_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 tud_audio_feature_unit_set_request(uint8_t rhport, audio_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 == AUDIO_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 == AUDIO_FU_CTRL_MUTE) {
- TU_VERIFY(request->wLength == sizeof(audio_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] = ((audio_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 == AUDIO_FU_CTRL_VOLUME) {
- TU_VERIFY(request->wLength == sizeof(audio_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] = ((audio_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;
}
}
-//--------------------------------------------------------------------+
-// Application Callback API Implementations
-//--------------------------------------------------------------------+
+static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
+
+ 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",
+ 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) {
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
+
+ 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",
+ entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest);
+
+ return false;
+}
+
+#endif // TUD_OPT_HIGH_SPEED
+
+// Invoked when audio class specific set request received for an EP
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) pBuff;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_ep(p_request, pBuff);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an EP
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_ep(rhport, p_request);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
// 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) {
- audio_control_request_t const *request = (audio_control_request_t const *) p_request;
+ (void) rhport;
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return tud_audio_clock_get_request(rhport, request);
- if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return tud_audio_feature_unit_get_request(rhport, request);
- else {
- TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_entity(rhport, p_request);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_get_req_entity(rhport, p_request);
+#endif
}
+
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) {
- audio_control_request_t const *request = (audio_control_request_t const *) p_request;
+ (void) rhport;
- if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return tud_audio_feature_unit_set_request(rhport, request, buf);
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return tud_audio_clock_set_request(rhport, request, buf);
- TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n",
- request->bEntityID, request->bControlSelector, request->bRequest);
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_entity(p_request, buf);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_set_req_entity(rhport, p_request, buf);
+#endif
+ }
return false;
}
@@ -292,8 +526,8 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
blink_interval_ms = BLINK_MOUNTED;
@@ -304,8 +538,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
@@ -329,7 +563,7 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_reques
// In a real application, this would be replaced with actual I2S send/receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
+ uint32_t curr_ms = tusb_time_millis_api();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
// When new data arrived, copy data from speaker buffer, to microphone buffer
@@ -375,11 +609,15 @@ void audio_control_task(void) {
static uint32_t start_ms = 0;
static uint32_t btn_prev = 0;
- if (board_millis() - start_ms < interval_ms) return;// not enough time
+ if (tusb_time_millis_api() - start_ms < interval_ms) return;// not enough time
start_ms += interval_ms;
uint32_t btn = board_button_read();
+ // Even UAC1 spec have status interrupt support like UAC2, most host do not support it
+ // So you have to either use UAC2 or use old day HID volume control
+ TU_VERIFY((tud_audio_version() == 1),);
+
if (!btn_prev && btn) {
// Adjust volume between 0dB (100%) and -30dB (10%)
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
@@ -387,14 +625,14 @@ void audio_control_task(void) {
}
// 6.1 Interrupt Data Message
- const audio_interrupt_data_t data = {
+ const audio_interrupt_data_t data = {.v2 = {
.bInfo = 0, // Class-specific interrupt, originated from an interface
- .bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings
+ .bAttribute = AUDIO20_CS_REQ_CUR, // Caused by current settings
.wValue_cn_or_mcn = 0, // CH0: master volume
- .wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change
+ .wValue_cs = AUDIO20_FU_CTRL_VOLUME, // Volume change
.wIndex_ep_or_int = 0, // From the interface itself
.wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT,// From feature unit
- };
+ }};
tud_audio_int_write(&data);
}
@@ -410,7 +648,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return;
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) return;
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/uac2_headset/src/tusb_config.h b/examples/device/uac2_headset/src/tusb_config.h
index e9165163b..a43a4bd25 100644
--- a/examples/device/uac2_headset/src/tusb_config.h
+++ b/examples/device/uac2_headset/src/tusb_config.h
@@ -24,8 +24,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -108,8 +108,6 @@ extern "C" {
// Allow volume controlled by on-baord button
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 1
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN
-
// How many formats are used, need to adjust USB descriptor if changed
#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2
@@ -126,43 +124,48 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX 2
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX 16
-#if defined(__RX__)
-// 8bit in 8bit slots
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 1
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 8
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX 1
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX 8
-#else
-// 24bit in 32bit slots
+// 24bit in 32bit slots (UAC2 only)
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 4
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 24
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX 4
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX 24
-#endif
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+// UAC1 (Full-Speed) Endpoint size calculation
+#define CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(false, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+
+// UAC2 (High-Speed) Endpoint size calculation
+#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
-#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
-#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
+// Maximum EP IN size for all AS alternate settings used
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, TU_MAX(CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN))
+
+// Tx flow control needs buffer size >= 4* EP size to work correctly
+// Example write FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, TU_MAX(32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_IN, 32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_IN))
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
-#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
+// UAC1 (Full-Speed) Endpoint size calculation
+#define CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(false, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
+
+// UAC2 (High-Speed) Endpoint size calculation
+#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
+#define CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
-#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
-#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
+// Maximum EP OUT size for all AS alternate settings used
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT, TU_MAX(CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_OUT))
-// Size of control request buffer
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
+// Rx flow control needs buffer size >= 4* EP size to work correctly
+// Example read FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT, TU_MAX(32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_1_EP_SZ_OUT, 32 * CFG_TUD_AUDIO20_FUNC_1_FORMAT_2_EP_SZ_OUT))
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/uac2_headset/src/usb_descriptors.c b/examples/device/uac2_headset/src/usb_descriptors.c
index bc9160d5e..b554e7195 100644
--- a/examples/device/uac2_headset/src/usb_descriptors.c
+++ b/examples/device/uac2_headset/src/usb_descriptors.c
@@ -34,14 +34,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -87,9 +87,9 @@ uint8_t const * tud_descriptor_device_cb(void)
#elif CFG_TUSB_MCU == OPT_MCU_CXD56
// CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
// 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
- // #define EPNUM_AUDIO_IN 0x01
- // #define EPNUM_AUDIO_OUT 0x02
- // #define EPNUM_AUDIO_INT 0x03
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+ #define EPNUM_AUDIO_INT 0x03
#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
// ISO endpoints for NRF5x are fixed to 0x08 (0x88)
@@ -97,12 +97,19 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_AUDIO_OUT 0x08
#define EPNUM_AUDIO_INT 0x01
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_IN 0x01
- #define EPNUM_AUDIO_OUT 0x02
- #define EPNUM_AUDIO_INT 0x03
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO_OUT 0x0A
+ #define EPNUM_AUDIO_IN 0x0B
+ #define EPNUM_AUDIO_INT 0x01
+ #else
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+ #define EPNUM_AUDIO_INT 0x03
+ #endif
#else
#define EPNUM_AUDIO_IN 0x01
@@ -110,22 +117,93 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_AUDIO_INT 0x02
#endif
-uint8_t const desc_configuration[] =
+#define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_HEADSET_STEREO_DESC_LEN(2))
+
+uint8_t const desc_uac1_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC1_TOTAL_LEN, 0x00, 100),
+
+ // Interface number, string index, bytes per sample RX/TX, bits used per sample RX/TX, EP Out & EP In address, EP sizes, sample rate
+ TUD_AUDIO10_HEADSET_STEREO_DESCRIPTOR(ITF_NUM_AUDIO_CONTROL, 4, \
+ CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX, \
+ CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX, \
+ EPNUM_AUDIO_OUT, CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_OUT, \
+ EPNUM_AUDIO_IN | 0x80, CFG_TUD_AUDIO10_FUNC_1_FORMAT_1_EP_SZ_IN, \
+ 44100, 48000)
+};
+
+TU_VERIFY_STATIC(sizeof(desc_uac1_configuration) == CONFIG_UAC1_TOTAL_LEN, "Incorrect size");
+
+#if TUD_OPT_HIGH_SPEED
+
+#define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_HEADSET_STEREO_DESC_LEN)
+
+uint8_t const desc_uac2_configuration[] =
+{
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC2_TOTAL_LEN, 0x00, 100),
+
+ // String index, EP Out & EP In address, EP Interrupt address
+ TUD_AUDIO20_HEADSET_STEREO_DESCRIPTOR(5, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80, EPNUM_AUDIO_INT | 0x80)
+};
- // Interface number, string index, EP Out & EP In address, EP size
- TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80, EPNUM_AUDIO_INT | 0x80)
+TU_VERIFY_STATIC(sizeof(desc_uac2_configuration) == CONFIG_UAC2_TOTAL_LEN, "Incorrect size");
+
+
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+tusb_desc_device_qualifier_t const desc_device_qualifier =
+{
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = 0x0200,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
};
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const *tud_descriptor_device_qualifier_cb(void)
+{
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index)
+{
+ (void) index; // for multiple configurations
+
+ // if link speed is high return fullspeed config, and vice versa
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_uac1_configuration : desc_uac2_configuration;
+}
+#endif
+
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
- return desc_configuration;
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ if(tud_speed_get() == TUSB_SPEED_FULL) {
+ return desc_uac1_configuration;
+ } else {
+ return desc_uac2_configuration;
+ }
+#else
+ return desc_uac1_configuration;
+#endif
}
//--------------------------------------------------------------------+
@@ -141,14 +219,14 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
- "TinyUSB headset", // 2: Product
+ "TinyUSB Headset", // 2: Product
NULL, // 3: Serials will use unique ID if possible
- "TinyUSB Speakers", // 4: Audio Interface
- "TinyUSB Microphone", // 5: Audio Interface
+ "TinyUSB UAC1 Headset", // 4: Function
+ "TinyUSB UAC2 Headset", // 5: Function
};
static uint16_t _desc_str[32 + 1];
diff --git a/examples/device/uac2_headset/src/usb_descriptors.h b/examples/device/uac2_headset/src/usb_descriptors.h
index da0da83e8..47154626e 100644
--- a/examples/device/uac2_headset/src/usb_descriptors.h
+++ b/examples/device/uac2_headset/src/usb_descriptors.h
@@ -23,10 +23,20 @@
*
*/
-#ifndef _USB_DESCRIPTORS_H_
-#define _USB_DESCRIPTORS_H_
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
-// #include "tusb.h"
+enum
+{
+ ITF_NUM_AUDIO_CONTROL = 0,
+ ITF_NUM_AUDIO_STREAMING_SPK,
+ ITF_NUM_AUDIO_STREAMING_MIC,
+ ITF_NUM_TOTAL
+};
+
+//--------------------------------------------------------------------+
+// UAC2 DESCRIPTOR TEMPLATES
+//--------------------------------------------------------------------+
// Unit numbers are arbitrary selected
#define UAC2_ENTITY_CLOCK 0x04
@@ -38,121 +48,192 @@
#define UAC2_ENTITY_MIC_INPUT_TERMINAL 0x11
#define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x13
-enum
-{
- ITF_NUM_AUDIO_CONTROL = 0,
- ITF_NUM_AUDIO_STREAMING_SPK,
- ITF_NUM_AUDIO_STREAMING_MIC,
- ITF_NUM_TOTAL
-};
-
-#define TUD_AUDIO_HEADSET_STEREO_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
- + TUD_AUDIO_DESC_STD_AC_LEN\
- + TUD_AUDIO_DESC_CS_AC_LEN\
- + TUD_AUDIO_DESC_CLK_SRC_LEN\
- + TUD_AUDIO_DESC_INPUT_TERM_LEN\
- + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN\
- + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
- + TUD_AUDIO_DESC_INPUT_TERM_LEN\
- + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
- + TUD_AUDIO_DESC_STD_AC_INT_EP_LEN\
+#define TUD_AUDIO20_HEADSET_STEREO_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_INT_EP_LEN\
/* Interface 1, Alternate 0 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
/* Interface 1, Alternate 1 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 1, Alternate 2 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 2, Alternate 0 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
/* Interface 2, Alternate 1 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
/* Interface 2, Alternate 2 */\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN)
-#define TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(_stridx, _epout, _epin, _epint) \
+#define TUD_AUDIO20_HEADSET_STEREO_DESCRIPTOR(_stridx, _epout, _epin, _epint) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitf*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ ITF_NUM_TOTAL, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_IAD(/*_firstitf*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ ITF_NUM_TOTAL, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
- TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
+ TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
- TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
/* Clock Source Descriptor(4.7.2.1) */\
- TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \
/* Input Terminal Descriptor(4.7.2.4) */\
- TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
- TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS)),\
/* Output Terminal Descriptor(4.7.2.5) */\
- TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Input Terminal Descriptor(4.7.2.4) */\
- TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Output Terminal Descriptor(4.7.2.5) */\
- TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */\
- TUD_AUDIO_DESC_STD_AC_INT_EP(/*_ep*/ _epint, /*_interval*/ 0x01), \
+ TUD_AUDIO20_DESC_STD_AC_INT_EP(/*_ep*/ _epint, /*_interval*/ 0x01), \
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x05),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
/* Interface 1, Alternate 2 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x04),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 2, Alternate 1 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
/* Interface 2, Alternate 2 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x02, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
+
+//--------------------------------------------------------------------+
+// UAC1 DESCRIPTOR TEMPLATES
+//--------------------------------------------------------------------+
+
+// UAC1 entity IDs for speaker and microphone
+// Speaker path
+#define UAC1_ENTITY_SPK_INPUT_TERMINAL 0x01
+#define UAC1_ENTITY_SPK_FEATURE_UNIT 0x02
+#define UAC1_ENTITY_SPK_OUTPUT_TERMINAL 0x03
+// Microphone path
+#define UAC1_ENTITY_MIC_INPUT_TERMINAL 0x11
+#define UAC1_ENTITY_MIC_OUTPUT_TERMINAL 0x13
+
+#define TUD_AUDIO10_HEADSET_STEREO_DESC_LEN(_nfreqs) (\
+ +TUD_AUDIO10_DESC_STD_AC_LEN\
+ + TUD_AUDIO10_DESC_CS_AC_LEN(2)\
+ + TUD_AUDIO10_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)\
+ + TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
+ /* Interface 1, Alternate 0 (speaker) */\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ /* Interface 1, Alternate 1 (speaker) */\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
+ + TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN\
+ /* Interface 2, Alternate 0 (microphone) */\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ /* Interface 2, Alternate 1 (microphone) */\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
+ + TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN)
+
+
+#define TUD_AUDIO10_HEADSET_STEREO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample_RX, _nBitsUsedPerSample_RX, _nBytesPerSample_TX, _nBitsUsedPerSample_TX, _epout, _epoutsize, _epin, _epinsize, ...) \
+ /* Standard AC Interface Descriptor(4.3.1) */\
+ TUD_AUDIO10_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ /* Class-Specific AC Interface Header Descriptor(4.3.2) */\
+ TUD_AUDIO10_DESC_CS_AC(/*_bcdADC*/ 0x0100, /*_totallen*/ (TUD_AUDIO10_DESC_INPUT_TERM_LEN+TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)+TUD_AUDIO10_DESC_OUTPUT_TERM_LEN+TUD_AUDIO10_DESC_INPUT_TERM_LEN+TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(1)+TUD_AUDIO10_DESC_OUTPUT_TERM_LEN), /*_itf*/ ((_itfnum)+1), ((_itfnum)+2)),\
+ /* Speaker Input Terminal Descriptor(4.3.2.1) */\
+ TUD_AUDIO10_DESC_INPUT_TERM(/*_termid*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC1_ENTITY_MIC_OUTPUT_TERMINAL, /*_nchannels*/ 0x02, /*_channelcfg*/ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_stridx*/ 0x00),\
+ /* Speaker Feature Unit Descriptor(4.3.2.5) */\
+ TUD_AUDIO10_DESC_FEATURE_UNIT(/*_unitid*/ UAC1_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlmaster*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch1*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch2*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME)),\
+ /* Speaker Output Terminal Descriptor(4.3.2.2) */\
+ TUD_AUDIO10_DESC_OUTPUT_TERM(/*_termid*/ UAC1_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, /*_srcid*/ UAC1_ENTITY_SPK_FEATURE_UNIT, /*_stridx*/ 0x00),\
+ /* Microphone Input Terminal Descriptor(4.3.2.1) */\
+ TUD_AUDIO10_DESC_INPUT_TERM(/*_termid*/ UAC1_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_nchannels*/ 0x01, /*_channelcfg*/ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_stridx*/ 0x00),\
+ /* Microphone Output Terminal Descriptor(4.3.2.2) */\
+ TUD_AUDIO10_DESC_OUTPUT_TERM(/*_termid*/ UAC1_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_srcid*/ UAC1_ENTITY_MIC_INPUT_TERMINAL, /*_stridx*/ 0x00),\
+ /* Standard AS Interface Descriptor(4.5.1) - Speaker Interface 1, Alternate 0 */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ /* Standard AS Interface Descriptor(4.5.1) - Speaker Interface 1, Alternate 1 */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
+ /* Class-Specific AS Interface Descriptor(4.5.2) */\
+ TUD_AUDIO10_DESC_CS_AS_INT(/*_termid*/ UAC1_ENTITY_SPK_INPUT_TERMINAL, /*_delay*/ 0x01, /*_formattype*/ AUDIO10_DATA_FORMAT_TYPE_I_PCM),\
+ /* Type I Format Type Descriptor(2.2.5) */\
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x02, /*_subframesize*/ _nBytesPerSample_RX, /*_bitresolution*/ _nBitsUsedPerSample_RX, /*_freqs*/ __VA_ARGS__),\
+ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01, /*_syncep*/ 0x00),\
+ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
+ /* Standard AS Interface Descriptor(4.5.1) - Microphone Interface 2, Alternate 0 */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+2), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ /* Standard AS Interface Descriptor(4.5.1) - Microphone Interface 2, Alternate 1 */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+2), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ _stridx),\
+ /* Class-Specific AS Interface Descriptor(4.5.2) */\
+ TUD_AUDIO10_DESC_CS_AS_INT(/*_termid*/ UAC1_ENTITY_MIC_OUTPUT_TERMINAL, /*_delay*/ 0x01, /*_formattype*/ AUDIO10_DATA_FORMAT_TYPE_I_PCM),\
+ /* Type I Format Type Descriptor(2.2.5) */\
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x01, /*_subframesize*/ _nBytesPerSample_TX, /*_bitresolution*/ _nBitsUsedPerSample_TX, /*_freqs*/ __VA_ARGS__),\
+ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS), /*_maxEPsize*/ _epinsize, /*_interval*/ 0x01, /*_syncep*/ 0x00),\
+ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001)
#endif
diff --git a/examples/device/uac2_speaker_fb/CMakeLists.txt b/examples/device/uac2_speaker_fb/CMakeLists.txt
index 0ed3db646..cddbf6a31 100644
--- a/examples/device/uac2_speaker_fb/CMakeLists.txt
+++ b/examples/device/uac2_speaker_fb/CMakeLists.txt
@@ -2,33 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(uac2_speaker_fb C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
- ${CMAKE_CURRENT_SOURCE_DIR}/src/quirk_os_guessing.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/uac2_speaker_fb/Makefile b/examples/device/uac2_speaker_fb/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/uac2_speaker_fb/Makefile
+++ b/examples/device/uac2_speaker_fb/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/uac2_speaker_fb/skip.txt b/examples/device/uac2_speaker_fb/skip.txt
index 234f1ebed..0c7339c65 100644
--- a/examples/device/uac2_speaker_fb/skip.txt
+++ b/examples/device/uac2_speaker_fb/skip.txt
@@ -6,3 +6,4 @@ mcu:SAME5X
mcu:SAMG
board:stm32l052dap52
family:broadcom_64bit
+mcu:CH583
diff --git a/examples/device/uac2_speaker_fb/src/audio_debug.py b/examples/device/uac2_speaker_fb/src/audio_debug.py
index 05b49baf6..1c6035a44 100755
--- a/examples/device/uac2_speaker_fb/src/audio_debug.py
+++ b/examples/device/uac2_speaker_fb/src/audio_debug.py
@@ -2,13 +2,15 @@
# Install python3 HID package https://pypi.org/project/hid/
# Install python3 matplotlib package https://pypi.org/project/matplotlib/
-from ctypes import *
+from ctypes import Structure, c_uint32, c_uint8, c_int8, c_int16, c_uint16
+import signal
try:
import hid
import matplotlib.pyplot as plt
import matplotlib.animation as animation
except:
print("Missing import, please try 'pip install hid matplotlib' or consult your OS's python package manager.")
+ exit(1)
# Example must be compiled with CFG_AUDIO_DEBUG=1
VID = 0xcafe
@@ -29,6 +31,7 @@ class audio_debug_info_t (Structure):
dev = hid.Device(VID, PID)
if dev:
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
# Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
@@ -61,10 +64,10 @@ if dev:
ax.set_ylim(bottom=0, top=info.fifo_size)
# Format plot
- plt.title('FIFO information')
- plt.grid()
+ ax.set_title('FIFO information')
+ ax.grid(True)
print(f'Sample rate:{info.sample_rate} | Alt settings:{info.alt_settings} | Volume:{info.volume[:]}')
- ani = animation.FuncAnimation(fig, animate, interval=10)
- plt.show()
+ ani = animation.FuncAnimation(fig, animate, interval=10, cache_frame_data=False) # type: ignore
+ plt.show(block=True)
diff --git a/examples/device/uac2_speaker_fb/src/common_types.h b/examples/device/uac2_speaker_fb/src/common_types.h
index 174e26671..b79ae2fd0 100644
--- a/examples/device/uac2_speaker_fb/src/common_types.h
+++ b/examples/device/uac2_speaker_fb/src/common_types.h
@@ -41,7 +41,7 @@ typedef struct
{
uint32_t sample_rate;
uint8_t alt_settings;
- int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];
+ uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];
uint16_t fifo_size;
uint16_t fifo_count;
diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c
index ed9e7716d..7e4d27f1c 100644
--- a/examples/device/uac2_speaker_fb/src/main.c
+++ b/examples/device/uac2_speaker_fb/src/main.c
@@ -31,25 +31,10 @@
#include "tusb.h"
#include "usb_descriptors.h"
-#ifdef CFG_QUIRK_OS_GUESSING
- #include "quirk_os_guessing.h"
-#endif
-
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
-// List of supported sample rates
-#if defined(__RX__)
-const uint32_t sample_rates[] = {44100, 48000};
-#else
-const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
-#endif
-
-uint32_t current_sample_rate = 44100;
-
-#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
-
/* Blink pattern
* - 25 ms : streaming data
* - 250 ms : device not mounted
@@ -82,8 +67,9 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
-int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
+uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
+uint32_t current_sample_rate = 44100;
// Buffer for speaker data
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 2];
@@ -153,18 +139,193 @@ void tud_resume_cb(void) {
// Application Callback API Implementations
//--------------------------------------------------------------------+
-// Helper for clock get requests
-static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) {
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
+//--------------------------------------------------------------------+
+// UAC1 Helper Functions
+//--------------------------------------------------------------------+
+
+static bool audio10_set_req_ep(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ // Request uses 3 bytes
+ TU_VERIFY(p_request->wLength == 3);
+
+ current_sample_rate = tu_unaligned_read32(pBuff) & 0x00FFFFFF;
+
+ TU_LOG2("EP set current freq: %" PRIu32 "\r\n", current_sample_rate);
+
+ return true;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_ep(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_GET_CUR) {
+ TU_LOG2("EP get current freq\r\n");
+
+ uint8_t freq[3];
+ freq[0] = (uint8_t) (current_sample_rate & 0xFF);
+ freq[1] = (uint8_t) ((current_sample_rate >> 8) & 0xFF);
+ freq[2] = (uint8_t) ((current_sample_rate >> 16) & 0xFF);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, freq, sizeof(freq));
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
+
+static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our feature unit
+ if (entityID == UAC1_ENTITY_FEATURE_UNIT) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 1);
+
+ mute[channelNum] = pBuff[0];
+
+ TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 2);
+
+ volume[channelNum] = (int16_t)tu_unaligned_read16(pBuff) / 256;
+
+ TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our feature unit
+ if (entityID == UAC1_ENTITY_FEATURE_UNIT) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
+ // There does not exist a range parameter block for mute
+ TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
+
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_GET_CUR:
+ TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
+ {
+ int16_t vol = (int16_t) volume[channelNum];
+ vol = vol * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
+ }
+
+ case AUDIO10_CS_REQ_GET_MIN:
+ TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum);
+ {
+ int16_t min = -90; // -90 dB
+ min = min * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
+ }
+
+ case AUDIO10_CS_REQ_GET_MAX:
+ TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum);
+ {
+ int16_t max = 30; // +30 dB
+ max = max * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
+ }
+
+ case AUDIO10_CS_REQ_GET_RES:
+ TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum);
+ {
+ int16_t res = 128; // 0.5 dB
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
+ }
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// UAC2 Helper Functions
+//--------------------------------------------------------------------+
+
+#if TUD_OPT_HIGH_SPEED
+// List of supported sample rates for UAC2
+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, tusb_control_request_t const *p_request) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue);
- if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
- if (request->bRequest == AUDIO_CS_REQ_CUR) {
- TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate);
+ 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);
- audio_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 == AUDIO_CS_REQ_RANGE) {
- audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
+ audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
+ 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)};
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
@@ -175,144 +336,133 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_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 == AUDIO_CS_CTRL_CLK_VALID &&
- request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_control_cur_1_t cur_valid = {.bCur = 1};
+ } 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, audio_control_request_t const *request, uint8_t const *buf) {
- (void) rhport;
+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);
- TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
- TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
+ if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) {
+ TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t));
- current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
+ current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
- TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate);
+ TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
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 tud_audio_feature_unit_get_request(uint8_t rhport, audio_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 == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
- audio_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 == AUDIO_FU_CTRL_VOLUME) {
- if (request->bRequest == AUDIO_CS_REQ_RANGE) {
- audio_control_range_2_n_t(1) range_vol = {
+ 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 == AUDIO_CS_REQ_CUR) {
- audio_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 tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
- (void) rhport;
+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_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
- TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
+ TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) {
- TU_VERIFY(request->wLength == sizeof(audio_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] = ((audio_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 == AUDIO_FU_CTRL_VOLUME) {
- TU_VERIFY(request->wLength == sizeof(audio_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] = ((audio_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;
}
}
-// 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) {
- audio_control_request_t const *request = (audio_control_request_t const *) p_request;
+static bool audio20_get_req_entity(uint8_t rhport, tusb_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 (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
- return tud_audio_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;
}
-// 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) {
- audio_control_request_t const *request = (audio_control_request_t const *) p_request;
+static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint8_t *buf) {
+ uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex);
- if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
- return tud_audio_feature_unit_set_request(rhport, request, buf);
- if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return tud_audio_clock_set_request(rhport, 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;
}
-bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
- (void) rhport;
+#endif // TUD_OPT_HIGH_SPEED
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
-
- if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0)
- blink_interval_ms = BLINK_MOUNTED;
-
- return true;
-}
+//--------------------------------------------------------------------+
+// Main Callback Functions
+//--------------------------------------------------------------------+
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0)
@@ -325,12 +475,93 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_reques
return true;
}
+// Invoked when audio class specific set request received for an EP
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) pBuff;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_ep(p_request, pBuff);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an EP
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_ep(rhport, p_request);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// 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) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_entity(p_request, buf);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_set_req_entity(p_request, buf);
+#endif
+ }
+
+ return false;
+}
+
+// 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) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_entity(rhport, p_request);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_get_req_entity(rhport, p_request);
+#endif
+ }
+
+ return false;
+}
+
+bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
+
+ if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) {
+ blink_interval_ms = BLINK_MOUNTED;
+ }
+
+ return true;
+}
+
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
(void) func_id;
(void) alt_itf;
// Set feedback method to fifo counting
feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT;
feedback_param->sample_freq = current_sample_rate;
+
+ // About FIFO threshold:
+ //
+ // By default the threshold is set to half FIFO size, which works well in most cases,
+ // you can reduce the threshold to have less latency.
+ //
+ // For example, here we could set the threshold to 2 ms of audio data, as audio_task() read audio data every 1 ms,
+ // having 2 ms threshold allows some margin and a quick response:
+ //
+ // feedback_param->fifo_count.fifo_threshold =
+ // current_sample_rate * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX / 1000 * 2;
}
#if CFG_AUDIO_DEBUG
@@ -343,22 +574,13 @@ bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t fu
fifo_count = tud_audio_available();
// Same averaging method used in UAC2 class
- fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ((uint32_t) fifo_count << 16)) >> 6);
+ const uint32_t ff_count32 = (uint32_t) fifo_count << 16;
+ fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ff_count32) >> 6);
return true;
}
#endif
-#if CFG_QUIRK_OS_GUESSING
-bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
- (void) func_id;
- if (tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
- return true;
- } else {
- return false;
- }
-}
-#endif
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
@@ -367,7 +589,7 @@ bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
// In a real application, this would be replaced with actual I2S transmit callback.
void audio_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
+ uint32_t curr_ms = tusb_time_millis_api();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
@@ -394,7 +616,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return;
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) return;
start_ms += blink_interval_ms;
board_led_write(led_state);
@@ -408,7 +630,7 @@ void led_blinking_task(void) {
// Every 1ms, we will sent 1 debug information report
void audio_debug_task(void) {
static uint32_t start_ms = 0;
- uint32_t curr_ms = board_millis();
+ uint32_t curr_ms = tusb_time_millis_api();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
diff --git a/examples/device/uac2_speaker_fb/src/quirk_os_guessing.c b/examples/device/uac2_speaker_fb/src/quirk_os_guessing.c
deleted file mode 100644
index 92b9ab6ee..000000000
--- a/examples/device/uac2_speaker_fb/src/quirk_os_guessing.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2024 HiFiPhile
- *
- * 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.
- *
- */
-
-#include "quirk_os_guessing.h"
-
-static tusb_desc_type_t desc_req_buf[2];
-static int desc_req_idx = 0;
-
-// Place at the start of tud_descriptor_device_cb()
-void quirk_os_guessing_desc_device_cb(void) {
- desc_req_idx = 0;
-}
-
-// Place at the start of tud_descriptor_configuration_cb()
-void quirk_os_guessing_desc_configuration_cb(void) {
- // Skip redundant request
- if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_CONFIGURATION)) {
- desc_req_buf[desc_req_idx++] = TUSB_DESC_CONFIGURATION;
- }
-}
-
-// Place at the start of tud_descriptor_bos_cb()
-void quirk_os_guessing_desc_bos_cb(void) {
- // Skip redundant request
- if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_BOS)) {
- desc_req_buf[desc_req_idx++] = TUSB_DESC_BOS;
- }
-}
-
-// Place at the start of tud_descriptor_string_cb()
-void quirk_os_guessing_desc_string_cb(void) {
- // Skip redundant request
- if (desc_req_idx == 0 || (desc_req_idx == 1 && desc_req_buf[0] != TUSB_DESC_STRING)) {
- desc_req_buf[desc_req_idx++] = TUSB_DESC_STRING;
- }
-}
-
-// Each OS request descriptors differently:
-// Windows 10 - 11
-// Device Desc
-// Config Desc
-// BOS Desc
-// String Desc
-// Linux 3.16 - 6.8
-// Device Desc
-// BOS Desc
-// Config Desc
-// String Desc
-// OS X Ventura - Sonoma
-// Device Desc
-// String Desc
-// Config Desc || BOS Desc
-// BOS Desc || Config Desc
-quirk_os_guessing_t quirk_os_guessing_get(void) {
- if (desc_req_idx < 2) {
- return QUIRK_OS_GUESSING_UNKNOWN;
- }
-
- if (desc_req_buf[0] == TUSB_DESC_BOS && desc_req_buf[1] == TUSB_DESC_CONFIGURATION) {
- return QUIRK_OS_GUESSING_LINUX;
- } else if (desc_req_buf[0] == TUSB_DESC_CONFIGURATION && desc_req_buf[1] == TUSB_DESC_BOS) {
- return QUIRK_OS_GUESSING_WINDOWS;
- } else if (desc_req_buf[0] == TUSB_DESC_STRING && (desc_req_buf[1] == TUSB_DESC_BOS || desc_req_buf[1] == TUSB_DESC_CONFIGURATION)) {
- return QUIRK_OS_GUESSING_OSX;
- }
-
- return QUIRK_OS_GUESSING_UNKNOWN;
-}
diff --git a/examples/device/uac2_speaker_fb/src/quirk_os_guessing.h b/examples/device/uac2_speaker_fb/src/quirk_os_guessing.h
deleted file mode 100644
index 1120355c9..000000000
--- a/examples/device/uac2_speaker_fb/src/quirk_os_guessing.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2024 HiFiPhile
- *
- * 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.
- *
- */
-
-#ifndef _QUIRK_OS_GUESSING_H_
-#define _QUIRK_OS_GUESSING_H_
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-#include "tusb.h"
-
-//================================== !!! WARNING !!! ====================================
-// This quirk operate out of USB specification in order to workaround specific issues.
-// It may not work on your platform.
-//=======================================================================================
-//
-// Prerequisites:
-// - Set USB version to at least 2.01 in Device Descriptor
-// - Has a valid BOS Descriptor, refer to webusb_serial example
-//
-// Attention:
-// Windows detection result comes out after Configuration Descriptor request,
-// meaning it will be too late to do descriptor adjustment. It's advised to make
-// Windows as default configuration and adjust to other OS accordingly.
-
-typedef enum {
- QUIRK_OS_GUESSING_UNKNOWN,
- QUIRK_OS_GUESSING_LINUX,
- QUIRK_OS_GUESSING_OSX,
- QUIRK_OS_GUESSING_WINDOWS,
-} quirk_os_guessing_t;
-
-// Get Host OS type
-quirk_os_guessing_t quirk_os_guessing_get(void);
-
-// Place at the start of tud_descriptor_device_cb()
-void quirk_os_guessing_desc_device_cb(void);
-
-// Place at the start of tud_descriptor_configuration_cb()
-void quirk_os_guessing_desc_configuration_cb(void);
-
-// Place at the start of tud_descriptor_bos_cb()
-void quirk_os_guessing_desc_bos_cb(void);
-
-// Place at the start of tud_descriptor_string_cb()
-void quirk_os_guessing_desc_string_cb(void);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _QUIRK_OS_GUESSING_H_ */
diff --git a/examples/device/uac2_speaker_fb/src/tusb_config.h b/examples/device/uac2_speaker_fb/src/tusb_config.h
index 18ab2ff96..46f5703c8 100644
--- a/examples/device/uac2_speaker_fb/src/tusb_config.h
+++ b/examples/device/uac2_speaker_fb/src/tusb_config.h
@@ -24,8 +24,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -87,14 +87,6 @@ extern "C" {
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
-/* (Needed for Full-Speed only)
- * Enable host OS guessing to workaround UAC2 compatibility issues between Windows and OS X
- * The default configuration only support Windows and Linux, enable this option for OS X
- * support. Otherwise if you don't need Windows support you can make OS X's configuration as
- * default.
- */
-#define CFG_QUIRK_OS_GUESSING 1
-
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
@@ -128,38 +120,33 @@ extern "C" {
// AUDIO CLASS DRIVER CONFIGURATION
//--------------------------------------------------------------------
-#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN
-
-// Can be enabled with Full-Speed device on OSX, which forces feedback EP size to 3, in this case CFG_QUIRK_OS_GUESSING can be disabled
-#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0
+#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
-// Audio format type I specifications
-#if defined(__RX__)
-#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
-#else
-#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 96000
-#endif
+// 16bit data in 16bit slots
+#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
+#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX 16
-#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
+// UAC1 Full-Speed endpoint size
+#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_FS 48000
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS TUD_AUDIO_EP_SIZE(false, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_FS, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
+// UAC2 High-Speed endpoint size
+#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_HS 96000
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS TUD_AUDIO_EP_SIZE(true, CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE_HS, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
-// 16bit in 16bit slots
-#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
-#define CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX 16
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS)
-// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
-#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
+// AUDIO_FEEDBACK_METHOD_FIFO_COUNT needs buffer size >= 4* EP size to work correctly
+// Example read FIFO every 1ms (8 HS frames), so buffer size should be 8 times larger for HS device
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(4 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS, 32 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS)
-#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
-#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
+// Enable OUT EP
+#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
// Enable feedback EP
-#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1
-
-// Size of control request buffer
-#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
+#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.c b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
index ee1b92225..f0c780e38 100644
--- a/examples/device/uac2_speaker_fb/src/usb_descriptors.c
+++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
@@ -28,28 +28,24 @@
#include "usb_descriptors.h"
#include "common_types.h"
-#ifdef CFG_QUIRK_OS_GUESSING
-#include "quirk_os_guessing.h"
-#endif
-
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0201,
+ .bcdUSB = 0x0200,
// Use Interface Association Descriptor (IAD) for Audio
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
@@ -71,12 +67,8 @@ tusb_desc_device_t const desc_device =
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
-#if CFG_QUIRK_OS_GUESSING
- quirk_os_guessing_desc_device_cb();
-#endif
- return (uint8_t const *)&desc_device;
+uint8_t const * tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
}
#if CFG_AUDIO_DEBUG
@@ -84,8 +76,7 @@ uint8_t const * tud_descriptor_device_cb(void)
// HID Report Descriptor
//--------------------------------------------------------------------+
-uint8_t const desc_hid_report[] =
-{
+uint8_t const desc_hid_report[] = {
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
HID_USAGE ( 0x01 ),\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
@@ -101,8 +92,7 @@ uint8_t const desc_hid_report[] =
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
-{
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) {
(void) itf;
return desc_hid_report;
}
@@ -112,109 +102,133 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
// Configuration Descriptor
//--------------------------------------------------------------------+
-#if CFG_AUDIO_DEBUG
- #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN + TUD_HID_DESC_LEN)
-#else
- #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN)
-#endif
-
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
+ #define EPNUM_AUDIO 0x03
#define EPNUM_AUDIO_FB 0x03
- #define EPNUM_AUDIO_OUT 0x03
#define EPNUM_DEBUG 0x04
-#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
- // ISO endpoints for NRF5x are fixed to 0x08 (0x88)
+#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
+ // nRF5x ISO can only be endpoint 8
+ #define EPNUM_AUDIO 0x08
#define EPNUM_AUDIO_FB 0x08
- #define EPNUM_AUDIO_OUT 0x08
#define EPNUM_DEBUG 0x01
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_FB 0x01
- #define EPNUM_AUDIO_OUT 0x02
- #define EPNUM_DEBUG 0x03
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+ #define EPNUM_AUDIO_FB 0x0B
+ #define EPNUM_DEBUG 0x01
+ #else
+ #define EPNUM_AUDIO 0x02
+ #define EPNUM_AUDIO_FB 0x01
+ #define EPNUM_DEBUG 0x03
+ #endif
#else
+ #define EPNUM_AUDIO 0x01
#define EPNUM_AUDIO_FB 0x01
- #define EPNUM_AUDIO_OUT 0x01
#define EPNUM_DEBUG 0x02
#endif
-uint8_t const desc_configuration_default[] =
-{
- // Config number, interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+#if CFG_AUDIO_DEBUG
+ #define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_SPEAKER_STEREO_FB_DESC_LEN(2) + TUD_HID_DESC_LEN)
+#else
+ #define CONFIG_UAC1_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO10_SPEAKER_STEREO_FB_DESC_LEN(2))
+#endif
- // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size,
- TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80, 4),
+uint8_t const desc_uac1_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC1_TOTAL_LEN, 0x00, 100),
+
+ // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, sample rates (44.1kHz, 48kHz)
+ TUD_AUDIO10_SPEAKER_STEREO_FB_DESCRIPTOR(ITF_NUM_AUDIO_CONTROL, 5, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_FS, EPNUM_AUDIO_FB | 0x80, 44100, 48000),
#if CFG_AUDIO_DEBUG
- // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
- TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
+ // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+ TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
#endif
};
-#if CFG_QUIRK_OS_GUESSING
-// OS X needs 3 bytes feedback endpoint on FS
-uint8_t const desc_configuration_osx_fs[] =
-{
- // Config number, interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+TU_VERIFY_STATIC(sizeof(desc_uac1_configuration) == CONFIG_UAC1_TOTAL_LEN, "Incorrect size");
- // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size,
- TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(0, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO_OUT, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, EPNUM_AUDIO_FB | 0x80, 3),
+#if TUD_OPT_HIGH_SPEED
#if CFG_AUDIO_DEBUG
- // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
- TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
-#endif
-};
+ #define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN + TUD_HID_DESC_LEN)
+#else
+ #define CONFIG_UAC2_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN)
#endif
-// Invoked when received GET CONFIGURATION DESCRIPTOR
-// Application return pointer to descriptor
-// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
- (void)index; // for multiple configurations
+uint8_t const desc_uac2_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_UAC2_TOTAL_LEN, 0x00, 100),
-#if CFG_QUIRK_OS_GUESSING
- quirk_os_guessing_desc_configuration_cb();
- if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
- return desc_configuration_osx_fs;
- }
-#endif
- return desc_configuration_default;
-}
+ // Interface number, string index, byte per sample, bit per sample, EP Out, EP size, EP feedback, feedback EP size,
+ TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR(ITF_NUM_AUDIO_CONTROL, 4, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_RESOLUTION_RX, EPNUM_AUDIO, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_HS, EPNUM_AUDIO_FB | 0x80, 4),
-//--------------------------------------------------------------------+
-// BOS Descriptor, required for OS guessing quirk
-//--------------------------------------------------------------------+
+#if CFG_AUDIO_DEBUG
+ // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+ TUD_HID_DESCRIPTOR(ITF_NUM_DEBUG, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_DEBUG | 0x80, CFG_TUD_HID_EP_BUFSIZE, 7)
+#endif
+};
-#define TUD_BOS_USB20_EXT_DESC_LEN 7
+TU_VERIFY_STATIC(sizeof(desc_uac2_configuration) == CONFIG_UAC2_TOTAL_LEN, "Incorrect size");
-#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_USB20_EXT_DESC_LEN)
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = 0x0200,
-// BOS Descriptor is required for webUSB
-uint8_t const desc_bos[] =
-{
- // total length, number of device caps
- TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
- // USB 2.0 Extension Descriptor
- 0x07, TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_USB20_EXTENSION, 0x00, 0x00, 0x00,0x00
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
};
-uint8_t const * tud_descriptor_bos_cb(void)
-{
-#if CFG_QUIRK_OS_GUESSING
- quirk_os_guessing_desc_bos_cb();
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;// for multiple configurations
+
+ // if link speed is high return fullspeed config, and vice versa
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_uac1_configuration : desc_uac2_configuration;
+}
+
+#endif // highspeed
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index; // for multiple configurations
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ if(tud_speed_get() == TUSB_SPEED_FULL) {
+ return desc_uac1_configuration;
+ } else {
+ return desc_uac2_configuration;
+ }
+#else
+ return desc_uac1_configuration;
#endif
- return desc_bos;
}
//--------------------------------------------------------------------+
@@ -230,13 +244,14 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Speaker", // 2: Product
NULL, // 3: Serials will use unique ID if possible
"UAC2 Speaker", // 4: Audio Interface
+ "UAC1 Speaker", // 5: UAC1 Audio Interface
};
static uint16_t _desc_str[32 + 1];
@@ -247,10 +262,6 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
size_t chr_count;
-#if CFG_QUIRK_OS_GUESSING
- quirk_os_guessing_desc_string_cb();
-#endif
-
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.h b/examples/device/uac2_speaker_fb/src/usb_descriptors.h
index 9511bf797..79f25bbfa 100644
--- a/examples/device/uac2_speaker_fb/src/usb_descriptors.h
+++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.h
@@ -23,60 +23,148 @@
*
*/
-#ifndef _USB_DESCRIPTORS_H_
-#define _USB_DESCRIPTORS_H_
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
-// Defined in TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR
+//--------------------------------------------------------------------+
+// UAC2 DESCRIPTOR TEMPLATES
+//--------------------------------------------------------------------+
+
+// Defined in TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR
#define UAC2_ENTITY_CLOCK 0x04
#define UAC2_ENTITY_INPUT_TERMINAL 0x01
#define UAC2_ENTITY_FEATURE_UNIT 0x02
#define UAC2_ENTITY_OUTPUT_TERMINAL 0x03
-#define TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
- + TUD_AUDIO_DESC_STD_AC_LEN\
- + TUD_AUDIO_DESC_CS_AC_LEN\
- + TUD_AUDIO_DESC_CLK_SRC_LEN\
- + TUD_AUDIO_DESC_INPUT_TERM_LEN\
- + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
- + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_STD_AS_INT_LEN\
- + TUD_AUDIO_DESC_CS_AS_INT_LEN\
- + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
- + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN)
+#define TUD_AUDIO20_SPEAKER_STEREO_FB_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2)\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_FB_EP_LEN)
-#define TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \
+#define TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
- TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
- TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
/* Clock Source Descriptor(4.7.2.1) */\
- TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ (AUDIO20_CTRL_RW << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
/* Input Terminal Descriptor(4.7.2.4) */\
- TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
/* Output Terminal Descriptor(4.7.2.5) */\
- TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
- TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS,/*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
- TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\
/* Class-Specific AS Interface Descriptor(4.9.2) */\
- TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
- TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
+ TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01),\
+ TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
- TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1)\
+ TUD_AUDIO20_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1)
+
+//--------------------------------------------------------------------+
+// UAC1 DESCRIPTOR TEMPLATES
+//--------------------------------------------------------------------+
+
+// Defined in TUD_AUDIO10_SPEAKER_STEREO_FB_DESCRIPTOR
+#define UAC1_ENTITY_INPUT_TERMINAL 0x01
+#define UAC1_ENTITY_FEATURE_UNIT 0x02
+#define UAC1_ENTITY_OUTPUT_TERMINAL 0x03
+
+#define TUD_AUDIO10_SPEAKER_STEREO_FB_DESC_LEN(_nfreqs) (\
+ + TUD_AUDIO10_DESC_STD_AC_LEN\
+ + TUD_AUDIO10_DESC_CS_AC_LEN(1)\
+ + TUD_AUDIO10_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
+ + TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO10_DESC_STD_AS_ISO_SYNC_EP_LEN)
+
+#define TUD_AUDIO10_SPEAKER_STEREO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, ...) \
+ /* Standard AC Interface Descriptor(4.3.1) */\
+ TUD_AUDIO10_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ /* Class-Specific AC Interface Header Descriptor(4.3.2) */\
+ TUD_AUDIO10_DESC_CS_AC(/*_bcdADC*/ 0x0100, /*_totallen*/ (TUD_AUDIO10_DESC_INPUT_TERM_LEN+TUD_AUDIO10_DESC_OUTPUT_TERM_LEN+TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(2)), /*_itf*/ ((_itfnum)+1)),\
+ /* Input Terminal Descriptor(4.3.2.1) */\
+ TUD_AUDIO10_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_nchannels*/ 0x02, /*_channelcfg*/ AUDIO10_CHANNEL_CONFIG_LEFT_FRONT | AUDIO10_CHANNEL_CONFIG_RIGHT_FRONT, /*_idxchannelnames*/ 0x00, /*_stridx*/ 0x00),\
+ /* Output Terminal Descriptor(4.3.2.2) */\
+ TUD_AUDIO10_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ 0x02, /*_stridx*/ 0x00),\
+ /* Feature Unit Descriptor(4.3.2.5) */\
+ TUD_AUDIO10_DESC_FEATURE_UNIT(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlmaster*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch1*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch2*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME)),\
+ /* Standard AS Interface Descriptor(4.5.1) */\
+ /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
+ /* Standard AS Interface Descriptor(4.5.1) */\
+ /* Interface 1, Alternate 1 - alternate interface for data streaming */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\
+ /* Class-Specific AS Interface Descriptor(4.5.2) */\
+ TUD_AUDIO10_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_delay*/ 0x00, /*_formattype*/ AUDIO10_DATA_FORMAT_TYPE_I_PCM),\
+ /* Type I Format Type Descriptor(2.2.5) */\
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x02, /*_subframesize*/ _nBytesPerSample, /*_bitresolution*/ _nBitsUsedPerSample, /*_freqs*/ __VA_ARGS__),\
+ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01, /*_sync_ep*/ _epfb),\
+ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
+ /* Standard AS Isochronous Synch Endpoint Descriptor (4.6.2.1) */\
+ TUD_AUDIO10_DESC_STD_AS_ISO_SYNC_EP(/*_ep*/ _epfb, /*_bRefresh*/ 0)
+
+//---------------------------------------------------------------------------+
+// UAC1 Isochronous Synch Endpoint bRefresh Workaround
+//
+// bRefresh value is set to 0, while UAC1 spec requires it to be between
+// 1 (2 ms) and 9 (512 ms)
+//
+// This value has been tested to work with Windows, macOS and Linux.
+//
+// Rationale:
+// Some USB device controllers (e.g. Synopsys DWC2) require a known transfer
+// interval to manually schedule isochronous IN transfers. For data isochronous
+// endpoints, the bInterval field in the endpoint descriptor is used. However,
+// for synch endpoint it's unclear which field the host uses to determine the
+// transfer interval. Windows and macOS use bRefresh, while Linux uses bInterval.
+//
+// Since bInterval is fixed to 1, if bRefresh is set to 2 then Windows and macOS
+// will schedule the feedback transfer every 4 ms, but Linux will schedule it
+// every 1 ms. DWC2 controller cannot handle this discrepancy without knwowing
+// the actual interval, therefore we set bRefresh to 0 to let the transfer
+// execute every 1 ms, which is the same as bInterval.
+//
+// Rant:
+// WTF USB-IF? Why have two fields that mean the same thing? Why not just use
+// bInterval for both data and synch endpoints? Why is bRefresh even necessary?
+//
+// Note:
+// For the moment DWC2 driver doesn't have proper support for bInterval > 1
+// for isochronous IN endpoints. The implementation would be complex and CPU
+// intensive (cfr.
+// https://github.com/torvalds/linux/blob/master/drivers/usb/dwc2/gadget.c)
+// It MAY work in some cases if you are lucky, but it's not guaranteed.
+//---------------------------------------------------------------------------+
#endif
diff --git a/examples/device/usbtmc/CMakeLists.txt b/examples/device/usbtmc/CMakeLists.txt
index d2deb72d5..6181c303b 100644
--- a/examples/device/usbtmc/CMakeLists.txt
+++ b/examples/device/usbtmc/CMakeLists.txt
@@ -2,33 +2,30 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(usbtmc C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usbtmc_app.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/usbtmc/Makefile b/examples/device/usbtmc/Makefile
index 1b4d398cf..1a4b428dc 100644
--- a/examples/device/usbtmc/Makefile
+++ b/examples/device/usbtmc/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/usbtmc/src/main.c b/examples/device/usbtmc/src/main.c
index f78cce91f..b1269b117 100644
--- a/examples/device/usbtmc/src/main.c
+++ b/examples/device/usbtmc/src/main.c
@@ -29,6 +29,7 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "main.h"
#include "usbtmc_app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
@@ -123,12 +124,12 @@ void led_blinking_task(void)
{
led_state = true;
board_led_write(true);
- start_ms = board_millis();
+ start_ms = tusb_time_millis_api();
doPulse = false;
}
else if (led_state == true)
{
- if ( board_millis() - start_ms < 750) //Spec says blink must be between 500 and 1000 ms.
+ if ( tusb_time_millis_api() - start_ms < 750) //Spec says blink must be between 500 and 1000 ms.
{
return; // not enough time
}
@@ -139,7 +140,7 @@ void led_blinking_task(void)
else
{
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/usbtmc/src/usb_descriptors.c b/examples/device/usbtmc/src/usb_descriptors.c
index 85acd990a..ecdcef834 100644
--- a/examples/device/usbtmc/src/usb_descriptors.c
+++ b/examples/device/usbtmc/src/usb_descriptors.c
@@ -34,9 +34,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xcafe
#define USB_BCD 0x0200
@@ -44,7 +44,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -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
@@ -112,7 +122,7 @@ enum
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_USBTMC_DESC_LEN)
-uint8_t const desc_fs_configuration[] =
+static uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -122,7 +132,7 @@ uint8_t const desc_fs_configuration[] =
#if TUD_OPT_HIGH_SPEED
-uint8_t const desc_hs_configuration[] =
+static uint8_t const desc_hs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -131,10 +141,10 @@ uint8_t const desc_hs_configuration[] =
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier =
+static tusb_desc_device_qualifier_t const desc_device_qualifier =
{
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
@@ -158,6 +168,23 @@ uint8_t const* tud_descriptor_device_qualifier_cb(void)
return (uint8_t const*) &desc_device_qualifier;
}
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index; // for multiple configurations
+
+ // if link speed is high return fullspeed config, and vice versa
+ // Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
+ memcpy(desc_other_speed_config,
+ (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+ CONFIG_TOTAL_LEN);
+
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+
+ return desc_other_speed_config;
+}
+
#endif
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -187,7 +214,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/device/usbtmc/src/usbtmc_app.c b/examples/device/usbtmc/src/usbtmc_app.c
index e738f1008..35e8618f5 100644
--- a/examples/device/usbtmc/src/usbtmc_app.c
+++ b/examples/device/usbtmc/src/usbtmc_app.c
@@ -28,6 +28,7 @@
#include "tusb.h"
#include "bsp/board_api.h"
#include "main.h"
+#include "usbtmc_app.h"
#if (CFG_TUD_USBTMC_ENABLE_488)
static usbtmc_response_capabilities_488_t const
@@ -208,19 +209,19 @@ void usbtmc_app_task_iter(void) {
case 0:
break;
case 1:
- queryDelayStart = board_millis();
+ queryDelayStart = tusb_time_millis_api();
queryState = 2;
break;
case 2:
- if( (board_millis() - queryDelayStart) > resp_delay) {
- queryDelayStart = board_millis();
+ if( (tusb_time_millis_api() - queryDelayStart) > resp_delay) {
+ queryDelayStart = tusb_time_millis_api();
queryState=3;
status |= 0x10u; // MAV
status |= 0x40u; // SRQ
}
break;
case 3:
- if( (board_millis() - queryDelayStart) > resp_delay) {
+ if( (tusb_time_millis_api() - queryDelayStart) > resp_delay) {
queryState = 4;
}
break;
diff --git a/examples/device/video_capture/CMakeLists.txt b/examples/device/video_capture/CMakeLists.txt
index 90788fa60..1f9b851ff 100644
--- a/examples/device/video_capture/CMakeLists.txt
+++ b/examples/device/video_capture/CMakeLists.txt
@@ -2,38 +2,35 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(video_capture C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
if (FORCE_READONLY)
-target_compile_definitions(${PROJECT} PRIVATE
+target_compile_definitions(${PROJECT_NAME} PRIVATE
CFG_EXAMPLE_VIDEO_READONLY
)
endif()
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/video_capture/Makefile b/examples/device/video_capture/Makefile
index d698a848d..6c248ab7b 100644
--- a/examples/device/video_capture/Makefile
+++ b/examples/device/video_capture/Makefile
@@ -1,4 +1,4 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
ifeq ($(DISABLE_MJPEG),1)
CFLAGS += -DCFG_EXAMPLE_VIDEO_DISABLE_MJPEG
@@ -9,10 +9,10 @@ endif
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/video_capture/skip.txt b/examples/device/video_capture/skip.txt
index 50302c544..5a7a1d00e 100644
--- a/examples/device/video_capture/skip.txt
+++ b/examples/device/video_capture/skip.txt
@@ -1,6 +1,6 @@
mcu:CH32V103
mcu:CH32V20X
-mcu:MCXA15
+mcu:CH583
mcu:MSP430x5xx
mcu:NUC121
mcu:SAMD11
diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c
index 29656e944..df9f77a1c 100644
--- a/examples/device/video_capture/src/main.c
+++ b/examples/device/video_capture/src/main.c
@@ -53,9 +53,23 @@ void usb_device_task(void *param);
void video_task(void* param);
#if CFG_TUSB_OS == OPT_OS_FREERTOS
-void freertos_init_task(void);
+void freertos_init(void);
#endif
+#if !defined(CFG_EXAMPLE_VIDEO_READONLY) || defined(CFG_EXAMPLE_VIDEO_BUFFERLESS)
+/* EBU color bars: https://stackoverflow.com/questions/6939422 */
+static uint8_t const bar_color[8][4] = {
+ /* Y, U, Y, V */
+ { 235, 128, 235, 128}, /* 100% White */
+ { 219, 16, 219, 138}, /* Yellow */
+ { 188, 154, 188, 16}, /* Cyan */
+ { 173, 42, 173, 26}, /* Green */
+ { 78, 214, 78, 230}, /* Magenta */
+ { 63, 102, 63, 240}, /* Red */
+ { 32, 240, 32, 118}, /* Blue */
+ { 16, 128, 16, 128}, /* Black */
+};
+#endif
//--------------------------------------------------------------------+
// Main
@@ -65,7 +79,7 @@ int main(void) {
// If using FreeRTOS: create blinky, tinyusb device, video task
#if CFG_TUSB_OS == OPT_OS_FREERTOS
- freertos_init_task();
+ freertos_init();
#else
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
@@ -111,12 +125,43 @@ void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
+#ifdef CFG_EXAMPLE_VIDEO_BUFFERLESS
+
+#ifndef CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
+ #error Demo only supports YUV2 please define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
+#endif
+
+void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request)
+{
+ static uint32_t frame_counter = 0;
+ (void)ctl_idx;
+ (void)stm_idx;
+
+ /* Offset will be zero at the start of a new frame */
+ if (!request->offset) frame_counter++;
+
+ for (size_t buf_pos = 0; buf_pos < request->length; buf_pos += 2) {
+
+ /* Position within the current line (pixel relative) */
+ int line_pos = ((request->offset + buf_pos)>>1) % FRAME_WIDTH;
+
+ /* Choose color based on the position and change the table offset every 4 frames */
+ const uint8_t* color = bar_color[(line_pos/(FRAME_WIDTH / 8) + (frame_counter>>2)) % 8];
+
+ /* Copy pixel data for odd or even pixels */
+ memcpy(&((uint8_t*)request->buf)[buf_pos], &color[(line_pos & 1) ? 2 : 0], 2);
+ }
+
+}
+#endif
+
//--------------------------------------------------------------------+
// USB Video
//--------------------------------------------------------------------+
static unsigned frame_num = 0;
static unsigned tx_busy = 0;
static unsigned interval_ms = 1000 / FRAME_RATE;
+#ifndef CFG_EXAMPLE_VIDEO_BUFFERLESS
#ifdef CFG_EXAMPLE_VIDEO_READONLY
// For mcus that does not have enough SRAM for frame buffer, we use fixed frame data.
@@ -145,18 +190,6 @@ static struct {
static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8];
static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
- /* EBU color bars: https://stackoverflow.com/questions/6939422 */
- static uint8_t const bar_color[8][4] = {
- /* Y, U, Y, V */
- { 235, 128, 235, 128}, /* 100% White */
- { 219, 16, 219, 138}, /* Yellow */
- { 188, 154, 188, 16}, /* Cyan */
- { 173, 42, 173, 26}, /* Green */
- { 78, 214, 78, 230}, /* Magenta */
- { 63, 102, 63, 240}, /* Red */
- { 32, 240, 32, 118}, /* Blue */
- { 16, 128, 16, 128}, /* Black */
- };
uint8_t* p;
/* Generate the 1st line */
@@ -183,6 +216,8 @@ static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
#endif
+#endif /* NDEF CFG_EXAMPLE_VIDEO_BUFFERLESS */
+
static void video_send_frame(void) {
static unsigned start_ms = 0;
static unsigned already_sent = 0;
@@ -196,8 +231,10 @@ static void video_send_frame(void) {
if (!already_sent) {
already_sent = 1;
tx_busy = 1;
- start_ms = board_millis();
-#ifdef CFG_EXAMPLE_VIDEO_READONLY
+ start_ms = tusb_time_millis_api();
+#if defined(CFG_EXAMPLE_VIDEO_BUFFERLESS)
+ tud_video_n_frame_xfer(0, 0, NULL, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
+#elif defined (CFG_EXAMPLE_VIDEO_READONLY)
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
@@ -210,13 +247,19 @@ static void video_send_frame(void) {
#endif
}
- unsigned cur = board_millis();
- if (cur - start_ms < interval_ms) return; // not enough time
- if (tx_busy) return;
+ unsigned cur = tusb_time_millis_api();
+ if (cur - start_ms < interval_ms) {
+ return; // not enough time
+ }
+ if (tx_busy) {
+ return;
+ }
start_ms += interval_ms;
tx_busy = 1;
-#ifdef CFG_EXAMPLE_VIDEO_READONLY
+#if defined(CFG_EXAMPLE_VIDEO_BUFFERLESS)
+ tud_video_n_frame_xfer(0, 0, NULL, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
+#elif defined(CFG_EXAMPLE_VIDEO_READONLY)
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
@@ -273,7 +316,9 @@ void led_blinking_task(void* param) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
#else
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
#endif
start_ms += blink_interval_ms;
@@ -336,7 +381,7 @@ void usb_device_task(void *param) {
}
}
-void freertos_init_task(void) {
+void freertos_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h
index 4ba86ca65..1aa1a5ce7 100644
--- a/examples/device/video_capture/src/tusb_config.h
+++ b/examples/device/video_capture/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -110,9 +110,10 @@
//#define CFG_EXAMPLE_VIDEO_READONLY
//#define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
+//#define CFG_EXAMPLE_VIDEO_BUFFERLESS
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c
index b3e19b0f0..b3382c82d 100644
--- a/examples/device/video_capture/src/usb_descriptors.c
+++ b/examples/device/video_capture/src/usb_descriptors.c
@@ -33,9 +33,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] VIDEO | AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VIDEO, 5) | _PID_MAP(VENDOR, 6) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VIDEO, 5) | PID_MAP(VENDOR, 6) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -63,7 +63,7 @@ char const* string_desc_arr[] = {
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -384,9 +384,9 @@ static uint8_t * get_hs_configuration_desc(void) {
}
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_MISC,
diff --git a/examples/device/video_capture_2ch/CMakeLists.txt b/examples/device/video_capture_2ch/CMakeLists.txt
index 90788fa60..cb1785d1f 100644
--- a/examples/device/video_capture_2ch/CMakeLists.txt
+++ b/examples/device/video_capture_2ch/CMakeLists.txt
@@ -2,38 +2,35 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(video_capture_2ch C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
if (FORCE_READONLY)
-target_compile_definitions(${PROJECT} PRIVATE
+target_compile_definitions(${PROJECT_NAME} PRIVATE
CFG_EXAMPLE_VIDEO_READONLY
)
endif()
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/video_capture_2ch/Makefile b/examples/device/video_capture_2ch/Makefile
index d698a848d..6c248ab7b 100644
--- a/examples/device/video_capture_2ch/Makefile
+++ b/examples/device/video_capture_2ch/Makefile
@@ -1,4 +1,4 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
ifeq ($(DISABLE_MJPEG),1)
CFLAGS += -DCFG_EXAMPLE_VIDEO_DISABLE_MJPEG
@@ -9,10 +9,10 @@ endif
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/video_capture_2ch/skip.txt b/examples/device/video_capture_2ch/skip.txt
index 0f6508226..c37205b8c 100644
--- a/examples/device/video_capture_2ch/skip.txt
+++ b/examples/device/video_capture_2ch/skip.txt
@@ -5,8 +5,8 @@ mcu:GD32VF103
mcu:CH32V103
mcu:CH32V20X
mcu:CH32V307
+mcu:CH583
mcu:STM32L0
-mcu:MCXA15
family:espressif
board:curiosity_nano
board:kuiic
diff --git a/examples/device/video_capture_2ch/src/main.c b/examples/device/video_capture_2ch/src/main.c
index f56738f67..debd336fd 100644
--- a/examples/device/video_capture_2ch/src/main.c
+++ b/examples/device/video_capture_2ch/src/main.c
@@ -53,7 +53,7 @@ void usb_device_task(void *param);
void video_task(void* param);
#if CFG_TUSB_OS == OPT_OS_FREERTOS
-void freertos_init_task(void);
+void freertos_init(void);
#endif
@@ -65,7 +65,7 @@ int main(void) {
// If using FreeRTOS: create blinky, tinyusb device, video task
#if CFG_TUSB_OS == OPT_OS_FREERTOS
- freertos_init_task();
+ freertos_init();
#else
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
@@ -180,7 +180,7 @@ static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
}
#endif
-size_t get_framebuf(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, size_t fnum, void **fb) {
+static size_t get_framebuf(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, size_t fnum, void **fb) {
uint32_t idx = ctl_idx + stm_idx;
if (idx == 0) {
@@ -205,8 +205,7 @@ size_t get_framebuf(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, size_t fnum, voi
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
-
-void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
+static void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
static unsigned start_ms[CFG_TUD_VIDEO_STREAMING] = {0, };
static unsigned already_sent = 0;
@@ -222,13 +221,13 @@ void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
if (!(already_sent & (1u << idx))) {
already_sent |= 1u << idx;
tx_busy |= 1u << idx;
- start_ms[idx] = board_millis();
+ start_ms[idx] = tusb_time_millis_api();
fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp);
tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size);
}
- unsigned cur = board_millis();
+ unsigned cur = tusb_time_millis_api();
if (cur - start_ms[idx] < interval_ms[idx]) return; // not enough time
if (tx_busy & (1u << idx)) return;
start_ms[idx] += interval_ms[idx];
@@ -281,7 +280,7 @@ void led_blinking_task(void* param) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
#else
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
#endif
start_ms += blink_interval_ms;
@@ -344,7 +343,7 @@ void usb_device_task(void *param) {
}
}
-void freertos_init_task(void) {
+void freertos_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
diff --git a/examples/device/video_capture_2ch/src/tusb_config.h b/examples/device/video_capture_2ch/src/tusb_config.h
index e84e49879..48ab4d20a 100644
--- a/examples/device/video_capture_2ch/src/tusb_config.h
+++ b/examples/device/video_capture_2ch/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -117,4 +117,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/video_capture_2ch/src/usb_descriptors.c b/examples/device/video_capture_2ch/src/usb_descriptors.c
index e78e452fc..8dc986da6 100644
--- a/examples/device/video_capture_2ch/src/usb_descriptors.c
+++ b/examples/device/video_capture_2ch/src/usb_descriptors.c
@@ -33,9 +33,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] VIDEO | AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VIDEO, 5) | _PID_MAP(VENDOR, 6) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VIDEO, 5) | PID_MAP(VENDOR, 6) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -68,7 +68,7 @@ char const* string_desc_arr[] = {
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -551,9 +551,9 @@ static uint8_t * get_hs_configuration_desc(void) {
}
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
- .bLength = sizeof(tusb_desc_device_t),
- .bDescriptorType = TUSB_DESC_DEVICE,
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_MISC,
diff --git a/examples/device/webusb_serial/CMakeLists.txt b/examples/device/webusb_serial/CMakeLists.txt
index ced98a909..3b214e7c9 100644
--- a/examples/device/webusb_serial/CMakeLists.txt
+++ b/examples/device/webusb_serial/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(webusb_serial C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/webusb_serial/Makefile b/examples/device/webusb_serial/Makefile
index 7fa475da5..035e90308 100644
--- a/examples/device/webusb_serial/Makefile
+++ b/examples/device/webusb_serial/Makefile
@@ -1,11 +1,11 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/webusb_serial/skip.txt b/examples/device/webusb_serial/skip.txt
new file mode 100644
index 000000000..2c6c2a64c
--- /dev/null
+++ b/examples/device/webusb_serial/skip.txt
@@ -0,0 +1 @@
+family:espressif
diff --git a/examples/device/webusb_serial/src/main.c b/examples/device/webusb_serial/src/main.c
index 4a724f45e..4be5e4db4 100644
--- a/examples/device/webusb_serial/src/main.c
+++ b/examples/device/webusb_serial/src/main.c
@@ -101,13 +101,13 @@ int main(void) {
while (1) {
tud_task(); // tinyusb device task
- cdc_task();
+ tud_cdc_write_flush();
led_blinking_task();
}
}
// send characters to both CDC and WebUSB
-void echo_all(const uint8_t buf[], uint32_t count) {
+static void echo_all(const uint8_t buf[], uint32_t count) {
// echo to web serial
if (web_serial_connected) {
tud_vendor_write(buf, count);
@@ -116,13 +116,7 @@ void echo_all(const uint8_t buf[], uint32_t count) {
// echo to cdc
if (tud_cdc_connected()) {
- for (uint32_t i = 0; i < count; i++) {
- tud_cdc_write_char(buf[i]);
- if (buf[i] == '\r') {
- tud_cdc_write_char('\n');
- }
- }
- tud_cdc_write_flush();
+ tud_cdc_write(buf, count);
}
}
@@ -162,7 +156,9 @@ void tud_resume_cb(void) {
// return false to stall control endpoint (e.g unsupported request)
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
// nothing to with DATA & ACK stage
- if (stage != CONTROL_STAGE_SETUP) return true;
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_VENDOR:
@@ -200,6 +196,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
tud_vendor_write_str("\r\nWebUSB interface connected\r\n");
tud_vendor_write_flush();
} else {
+ tud_vendor_write_clear(); // anything left in the buffer is now thrown out
blink_interval_ms = BLINK_MOUNTED;
}
@@ -215,33 +212,21 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
return false;
}
-void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize) {
- (void) itf;
+void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize) {
+ (void)idx;
+ (void)buffer;
+ (void)bufsize;
- echo_all(buffer, bufsize);
-
- // if using RX buffered is enabled, we need to flush the buffer to make room for new data
- #if CFG_TUD_VENDOR_RX_BUFSIZE > 0
- tud_vendor_read_flush();
- #endif
+ while (tud_vendor_available()) {
+ uint8_t buf[64];
+ const uint32_t count = tud_vendor_read(buf, sizeof(buf));
+ echo_all(buf, count);
+ }
}
//--------------------------------------------------------------------+
// USB CDC
//--------------------------------------------------------------------+
-void cdc_task(void) {
- if (tud_cdc_connected()) {
- // connected and there are data available
- if (tud_cdc_available()) {
- uint8_t buf[64];
-
- uint32_t count = tud_cdc_read(buf, sizeof(buf));
-
- // echo back to both web serial and cdc
- echo_all(buf, count);
- }
- }
-}
// Invoked when cdc when line state changed e.g connected/disconnected
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
@@ -255,8 +240,13 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
}
// Invoked when CDC interface received data from host
-void tud_cdc_rx_cb(uint8_t itf) {
- (void)itf;
+void tud_cdc_rx_cb(uint8_t idx) {
+ (void)idx;
+ while (tud_cdc_available()) {
+ uint8_t buf[64];
+ const uint32_t count = tud_cdc_read(buf, sizeof(buf));
+ echo_all(buf, count); // echo back to both web serial and cdc
+ }
}
//--------------------------------------------------------------------+
@@ -267,7 +257,9 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/device/webusb_serial/src/tusb_config.h b/examples/device/webusb_serial/src/tusb_config.h
index b86ad3752..b6cdf7e56 100644
--- a/examples/device/webusb_serial/src/tusb_config.h
+++ b/examples/device/webusb_serial/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -111,4 +111,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/webusb_serial/src/usb_descriptors.c b/examples/device/webusb_serial/src/usb_descriptors.c
index 2b69a5b56..527837161 100644
--- a/examples/device/webusb_serial/src/usb_descriptors.c
+++ b/examples/device/webusb_serial/src/usb_descriptors.c
@@ -33,14 +33,14 @@
* Auto ProductID layout's Bitmap:
* [MSB] MIDI | HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
+static tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -104,15 +104,25 @@ enum
#define EPNUM_VENDOR_OUT 0x05
#define EPNUM_VENDOR_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+
+ #define EPNUM_VENDOR_OUT 0x0A
+ #define EPNUM_VENDOR_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
- #define EPNUM_VENDOR_OUT 0x04
- #define EPNUM_VENDOR_IN 0x85
+ #define EPNUM_VENDOR_OUT 0x04
+ #define EPNUM_VENDOR_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
@@ -228,7 +238,7 @@ enum {
};
// array of pointer to string descriptors
-char const *string_desc_arr[] =
+static char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
diff --git a/examples/dual/CMakeLists.txt b/examples/dual/CMakeLists.txt
index c5e3ffce4..8727ea938 100644
--- a/examples/dual/CMakeLists.txt
+++ b/examples/dual/CMakeLists.txt
@@ -9,6 +9,13 @@ 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
+ dynamic_switch
+ )
+
+ foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+ endforeach ()
endif ()
diff --git a/examples/dual/dynamic_switch/CMakeLists.txt b/examples/dual/dynamic_switch/CMakeLists.txt
new file mode 100644
index 000000000..7cad30727
--- /dev/null
+++ b/examples/dual/dynamic_switch/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(dynamic_switch C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_dual_usb_example(${PROJECT_NAME} noos)
diff --git a/examples/dual/dynamic_switch/CMakePresets.json b/examples/dual/dynamic_switch/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/dual/dynamic_switch/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/dual/dynamic_switch/Makefile b/examples/dual/dynamic_switch/Makefile
new file mode 100644
index 000000000..44ab54ad7
--- /dev/null
+++ b/examples/dual/dynamic_switch/Makefile
@@ -0,0 +1,16 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+# Include device and host stack
+SRC_C += \
+ src/class/cdc/cdc_device.c \
+ src/host/hub.c \
+ src/host/usbh.c
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/dual/dynamic_switch/README.md b/examples/dual/dynamic_switch/README.md
new file mode 100644
index 000000000..034787f18
--- /dev/null
+++ b/examples/dual/dynamic_switch/README.md
@@ -0,0 +1,60 @@
+# Dynamic Switch Example
+
+This example demonstrates TinyUSB's dual-role capability by allowing runtime switching between USB device and host modes.
+
+## Features
+
+- **Button-triggered mode switching**: Press the board button to switch between device and host modes
+- **Device Mode**: Acts as a USB CDC (Virtual Serial Port) that echoes all received data
+- **Host Mode**: Enumerates connected USB devices and prints device information
+- **Dynamic switching**: Deinitializes the current stack and reinitializes in the new mode
+
+## Usage
+
+1. **Build and flash** the example to your board
+2. **Default behavior**: The board starts in **Device mode**
+3. **Device mode**:
+ - Connect the board to a PC
+ - Open a serial terminal (e.g., `screen /dev/ttyACM0` or PuTTY)
+ - Type characters - they will be echoed back to you
+4. **Switch to Host mode**:
+ - Press the board button
+ - Connect a USB device to the board
+ - The board will enumerate the device and print its descriptors to the debug console
+5. **Switch back to Device mode**: Press the button again
+
+## LED Patterns
+
+The onboard LED indicates the USB connection status:
+
+- **Fast blink (250ms)**: Not mounted/connected
+- **Slow blink (1000ms)**: Successfully mounted/connected
+- **Very slow blink (2500ms)**: Suspended (device mode only)
+
+## Serial Output
+
+The example prints status messages to the debug UART:
+
+```
+======================================
+TinyUSB Dynamic Switch Example
+Press button to switch between device and host modes
+Starting in DEVICE mode...
+======================================
+
+[DEVICE] Mounted
+
+--- Switching USB mode ---
+Stopping DEVICE mode...
+Starting HOST mode...
+Mode switch complete!
+
+[HOST] Device attached, address = 1
+Device 1: ID 1234:5678 SN ABC123
+Device Descriptor:
+ bLength 18
+ bDescriptorType 1
+ bcdUSB 0200
+ bDeviceClass 239
+ ...
+```
diff --git a/examples/dual/dynamic_switch/only.txt b/examples/dual/dynamic_switch/only.txt
new file mode 100644
index 000000000..e1038f9d4
--- /dev/null
+++ b/examples/dual/dynamic_switch/only.txt
@@ -0,0 +1,13 @@
+family:espressif
+mcu:LPC43XX
+mcu:MIMXRT1XXX
+mcu:STM32C0
+mcu:STM32C5
+mcu:STM32G0
+mcu:STM32H5
+mcu:STM32F2
+mcu:STM32F4
+mcu:STM32U5
+mcu:STM32F7
+mcu:STM32H7
+mcu:STM32H7RS
diff --git a/examples/dual/dynamic_switch/src/CMakeLists.txt b/examples/dual/dynamic_switch/src/CMakeLists.txt
new file mode 100644
index 000000000..cef2b46ee
--- /dev/null
+++ b/examples/dual/dynamic_switch/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "main.c" "usb_descriptors.c"
+ INCLUDE_DIRS "."
+ REQUIRES boards tinyusb_src)
diff --git a/examples/dual/dynamic_switch/src/main.c b/examples/dual/dynamic_switch/src/main.c
new file mode 100644
index 000000000..f67cd885c
--- /dev/null
+++ b/examples/dual/dynamic_switch/src/main.c
@@ -0,0 +1,501 @@
+/*
+ * 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 example demonstrates dynamic switching between device and host modes:
+ * - Press button to switch between device and host modes
+ * - Device mode: CDC echo (echoes input back to output)
+ * - Host mode: Prints connected device information
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ #ifdef ESP_PLATFORM
+ #define USBD_STACK_SIZE 4096
+ #define USBH_STACK_SIZE 4096
+ #else
+ // Increase stack size when debug log is enabled
+ #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #define USBH_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #endif
+
+ #define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 2 : 1))
+ #define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
+#endif
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTOTYPES
+//--------------------------------------------------------------------+
+
+// English
+#define LANGUAGE_ID 0x0409
+
+/* Blink pattern
+ * - 250 ms : not mounted
+ * - 1000 ms : mounted
+ * - 2500 ms : suspended
+ */
+enum {
+ BLINK_NOT_MOUNTED = 250,
+ BLINK_MOUNTED = 1000,
+ BLINK_SUSPENDED = 2500,
+};
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+// static task for FreeRTOS
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t blinky_stack[BLINKY_STACK_SIZE];
+StaticTask_t blinky_taskdef;
+
+StackType_t usb_stack[USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE];
+StaticTask_t usb_taskdef;
+
+StackType_t cdc_stack[CDC_STACK_SIZE];
+StaticTask_t cdc_taskdef;
+
+StackType_t devinfo_stack[USBH_STACK_SIZE];
+StaticTask_t devinfo_taskdef;
+#endif
+#endif
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+static tusb_role_t current_role = TUSB_ROLE_DEVICE;
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+static void usb_task(void *param);
+#endif
+void led_blinking_task(void *param);
+void cdc_task(void *param);
+void print_devinfo_task(void *param);
+
+void usb_mode_switch(void);
+static void print_one_device(uint8_t daddr);
+static void print_utf16(uint16_t* temp_buf, size_t buf_len);
+
+// Declare buffer for USB transfer
+CFG_TUH_MEM_SECTION struct {
+ TUH_EPBUF_TYPE_DEF(tusb_desc_device_t, device);
+ TUH_EPBUF_DEF(serial, 64*sizeof(uint16_t));
+ TUH_EPBUF_DEF(buf, 128*sizeof(uint16_t));
+} desc;
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+
+int main(void) {
+ board_init();
+
+ printf("\r\n======================================\r\n");
+ printf("TinyUSB Dynamic Switch Example\r\n");
+ printf("Press button to switch between device and host modes\r\n");
+ printf("Starting in DEVICE mode...\r\n");
+ printf("======================================\r\n\r\n");
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ // Create FreeRTOS tasks
+#if configSUPPORT_STATIC_ALLOCATION
+ xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
+ xTaskCreateStatic(usb_task, "usb", USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE,
+ NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef);
+ xTaskCreateStatic(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, cdc_stack, &cdc_taskdef);
+ xTaskCreateStatic(print_devinfo_task, "devinfo", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, devinfo_stack, &devinfo_taskdef);
+#else
+ xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
+ xTaskCreate(usb_task, "usb", USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE,
+ NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
+ xTaskCreate(print_devinfo_task, "devinfo", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
+#endif
+
+#ifndef ESP_PLATFORM
+ // only start scheduler for non-espressif mcu
+ vTaskStartScheduler();
+#endif
+
+#else
+ // Initialize in device mode by default
+ tusb_rhport_init_t dev_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_AUTO
+ };
+ tusb_init(BOARD_RHPORT, &dev_init);
+ current_role = TUSB_ROLE_DEVICE;
+
+ board_init_after_tusb();
+
+ while (1) {
+ // Check for button press to switch modes
+ static bool pending_switch = false;
+ if (board_button_read()) {
+ if (!pending_switch) {
+ pending_switch = true;
+ usb_mode_switch();
+ }
+ } else {
+ pending_switch = false;
+ }
+
+ // Process USB tasks based on current mode
+ if (current_role == TUSB_ROLE_DEVICE) {
+ tud_task();
+ cdc_task(NULL);
+ } else {
+ tuh_task();
+ print_devinfo_task(NULL);
+ }
+
+ led_blinking_task(NULL);
+ }
+#endif
+}
+
+#ifdef ESP_PLATFORM
+void app_main(void) {
+ main();
+}
+#endif
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+// USB Task for FreeRTOS
+// This top level thread processes all usb events and mode switching
+static void usb_task(void *param) {
+ (void) param;
+
+ // init device stack on configured roothub port
+ // This should be called after scheduler/kernel is started.
+ // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+ tusb_rhport_init_t dev_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_AUTO
+ };
+ tusb_init(BOARD_RHPORT, &dev_init);
+ current_role = TUSB_ROLE_DEVICE;
+
+ board_init_after_tusb();
+
+ // RTOS forever loop
+ while (1) {
+ // Check for button press to switch modes
+ static bool pending_switch = false;
+ if (board_button_read()) {
+ if (!pending_switch) {
+ pending_switch = true;
+ usb_mode_switch();
+ }
+ } else {
+ pending_switch = false;
+ }
+
+ // Process USB tasks based on current mode
+ // Use _ext version to allow return and read button state
+ if (current_role == TUSB_ROLE_DEVICE) {
+ tud_task_ext(10, false);
+ } else {
+ tuh_task_ext(10, false);
+ }
+ }
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Mode Switching
+//--------------------------------------------------------------------+
+
+void usb_mode_switch(void) {
+ printf("\r\n--- Switching USB mode ---\r\n");
+
+ // Snapshot then clear current_role BEFORE tusb_deinit() so concurrent
+ // tasks (cdc_task / print_devinfo_task on RTOS) see the role-change
+ // boundary and exit cleanly instead of calling host/device APIs against
+ // a deinitialised stack.
+ const tusb_role_t prev_role = current_role;
+ current_role = TUSB_ROLE_INVALID;
+
+ if (prev_role == TUSB_ROLE_DEVICE) {
+ printf("Stopping DEVICE mode...\r\n");
+ } else {
+ printf("Stopping HOST mode...\r\n");
+ }
+ tusb_deinit(BOARD_RHPORT);
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(100)); // Small delay for clean transition
+#else
+ tusb_time_delay_ms_api(100);
+#endif
+
+ // Switch to the other mode
+ if (prev_role == TUSB_ROLE_DEVICE) {
+ printf("Starting HOST mode...\r\n");
+ tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUSB_SPEED_AUTO
+ };
+ tusb_init(BOARD_RHPORT, &host_init);
+ current_role = TUSB_ROLE_HOST;
+ } else {
+ printf("Starting DEVICE mode...\r\n");
+ tusb_rhport_init_t dev_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_AUTO
+ };
+ tusb_init(BOARD_RHPORT, &dev_init);
+ current_role = TUSB_ROLE_DEVICE;
+ }
+
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+ printf("Mode switch complete!\r\n\r\n");
+}
+
+//--------------------------------------------------------------------+
+// Device Mode: CDC Task
+//--------------------------------------------------------------------+
+
+void cdc_task(void *param) {
+ (void) param;
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+#endif
+ // Only touch device-CDC APIs while we're in device mode. After
+ // usb_mode_switch() sets current_role to INVALID and tusb_deinit() runs,
+ // calling tud_cdc_write_flush() here would hit a deinit'd device stack.
+ if (current_role == TUSB_ROLE_DEVICE) {
+ if (tud_cdc_available()) {
+ uint8_t buf[64];
+ const uint32_t count = tud_cdc_read(buf, sizeof(buf));
+ if (count) {
+ tud_cdc_write(buf, count);
+ }
+ }
+ tud_cdc_write_flush();
+ }
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+#endif
+}
+
+//--------------------------------------------------------------------+
+// Device Callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void) {
+ printf("[DEVICE] Mounted\r\n");
+ blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void) {
+ printf("[DEVICE] Unmounted\r\n");
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+// Invoked when usb bus is suspended
+void tud_suspend_cb(bool remote_wakeup_en) {
+ (void) remote_wakeup_en;
+ printf("[DEVICE] Suspended\r\n");
+ blink_interval_ms = BLINK_SUSPENDED;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void) {
+ printf("[DEVICE] Resumed\r\n");
+ blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
+}
+
+//--------------------------------------------------------------------+
+// Host Callbacks
+//--------------------------------------------------------------------+
+
+// One flag per possible device address — set by tuh_mount_cb (host task) and
+// cleared by print_devinfo_task once the device's descriptors are printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// minimal; descriptor fetching happens in print_devinfo_task (different
+// context so sync helpers are safe).
+void tuh_mount_cb(uint8_t daddr) {
+ printf("[HOST] Device attached, address = %d\r\n", daddr);
+ blink_interval_ms = BLINK_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
+}
+
+// Invoked when device is unmounted (unplugged)
+void tuh_umount_cb(uint8_t daddr) {
+ printf("[HOST] Device removed, address = %d\r\n", daddr);
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
+}
+
+//--------------------------------------------------------------------+
+// Host Device Info — serialises descriptor fetching across all mounted
+// devices using sync helpers. Safe to call from main loop (OS_NONE) or a
+// dedicated task (FreeRTOS) — but NOT from a host-stack callback.
+//--------------------------------------------------------------------+
+
+void print_devinfo_task(void *param) {
+ (void) param;
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+#endif
+ if (current_role == TUSB_ROLE_HOST) {
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+ }
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+#endif
+}
+
+static void print_one_device(uint8_t daddr) {
+ // Get Device Descriptor
+ uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18);
+ if (XFER_RESULT_SUCCESS != xfer_result) {
+ printf("Failed to get device descriptor\r\n");
+ return;
+ }
+
+ printf("Device %u: ID %04x:%04x SN ", daddr, desc.device.idVendor, desc.device.idProduct);
+
+ xfer_result = XFER_RESULT_FAILED;
+ if (desc.device.iSerialNumber != 0) {
+ xfer_result = tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, desc.serial, sizeof(desc.serial));
+ }
+ if (XFER_RESULT_SUCCESS != xfer_result) {
+ uint16_t* serial = (uint16_t*)(uintptr_t) desc.serial;
+ serial[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * 3 + 2));
+ serial[1] = 'n';
+ serial[2] = '/';
+ serial[3] = 'a';
+ serial[4] = 0;
+ }
+ print_utf16((uint16_t*)(uintptr_t) desc.serial, sizeof(desc.serial)/2);
+ printf("\r\n");
+
+ printf("Device Descriptor:\r\n");
+ printf(" bLength %u\r\n", desc.device.bLength);
+ printf(" bDescriptorType %u\r\n", desc.device.bDescriptorType);
+ printf(" bcdUSB %04x\r\n", desc.device.bcdUSB);
+ printf(" bDeviceClass %u\r\n", desc.device.bDeviceClass);
+ printf(" bDeviceSubClass %u\r\n", desc.device.bDeviceSubClass);
+ printf(" bDeviceProtocol %u\r\n", desc.device.bDeviceProtocol);
+ printf(" bMaxPacketSize0 %u\r\n", desc.device.bMaxPacketSize0);
+ printf(" idVendor 0x%04x\r\n", desc.device.idVendor);
+ printf(" idProduct 0x%04x\r\n", desc.device.idProduct);
+ printf(" bcdDevice %04x\r\n", desc.device.bcdDevice);
+
+ // Get Manufacturer string
+ if (desc.device.iManufacturer) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
+ printf(" iManufacturer %u ", desc.device.iManufacturer);
+ print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
+ printf("\r\n");
+ }
+ }
+
+ // Get Product string
+ if (desc.device.iProduct) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
+ printf(" iProduct %u ", desc.device.iProduct);
+ print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
+ printf("\r\n");
+ }
+ }
+
+ // Get Serial string
+ if (desc.device.iSerialNumber) {
+ printf(" iSerialNumber %u ", desc.device.iSerialNumber);
+ print_utf16((uint16_t*)(uintptr_t) desc.serial, sizeof(desc.serial)/2);
+ printf("\r\n");
+ } else {
+ printf(" iSerialNumber 0\r\n");
+ }
+
+ printf(" bNumConfigurations %u\r\n", desc.device.bNumConfigurations);
+ printf("\r\n");
+}
+
+static void print_utf16(uint16_t* temp_buf, size_t buf_len) {
+ if (temp_buf[0] == 0 || (temp_buf[0] >> 8) != TUSB_DESC_STRING) {
+ printf("(invalid)");
+ return;
+ }
+
+ size_t chr_count = (temp_buf[0] & 0xff) / 2 - 1;
+ if (chr_count > buf_len - 1) {
+ chr_count = buf_len - 1;
+ }
+
+ for (size_t i = 0; i < chr_count; i++) {
+ uint16_t ch = temp_buf[1 + i];
+ if (ch <= 0x7F) {
+ putchar((char) ch);
+ } else {
+ // TODO support UTF16 to UTF8 conversion
+ putchar('?');
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Blinking Task
+//--------------------------------------------------------------------+
+
+void led_blinking_task(void *param) {
+ (void) param;
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+ vTaskDelay(pdMS_TO_TICKS(blink_interval_ms));
+ start_ms += blink_interval_ms;
+ board_led_write(led_state);
+ led_state = 1 - led_state;
+ }
+#else
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
+ start_ms += blink_interval_ms;
+ board_led_write(led_state);
+ led_state = 1 - led_state;
+#endif
+}
diff --git a/examples/dual/dynamic_switch/src/tusb_config.h b/examples/dual/dynamic_switch/src/tusb_config.h
new file mode 100644
index 000000000..f3e016305
--- /dev/null
+++ b/examples/dual/dynamic_switch/src/tusb_config.h
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+// RHPort number used can be defined by board.mk, default to port 0
+#ifndef BOARD_RHPORT
+ #if defined(BOARD_TUD_RHPORT)
+ #define BOARD_RHPORT BOARD_TUD_RHPORT
+ #else
+ #define BOARD_RHPORT 0
+ #define BOARD_TUD_RHPORT 0
+ #endif
+#endif
+
+#if defined(BOARD_TUH_RHPORT)
+ #if BOARD_TUH_RHPORT != BOARD_RHPORT
+ #undef BOARD_TUH_RHPORT
+ #define BOARD_TUH_RHPORT BOARD_RHPORT
+ #endif
+#else
+ #define BOARD_TUH_RHPORT BOARD_RHPORT
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_MAX_SPEED
+ #if defined(BOARD_TUD_MAX_SPEED)
+ #define BOARD_MAX_SPEED BOARD_TUD_MAX_SPEED
+ #else
+ #define BOARD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+ #endif
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device and Host stacks (dual role)
+#define CFG_TUD_ENABLED 1
+#define CFG_TUH_ENABLED 1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED BOARD_MAX_SPEED
+#define CFG_TUH_MAX_SPEED BOARD_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUD_MEM_SECTION
+#define CFG_TUD_MEM_SECTION
+#endif
+
+#ifndef CFG_TUD_MEM_ALIGN
+#define CFG_TUD_MEM_ALIGN __attribute__((aligned(4)))
+#endif
+
+#ifndef CFG_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION CFG_TUD_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN CFG_TUD_MEM_ALIGN
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC 1
+#define CFG_TUD_MSC 0
+#define CFG_TUD_HID 0
+#define CFG_TUD_MIDI 0
+#define CFG_TUD_VENDOR 0
+
+// CDC FIFO size of TX and RX
+#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+//--------------------------------------------------------------------
+// HOST CONFIGURATION
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB 1
+// max device support (excluding hub device)
+#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
+
+#define CFG_TUH_CDC 0
+#define CFG_TUH_HID 0
+#define CFG_TUH_MSC 0
+#define CFG_TUH_VENDOR 0
+
+// max endpoint pair supported by each device
+#define CFG_TUH_ENDPOINT_MAX 16
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/examples/dual/dynamic_switch/src/usb_descriptors.c b/examples/dual/dynamic_switch/src/usb_descriptors.c
new file mode 100644
index 000000000..ef6d795b7
--- /dev/null
+++ b/examples/dual/dynamic_switch/src/usb_descriptors.c
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
+ * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
+ *
+ * Auto ProductID layout's Bitmap:
+ * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
+ */
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
+
+#define USB_VID 0xCafe
+#define USB_BCD 0x0200
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+
+enum {
+ ITF_NUM_CDC = 0,
+ ITF_NUM_CDC_DATA,
+ ITF_NUM_TOTAL
+};
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+ // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+ // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
+ // e.g EP1 OUT & EP1 IN cannot exist together
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+#else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+
+#endif
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
+
+static uint8_t const desc_fs_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+
+ // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+};
+
+#if TUD_OPT_HIGH_SPEED
+static uint8_t const desc_hs_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+
+ // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+};
+#endif
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index; // for multiple configurations
+
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
+ return desc_fs_configuration;
+#endif
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// String Descriptor Index
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+};
+
+// array of pointer to string descriptors
+static char const *string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB Device", // 2: Product
+ NULL, // 3: Serials, will use unique ID if possible
+ "TinyUSB CDC", // 4: CDC Interface
+};
+
+static uint16_t _desc_str[32 + 1];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch (index) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; }
+
+ const char *str = string_desc_arr[index];
+
+ // Cap at max char
+ chr_count = strlen(str);
+ size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+ if (chr_count > max_count) { chr_count = max_count; }
+
+ // Convert ASCII string into UTF-16
+ for (size_t i = 0; i < chr_count; i++) {
+ _desc_str[1 + i] = str[i];
+ }
+ break;
+ }
+
+ // first byte is length (including header), second byte is string type
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ return _desc_str;
+}
diff --git a/examples/dual/host_hid_to_device_cdc/CMakeLists.txt b/examples/dual/host_hid_to_device_cdc/CMakeLists.txt
index 6ae5b5766..38c678cf2 100644
--- a/examples/dual/host_hid_to_device_cdc/CMakeLists.txt
+++ b/examples/dual/host_hid_to_device_cdc/CMakeLists.txt
@@ -2,34 +2,31 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(host_hid_to_device_cdc C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_dual_usb_example(${PROJECT} noos)
+family_configure_dual_usb_example(${PROJECT_NAME} noos)
# due to warnings from Pico-PIO-USB
if (FAMILY STREQUAL rp2040)
- target_compile_options(${PROJECT} PUBLIC
+ target_compile_options(${PROJECT_NAME} PUBLIC
-Wno-error=shadow
-Wno-error=cast-align
-Wno-error=cast-qual
diff --git a/examples/dual/host_hid_to_device_cdc/Makefile b/examples/dual/host_hid_to_device_cdc/Makefile
index 474ae9814..595cd7dec 100644
--- a/examples/dual/host_hid_to_device_cdc/Makefile
+++ b/examples/dual/host_hid_to_device_cdc/Makefile
@@ -1,18 +1,18 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-CFLAGS_GCC += -Wno-error=cast-align -Wno-error=null-dereference
+CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference
SRC_C += \
src/class/hid/hid_host.c \
src/host/hub.c \
src/host/usbh.c
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/dual/host_hid_to_device_cdc/src/main.c b/examples/dual/host_hid_to_device_cdc/src/main.c
index 8c53588c3..c8fca48f8 100644
--- a/examples/dual/host_hid_to_device_cdc/src/main.c
+++ b/examples/dual/host_hid_to_device_cdc/src/main.c
@@ -98,8 +98,6 @@ int main(void) {
tuh_task(); // tinyusb host task
led_blinking_task();
}
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -284,7 +282,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h
index 2843e0b83..fb2a401b5 100644
--- a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h
+++ b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h
@@ -114,8 +114,10 @@
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-// CDC Endpoint transfer buffer size, more is faster
-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
//--------------------------------------------------------------------
// HOST CONFIGURATION
diff --git a/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c b/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
index b7cffe23d..3efa30e20 100644
--- a/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
+++ b/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -122,7 +122,7 @@ enum {
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
// full speed configuration
-uint8_t const desc_fs_configuration[] = {
+static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -134,7 +134,7 @@ uint8_t const desc_fs_configuration[] = {
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -143,10 +143,10 @@ uint8_t const desc_hs_configuration[] = {
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
diff --git a/examples/dual/host_info_to_device_cdc/CMakeLists.txt b/examples/dual/host_info_to_device_cdc/CMakeLists.txt
index 6ae5b5766..87b5336f1 100644
--- a/examples/dual/host_info_to_device_cdc/CMakeLists.txt
+++ b/examples/dual/host_info_to_device_cdc/CMakeLists.txt
@@ -2,34 +2,36 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(host_info_to_device_cdc C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_dual_usb_example(${PROJECT} noos)
+family_configure_dual_usb_example(${PROJECT_NAME} ${RTOS})
# due to warnings from Pico-PIO-USB
if (FAMILY STREQUAL rp2040)
- target_compile_options(${PROJECT} PUBLIC
+ target_compile_options(${PROJECT_NAME} PUBLIC
-Wno-error=shadow
-Wno-error=cast-align
-Wno-error=cast-qual
diff --git a/examples/dual/host_info_to_device_cdc/Makefile b/examples/dual/host_info_to_device_cdc/Makefile
index 083c9169a..3a1d87f57 100644
--- a/examples/dual/host_info_to_device_cdc/Makefile
+++ b/examples/dual/host_info_to_device_cdc/Makefile
@@ -1,17 +1,17 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-CFLAGS_GCC += -Wno-error=cast-align -Wno-error=null-dereference
+CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference
SRC_C += \
src/host/hub.c \
src/host/usbh.c
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/dual/host_info_to_device_cdc/only.txt b/examples/dual/host_info_to_device_cdc/only.txt
index 35f896f1e..4431065ba 100644
--- a/examples/dual/host_info_to_device_cdc/only.txt
+++ b/examples/dual/host_info_to_device_cdc/only.txt
@@ -8,3 +8,4 @@ mcu:MAX3421
mcu:STM32F4
mcu:STM32F7
mcu:STM32H7
+mcu:ESP32P4
diff --git a/examples/dual/host_info_to_device_cdc/src/CMakeLists.txt b/examples/dual/host_info_to_device_cdc/src/CMakeLists.txt
new file mode 100644
index 000000000..cef2b46ee
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "main.c" "usb_descriptors.c"
+ INCLUDE_DIRS "."
+ REQUIRES boards tinyusb_src)
diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c
index 67e905b9d..5186f91dc 100644
--- a/examples/dual/host_info_to_device_cdc/src/main.c
+++ b/examples/dual/host_info_to_device_cdc/src/main.c
@@ -69,14 +69,18 @@ enum {
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
-static bool is_print[CFG_TUH_DEVICE_MAX+1] = { 0 };
+static bool is_printable[CFG_TUH_DEVICE_MAX + 1] = {0};
static tusb_desc_device_t descriptor_device[CFG_TUH_DEVICE_MAX+1];
static void print_utf16(uint16_t *temp_buf, size_t buf_len);
static void print_device_info(uint8_t daddr, const tusb_desc_device_t* desc_device);
-void led_blinking_task(void);
-void cdc_task(void);
+static void led_blinking_task(void);
+static void cdc_task(void);
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+static void freertos_init(void);
+#endif
#define cdc_printf(...) \
do { \
@@ -94,37 +98,86 @@ void cdc_task(void);
} \
} while(0)
-/*------------- MAIN -------------*/
-int main(void) {
- board_init();
-
- printf("TinyUSB Host Information -> Device CDC Example\r\n");
+static void usb_device_init(void) {
// init device and host stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
+ board_init_after_tusb();
+}
+static void usb_host_init(void) {
+ // init host stack on configured roothub port
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUH_RHPORT, &host_init);
-
board_init_after_tusb();
+}
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+static void main_task(void* param) {
+ (void) param;
while (1) {
- tud_task(); // tinyusb device task
- tuh_task(); // tinyusb host task
cdc_task();
led_blinking_task();
+
+ // preempted RTOS run device/host stack in its own task
+#if CFG_TUSB_OS_HAS_SCHEDULER == 0
+ tud_task(); // tinyusb device task
+ tuh_task(); // tinyusb host task
+#endif
}
+}
+
+int main(void) {
+ board_init();
+
+#if CFG_TUSB_OS_HAS_SCHEDULER == 0
+ printf("TinyUSB Host Information -> Device CDC Example\r\n");
+
+ usb_device_init();
+ usb_host_init();
+
+ main_task(NULL);
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+ freertos_init(); // create RTOS tasks for device, host stack and main_task()
+#else
+ #error RTOS not supported
+#endif
return 0;
}
+#if CFG_TUSB_OS_HAS_SCHEDULER
+// USB Device Driver task for RTOS
+static void usb_device_task(void *param) {
+ (void) param;
+ usb_device_init();
+ while (1) {
+ // put this thread to waiting state until there is new events
+ tud_task();
+ }
+}
+
+static void usb_host_task(void *param) {
+ (void) param;
+ usb_host_init();
+ while (1) {
+ // put this thread to waiting state until there is new events
+ tuh_task();
+ }
+}
+#endif
+
+
//--------------------------------------------------------------------+
// Device CDC
//--------------------------------------------------------------------+
@@ -153,17 +206,23 @@ void tud_resume_cb(void) {
}
void cdc_task(void) {
+ static uint32_t connected_ms = 0;
+
if (!tud_cdc_connected()) {
- // delay a bit otherwise we can outpace host's terminal. Linux will set LineState (DTR) then Line Coding.
- // If we send data before Linux's terminal set Line Coding, it can be ignored --> missing data with hardware test loop
- board_delay(20);
+ connected_ms = tusb_time_millis_api();
return;
}
+ // delay a bit otherwise we can outpace host's terminal. Linux will set LineState (DTR) then Line Coding.
+ // If we send data before Linux's terminal set Line Coding, it can be ignored --> missing data with hardware test loop
+ if (tusb_time_millis_api() - connected_ms < 100) {
+ return; // wait for stable connection
+ }
+
for (uint8_t daddr = 1; daddr <= CFG_TUH_DEVICE_MAX; daddr++) {
if (tuh_mounted(daddr)) {
- if (is_print[daddr]) {
- is_print[daddr] = false;
+ if (is_printable[daddr]) {
+ is_printable[daddr] = false;
print_device_info(daddr, &descriptor_device[daddr]);
tud_cdc_write_flush();
}
@@ -183,10 +242,9 @@ static void print_device_info(uint8_t daddr, const tusb_desc_device_t* desc_devi
cdc_printf("Device %u: ID %04x:%04x SN ", daddr, desc_device->idVendor, desc_device->idProduct);
uint8_t xfer_result = tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, serial, sizeof(serial));
if (XFER_RESULT_SUCCESS != xfer_result) {
- serial[0] = 'n';
- serial[1] = '/';
- serial[2] = 'a';
- serial[3] = 0;
+ serial[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * 1 + 2));
+ serial[1] = '0';
+ serial[2] = 0;
}
print_utf16(serial, TU_ARRAY_SIZE(serial));
cdc_printf("\r\n");
@@ -218,9 +276,7 @@ static void print_device_info(uint8_t daddr, const tusb_desc_device_t* desc_devi
cdc_printf("\r\n");
cdc_printf(" iSerialNumber %u " , desc_device->iSerialNumber);
- cdc_printf((char*)serial); // serial is already to UTF-8
- cdc_printf("\r\n");
-
+ cdc_printf("%s \r\n", (char*)serial); // serial is already to UTF-8
cdc_printf(" bNumConfigurations %u\r\n" , desc_device->bNumConfigurations);
}
@@ -232,13 +288,13 @@ void tuh_enum_descriptor_device_cb(uint8_t daddr, tusb_desc_device_t const* desc
void tuh_mount_cb(uint8_t daddr) {
cdc_printf("mounted device %u\r\n", daddr);
tud_cdc_write_flush();
- is_print[daddr] = true;
+ is_printable[daddr] = true;
}
void tuh_umount_cb(uint8_t daddr) {
cdc_printf("unmounted device %u\r\n", daddr);
tud_cdc_write_flush();
- is_print[daddr] = false;
+ is_printable[daddr] = false;
}
//--------------------------------------------------------------------+
@@ -249,7 +305,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < blink_interval_ms) {
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
return;// not enough time
}
start_ms += blink_interval_ms;
@@ -261,7 +317,6 @@ void led_blinking_task(void) {
//--------------------------------------------------------------------+
// String Descriptor Helper
//--------------------------------------------------------------------+
-
static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
// TODO: Check for runover.
(void)utf8_len;
@@ -310,5 +365,55 @@ static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
_convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
((uint8_t*) temp_buf)[utf8_len] = '\0';
- cdc_printf((char*) temp_buf);
+ cdc_printf("%s", (char*) temp_buf);
}
+
+//--------------------------------------------------------------------+
+// FreeRTOS
+//--------------------------------------------------------------------+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+
+#ifdef ESP_PLATFORM
+ #define USBD_STACK_SIZE 4096
+ #define USBH_STACK_SIZE 4096
+ void app_main(void) {
+ main();
+ }
+#else
+ // Increase stack size when debug log is enabled
+ #define USBD_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
+ #define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
+#endif
+
+#define MAIN_STACK_SIZE (configMINIMAL_STACK_SIZE*4)
+
+// static task
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t main_stack[MAIN_STACK_SIZE];
+StaticTask_t main_taskdef;
+
+StackType_t usb_device_stack[USBD_STACK_SIZE];
+StaticTask_t usb_device_taskdef;
+
+StackType_t usb_host_stack[USBH_STACK_SIZE];
+StaticTask_t usb_host_taskdef;
+#endif
+
+void freertos_init(void) {
+ #if configSUPPORT_STATIC_ALLOCATION
+ xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
+ xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_host_stack, &usb_host_taskdef);
+ xTaskCreateStatic(main_task, "main", MAIN_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, main_stack, &main_taskdef);
+ #else
+ xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(main_task, "main", MAIN_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
+ #endif
+
+ // only start scheduler for non-espressif mcu
+ #ifndef ESP_PLATFORM
+ vTaskStartScheduler();
+ #endif
+}
+
+#endif
diff --git a/examples/dual/host_info_to_device_cdc/src/tusb_config.h b/examples/dual/host_info_to_device_cdc/src/tusb_config.h
index bb47fbf4a..99e9315a4 100644
--- a/examples/dual/host_info_to_device_cdc/src/tusb_config.h
+++ b/examples/dual/host_info_to_device_cdc/src/tusb_config.h
@@ -112,10 +112,12 @@
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 256)
-// CDC Endpoint transfer buffer size, more is faster
-#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// CDC Endpoint transfer buffer size, default to max bulk packet size (HS 512, FS 64). Larger is faster.
+// Larger RX_EPSIZE requires CFG_TUD_CDC_RX_NEED_ZLP = 1 and host ZLP support
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_EPSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
//--------------------------------------------------------------------
// HOST CONFIGURATION
diff --git a/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c b/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c
index b7cffe23d..3efa30e20 100644
--- a/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c
+++ b/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c
@@ -32,9 +32,9 @@
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
-#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
-#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
- _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
+#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
+ PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device = {
+static tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
@@ -122,7 +122,7 @@ enum {
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
// full speed configuration
-uint8_t const desc_fs_configuration[] = {
+static uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -134,7 +134,7 @@ uint8_t const desc_fs_configuration[] = {
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
-uint8_t const desc_hs_configuration[] = {
+static uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -143,10 +143,10 @@ uint8_t const desc_hs_configuration[] = {
};
// other speed configuration
-uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier = {
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt
index 2783dd84e..7c74e3c73 100644
--- a/examples/host/CMakeLists.txt
+++ b/examples/host/CMakeLists.txt
@@ -6,10 +6,18 @@ 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
+ midi2_host
+ msc_file_explorer
+ msc_file_explorer_freertos
+ )
+
+foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+endforeach ()
diff --git a/examples/host/bare_api/CMakeLists.txt b/examples/host/bare_api/CMakeLists.txt
index 0efe84b60..4993f93b9 100644
--- a/examples/host/bare_api/CMakeLists.txt
+++ b/examples/host/bare_api/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(bare_api C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/bare_api/Makefile b/examples/host/bare_api/Makefile
index 0235e08c3..f8292385e 100644
--- a/examples/host/bare_api/Makefile
+++ b/examples/host/bare_api/Makefile
@@ -1,13 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/bare_api/only.txt b/examples/host/bare_api/only.txt
index cba58f8e8..a2ff93be5 100644
--- a/examples/host/bare_api/only.txt
+++ b/examples/host/bare_api/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,25 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/bare_api/skip.txt b/examples/host/bare_api/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/bare_api/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c
index c693d6b00..679ce6f43 100644
--- a/examples/host/bare_api/src/main.c
+++ b/examples/host/bare_api/src/main.c
@@ -37,23 +37,30 @@
#define BUF_COUNT 4
-tusb_desc_device_t desc_device;
+CFG_TUH_MEM_SECTION tusb_desc_device_t desc_device;
-uint8_t buf_pool[BUF_COUNT][64];
+CFG_TUH_MEM_SECTION uint8_t buf_pool[BUF_COUNT][64];
uint8_t buf_owner[BUF_COUNT] = { 0 }; // device address that owns buffer
+CFG_TUH_MEM_SECTION uint16_t temp_buf[128]; // temp buffer for string descriptor
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
+void print_devinfo_task(void);
static void print_utf16(uint16_t *temp_buf, size_t buf_len);
-void print_device_descriptor(tuh_xfer_t* xfer);
+static void print_one_device(uint8_t daddr);
void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
uint8_t* get_hid_buf(uint8_t daddr);
void free_hid_buf(uint8_t daddr);
+// One flag per possible device address — set in tuh_mount_cb (host task) and
+// cleared by print_devinfo_task (main loop) once the descriptors are printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
/*------------- MAIN -------------*/
int main(void) {
board_init();
@@ -72,40 +79,53 @@ int main(void) {
while (1) {
// tinyusb host task
tuh_task();
+ print_devinfo_task();
led_blinking_task();
}
-
- return 0;
}
/*------------- TinyUSB Callbacks -------------*/
-// Invoked when device is mounted (configured)
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// it minimal. The descriptor fetching/printing happens in print_devinfo_task()
+// below where the sync helpers are safe (different context).
void tuh_mount_cb(uint8_t daddr) {
printf("Device attached, address = %d\r\n", daddr);
-
- // Get Device Descriptor
- // TODO: invoking control transfer now has issue with mounting hub with multiple devices attached, fix later
- tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr) {
printf("Device removed, address = %d\r\n", daddr);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
free_hid_buf(daddr);
}
//--------------------------------------------------------------------+
-// Device Descriptor
+// Print device info task — serialises descriptor fetching across all
+// mounted devices via sync helpers. Sync calls are safe here because this
+// runs in the main loop (outside any host-stack callback context).
//--------------------------------------------------------------------+
-void print_device_descriptor(tuh_xfer_t *xfer) {
- if (XFER_RESULT_SUCCESS != xfer->result) {
+void print_devinfo_task(void) {
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+}
+
+static void print_one_device(uint8_t daddr) {
+ // Get Device Descriptor
+ if (XFER_RESULT_SUCCESS != tuh_descriptor_get_device_sync(daddr, &desc_device, 18)) {
printf("Failed to get device descriptor\r\n");
return;
}
- uint8_t const daddr = xfer->daddr;
-
printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
printf("Device Descriptor:\r\n");
printf(" bLength %u\r\n" , desc_device.bLength);
@@ -119,9 +139,6 @@ void print_device_descriptor(tuh_xfer_t *xfer) {
printf(" idProduct 0x%04x\r\n" , desc_device.idProduct);
printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice);
- // Get String descriptor using Sync API
- uint16_t temp_buf[128];
-
printf(" iManufacturer %u ", desc_device.iManufacturer);
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf))) {
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
@@ -335,7 +352,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) {
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
return; // not enough time
}
start_ms += interval_ms;
@@ -386,12 +403,12 @@ static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
return (int) total_bytes;
}
-static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
- if ((temp_buf[0] & 0xff) == 0) return;// empty
- size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
- size_t utf8_len = (size_t) _count_utf8_bytes(temp_buf + 1, utf16_len);
- _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
- ((uint8_t *) temp_buf)[utf8_len] = '\0';
+static void print_utf16(uint16_t *buf, size_t buf_len) {
+ if ((buf[0] & 0xff) == 0) return;// empty
+ size_t utf16_len = ((buf[0] & 0xff) - 2) / sizeof(uint16_t);
+ size_t utf8_len = (size_t) _count_utf8_bytes(buf + 1, utf16_len);
+ _convert_utf16le_to_utf8(buf + 1, utf16_len, (uint8_t *) buf, sizeof(uint16_t) * buf_len);
+ ((uint8_t *) buf)[utf8_len] = '\0';
- printf("%s", (char *) temp_buf);
+ printf("%s", (char *) buf);
}
diff --git a/examples/host/cdc_msc_hid/CMakeLists.txt b/examples/host/cdc_msc_hid/CMakeLists.txt
index e8928cda5..3098c37fa 100644
--- a/examples/host/cdc_msc_hid/CMakeLists.txt
+++ b/examples/host/cdc_msc_hid/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_msc_hid C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
@@ -26,10 +23,10 @@ target_sources(${PROJECT} PUBLIC
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/cdc_msc_hid/Makefile b/examples/host/cdc_msc_hid/Makefile
index 213c02f9c..d72e91e74 100644
--- a/examples/host/cdc_msc_hid/Makefile
+++ b/examples/host/cdc_msc_hid/Makefile
@@ -1,8 +1,8 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
@@ -11,6 +11,6 @@ EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/cdc_msc_hid/only.txt b/examples/host/cdc_msc_hid/only.txt
index cba58f8e8..a2ff93be5 100644
--- a/examples/host/cdc_msc_hid/only.txt
+++ b/examples/host/cdc_msc_hid/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,25 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/cdc_msc_hid/skip.txt b/examples/host/cdc_msc_hid/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/cdc_msc_hid/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/cdc_msc_hid/src/app.h b/examples/host/cdc_msc_hid/src/app.h
new file mode 100644
index 000000000..49783b4ae
--- /dev/null
+++ b/examples/host/cdc_msc_hid/src/app.h
@@ -0,0 +1,34 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 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.
+ */
+#ifndef TUSB_TINYUSB_EXAMPLES_APP_H
+#define TUSB_TINYUSB_EXAMPLES_APP_H
+
+#include <stdio.h>
+
+void cdc_app_task(void);
+void hid_app_task(void);
+
+#endif
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index 97f1a96d6..e6c190715 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -26,12 +26,15 @@
#include "tusb.h"
#include "bsp/board_api.h"
+#include "app.h"
-static size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
+static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
- int ch = board_getchar();
- if (ch <= 0) { break; }
+ const int ch = board_getchar();
+ if (ch < 0) {
+ break;
+ }
buf[count] = (uint8_t) ch;
count++;
}
@@ -39,46 +42,50 @@ static size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
return count;
}
+static size_t console_write(const uint8_t *buf, size_t bufsize) {
+ // Use board_uart_write directly for non-blocking behavior.
+ // board_putchar -> sys_write has a blocking retry loop that causes UART RX overrun.
+ int wr = board_uart_write(buf, (int) bufsize);
+ return (wr > 0) ? (size_t) wr : 0;
+}
+
+// forward from console to usbh
+static void console_to_usbh(uint8_t idx) {
+ uint8_t buf[64];
+ size_t count = console_read(buf, sizeof(buf));
+ if (count > 0) {
+ tuh_cdc_write(idx, buf, count);
+ }
+}
+
void cdc_app_task(void) {
- uint8_t buf[64 + 1]; // +1 for extra null character
- uint32_t const bufsize = sizeof(buf) - 1;
+ const uint8_t idx = 0;
+
+ // Bidirectional forwarding: console <-> host cdc interfaces
+ if (!tuh_cdc_mounted(idx)) {
+ return;
+ }
- uint32_t count = get_console_inputs(buf, bufsize);
- buf[count] = 0;
+ // usbh -> uart
+ uint8_t buf[64];
+ uint32_t count = tuh_cdc_read(idx, buf, sizeof(buf));
+ uint32_t wr = 0;
- // loop over all mounted interfaces
- for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
- if (tuh_cdc_mounted(idx)) {
- // console --> cdc interfaces
- if (count) {
- tuh_cdc_write(idx, buf, count);
- tuh_cdc_write_flush(idx);
- }
+ do {
+ // uart write is slow, while waiting forward uart -> usbh else uart rx can be overflow
+ if (count) {
+ wr += console_write(buf + wr, count - wr);
}
- }
+ console_to_usbh(idx);
+ } while (wr < count);
+
+ tuh_cdc_write_flush(idx);
}
//--------------------------------------------------------------------+
// TinyUSB callbacks
//--------------------------------------------------------------------+
-// Invoked when received new data
-void tuh_cdc_rx_cb(uint8_t idx) {
- uint8_t buf[64 + 1]; // +1 for extra null character
- uint32_t const bufsize = sizeof(buf) - 1;
-
- // forward cdc interfaces -> console
- const uint32_t count = tuh_cdc_read(idx, buf, bufsize);
- if (count) {
- buf[count] = 0;
- printf("%s", (char*) buf);
-
- #ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
- fflush(stdout);// flush right away, else nanolib will wait for newline
- #endif
- }
-}
-
// Invoked when a device with CDC interface is mounted
// idx is index of cdc interface in the internal pool.
void tuh_cdc_mount_cb(uint8_t idx) {
@@ -88,7 +95,6 @@ void tuh_cdc_mount_cb(uint8_t idx) {
printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr,
itf_info.desc.bInterfaceNumber);
-#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
// If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack
// while eneumerating new cdc device
cdc_line_coding_t line_coding = {0};
@@ -96,11 +102,6 @@ void tuh_cdc_mount_cb(uint8_t idx) {
printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
}
-#else
- // Set Line Coding upon mounted
- cdc_line_coding_t new_line_coding = { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 };
- tuh_cdc_set_line_coding(idx, &new_line_coding, NULL, 0);
-#endif
}
// Invoked when a device with CDC interface is unmounted
diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c
index 6f01d6f45..eb20356cb 100644
--- a/examples/host/cdc_msc_hid/src/hid_app.c
+++ b/examples/host/cdc_msc_hid/src/hid_app.c
@@ -22,9 +22,9 @@
* THE SOFTWARE.
*
*/
-
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
diff --git a/examples/host/cdc_msc_hid/src/main.c b/examples/host/cdc_msc_hid/src/main.c
index e2dd6e5d2..c27ad93fe 100644
--- a/examples/host/cdc_msc_hid/src/main.c
+++ b/examples/host/cdc_msc_hid/src/main.c
@@ -29,13 +29,12 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
-extern void cdc_app_task(void);
-extern void hid_app_task(void);
/*------------- MAIN -------------*/
int main(void) {
@@ -93,7 +92,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) {
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
return;// not enough time
}
start_ms += interval_ms;
diff --git a/examples/host/cdc_msc_hid/src/msc_app.c b/examples/host/cdc_msc_hid/src/msc_app.c
index 0e9c99766..8181bdcb9 100644
--- a/examples/host/cdc_msc_hid/src/msc_app.c
+++ b/examples/host/cdc_msc_hid/src/msc_app.c
@@ -23,6 +23,7 @@
*
*/
+#include <stdio.h>
#include "tusb.h"
//--------------------------------------------------------------------+
@@ -40,14 +41,14 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
}
// Print out Vendor ID, Product ID and Rev
- printf("%.8s %.16s rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
+ printf("%.8s %.16s %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
// Get capacity of device
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
- printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024*1024)/block_size));
- printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
return true;
}
diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h
index 2f8cb5e03..26fcdd1cb 100644
--- a/examples/host/cdc_msc_hid/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -102,8 +102,11 @@
// Size of buffer to hold descriptors and other data used for enumeration
#define CFG_TUH_ENUMERATION_BUFSIZE 256
+// Increase task event queue to handle rapid bulk transfer completions
+#define CFG_TUH_TASK_QUEUE_SZ 64
+
#define CFG_TUH_HUB 1 // number of supported hubs
-#define CFG_TUH_CDC 2 // number of supported CDC devices. also activates CDC ACM
+#define CFG_TUH_CDC 1 // number of supported CDC devices. also activates CDC ACM
#define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API
@@ -134,4 +137,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/cdc_msc_hid_freertos/CMakeLists.txt b/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
index 78b2784fe..3ceefb3f4 100644
--- a/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
+++ b/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_msc_hid_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
@@ -26,10 +23,10 @@ target_sources(${PROJECT} PUBLIC
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} freertos)
+family_configure_host_example(${PROJECT_NAME} freertos)
diff --git a/examples/host/cdc_msc_hid_freertos/Makefile b/examples/host/cdc_msc_hid_freertos/Makefile
index 178341f81..2e323ed56 100644
--- a/examples/host/cdc_msc_hid_freertos/Makefile
+++ b/examples/host/cdc_msc_hid_freertos/Makefile
@@ -1,9 +1,9 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
@@ -12,6 +12,6 @@ EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/cdc_msc_hid_freertos/only.txt b/examples/host/cdc_msc_hid_freertos/only.txt
index 576271aff..4ab8a906e 100644
--- a/examples/host/cdc_msc_hid_freertos/only.txt
+++ b/examples/host/cdc_msc_hid_freertos/only.txt
@@ -1,19 +1,28 @@
-mcu:ESP32P4
+family:espressif
+family:samd21
+family:samd5x_e5x
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
mcu:MSP432E4
+mcu:RW61X
mcu:RX65X
-mcu:MAX3421
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/cdc_msc_hid_freertos/skip.txt b/examples/host/cdc_msc_hid_freertos/skip.txt
index 2ba4438fd..f0be07d25 100644
--- a/examples/host/cdc_msc_hid_freertos/skip.txt
+++ b/examples/host/cdc_msc_hid_freertos/skip.txt
@@ -1 +1,3 @@
-mcu:RP2040
+mcu:CH32F20X
+board:lpcxpresso54114
+mcu:FT90X
diff --git a/examples/host/cdc_msc_hid_freertos/src/app.h b/examples/host/cdc_msc_hid_freertos/src/app.h
new file mode 100644
index 000000000..8892a1b6b
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/app.h
@@ -0,0 +1,35 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 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.
+ */
+#ifndef TUSB_TINYUSB_EXAMPLES_APP_H
+#define TUSB_TINYUSB_EXAMPLES_APP_H
+
+#include <stdio.h>
+
+void cdc_app_init(void);
+void hid_app_init(void);
+void msc_app_init(void);
+
+#endif
diff --git a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
index d99760a02..9fad775e9 100644
--- a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
@@ -26,18 +26,19 @@
#include "tusb.h"
#include "bsp/board_api.h"
+#include "app.h"
#ifdef ESP_PLATFORM
- #define CDC_STACK_SZIE 2048
+ #define CDC_STACK_SIZE 2048
#else
- #define CDC_STACK_SZIE (3*configMINIMAL_STACK_SIZE/2)
+ #define CDC_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
#endif
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
#if configSUPPORT_STATIC_ALLOCATION
-StackType_t cdc_stack[CDC_STACK_SZIE];
+StackType_t cdc_stack[CDC_STACK_SIZE];
StaticTask_t cdc_taskdef;
#endif
@@ -45,18 +46,20 @@ static void cdc_app_task(void* param);
void cdc_app_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
- xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
+ (void) xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
#else
- xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, NULL);
+ (void) xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, NULL);
#endif
}
// helper
-static size_t get_console_inputs(uint8_t *buf, size_t bufsize) {
+static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
int ch = board_getchar();
- if (ch <= 0) break;
+ if (ch <= 0) {
+ break;
+ }
buf[count] = (uint8_t) ch;
count++;
@@ -72,7 +75,7 @@ static void cdc_app_task(void* param) {
uint32_t const bufsize = sizeof(buf) - 1;
while (1) {
- uint32_t count = get_console_inputs(buf, bufsize);
+ uint32_t count = console_read(buf, bufsize);
buf[count] = 0;
if (count) {
diff --git a/examples/host/cdc_msc_hid_freertos/src/hid_app.c b/examples/host/cdc_msc_hid_freertos/src/hid_app.c
index 9ea5c1be0..79e0b5a28 100644
--- a/examples/host/cdc_msc_hid_freertos/src/hid_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/hid_app.c
@@ -25,6 +25,7 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
@@ -160,7 +161,7 @@ static void process_kbd_report(hid_keyboard_report_t const *report) {
// Mouse
//--------------------------------------------------------------------+
-void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
+static void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
#if USE_ANSI_ESCAPE
// Move X using ansi escape
if ( x < 0) {
@@ -210,6 +211,7 @@ static void process_mouse_report(hid_mouse_report_t const *report) {
//--------------------------------------------------------------------+
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
(void) dev_addr;
+ (void) len;
uint8_t const rpt_count = hid_info[instance].report_count;
tuh_hid_report_info_t *rpt_info_arr = hid_info[instance].report_info;
diff --git a/examples/host/cdc_msc_hid_freertos/src/main.c b/examples/host/cdc_msc_hid_freertos/src/main.c
index d498c1b57..a41de2769 100644
--- a/examples/host/cdc_msc_hid_freertos/src/main.c
+++ b/examples/host/cdc_msc_hid_freertos/src/main.c
@@ -29,12 +29,13 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
#ifdef ESP_PLATFORM
#define USBH_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled
- #define USBH_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
@@ -65,9 +66,6 @@ TimerHandle_t blinky_tm;
static void led_blinky_cb(TimerHandle_t xTimer);
static void usb_host_task(void* param);
-extern void cdc_app_init(void);
-extern void hid_app_init(void);
-extern void msc_app_init(void);
/*------------- MAIN -------------*/
int main(void) {
diff --git a/examples/host/cdc_msc_hid_freertos/src/msc_app.c b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
index 6439495a8..17df11951 100644
--- a/examples/host/cdc_msc_hid_freertos/src/msc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
@@ -23,7 +23,10 @@
*
*/
+#include <stdio.h>
+
#include "tusb.h"
+#include "app.h"
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
CFG_TUH_MEM_SECTION static struct {
@@ -44,14 +47,14 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
}
// Print out Vendor ID, Product ID and Rev
- printf("%.8s %.16s rev %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
+ printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
// Get capacity of device
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
- printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024 * 1024) / block_size));
- printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
return true;
}
diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
index 3cdb227e2..8583e7176 100644
--- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -139,4 +139,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/device_info/CMakeLists.txt b/examples/host/device_info/CMakeLists.txt
index 33953233d..9b96a8af8 100644
--- a/examples/host/device_info/CMakeLists.txt
+++ b/examples/host/device_info/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(device_info C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/device_info/Makefile b/examples/host/device_info/Makefile
index 0235e08c3..f8292385e 100644
--- a/examples/host/device_info/Makefile
+++ b/examples/host/device_info/Makefile
@@ -1,13 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt
index 133a7c9a0..4c2cb0f35 100644
--- a/examples/host/device_info/only.txt
+++ b/examples/host/device_info/only.txt
@@ -1,25 +1,33 @@
+family:espressif
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
-mcu:ESP32S2
-mcu:ESP32S3
-mcu:ESP32P4
mcu:KINETIS_KL
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
mcu:MAX3421
-mcu:MIMXRT1XXX
mcu:MIMXRT10XX
mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
mcu:MSP432E4
+mcu:RAXXX
mcu:RP2040
+mcu:RW61X
mcu:RX65X
-mcu:RAXXX
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/device_info/skip.txt b/examples/host/device_info/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/device_info/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c
index 419806551..f32ed1a3e 100644
--- a/examples/host/device_info/src/main.c
+++ b/examples/host/device_info/src/main.c
@@ -72,8 +72,14 @@ CFG_TUH_MEM_SECTION struct {
} desc;
void led_blinking_task(void* param);
+void print_devinfo_task(void* param);
static void print_utf16(uint16_t* temp_buf, size_t buf_len);
+// One flag per possible device address — set by tuh_mount_cb (host task) and
+// cleared by print_devinfo_task (separate task / main loop) once the device's
+// descriptor info has been printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
void init_freertos_task(void);
#endif
@@ -99,21 +105,46 @@ int main(void) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
init_freertos_task();
#else
+ board_delay(100); // wait for uart to be ready
init_tinyusb();
while (1) {
tuh_task(); // tinyusb host task
+ print_devinfo_task(NULL);
led_blinking_task(NULL);
}
- return 0;
#endif
}
/*------------- TinyUSB Callbacks -------------*/
-// Invoked when device is mounted (configured)
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// it minimal. The actual descriptor fetching/printing happens in
+// print_devinfo_task() below, which runs in a different context (main loop
+// on OS_NONE / dedicated task on RTOS) where the sync helpers are safe.
void tuh_mount_cb(uint8_t daddr) {
blink_interval_ms = BLINK_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
+}
+// Invoked when device is unmounted (bus reset/unplugged)
+void tuh_umount_cb(uint8_t daddr) {
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
+ printf("Device removed, address = %d\r\n", daddr);
+}
+
+//--------------------------------------------------------------------+
+// Print device info task — serialises descriptor fetching across all
+// mounted devices using the sync helpers. Sync calls are safe here because
+// this task runs outside the host-task callback context (main loop on
+// OS_NONE / dedicated FreeRTOS task on RTOS).
+//--------------------------------------------------------------------+
+
+static void print_one_device(uint8_t daddr) {
// Get Device Descriptor
uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18);
if (XFER_RESULT_SUCCESS != xfer_result) {
@@ -129,11 +160,9 @@ void tuh_mount_cb(uint8_t daddr) {
}
if (XFER_RESULT_SUCCESS != xfer_result) {
uint16_t* serial = (uint16_t*)(uintptr_t) desc.serial;
- serial[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * 3 + 2));
- serial[1] = 'n';
- serial[2] = '/';
- serial[3] = 'a';
- serial[4] = 0;
+ serial[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * 1 + 2));
+ serial[1] = '0';
+ serial[2] = 0;
}
print_utf16((uint16_t*)(uintptr_t) desc.serial, sizeof(desc.serial)/2);
printf("\r\n");
@@ -150,12 +179,9 @@ void tuh_mount_cb(uint8_t daddr) {
printf(" idProduct 0x%04x\r\n", desc.device.idProduct);
printf(" bcdDevice %04x\r\n", desc.device.bcdDevice);
- // Get String descriptor using Sync API
-
printf(" iManufacturer %u ", desc.device.iManufacturer);
if (desc.device.iManufacturer != 0) {
- xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf));
- if (XFER_RESULT_SUCCESS == xfer_result) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
}
}
@@ -163,24 +189,33 @@ void tuh_mount_cb(uint8_t daddr) {
printf(" iProduct %u ", desc.device.iProduct);
if (desc.device.iProduct != 0) {
- xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf));
- if (XFER_RESULT_SUCCESS == xfer_result) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
}
}
printf("\r\n");
printf(" iSerialNumber %u ", desc.device.iSerialNumber);
- printf((char*)desc.serial); // serial is already to UTF-8
- printf("\r\n");
-
+ printf("%s\r\n", (char*)desc.serial); // serial is already UTF-8
printf(" bNumConfigurations %u\r\n", desc.device.bNumConfigurations);
}
-// Invoked when device is unmounted (bus reset/unplugged)
-void tuh_umount_cb(uint8_t daddr) {
- blink_interval_ms = BLINK_NOT_MOUNTED;
- printf("Device removed, address = %d\r\n", daddr);
+void print_devinfo_task(void* param) {
+ (void) param;
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+#endif
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+#endif
}
//--------------------------------------------------------------------+
@@ -248,7 +283,7 @@ void led_blinking_task(void* param) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
#else
- if (board_millis() - start_ms < blink_interval_ms) {
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
return; // not enough time
}
#endif
@@ -281,6 +316,9 @@ StaticTask_t blinky_taskdef;
StackType_t usb_stack[USB_STACK_SIZE];
StaticTask_t usb_taskdef;
+
+StackType_t devinfo_stack[USB_STACK_SIZE];
+StaticTask_t devinfo_taskdef;
#endif
#ifdef ESP_PLATFORM
@@ -291,6 +329,7 @@ void app_main(void) {
void usb_host_task(void *param) {
(void) param;
+ board_delay(100); // wait for uart to be ready
init_tinyusb();
while (1) {
tuh_task();
@@ -301,9 +340,11 @@ void init_freertos_task(void) {
#if configSUPPORT_STATIC_ALLOCATION
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
xTaskCreateStatic(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef);
+ xTaskCreateStatic(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-2, devinfo_stack, &devinfo_taskdef);
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
#endif
// only start scheduler for non-espressif mcu
diff --git a/examples/host/hid_controller/CMakeLists.txt b/examples/host/hid_controller/CMakeLists.txt
index fb5faf210..287e9fb7f 100644
--- a/examples/host/hid_controller/CMakeLists.txt
+++ b/examples/host/hid_controller/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(hid_controller C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/hid_controller/Makefile b/examples/host/hid_controller/Makefile
index 1377f1f90..732520c63 100644
--- a/examples/host/hid_controller/Makefile
+++ b/examples/host/hid_controller/Makefile
@@ -1,14 +1,14 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/hid_app.c \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/hid_controller/only.txt b/examples/host/hid_controller/only.txt
index cba58f8e8..45ca6846f 100644
--- a/examples/host/hid_controller/only.txt
+++ b/examples/host/hid_controller/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,23 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/hid_controller/skip.txt b/examples/host/hid_controller/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/hid_controller/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/hid_controller/src/app.h b/examples/host/hid_controller/src/app.h
new file mode 100644
index 000000000..b4d072668
--- /dev/null
+++ b/examples/host/hid_controller/src/app.h
@@ -0,0 +1,33 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 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.
+ */
+#ifndef TUSB_TINYUSB_EXAMPLES_APP_H
+#define TUSB_TINYUSB_EXAMPLES_APP_H
+
+#include <stdio.h>
+
+void hid_app_task(void);
+
+#endif
diff --git a/examples/host/hid_controller/src/hid_app.c b/examples/host/hid_controller/src/hid_app.c
index 1d6ca8b07..5417811f0 100644
--- a/examples/host/hid_controller/src/hid_app.c
+++ b/examples/host/hid_controller/src/hid_app.c
@@ -25,6 +25,7 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
/* From https://www.kernel.org/doc/html/latest/input/gamepad.html
____________________________ __
@@ -167,7 +168,7 @@ void hid_app_task(void)
const uint32_t interval_ms = 200;
static uint32_t start_ms = 0;
- uint32_t current_time_ms = board_millis();
+ uint32_t current_time_ms = tusb_time_millis_api();
if ( current_time_ms - start_ms >= interval_ms)
{
start_ms = current_time_ms;
diff --git a/examples/host/hid_controller/src/main.c b/examples/host/hid_controller/src/main.c
index f3244db95..a9eebc90b 100644
--- a/examples/host/hid_controller/src/main.c
+++ b/examples/host/hid_controller/src/main.c
@@ -34,18 +34,15 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
-extern void cdc_task(void);
-extern void hid_app_task(void);
-
/*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
board_init();
printf("TinyUSB Host HID Controller Example\r\n");
@@ -60,19 +57,11 @@ int main(void)
board_init_after_tusb();
- while (1)
- {
+ while (1) {
// tinyusb host task
tuh_task();
led_blinking_task();
-
-#if CFG_TUH_CDC
- cdc_task();
-#endif
-
-#if CFG_TUH_HID
hid_app_task();
-#endif
}
}
@@ -83,15 +72,14 @@ int main(void)
//--------------------------------------------------------------------+
// Blinking Task
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
const uint32_t interval_ms = 1000;
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
board_led_write(led_state);
diff --git a/examples/host/hid_controller/src/tusb_config.h b/examples/host/hid_controller/src/tusb_config.h
index 351fe0178..a5c202fda 100644
--- a/examples/host/hid_controller/src/tusb_config.h
+++ b/examples/host/hid_controller/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -119,4 +119,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/midi2_host/CMakeLists.txt b/examples/host/midi2_host/CMakeLists.txt
new file mode 100644
index 000000000..0ec03bf5f
--- /dev/null
+++ b/examples/host/midi2_host/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(midi2_host C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/midi2_host/CMakePresets.json b/examples/host/midi2_host/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/host/midi2_host/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/host/midi2_host/Makefile b/examples/host/midi2_host/Makefile
new file mode 100644
index 000000000..f8292385e
--- /dev/null
+++ b/examples/host/midi2_host/Makefile
@@ -0,0 +1,13 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += \
+ src/main.c
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/midi2_host/only.txt b/examples/host/midi2_host/only.txt
new file mode 100644
index 000000000..c71aacd87
--- /dev/null
+++ b/examples/host/midi2_host/only.txt
@@ -0,0 +1,34 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
+mcu:CH32V20X
+mcu:ESP32P4
+mcu:ESP32S2
+mcu:ESP32S3
+mcu:KINETIS_KL
+mcu:LPC175X_6X
+mcu:LPC177X_8X
+mcu:LPC18XX
+mcu:LPC40XX
+mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
+mcu:MIMXRT10XX
+mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
+mcu:MSP432E4
+mcu:RAXXX
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32F4
+mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
+mcu:STM32H7
+mcu:STM32H7RS
+mcu:STM32N6
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/midi2_host/skip.txt b/examples/host/midi2_host/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/midi2_host/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/midi2_host/src/main.c b/examples/host/midi2_host/src/main.c
new file mode 100644
index 000000000..d81e77a33
--- /dev/null
+++ b/examples/host/midi2_host/src/main.c
@@ -0,0 +1,163 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+// Minimal USB-MIDI 2.0 host example.
+// Receives UMP from any MIDI 2.0 device, prints each packet to stdout.
+
+#include <stdio.h>
+#include <string.h>
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "class/midi/midi2_host.h"
+
+//--------------------------------------------------------------------+
+// State
+//--------------------------------------------------------------------+
+
+TU_ATTR_UNUSED static uint8_t midi2_idx = 0xFF;
+
+//--------------------------------------------------------------------+
+// UMP printer - shows MT and word(s) in hex; decodes Channel Voice
+//--------------------------------------------------------------------+
+
+static void print_ump(const uint32_t* words, uint8_t wc) {
+ uint8_t mt = (uint8_t)((words[0] >> 28) & 0x0F);
+ uint8_t group = (uint8_t)((words[0] >> 24) & 0x0F);
+
+ if (mt == 0x4 && wc >= 2) {
+ // MIDI 2.0 Channel Voice
+ uint8_t status = (uint8_t)((words[0] >> 20) & 0x0F);
+ uint8_t channel = (uint8_t)((words[0] >> 16) & 0x0F);
+ uint8_t data1 = (uint8_t)((words[0] >> 8) & 0x7F);
+ switch (status) {
+ case 0x9:
+ printf("[g%u ch%u] M2 NoteOn n=%u vel=%04X attr=%04X\r\n",
+ group, channel, data1,
+ (unsigned)((words[1] >> 16) & 0xFFFF),
+ (unsigned)(words[1] & 0xFFFF));
+ break;
+ case 0x8:
+ printf("[g%u ch%u] M2 NoteOff n=%u vel=%04X\r\n",
+ group, channel, data1,
+ (unsigned)((words[1] >> 16) & 0xFFFF));
+ break;
+ case 0xB:
+ printf("[g%u ch%u] M2 CC#%u = %08lX\r\n",
+ group, channel, data1, (unsigned long)words[1]);
+ break;
+ case 0xC:
+ printf("[g%u ch%u] M2 ProgChg %u\r\n",
+ group, channel, (unsigned)((words[1] >> 24) & 0x7F));
+ break;
+ case 0xD:
+ printf("[g%u ch%u] M2 ChanPress %08lX\r\n",
+ group, channel, (unsigned long)words[1]);
+ break;
+ case 0xE:
+ printf("[g%u ch%u] M2 PitchBend %08lX\r\n",
+ group, channel, (unsigned long)words[1]);
+ break;
+ default:
+ printf("[g%u ch%u] M2 status=0x%X w0=%08lX w1=%08lX\r\n",
+ group, channel, status,
+ (unsigned long)words[0], (unsigned long)words[1]);
+ break;
+ }
+ } else if (mt == 0x2 && wc == 1) {
+ // MIDI 1.0 Channel Voice
+ uint8_t status = (uint8_t)((words[0] >> 16) & 0xFF);
+ uint8_t data1 = (uint8_t)((words[0] >> 8) & 0x7F);
+ uint8_t data2 = (uint8_t)(words[0] & 0x7F);
+ printf("[g%u] M1 %02X %02X %02X\r\n", group, status, data1, data2);
+ } else {
+ printf("UMP MT=0x%X wc=%u w0=%08lX\r\n",
+ mt, wc, (unsigned long)words[0]);
+ }
+}
+
+//--------------------------------------------------------------------+
+// MIDI 2.0 Host Callbacks
+//--------------------------------------------------------------------+
+
+void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t* d) {
+ (void)idx;
+ printf("MIDI2 descriptor: %s, RX cables=%u TX cables=%u\r\n",
+ d->protocol_version ? "MIDI 2.0" : "MIDI 1.0",
+ d->rx_cable_count, d->tx_cable_count);
+}
+
+void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t* m) {
+ midi2_idx = idx;
+ printf("MIDI2 mounted: idx=%u protocol=%s\r\n",
+ idx, m->protocol_version ? "MIDI 2.0" : "MIDI 1.0");
+}
+
+void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void)xferred_bytes;
+
+ uint32_t words[16];
+ while (1) {
+ uint32_t n = tuh_midi2_ump_read(idx, words, 16);
+ if (n == 0) break;
+
+ uint32_t i = 0;
+ while (i < n) {
+ uint8_t mt = (uint8_t)((words[i] >> 28) & 0x0F);
+ uint8_t wc = midi2_ump_word_count(mt);
+ if (i + wc > n) break;
+ print_ump(&words[i], wc);
+ i += wc;
+ }
+ }
+}
+
+void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void)idx; (void)xferred_bytes;
+}
+
+void tuh_midi2_umount_cb(uint8_t idx) {
+ (void)idx;
+ midi2_idx = 0xFF;
+ printf("MIDI2 unmounted\r\n");
+}
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+
+int main(void) {
+ board_init();
+
+ printf("\r\nTinyUSB Host MIDI 2.0 Example\r\n");
+
+ tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUSB_SPEED_AUTO,
+ };
+ tusb_init(BOARD_TUH_RHPORT, &host_init);
+
+ while (1) {
+ tuh_task();
+ }
+}
diff --git a/examples/host/midi2_host/src/tusb_config.h b/examples/host/midi2_host/src/tusb_config.h
new file mode 100644
index 000000000..4bd6ef2ea
--- /dev/null
+++ b/examples/host/midi2_host/src/tusb_config.h
@@ -0,0 +1,106 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Common Configuration
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifdef ESP_PLATFORM
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+#ifndef CFG_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------+
+// Host Configuration
+//--------------------------------------------------------------------+
+
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_MAX3421 1 // use max3421 as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------+
+
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------+
+// Driver Configuration
+//--------------------------------------------------------------------+
+
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB 1
+#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
+
+// USB-MIDI 2.0 Host
+#define CFG_TUH_MIDI2 CFG_TUH_DEVICE_MAX
+#define CFG_TUH_MIDI2_RX_BUFSIZE 512
+#define CFG_TUH_MIDI2_TX_BUFSIZE 512
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/examples/host/midi_rx/CMakeLists.txt b/examples/host/midi_rx/CMakeLists.txt
index 33953233d..62bb89e95 100644
--- a/examples/host/midi_rx/CMakeLists.txt
+++ b/examples/host/midi_rx/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(midi_rx C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/midi_rx/Makefile b/examples/host/midi_rx/Makefile
index 0235e08c3..f8292385e 100644
--- a/examples/host/midi_rx/Makefile
+++ b/examples/host/midi_rx/Makefile
@@ -1,13 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/midi_rx/only.txt b/examples/host/midi_rx/only.txt
index 133a7c9a0..65ef8fac9 100644
--- a/examples/host/midi_rx/only.txt
+++ b/examples/host/midi_rx/only.txt
@@ -1,25 +1,35 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
+mcu:ESP32P4
mcu:ESP32S2
mcu:ESP32S3
-mcu:ESP32P4
mcu:KINETIS_KL
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
mcu:MAX3421
-mcu:MIMXRT1XXX
mcu:MIMXRT10XX
mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
mcu:MSP432E4
+mcu:RAXXX
mcu:RP2040
+mcu:RW61X
mcu:RX65X
-mcu:RAXXX
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/midi_rx/skip.txt b/examples/host/midi_rx/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/midi_rx/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/midi_rx/src/main.c b/examples/host/midi_rx/src/main.c
index 78b1a11b9..fb36906c6 100644
--- a/examples/host/midi_rx/src/main.c
+++ b/examples/host/midi_rx/src/main.c
@@ -58,8 +58,6 @@ int main(void) {
led_blinking_task();
midi_host_rx_task();
}
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -72,7 +70,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) return;// not enough time
+ if (tusb_time_millis_api() - start_ms < interval_ms) return;// not enough time
start_ms += interval_ms;
board_led_write(led_state);
diff --git a/examples/host/msc_file_explorer/CMakeLists.txt b/examples/host/msc_file_explorer/CMakeLists.txt
index 5ac75c04a..c8d196447 100644
--- a/examples/host/msc_file_explorer/CMakeLists.txt
+++ b/examples/host/msc_file_explorer/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(msc_file_explorer C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
${TOP}/lib/fatfs/source/ff.c
@@ -26,17 +23,8 @@ target_sources(${PROJECT} PUBLIC
${TOP}/lib/fatfs/source/ffunicode.c
)
-# Suppress warnings on fatfs
-if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
- set_source_files_properties(
- ${TOP}/lib/fatfs/source/ff.c
- PROPERTIES
- COMPILE_FLAGS "-Wno-conversion -Wno-cast-qual"
- )
-endif ()
-
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${TOP}/lib/fatfs/source
${TOP}/lib/embedded-cli
@@ -44,4 +32,11 @@ target_include_directories(${PROJECT} PUBLIC
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
+
+# Suppress warnings on fatfs
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ set_source_files_properties(${TOP}/lib/fatfs/source/ff.c PROPERTIES
+ COMPILE_OPTIONS "-Wno-conversion;-Wno-cast-qual"
+ )
+endif ()
diff --git a/examples/host/msc_file_explorer/Makefile b/examples/host/msc_file_explorer/Makefile
index f0872376f..8c0b012ad 100644
--- a/examples/host/msc_file_explorer/Makefile
+++ b/examples/host/msc_file_explorer/Makefile
@@ -1,10 +1,9 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
FATFS_PATH = lib/fatfs/source
INC += \
src \
- $(TOP)/hw \
$(TOP)/$(FATFS_PATH) \
$(TOP)/lib/embedded-cli \
@@ -13,7 +12,7 @@ EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
# FatFS source
SRC_C += \
@@ -22,6 +21,6 @@ SRC_C += \
$(FATFS_PATH)/ffunicode.c \
# suppress warning caused by fatfs
-CFLAGS_GCC += -Wno-error=cast-qual
+CFLAGS += -Wno-error=cast-qual
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/msc_file_explorer/README.md b/examples/host/msc_file_explorer/README.md
new file mode 100644
index 000000000..e220bedea
--- /dev/null
+++ b/examples/host/msc_file_explorer/README.md
@@ -0,0 +1,105 @@
+# MSC File Explorer
+
+This host example implements an interactive command-line file browser for USB Mass Storage devices.
+When a USB flash drive is connected, the device is automatically mounted using FatFS and a shell-like
+CLI is presented over the board's serial console.
+
+## Features
+
+- Automatic mount/unmount of USB storage devices
+- FAT12/16/32 filesystem support via FatFS
+- Interactive CLI with command history
+- Read speed benchmarking with `dd`
+- Support for up to 4 simultaneous USB storage devices (via hub)
+
+## Supported Commands
+
+| Command | Usage | Description |
+|---------|--------------------|------------------------------------------------------|
+| help | `help` | Print list of available commands |
+| cat | `cat <file>` | Print file contents to the console |
+| cd | `cd <dir>` | Change current working directory |
+| cp | `cp <src> <dest>` | Copy a file |
+| dd | `dd [count]` | Read sectors and report speed (default 1024 sectors) |
+| ls | `ls [dir]` | List directory contents |
+| pwd | `pwd` | Print current working directory |
+| mkdir | `mkdir <dir>` | Create a directory |
+| mv | `mv <src> <dest>` | Rename/move a file or directory |
+| rm | `rm <file>` | Remove a file |
+
+## Build
+
+Build for a specific board using CMake (see [Getting Started](https://docs.tinyusb.org/en/latest/getting_started.html)):
+
+```bash
+# Example: build for Raspberry Pi Pico
+cmake -B build -DBOARD=raspberry_pi_pico -DFAMILY=rp2040 examples/host/msc_file_explorer
+cmake --build build
+```
+
+## Usage
+
+1. Flash the firmware to your board.
+2. Open a serial terminal (e.g. `minicom`, `screen`, `PuTTY`) at 115200 baud.
+3. Plug a USB flash drive into the board's USB host port.
+4. The device is auto-mounted and the prompt appears:
+
+```
+TinyUSB MSC File Explorer Example
+
+Device connected
+ Vendor : Kingston
+ Product : DataTraveler 2.0
+ Rev : 1.0
+ Capacity: 1.9 GB
+
+0:/> _
+```
+
+### Browsing Files
+
+```
+0:/> ls
+----a 1234 readme.txt
+d---- 0 photos
+d---- 0 docs
+
+0:/> cd photos
+0:/photos> ls
+----a 520432 vacation.jpg
+----a 312088 family.png
+
+0:/> cat readme.txt
+Hello from USB drive!
+```
+
+### Copying and Moving Files
+
+```
+0:/> cp readme.txt backup.txt
+0:/> mv backup.txt docs/backup.txt
+```
+
+### Measuring Read Speed
+
+```
+0:/> dd
+Reading 1024 sectors...
+ Data speed: 823 KB/s
+```
+
+### Multiple Devices
+
+When using a USB hub, multiple drives are mounted as `0:`, `1:`, etc. Use the drive prefix to
+navigate between them:
+
+```
+0:/> cd 1:
+1:/> ls
+```
+
+## Testing
+
+This example is part of the TinyUSB HIL (Hardware-in-the-Loop) test suite. The HIL test
+automatically flashes, runs the example, and verifies MSC enumeration and file operations
+against a known USB drive.
diff --git a/examples/host/msc_file_explorer/only.txt b/examples/host/msc_file_explorer/only.txt
index cba58f8e8..a2ff93be5 100644
--- a/examples/host/msc_file_explorer/only.txt
+++ b/examples/host/msc_file_explorer/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,25 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/msc_file_explorer/skip.txt b/examples/host/msc_file_explorer/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/msc_file_explorer/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/msc_file_explorer/src/ffconf.h b/examples/host/msc_file_explorer/src/ffconf.h
new file mode 100644
index 000000000..5c89136fe
--- /dev/null
+++ b/examples/host/msc_file_explorer/src/ffconf.h
@@ -0,0 +1,313 @@
+/*---------------------------------------------------------------------------/
+/ Configurations of FatFs Module
+/---------------------------------------------------------------------------*/
+
+#define FFCONF_DEF 80386 /* Revision ID */
+
+/*---------------------------------------------------------------------------/
+/ Function Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_READONLY 0
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
+/ Read-only configuration removes writing API functions, f_write(), f_sync(),
+/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
+/ and optional writing functions as well. */
+
+
+#define FF_FS_MINIMIZE 0
+/* This option defines minimization level to remove some basic API functions.
+/
+/ 0: Basic functions are fully enabled.
+/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
+/ are removed.
+/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/ 3: f_lseek() function is removed in addition to 2. */
+
+
+#define FF_USE_FIND 0
+/* This option switches filtered directory read functions, f_findfirst() and
+/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
+
+
+#define FF_USE_MKFS 0
+/* This option switches f_mkfs(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FASTSEEK 0
+/* This option switches fast seek feature. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_EXPAND 0
+/* This option switches f_expand(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_CHMOD 0
+/* This option switches attribute control API functions, f_chmod() and f_utime().
+/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
+
+
+#define FF_USE_LABEL 0
+/* This option switches volume label API functions, f_getlabel() and f_setlabel().
+/ (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FORWARD 0
+/* This option switches f_forward(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_STRFUNC 0
+#define FF_PRINT_LLI 0
+#define FF_PRINT_FLOAT 0
+#define FF_STRF_ENCODE 0
+/* FF_USE_STRFUNC switches string API functions, f_gets(), f_putc(), f_puts() and
+/ f_printf().
+/
+/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
+/ 1: Enable without LF-CRLF conversion.
+/ 2: Enable with LF-CRLF conversion.
+/
+/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
+/ makes f_printf() support floating point argument. These features want C99 or later.
+/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
+/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
+/ to be read/written via those functions.
+/
+/ 0: ANSI/OEM in current CP
+/ 1: Unicode in UTF-16LE
+/ 2: Unicode in UTF-16BE
+/ 3: Unicode in UTF-8
+*/
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_CODE_PAGE 437
+/* This option specifies the OEM code page to be used on the target system.
+/ Incorrect code page setting can cause a file open failure.
+/
+/ 437 - U.S.
+/ 720 - Arabic
+/ 737 - Greek
+/ 771 - KBL
+/ 775 - Baltic
+/ 850 - Latin 1
+/ 852 - Latin 2
+/ 855 - Cyrillic
+/ 857 - Turkish
+/ 860 - Portuguese
+/ 861 - Icelandic
+/ 862 - Hebrew
+/ 863 - Canadian French
+/ 864 - Arabic
+/ 865 - Nordic
+/ 866 - Russian
+/ 869 - Greek 2
+/ 932 - Japanese (DBCS)
+/ 936 - Simplified Chinese (DBCS)
+/ 949 - Korean (DBCS)
+/ 950 - Traditional Chinese (DBCS)
+/ 0 - Include all code pages above and configured by f_setcp()
+*/
+
+
+#define FF_USE_LFN 1
+#define FF_MAX_LFN 255
+/* The FF_USE_LFN switches the support for LFN (long file name).
+/
+/ 0: Disable LFN. FF_MAX_LFN has no effect.
+/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
+/ 2: Enable LFN with dynamic working buffer on the STACK.
+/ 3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
+/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
+/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
+/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
+/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
+/ specification.
+/ When use stack for the working buffer, take care on stack overflow. When use heap
+/ memory for the working buffer, memory management functions, ff_memalloc() and
+/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
+
+
+#define FF_LFN_UNICODE 0
+/* This option switches the character encoding on the API when LFN is enabled.
+/
+/ 0: ANSI/OEM in current CP (TCHAR = char)
+/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
+/ 2: Unicode in UTF-8 (TCHAR = char)
+/ 3: Unicode in UTF-32 (TCHAR = DWORD)
+/
+/ Also behavior of string I/O functions will be affected by this option.
+/ When LFN is not enabled, this option has no effect. */
+
+
+#define FF_LFN_BUF 255
+#define FF_SFN_BUF 12
+/* This set of options defines size of file name members in the FILINFO structure
+/ which is used to read out directory items. These values should be sufficient for
+/ the file names to read. The maximum possible length of the read file name depends
+/ on character encoding. When LFN is not enabled, these options have no effect. */
+
+
+#define FF_FS_RPATH 2
+/* This option configures support for relative path feature.
+/
+/ 0: Disable relative path and remove related API functions.
+/ 1: Enable relative path and dot names. f_chdir() and f_chdrive() are available.
+/ 2: f_getcwd() is available in addition to 1.
+*/
+
+
+#define FF_PATH_DEPTH 10
+/* This option defines maximum depth of directory in the exFAT volume. It is NOT
+/ relevant to FAT/FAT32 volume.
+/ For example, FF_PATH_DEPTH = 3 will able to follow a path "/dir1/dir2/dir3/file"
+/ but a sub-directory in the dir3 will not able to be followed and set current
+/ directory.
+/ The size of filesystem object (FATFS) increases FF_PATH_DEPTH * 24 bytes.
+/ When FF_FS_EXFAT == 0 or FF_FS_RPATH == 0, this option has no effect.
+*/
+
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_VOLUMES 4
+/* Number of volumes (logical drives) to be used. (1-10) */
+
+
+#define FF_STR_VOLUME_ID 0
+#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
+/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
+/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
+/ logical drive. Number of items must not be less than FF_VOLUMES. Valid
+/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
+/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
+/ not defined, a user defined volume string table is needed as:
+/
+/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
+*/
+
+
+#define FF_MULTI_PARTITION 0
+/* This option switches support for multiple volumes on the physical drive.
+/ By default (0), each logical drive number is bound to the same physical drive
+/ number and only an FAT volume found on the physical drive will be mounted.
+/ When this feature is enabled (1), each logical drive number can be bound to
+/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
+/ will be available. */
+
+
+#define FF_MIN_SS 512
+#define FF_MAX_SS 512
+/* This set of options configures the range of sector size to be supported. (512,
+/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
+/ harddisk, but a larger value may be required for on-board flash memory and some
+/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
+/ configured for variable sector size mode and disk_ioctl() needs to implement
+/ GET_SECTOR_SIZE command. */
+
+
+#define FF_LBA64 0
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
+/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
+
+
+#define FF_MIN_GPT 0x10000000
+/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
+/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */
+
+
+#define FF_USE_TRIM 0
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
+/ To enable this feature, also CTRL_TRIM command should be implemented to
+/ the disk_ioctl(). */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_TINY 0
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
+/ At the tiny configuration, size of file object (FIL) is reduced FF_MAX_SS bytes.
+/ Instead of private sector buffer eliminated from the file object, common sector
+/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
+
+
+#define FF_FS_EXFAT 0
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
+/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
+/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
+
+
+#define FF_FS_NORTC 1
+#define FF_NORTC_MON 1
+#define FF_NORTC_MDAY 1
+#define FF_NORTC_YEAR 2025
+/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
+/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
+/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
+/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
+/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
+/ to the project to read current time form real-time clock. FF_NORTC_MON,
+/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
+/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
+
+
+#define FF_FS_CRTIME 0
+/* This option enables(1)/disables(0) the timestamp of the file created. When
+/ set 1, the file created time is available in FILINFO structure. */
+
+
+#define FF_FS_NOFSINFO 0
+/* If you need to know the correct free space on the FAT32 volume, set bit 0 of
+/ this option, and f_getfree() on the first time after volume mount will force
+/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
+/
+/ bit0=0: Use free cluster count in the FSINFO if available.
+/ bit0=1: Do not trust free cluster count in the FSINFO.
+/ bit1=0: Use last allocated cluster number in the FSINFO if available.
+/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+#define FF_FS_LOCK 0
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
+/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
+/ is 1.
+/
+/ 0: Disable file lock function. To avoid volume corruption, application program
+/ should avoid illegal open, remove and rename to the open objects.
+/ >0: Enable file lock function. The value defines how many files/sub-directories
+/ can be opened simultaneously under file lock control. Note that the file
+/ lock control is independent of re-entrancy. */
+
+
+#define FF_FS_REENTRANT 0
+#define FF_FS_TIMEOUT 1000
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
+/ module itself. Note that regardless of this option, file access to different
+/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
+/ and f_fdisk(), are always not re-entrant. Only file/directory access to
+/ the same volume is under control of this featuer.
+/
+/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
+/ 1: Enable re-entrancy. Also user provided synchronization handlers,
+/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
+/ must be added to the project. Samples are available in ffsystem.c.
+/
+/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
+*/
+
+
+
+/*--- End of configuration options ---*/
diff --git a/examples/host/msc_file_explorer/src/main.c b/examples/host/msc_file_explorer/src/main.c
index 506c3b015..07515e626 100644
--- a/examples/host/msc_file_explorer/src/main.c
+++ b/examples/host/msc_file_explorer/src/main.c
@@ -23,40 +23,6 @@
*
*/
-/* Example to show how to navigate mass storage device with built-in command line.
- * Type help for list of supported commands and syntax (mostly linux commands)
-
- > help
- * help
- Print list of commands
- * cat
- Usage: cat [FILE]...
- Concatenate FILE(s) to standard output..
- * cd
- Usage: cd [DIR]...
- Change the current directory to DIR.
- * cp
- Usage: cp SOURCE DEST
- Copy SOURCE to DEST.
- * ls
- Usage: ls [DIR]...
- List information about the FILEs (the current directory by default).
- * pwd
- Usage: pwd
- Print the name of the current working directory.
- * mkdir
- Usage: mkdir DIR...
- Create the DIRECTORY(ies), if they do not already exist..
- * mv
- Usage: mv SOURCE DEST...
- Rename SOURCE to DEST.
- * rm
- Usage: rm [FILE]...
- Remove (unlink) the FILE(s).
- */
-
-#include <stdlib.h>
-#include <stdio.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -93,8 +59,6 @@ int main(void) {
msc_app_task();
led_blinking_task();
}
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -119,7 +83,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
board_led_write(led_state);
diff --git a/examples/host/msc_file_explorer/src/msc_app.c b/examples/host/msc_file_explorer/src/msc_app.c
index 40a9ef57e..3f15bb84f 100644
--- a/examples/host/msc_file_explorer/src/msc_app.c
+++ b/examples/host/msc_file_explorer/src/msc_app.c
@@ -46,14 +46,21 @@
#define CLI_RX_BUFFER_SIZE 16
#define CLI_CMD_BUFFER_SIZE 64
#define CLI_HISTORY_SIZE 32
-#define CLI_BINDING_COUNT 8
+#define CLI_BINDING_COUNT 9
static EmbeddedCli *_cli;
-static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
+static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
//------------- Elm Chan FatFS -------------//
-static FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
-static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
+static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
+static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
+
+static CFG_TUH_MEM_SECTION FIL file1, file2;
+
+#ifndef CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE
+#define CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE 4096
+#endif
+static CFG_TUH_MEM_SECTION uint8_t rw_buf[CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE];
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
CFG_TUH_MEM_SECTION static struct {
@@ -67,34 +74,30 @@ CFG_TUH_MEM_SECTION static struct {
bool cli_init(void);
-bool msc_app_init(void)
-{
- for(size_t i=0; i<CFG_TUH_DEVICE_MAX; i++) {
+bool msc_app_init(void) {
+ for (size_t i = 0; i < CFG_TUH_DEVICE_MAX; i++) {
_disk_busy[i] = false;
}
- // disable stdout buffered for echoing typing command
- #ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
+// disable stdout buffered for echoing typing command
+#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
setbuf(stdout, NULL);
- #endif
+#endif
cli_init();
return true;
}
-void msc_app_task(void)
-{
+void msc_app_task(void) {
if (!_cli) {
return;
}
int ch = board_getchar();
- if ( ch > 0 )
- {
- while( ch > 0 )
- {
- embeddedCliReceiveChar(_cli, (char) ch);
+ if (ch > 0) {
+ while (ch > 0) {
+ embeddedCliReceiveChar(_cli, (char)ch);
ch = board_getchar();
}
embeddedCliProcess(_cli);
@@ -105,185 +108,172 @@ void msc_app_task(void)
//
//--------------------------------------------------------------------+
-static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data) {
- msc_cbw_t const* cbw = cb_data->cbw;
- msc_csw_t const* csw = cb_data->csw;
+static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
+ const msc_cbw_t *cbw = cb_data->cbw;
+ const msc_csw_t *csw = cb_data->csw;
- if (csw->status != 0)
- {
+ if (csw->status != 0) {
printf("Inquiry failed\r\n");
return false;
}
// Print out Vendor ID, Product ID and Rev
- printf("%.8s %.16s rev %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
+ printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id,
+ scsi_resp.inquiry.product_rev);
// Get capacity of device
- uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
- uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
+ const uint32_t block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
+ const uint32_t block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
- printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024*1024)/block_size));
- // printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
// For simplicity: we only mount 1 LUN per device
- uint8_t const drive_num = dev_addr-1;
- char drive_path[3] = "0:";
+ const uint8_t drive_num = dev_addr - 1;
+ char drive_path[3] = "0:";
drive_path[0] += drive_num;
- if ( f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK )
- {
- puts("mount failed");
+ if (f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK) {
+ printf("mount failed\r\n");
+ return true;
}
// change to newly mounted drive
- f_chdir(drive_path);
+ f_chdrive(drive_path);
+ FRESULT rc = f_chdir("/");
+ if (rc != FR_OK) {
+ printf("chdir failed: %d\r\n", rc);
+ }
// print the drive label
-// char label[34];
-// if ( FR_OK == f_getlabel(drive_path, label, NULL) )
-// {
-// puts(label);
-// }
+ // char label[34];
+ // if ( FR_OK == f_getlabel(drive_path, label, NULL) )
+ // {
+ // puts(label);
+ // }
return true;
}
//------------- IMPLEMENTATION -------------//
-void tuh_msc_mount_cb(uint8_t dev_addr)
-{
- printf("A MassStorage device is mounted\r\n");
+void tuh_msc_mount_cb(uint8_t dev_addr) {
+ printf("A MassStorage device (addr = %u) is mounted\r\n", dev_addr);
- uint8_t const lun = 0;
+ const uint8_t lun = 0;
tuh_msc_inquiry(dev_addr, lun, &scsi_resp.inquiry, inquiry_complete_cb, 0);
}
-void tuh_msc_umount_cb(uint8_t dev_addr)
-{
+void tuh_msc_umount_cb(uint8_t dev_addr) {
printf("A MassStorage device is unmounted\r\n");
- uint8_t const drive_num = dev_addr-1;
- char drive_path[3] = "0:";
+ const uint8_t drive_num = dev_addr - 1;
+ char drive_path[3] = "0:";
drive_path[0] += drive_num;
f_unmount(drive_path);
-// if ( phy_disk == f_get_current_drive() )
-// { // active drive is unplugged --> change to other drive
-// for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
-// {
-// if ( disk_is_ready(i) )
-// {
-// f_chdrive(i);
-// cli_init(); // refractor, rename
-// }
-// }
-// }
+ // if ( phy_disk == f_get_current_drive() )
+ // { // active drive is unplugged --> change to other drive
+ // for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
+ // {
+ // if ( disk_is_ready(i) )
+ // {
+ // f_chdrive(i);
+ // cli_init(); // refractor, rename
+ // }
+ // }
+ // }
}
//--------------------------------------------------------------------+
// DiskIO
//--------------------------------------------------------------------+
-static void wait_for_disk_io(BYTE pdrv)
-{
- while(_disk_busy[pdrv])
- {
+static void wait_for_disk_io(BYTE pdrv) {
+ while (_disk_busy[pdrv]) {
tuh_task();
}
}
-static bool disk_io_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
- (void) dev_addr; (void) cb_data;
- _disk_busy[dev_addr-1] = false;
+static bool disk_io_complete(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
+ (void)dev_addr;
+ (void)cb_data;
+ _disk_busy[dev_addr - 1] = false;
return true;
}
-DSTATUS disk_status (
- BYTE pdrv /* Physical drive nmuber to identify the drive */
-)
-{
+DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
+) {
uint8_t dev_addr = pdrv + 1;
return tuh_msc_mounted(dev_addr) ? 0 : STA_NODISK;
}
-DSTATUS disk_initialize (
- BYTE pdrv /* Physical drive nmuber to identify the drive */
-)
-{
- (void) pdrv;
- return 0; // nothing to do
+DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
+) {
+ (void)pdrv;
+ return 0; // nothing to do
}
-DRESULT disk_read (
- BYTE pdrv, /* Physical drive nmuber to identify the drive */
- BYTE *buff, /* Data buffer to store read data */
- LBA_t sector, /* Start sector in LBA */
- UINT count /* Number of sectors to read */
-)
-{
- uint8_t const dev_addr = pdrv + 1;
- uint8_t const lun = 0;
+DRESULT disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ BYTE *buff, /* Data buffer to store read data */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to read */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
- _disk_busy[pdrv] = true;
- tuh_msc_read10(dev_addr, lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
- wait_for_disk_io(pdrv);
+ _disk_busy[pdrv] = true;
+ tuh_msc_read10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
- return RES_OK;
+ return RES_OK;
}
#if FF_FS_READONLY == 0
-DRESULT disk_write (
- BYTE pdrv, /* Physical drive nmuber to identify the drive */
- const BYTE *buff, /* Data to be written */
- LBA_t sector, /* Start sector in LBA */
- UINT count /* Number of sectors to write */
-)
-{
- uint8_t const dev_addr = pdrv + 1;
- uint8_t const lun = 0;
+DRESULT disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ const BYTE *buff, /* Data to be written */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to write */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
- _disk_busy[pdrv] = true;
- tuh_msc_write10(dev_addr, lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
- wait_for_disk_io(pdrv);
+ _disk_busy[pdrv] = true;
+ tuh_msc_write10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
- return RES_OK;
+ return RES_OK;
}
#endif
-DRESULT disk_ioctl (
- BYTE pdrv, /* Physical drive nmuber (0..) */
- BYTE cmd, /* Control code */
- void *buff /* Buffer to send/receive control data */
-)
-{
- uint8_t const dev_addr = pdrv + 1;
- uint8_t const lun = 0;
- switch ( cmd )
- {
+DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
+ BYTE cmd, /* Control code */
+ void *buff /* Buffer to send/receive control data */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+ switch (cmd) {
case CTRL_SYNC:
// nothing to do since we do blocking
return RES_OK;
case GET_SECTOR_COUNT:
- *((DWORD*) buff) = (WORD) tuh_msc_get_block_count(dev_addr, lun);
+ *((DWORD *)buff) = (DWORD)tuh_msc_get_block_count(dev_addr, lun);
return RES_OK;
case GET_SECTOR_SIZE:
- *((WORD*) buff) = (WORD) tuh_msc_get_block_size(dev_addr, lun);
+ *((WORD *)buff) = (WORD)tuh_msc_get_block_size(dev_addr, lun);
return RES_OK;
case GET_BLOCK_SIZE:
- *((DWORD*) buff) = 1; // erase block size in units of sector size
+ *((DWORD *)buff) = 1; // erase block size in units of sector size
return RES_OK;
default:
return RES_PARERR;
}
-
- return RES_OK;
}
//--------------------------------------------------------------------+
@@ -293,6 +283,7 @@ DRESULT disk_ioctl (
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context);
@@ -300,12 +291,11 @@ void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context);
void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context);
static void cli_write_char(EmbeddedCli *cli, char c) {
- (void) cli;
- putchar((int) c);
+ (void)cli;
+ putchar((int)c);
}
-bool cli_init(void)
-{
+bool cli_init(void) {
EmbeddedCliConfig *config = embeddedCliDefaultConfig();
config->cliBuffer = cli_buffer;
config->cliBufferSize = CLI_BUFFER_SIZE;
@@ -321,230 +311,239 @@ bool cli_init(void)
_cli->writeChar = cli_write_char;
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "cat",
- "Usage: cat [FILE]...\r\n\tConcatenate FILE(s) to standard output..",
- true,
- NULL,
- cli_cmd_cat
- });
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"cat", "Usage: cat [FILE]...\r\n\tConcatenate FILE(s) to standard output..",
+ true, NULL, cli_cmd_cat});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "cd",
- "Usage: cd [DIR]...\r\n\tChange the current directory to DIR.",
- true,
- NULL,
- cli_cmd_cd
- });
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"cd", "Usage: cd [DIR]...\r\n\tChange the current directory to DIR.",
+ true, NULL, cli_cmd_cd});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "cp",
- "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.",
- true,
- NULL,
- cli_cmd_cp
- });
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
+ cli_cmd_cp});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "ls",
- "Usage: ls [DIR]...\r\n\tList information about the FILEs (the current directory by default).",
- true,
- NULL,
- cli_cmd_ls
- });
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
+ cli_cmd_dd});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "pwd",
- "Usage: pwd\r\n\tPrint the name of the current working directory.",
- true,
- NULL,
- cli_cmd_pwd
- });
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"ls",
+ "Usage: ls [DIR]...\r\n\tList information about the FILEs (the "
+ "current directory by default).",
+ true, NULL, cli_cmd_ls});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "mkdir",
- "Usage: mkdir DIR...\r\n\tCreate the DIRECTORY(ies), if they do not already exist..",
- true,
- NULL,
- cli_cmd_mkdir
- });
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"pwd", "Usage: pwd\r\n\tPrint the name of the current working directory.",
+ true, NULL, cli_cmd_pwd});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "mv",
- "Usage: mv SOURCE DEST...\r\n\tRename SOURCE to DEST.",
- true,
- NULL,
- cli_cmd_mv
- });
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"mkdir",
+ "Usage: mkdir DIR...\r\n\tCreate the DIRECTORY(ies), if they do not "
+ "already exist..",
+ true, NULL, cli_cmd_mkdir});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "rm",
- "Usage: rm [FILE]...\r\n\tRemove (unlink) the FILE(s).",
- true,
- NULL,
- cli_cmd_rm
- });
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"mv", "Usage: mv SOURCE DEST...\r\n\tRename SOURCE to DEST.", true,
+ NULL, cli_cmd_mv});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"rm", "Usage: rm [FILE]...\r\n\tRemove (unlink) the FILE(s).", true,
+ NULL, cli_cmd_rm});
return true;
}
-void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint32_t count = 1024; // default sectors to read
+ if (embeddedCliGetTokenCount(args) >= 1) {
+ count = (uint32_t)atoi(embeddedCliGetToken(args, 1));
+ if (count == 0) {
+ count = 1024;
+ }
+ }
+
+ // find first mounted MSC device
+ uint8_t dev_addr = 0;
+ for (uint8_t i = 1; i <= CFG_TUH_DEVICE_MAX; i++) {
+ if (tuh_msc_mounted(i)) {
+ dev_addr = i;
+ break;
+ }
+ }
+ if (dev_addr == 0) {
+ printf("no MSC device mounted\r\n");
+ return;
+ }
+
+ const uint8_t lun = 0;
+ const uint32_t block_size = tuh_msc_get_block_size(dev_addr, lun);
+ const uint32_t block_count = tuh_msc_get_block_count(dev_addr, lun);
+ if (count > block_count) {
+ count = block_count;
+ }
+
+ const uint16_t sectors_per_xfer = (uint16_t)(sizeof(rw_buf) / block_size);
+ const uint32_t xfer_count = (count + sectors_per_xfer - 1) / sectors_per_xfer;
+
+ printf("dd: reading %" PRIu32 " sectors (%" PRIu32 " bytes), %u sectors/xfer ...\r\n",
+ count, count * block_size, sectors_per_xfer);
+
+ const uint32_t start_ms = tusb_time_millis_api();
+ const uint8_t pdrv = dev_addr - 1;
+
+ for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
+ const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
+ _disk_busy[pdrv] = true;
+ tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
+ }
+
+ const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
+ const uint32_t total_data = count * block_size;
+ // each SCSI transaction has 31-byte CBW + data + 13-byte CSW
+ const uint32_t total_bus = total_data + xfer_count * (31 + 13);
+
+ if (elapsed_ms > 0) {
+ const uint32_t data_kbs = total_data / elapsed_ms; // KB/s (bytes/ms = KB/s)
+ const uint32_t bus_kbs = total_bus / elapsed_ms;
+ printf("dd: %" PRIu32 " bytes in %" PRIu32 " ms = %" PRIu32 " KB/s (bus %" PRIu32 " KB/s)\r\n",
+ total_data, elapsed_ms, data_kbs, bus_kbs);
+ } else {
+ printf("dd: %" PRIu32 " bytes in <1 ms\r\n", total_data);
+ }
+}
+
+void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// need at least 1 argument
- if ( argc == 0 )
- {
+ if (argc == 0) {
printf("invalid arguments\r\n");
return;
}
- for(uint16_t i=0; i<argc; i++)
- {
- FIL fi;
- const char* fpath = embeddedCliGetToken(args, i+1); // token count from 1
+ for (uint16_t i = 0; i < argc; i++) {
+ FIL *fi = &file1;
+ const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
- if ( FR_OK != f_open(&fi, fpath, FA_READ) )
- {
+ if (FR_OK != f_open(fi, fpath, FA_READ)) {
printf("%s: No such file or directory\r\n", fpath);
- }else
- {
- uint8_t buf[512];
+ } else {
UINT count = 0;
- while ( (FR_OK == f_read(&fi, buf, sizeof(buf), &count)) && (count > 0) )
- {
- for(UINT c = 0; c < count; c++)
- {
- const uint8_t ch = buf[c];
- if (isprint(ch) || iscntrl(ch))
- {
+ while ((FR_OK == f_read(fi, rw_buf, sizeof(rw_buf), &count)) && (count > 0)) {
+ for (UINT c = 0; c < count; c++) {
+ const uint8_t ch = rw_buf[c];
+ if (isprint(ch) || iscntrl(ch)) {
putchar(ch);
- }else
- {
+ } else {
putchar('.');
}
}
}
}
- f_close(&fi);
+ f_close(fi);
}
}
-void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// only support 1 argument
- if ( argc != 1 )
- {
+ if (argc != 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* dpath = args;
+ const char *dpath = args;
- if ( FR_OK != f_chdir(dpath) )
- {
+ if (FR_OK != f_chdir(dpath)) {
printf("%s: No such file or directory\r\n", dpath);
return;
}
}
-void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
- if ( argc != 2 )
- {
+ if (argc != 2) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* src = embeddedCliGetToken(args, 1);
- const char* dst = embeddedCliGetToken(args, 2);
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
- FIL f_src;
- FIL f_dst;
+ FIL *f_src = &file1;
+ FIL *f_dst = &file2;
- if ( FR_OK != f_open(&f_src, src, FA_READ) )
- {
+ if (FR_OK != f_open(f_src, src, FA_READ)) {
printf("cannot stat '%s': No such file or directory\r\n", src);
return;
}
- if ( FR_OK != f_open(&f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS) )
- {
+ if (FR_OK != f_open(f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS)) {
printf("cannot create '%s'\r\n", dst);
return;
- }else
- {
- uint8_t buf[512];
+ } else {
UINT rd_count = 0;
- while ( (FR_OK == f_read(&f_src, buf, sizeof(buf), &rd_count)) && (rd_count > 0) )
- {
+ while ((FR_OK == f_read(f_src, rw_buf, sizeof(rw_buf), &rd_count)) && (rd_count > 0)) {
UINT wr_count = 0;
- if ( FR_OK != f_write(&f_dst, buf, rd_count, &wr_count) )
- {
+ if (FR_OK != f_write(f_dst, rw_buf, rd_count, &wr_count)) {
printf("cannot write to '%s'\r\n", dst);
break;
}
}
}
- f_close(&f_src);
- f_close(&f_dst);
+ f_close(f_src);
+ f_close(f_dst);
}
-void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// only support 1 argument
- if ( argc > 1 )
- {
+ if (argc > 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* dpath = ".";
- if (argc) dpath = args;
+ const char *dpath = ".";
+ if (argc) {
+ dpath = args;
+ }
DIR dir;
- if ( FR_OK != f_opendir(&dir, dpath) )
- {
+ if (FR_OK != f_opendir(&dir, dpath)) {
printf("cannot access '%s': No such file or directory\r\n", dpath);
return;
}
FILINFO fno;
- while( (f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0) )
- {
- if ( fno.fname[0] != '.' ) // ignore . and .. entry
+ while ((f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0)) {
+ if (fno.fname[0] != '.') // ignore . and .. entry
{
- if ( fno.fattrib & AM_DIR )
- {
+ if (fno.fattrib & AM_DIR) {
// directory
printf("/%s\r\n", fno.fname);
- }else
- {
+ } else {
printf("%-40s", fno.fname);
- if (fno.fsize < 1024)
- {
+ if (fno.fsize < 1024) {
printf("%" PRIu32 " B\r\n", fno.fsize);
- }else
- {
+ } else {
printf("%" PRIu32 " KB\r\n", fno.fsize / 1024);
}
}
@@ -554,90 +553,81 @@ void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context)
f_closedir(&dir);
}
-void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
- if (argc != 0)
- {
+ if (argc != 0) {
printf("invalid arguments\r\n");
return;
}
char path[256];
- if (FR_OK != f_getcwd(path, sizeof(path)))
- {
+ if (FR_OK != f_getcwd(path, sizeof(path))) {
printf("cannot get current working directory\r\n");
}
puts(path);
}
-void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// only support 1 argument
- if ( argc != 1 )
- {
+ if (argc != 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* dpath = args;
+ const char *dpath = args;
- if ( FR_OK != f_mkdir(dpath) )
- {
+ if (FR_OK != f_mkdir(dpath)) {
printf("%s: cannot create this directory\r\n", dpath);
return;
}
}
-void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
- if ( argc != 2 )
- {
+ if (argc != 2) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* src = embeddedCliGetToken(args, 1);
- const char* dst = embeddedCliGetToken(args, 2);
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
- if ( FR_OK != f_rename(src, dst) )
- {
+ if (FR_OK != f_rename(src, dst)) {
printf("cannot mv %s to %s\r\n", src, dst);
return;
}
}
-void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
// need at least 1 argument
- if ( argc == 0 )
- {
+ if (argc == 0) {
printf("invalid arguments\r\n");
return;
}
- for(uint16_t i=0; i<argc; i++)
- {
- const char* fpath = embeddedCliGetToken(args, i+1); // token count from 1
+ for (uint16_t i = 0; i < argc; i++) {
+ const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
- if ( FR_OK != f_unlink(fpath) )
- {
+ if (FR_OK != f_unlink(fpath)) {
printf("cannot remove '%s': No such file or directory\r\n", fpath);
}
}
diff --git a/examples/host/msc_file_explorer/src/msc_app.h b/examples/host/msc_file_explorer/src/msc_app.h
index 3ba03d0dc..a99da5b5c 100644
--- a/examples/host/msc_file_explorer/src/msc_app.h
+++ b/examples/host/msc_file_explorer/src/msc_app.h
@@ -27,6 +27,7 @@
#define MSC_APP_H
#include <stdbool.h>
+#include <stdio.h>
bool msc_app_init(void);
void msc_app_task(void);
diff --git a/examples/host/msc_file_explorer/src/tusb_config.h b/examples/host/msc_file_explorer/src/tusb_config.h
index c1829c300..a9d24c89f 100644
--- a/examples/host/msc_file_explorer/src/tusb_config.h
+++ b/examples/host/msc_file_explorer/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -118,4 +118,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/msc_file_explorer_freertos/CMakeLists.txt b/examples/host/msc_file_explorer_freertos/CMakeLists.txt
new file mode 100644
index 000000000..4893dd1fb
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(msc_file_explorer_freertos C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
+ ${TOP}/lib/fatfs/source/ff.c
+ ${TOP}/lib/fatfs/source/ffsystem.c
+ ${TOP}/lib/fatfs/source/ffunicode.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${TOP}/lib/fatfs/source
+ ${TOP}/lib/embedded-cli
+ )
+
+# Configure compilation flags and libraries for the example with FreeRTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_host_example(${PROJECT_NAME} freertos)
+
+# Suppress warnings on fatfs
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ set_source_files_properties(${TOP}/lib/fatfs/source/ff.c PROPERTIES
+ COMPILE_OPTIONS "-Wno-conversion;-Wno-cast-qual"
+ )
+endif ()
diff --git a/examples/host/msc_file_explorer_freertos/CMakePresets.json b/examples/host/msc_file_explorer_freertos/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/host/msc_file_explorer_freertos/Makefile b/examples/host/msc_file_explorer_freertos/Makefile
new file mode 100644
index 000000000..15c7420d4
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/Makefile
@@ -0,0 +1,27 @@
+RTOS = freertos
+include ../../../hw/bsp/family_support.mk
+
+FATFS_PATH = lib/fatfs/source
+
+INC += \
+ src \
+ $(TOP)/$(FATFS_PATH) \
+ $(TOP)/lib/embedded-cli \
+
+# Example source
+EXAMPLE_SOURCE = \
+ src/main.c \
+ src/msc_app.c \
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+# FatFS source
+SRC_C += \
+ $(FATFS_PATH)/ff.c \
+ $(FATFS_PATH)/ffsystem.c \
+ $(FATFS_PATH)/ffunicode.c \
+
+# suppress warning caused by fatfs
+CFLAGS += -Wno-error=cast-qual
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/msc_file_explorer_freertos/README.md b/examples/host/msc_file_explorer_freertos/README.md
new file mode 100644
index 000000000..4ee6b96fa
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/README.md
@@ -0,0 +1,105 @@
+# MSC File Explorer (FreeRTOS)
+
+This host example implements an interactive command-line file browser for USB Mass Storage devices.
+When a USB flash drive is connected, the device is automatically mounted using FatFS and a shell-like
+CLI is presented over the board's serial console.
+
+## Features
+
+- Automatic mount/unmount of USB storage devices
+- FAT12/16/32 filesystem support via FatFS
+- Interactive CLI with command history
+- Read speed benchmarking with `dd`
+- Support for up to 4 simultaneous USB storage devices (via hub)
+
+## Supported Commands
+
+| Command | Usage | Description |
+|---------|--------------------|------------------------------------------------------|
+| help | `help` | Print list of available commands |
+| cat | `cat <file>` | Print file contents to the console |
+| cd | `cd <dir>` | Change current working directory |
+| cp | `cp <src> <dest>` | Copy a file |
+| dd | `dd [count]` | Read sectors and report speed (default 1024 sectors) |
+| ls | `ls [dir]` | List directory contents |
+| pwd | `pwd` | Print current working directory |
+| mkdir | `mkdir <dir>` | Create a directory |
+| mv | `mv <src> <dest>` | Rename/move a file or directory |
+| rm | `rm <file>` | Remove a file |
+
+## Build
+
+Build for a specific board using CMake (see [Getting Started](https://docs.tinyusb.org/en/latest/getting_started.html)):
+
+```bash
+# Example: build for STM32F407 Discovery board
+cmake -B build -DBOARD=stm32f407disco -GNinja examples/host/msc_file_explorer_freertos
+cmake --build build
+```
+
+## Usage
+
+1. Flash the firmware to your board.
+2. Open a serial terminal (e.g. `minicom`, `screen`, `PuTTY`) at 115200 baud.
+3. Plug a USB flash drive into the board's USB host port.
+4. The device is auto-mounted and the prompt appears:
+
+```
+TinyUSB MSC File Explorer Example
+
+Device connected
+ Vendor : Kingston
+ Product : DataTraveler 2.0
+ Rev : 1.0
+ Capacity: 1.9 GB
+
+0:/> _
+```
+
+### Browsing Files
+
+```
+0:/> ls
+----a 1234 readme.txt
+d---- 0 photos
+d---- 0 docs
+
+0:/> cd photos
+0:/photos> ls
+----a 520432 vacation.jpg
+----a 312088 family.png
+
+0:/> cat readme.txt
+Hello from USB drive!
+```
+
+### Copying and Moving Files
+
+```
+0:/> cp readme.txt backup.txt
+0:/> mv backup.txt docs/backup.txt
+```
+
+### Measuring Read Speed
+
+```
+0:/> dd
+Reading 1024 sectors...
+ Data speed: 823 KB/s
+```
+
+### Multiple Devices
+
+When using a USB hub, multiple drives are mounted as `0:`, `1:`, etc. Use the drive prefix to
+navigate between them:
+
+```
+0:/> cd 1:
+1:/> ls
+```
+
+## Testing
+
+Build-time validation follows the standard TinyUSB host example flow. Runtime behavior should be
+verified on hardware by attaching an MSC device and exercising CLI commands such as `ls`, `pwd`,
+and `dd`.
diff --git a/examples/host/msc_file_explorer_freertos/only.txt b/examples/host/msc_file_explorer_freertos/only.txt
new file mode 100644
index 000000000..519ac2ebd
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/only.txt
@@ -0,0 +1,28 @@
+family:espressif
+family:samd21
+family:samd5x_e5x
+mcu:LPC175X_6X
+mcu:LPC177X_8X
+mcu:LPC18XX
+mcu:LPC40XX
+mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
+mcu:MIMXRT10XX
+mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
+mcu:MSP432E4
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32F4
+mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
+mcu:STM32H7
+mcu:STM32H7RS
+mcu:STM32N6
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/msc_file_explorer_freertos/skip.txt b/examples/host/msc_file_explorer_freertos/skip.txt
new file mode 100644
index 000000000..a8c9bea2a
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/skip.txt
@@ -0,0 +1,4 @@
+mcu:CH32F20X
+board:lpcxpresso54114
+mcu:FT90X
+board:stm32h7s3nucleo
diff --git a/examples/host/msc_file_explorer_freertos/src/CMakeLists.txt b/examples/host/msc_file_explorer_freertos/src/CMakeLists.txt
new file mode 100644
index 000000000..c3fb35607
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/CMakeLists.txt
@@ -0,0 +1,13 @@
+# This file is for ESP-IDF only
+set(FATFS_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../lib/fatfs/source)
+set(EMBEDDED_CLI_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../lib/embedded-cli)
+
+idf_component_register(
+ SRCS "main.c" "msc_app.c"
+ ${FATFS_DIR}/ff.c
+ ${FATFS_DIR}/ffsystem.c
+ ${FATFS_DIR}/ffunicode.c
+ INCLUDE_DIRS "." ${FATFS_DIR} ${EMBEDDED_CLI_DIR}
+ REQUIRES boards tinyusb_src)
+
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format)
diff --git a/examples/host/msc_file_explorer_freertos/src/ffconf.h b/examples/host/msc_file_explorer_freertos/src/ffconf.h
new file mode 100644
index 000000000..5c89136fe
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/ffconf.h
@@ -0,0 +1,313 @@
+/*---------------------------------------------------------------------------/
+/ Configurations of FatFs Module
+/---------------------------------------------------------------------------*/
+
+#define FFCONF_DEF 80386 /* Revision ID */
+
+/*---------------------------------------------------------------------------/
+/ Function Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_READONLY 0
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
+/ Read-only configuration removes writing API functions, f_write(), f_sync(),
+/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
+/ and optional writing functions as well. */
+
+
+#define FF_FS_MINIMIZE 0
+/* This option defines minimization level to remove some basic API functions.
+/
+/ 0: Basic functions are fully enabled.
+/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
+/ are removed.
+/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/ 3: f_lseek() function is removed in addition to 2. */
+
+
+#define FF_USE_FIND 0
+/* This option switches filtered directory read functions, f_findfirst() and
+/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
+
+
+#define FF_USE_MKFS 0
+/* This option switches f_mkfs(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FASTSEEK 0
+/* This option switches fast seek feature. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_EXPAND 0
+/* This option switches f_expand(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_CHMOD 0
+/* This option switches attribute control API functions, f_chmod() and f_utime().
+/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
+
+
+#define FF_USE_LABEL 0
+/* This option switches volume label API functions, f_getlabel() and f_setlabel().
+/ (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FORWARD 0
+/* This option switches f_forward(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_STRFUNC 0
+#define FF_PRINT_LLI 0
+#define FF_PRINT_FLOAT 0
+#define FF_STRF_ENCODE 0
+/* FF_USE_STRFUNC switches string API functions, f_gets(), f_putc(), f_puts() and
+/ f_printf().
+/
+/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
+/ 1: Enable without LF-CRLF conversion.
+/ 2: Enable with LF-CRLF conversion.
+/
+/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
+/ makes f_printf() support floating point argument. These features want C99 or later.
+/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
+/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
+/ to be read/written via those functions.
+/
+/ 0: ANSI/OEM in current CP
+/ 1: Unicode in UTF-16LE
+/ 2: Unicode in UTF-16BE
+/ 3: Unicode in UTF-8
+*/
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_CODE_PAGE 437
+/* This option specifies the OEM code page to be used on the target system.
+/ Incorrect code page setting can cause a file open failure.
+/
+/ 437 - U.S.
+/ 720 - Arabic
+/ 737 - Greek
+/ 771 - KBL
+/ 775 - Baltic
+/ 850 - Latin 1
+/ 852 - Latin 2
+/ 855 - Cyrillic
+/ 857 - Turkish
+/ 860 - Portuguese
+/ 861 - Icelandic
+/ 862 - Hebrew
+/ 863 - Canadian French
+/ 864 - Arabic
+/ 865 - Nordic
+/ 866 - Russian
+/ 869 - Greek 2
+/ 932 - Japanese (DBCS)
+/ 936 - Simplified Chinese (DBCS)
+/ 949 - Korean (DBCS)
+/ 950 - Traditional Chinese (DBCS)
+/ 0 - Include all code pages above and configured by f_setcp()
+*/
+
+
+#define FF_USE_LFN 1
+#define FF_MAX_LFN 255
+/* The FF_USE_LFN switches the support for LFN (long file name).
+/
+/ 0: Disable LFN. FF_MAX_LFN has no effect.
+/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
+/ 2: Enable LFN with dynamic working buffer on the STACK.
+/ 3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
+/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
+/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
+/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
+/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
+/ specification.
+/ When use stack for the working buffer, take care on stack overflow. When use heap
+/ memory for the working buffer, memory management functions, ff_memalloc() and
+/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
+
+
+#define FF_LFN_UNICODE 0
+/* This option switches the character encoding on the API when LFN is enabled.
+/
+/ 0: ANSI/OEM in current CP (TCHAR = char)
+/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
+/ 2: Unicode in UTF-8 (TCHAR = char)
+/ 3: Unicode in UTF-32 (TCHAR = DWORD)
+/
+/ Also behavior of string I/O functions will be affected by this option.
+/ When LFN is not enabled, this option has no effect. */
+
+
+#define FF_LFN_BUF 255
+#define FF_SFN_BUF 12
+/* This set of options defines size of file name members in the FILINFO structure
+/ which is used to read out directory items. These values should be sufficient for
+/ the file names to read. The maximum possible length of the read file name depends
+/ on character encoding. When LFN is not enabled, these options have no effect. */
+
+
+#define FF_FS_RPATH 2
+/* This option configures support for relative path feature.
+/
+/ 0: Disable relative path and remove related API functions.
+/ 1: Enable relative path and dot names. f_chdir() and f_chdrive() are available.
+/ 2: f_getcwd() is available in addition to 1.
+*/
+
+
+#define FF_PATH_DEPTH 10
+/* This option defines maximum depth of directory in the exFAT volume. It is NOT
+/ relevant to FAT/FAT32 volume.
+/ For example, FF_PATH_DEPTH = 3 will able to follow a path "/dir1/dir2/dir3/file"
+/ but a sub-directory in the dir3 will not able to be followed and set current
+/ directory.
+/ The size of filesystem object (FATFS) increases FF_PATH_DEPTH * 24 bytes.
+/ When FF_FS_EXFAT == 0 or FF_FS_RPATH == 0, this option has no effect.
+*/
+
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_VOLUMES 4
+/* Number of volumes (logical drives) to be used. (1-10) */
+
+
+#define FF_STR_VOLUME_ID 0
+#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
+/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
+/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
+/ logical drive. Number of items must not be less than FF_VOLUMES. Valid
+/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
+/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
+/ not defined, a user defined volume string table is needed as:
+/
+/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
+*/
+
+
+#define FF_MULTI_PARTITION 0
+/* This option switches support for multiple volumes on the physical drive.
+/ By default (0), each logical drive number is bound to the same physical drive
+/ number and only an FAT volume found on the physical drive will be mounted.
+/ When this feature is enabled (1), each logical drive number can be bound to
+/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
+/ will be available. */
+
+
+#define FF_MIN_SS 512
+#define FF_MAX_SS 512
+/* This set of options configures the range of sector size to be supported. (512,
+/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
+/ harddisk, but a larger value may be required for on-board flash memory and some
+/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
+/ configured for variable sector size mode and disk_ioctl() needs to implement
+/ GET_SECTOR_SIZE command. */
+
+
+#define FF_LBA64 0
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
+/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
+
+
+#define FF_MIN_GPT 0x10000000
+/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
+/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */
+
+
+#define FF_USE_TRIM 0
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
+/ To enable this feature, also CTRL_TRIM command should be implemented to
+/ the disk_ioctl(). */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_TINY 0
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
+/ At the tiny configuration, size of file object (FIL) is reduced FF_MAX_SS bytes.
+/ Instead of private sector buffer eliminated from the file object, common sector
+/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
+
+
+#define FF_FS_EXFAT 0
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
+/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
+/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
+
+
+#define FF_FS_NORTC 1
+#define FF_NORTC_MON 1
+#define FF_NORTC_MDAY 1
+#define FF_NORTC_YEAR 2025
+/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
+/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
+/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
+/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
+/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
+/ to the project to read current time form real-time clock. FF_NORTC_MON,
+/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
+/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
+
+
+#define FF_FS_CRTIME 0
+/* This option enables(1)/disables(0) the timestamp of the file created. When
+/ set 1, the file created time is available in FILINFO structure. */
+
+
+#define FF_FS_NOFSINFO 0
+/* If you need to know the correct free space on the FAT32 volume, set bit 0 of
+/ this option, and f_getfree() on the first time after volume mount will force
+/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
+/
+/ bit0=0: Use free cluster count in the FSINFO if available.
+/ bit0=1: Do not trust free cluster count in the FSINFO.
+/ bit1=0: Use last allocated cluster number in the FSINFO if available.
+/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+#define FF_FS_LOCK 0
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
+/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
+/ is 1.
+/
+/ 0: Disable file lock function. To avoid volume corruption, application program
+/ should avoid illegal open, remove and rename to the open objects.
+/ >0: Enable file lock function. The value defines how many files/sub-directories
+/ can be opened simultaneously under file lock control. Note that the file
+/ lock control is independent of re-entrancy. */
+
+
+#define FF_FS_REENTRANT 0
+#define FF_FS_TIMEOUT 1000
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
+/ module itself. Note that regardless of this option, file access to different
+/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
+/ and f_fdisk(), are always not re-entrant. Only file/directory access to
+/ the same volume is under control of this featuer.
+/
+/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
+/ 1: Enable re-entrancy. Also user provided synchronization handlers,
+/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
+/ must be added to the project. Samples are available in ffsystem.c.
+/
+/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
+*/
+
+
+
+/*--- End of configuration options ---*/
diff --git a/examples/host/msc_file_explorer_freertos/src/main.c b/examples/host/msc_file_explorer_freertos/src/main.c
new file mode 100644
index 000000000..d1e627f4f
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/main.c
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ *
+ */
+
+#include <string.h>
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+#ifdef ESP_PLATFORM
+ // ESP-IDF need "freertos/" prefix in include path.
+ // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+ #include "freertos/FreeRTOS.h"
+ #include "freertos/task.h"
+ #include "freertos/timers.h"
+#else
+ #include "FreeRTOS.h"
+ #include "task.h"
+ #include "timers.h"
+#endif
+
+#include "msc_app.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+#ifdef ESP_PLATFORM
+ #define USBH_STACK_SIZE 4096
+#else
+ // Increase stack size when debug log is enabled.
+ #define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 3))
+#endif
+
+enum {
+ BLINK_MOUNTED = 1000,
+};
+
+#if configSUPPORT_STATIC_ALLOCATION
+StaticTimer_t blinky_tmdef;
+
+StackType_t usb_host_stack[USBH_STACK_SIZE];
+StaticTask_t usb_host_taskdef;
+#endif
+
+TimerHandle_t blinky_tm;
+
+static void led_blinky_cb(TimerHandle_t xTimer);
+static void usb_host_task(void* param);
+
+/*------------- MAIN -------------*/
+int main(void) {
+ board_init();
+
+ printf("TinyUSB Host MassStorage Explorer FreeRTOS Example\r\n");
+
+ // Create soft timer for blinky and task for TinyUSB host stack.
+#if configSUPPORT_STATIC_ALLOCATION
+ blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
+ xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, usb_host_stack,
+ &usb_host_taskdef);
+#else
+ blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb);
+ xTaskCreate(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+#endif
+
+ xTimerStart(blinky_tm, 0);
+
+ // only start scheduler for non-espressif mcu
+#ifndef ESP_PLATFORM
+ vTaskStartScheduler();
+#endif
+
+ return 0;
+}
+
+#ifdef ESP_PLATFORM
+void app_main(void) {
+ main();
+}
+#endif
+
+// USB Host task
+// This top-level thread processes all USB events and invokes callbacks.
+static void usb_host_task(void* param) {
+ (void) param;
+
+ // init host stack on configured roothub port
+ tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUSB_SPEED_AUTO
+ };
+
+ if (!tusb_init(BOARD_TUH_RHPORT, &host_init)) {
+ printf("Failed to init USB Host Stack\r\n");
+ vTaskSuspend(NULL);
+ }
+
+ board_init_after_tusb();
+
+#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
+ // FeatherWing MAX3421E uses MAX3421E GPIO0 for VBUS enable.
+ enum { IOPINS1_ADDR = 20u << 3 };
+ tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
+#endif
+
+ if (!msc_app_init()) {
+ printf("Failed to init MSC app\r\n");
+ vTaskSuspend(NULL);
+ }
+
+ while (1) {
+ // TinyUSB host task.
+ tuh_task();
+ }
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+void tuh_mount_cb(uint8_t dev_addr) {
+ (void) dev_addr;
+}
+
+void tuh_umount_cb(uint8_t dev_addr) {
+ (void) dev_addr;
+}
+
+//--------------------------------------------------------------------+
+// Blinking Task
+//--------------------------------------------------------------------+
+static void led_blinky_cb(TimerHandle_t xTimer) {
+ (void) xTimer;
+ static bool led_state = false;
+
+ board_led_write(led_state);
+ led_state = 1 - led_state; // toggle
+}
diff --git a/examples/host/msc_file_explorer_freertos/src/msc_app.c b/examples/host/msc_file_explorer_freertos/src/msc_app.c
new file mode 100644
index 000000000..c7e00e52a
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/msc_app.c
@@ -0,0 +1,709 @@
+/*
+ * 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.
+ *
+ */
+
+#include <ctype.h>
+#include "tusb.h"
+#include "bsp/board_api.h"
+#ifdef ESP_PLATFORM
+ // ESP-IDF need "freertos/" prefix in include path.
+ // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+ #include "freertos/FreeRTOS.h"
+ #include "freertos/task.h"
+ #include "freertos/timers.h"
+#else
+ #include "FreeRTOS.h"
+ #include "task.h"
+ #include "timers.h"
+#endif
+
+#include "ff.h"
+#include "diskio.h"
+
+// lib/embedded-cli
+#define EMBEDDED_CLI_IMPL
+#include "embedded_cli.h"
+
+#include "msc_app.h"
+
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+//------------- embedded-cli -------------//
+#define CLI_BUFFER_SIZE 512
+#define CLI_RX_BUFFER_SIZE 16
+#define CLI_CMD_BUFFER_SIZE 64
+#define CLI_HISTORY_SIZE 32
+#define CLI_BINDING_COUNT 9
+
+#ifdef ESP_PLATFORM
+ #define MSC_APP_STACK_SIZE 4096
+#else
+ #define MSC_APP_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
+#endif
+
+static EmbeddedCli *_cli;
+static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
+
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t msc_app_stack[MSC_APP_STACK_SIZE];
+StaticTask_t msc_app_taskdef;
+#endif
+
+//------------- Elm Chan FatFS -------------//
+static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
+static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
+static volatile bool _mount_pending[CFG_TUH_DEVICE_MAX];
+
+static CFG_TUH_MEM_SECTION FIL file1, file2;
+
+#ifndef CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE
+#define CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE 4096
+#endif
+static CFG_TUH_MEM_SECTION uint8_t rw_buf[CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE];
+
+// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
+CFG_TUH_MEM_SECTION static struct {
+ TUH_EPBUF_TYPE_DEF(scsi_inquiry_resp_t, inquiry);
+} scsi_resp;
+
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+static bool cli_init(void);
+static void msc_app_task(void* param);
+static void process_pending_mount(void);
+
+bool msc_app_init(void) {
+ for (size_t i = 0; i < CFG_TUH_DEVICE_MAX; i++) {
+ _disk_busy[i] = false;
+ _mount_pending[i] = false;
+ }
+
+// disable stdout buffered for echoing typing command
+#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
+ setbuf(stdout, NULL);
+#endif
+
+ cli_init();
+
+#if configSUPPORT_STATIC_ALLOCATION
+ TaskHandle_t task_hdl = xTaskCreateStatic(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL,
+ configMAX_PRIORITIES - 2, msc_app_stack, &msc_app_taskdef);
+ TU_ASSERT(task_hdl != NULL);
+#else
+ TU_ASSERT(xTaskCreate(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL) == pdPASS);
+#endif
+
+ return true;
+}
+
+static void msc_app_task(void* param) {
+ (void) param;
+
+ while (1) {
+ process_pending_mount();
+
+ if (!_cli) {
+ vTaskDelay(1);
+ continue;
+ }
+
+ int ch = board_getchar();
+ if (ch > 0) {
+ while (ch > 0) {
+ embeddedCliReceiveChar(_cli, (char) ch);
+ ch = board_getchar();
+ }
+ embeddedCliProcess(_cli);
+ }
+
+ vTaskDelay(1);
+ }
+}
+
+static void process_pending_mount(void) {
+ for (uint8_t drive_num = 0; drive_num < CFG_TUH_DEVICE_MAX; drive_num++) {
+ if (!_mount_pending[drive_num]) {
+ continue;
+ }
+
+ _mount_pending[drive_num] = false;
+
+ const uint8_t dev_addr = drive_num + 1;
+ if (!tuh_msc_mounted(dev_addr)) {
+ continue;
+ }
+
+ char drive_path[3] = "0:";
+ drive_path[0] += drive_num;
+
+ if (f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK) {
+ printf("mount failed\r\n");
+ continue;
+ }
+
+ f_chdrive(drive_path);
+ FRESULT rc = f_chdir("/");
+ if (rc != FR_OK) {
+ printf("chdir failed: %d\r\n", rc);
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
+ const msc_cbw_t *cbw = cb_data->cbw;
+ const msc_csw_t *csw = cb_data->csw;
+
+ if (csw->status != 0) {
+ printf("Inquiry failed\r\n");
+ return false;
+ }
+
+ // Print out Vendor ID, Product ID and Rev
+ printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id,
+ scsi_resp.inquiry.product_rev);
+
+ // Get capacity of device
+ const uint32_t block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
+ const uint32_t block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
+
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
+
+ // For simplicity: we only mount 1 LUN per device
+ const uint8_t drive_num = dev_addr - 1;
+ _mount_pending[drive_num] = true;
+
+ // print the drive label
+ // char label[34];
+ // if ( FR_OK == f_getlabel(drive_path, label, NULL) )
+ // {
+ // puts(label);
+ // }
+
+ return true;
+}
+
+//------------- IMPLEMENTATION -------------//
+void tuh_msc_mount_cb(uint8_t dev_addr) {
+ printf("A MassStorage device (addr = %u) is mounted\r\n", dev_addr);
+
+ const uint8_t lun = 0;
+ tuh_msc_inquiry(dev_addr, lun, &scsi_resp.inquiry, inquiry_complete_cb, 0);
+}
+
+void tuh_msc_umount_cb(uint8_t dev_addr) {
+ printf("A MassStorage device is unmounted\r\n");
+
+ const uint8_t drive_num = dev_addr - 1;
+ char drive_path[3] = "0:";
+ drive_path[0] += drive_num;
+
+ _mount_pending[drive_num] = false;
+
+ f_unmount(drive_path);
+
+ // if ( phy_disk == f_get_current_drive() )
+ // { // active drive is unplugged --> change to other drive
+ // for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
+ // {
+ // if ( disk_is_ready(i) )
+ // {
+ // f_chdrive(i);
+ // cli_init(); // refractor, rename
+ // }
+ // }
+ // }
+}
+
+//--------------------------------------------------------------------+
+// DiskIO
+//--------------------------------------------------------------------+
+
+static void wait_for_disk_io(BYTE pdrv) {
+ while (_disk_busy[pdrv]) {
+ vTaskDelay(1);
+ }
+}
+
+static bool disk_io_complete(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
+ (void)dev_addr;
+ (void)cb_data;
+ _disk_busy[dev_addr - 1] = false;
+ return true;
+}
+
+DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
+) {
+ uint8_t dev_addr = pdrv + 1;
+ return tuh_msc_mounted(dev_addr) ? 0 : STA_NODISK;
+}
+
+DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
+) {
+ (void)pdrv;
+ return 0; // nothing to do
+}
+
+DRESULT disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ BYTE *buff, /* Data buffer to store read data */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to read */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+
+ _disk_busy[pdrv] = true;
+ tuh_msc_read10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
+
+ return RES_OK;
+}
+
+#if FF_FS_READONLY == 0
+
+DRESULT disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ const BYTE *buff, /* Data to be written */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to write */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+
+ _disk_busy[pdrv] = true;
+ tuh_msc_write10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
+
+ return RES_OK;
+}
+
+#endif
+
+DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
+ BYTE cmd, /* Control code */
+ void *buff /* Buffer to send/receive control data */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+ switch (cmd) {
+ case CTRL_SYNC:
+ // nothing to do since we do blocking
+ return RES_OK;
+
+ case GET_SECTOR_COUNT:
+ *((DWORD *)buff) = (DWORD)tuh_msc_get_block_count(dev_addr, lun);
+ return RES_OK;
+
+ case GET_SECTOR_SIZE:
+ *((WORD *)buff) = (WORD)tuh_msc_get_block_size(dev_addr, lun);
+ return RES_OK;
+
+ case GET_BLOCK_SIZE:
+ *((DWORD *)buff) = 1; // erase block size in units of sector size
+ return RES_OK;
+
+ default:
+ return RES_PARERR;
+ }
+}
+
+//--------------------------------------------------------------------+
+// CLI Commands
+//--------------------------------------------------------------------+
+
+void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context);
+
+static void cli_write_char(EmbeddedCli *cli, char c) {
+ (void)cli;
+ putchar((int)c);
+}
+
+bool cli_init(void) {
+ EmbeddedCliConfig *config = embeddedCliDefaultConfig();
+ config->cliBuffer = cli_buffer;
+ config->cliBufferSize = CLI_BUFFER_SIZE;
+ config->rxBufferSize = CLI_RX_BUFFER_SIZE;
+ config->cmdBufferSize = CLI_CMD_BUFFER_SIZE;
+ config->historyBufferSize = CLI_HISTORY_SIZE;
+ config->maxBindingCount = CLI_BINDING_COUNT;
+
+ TU_ASSERT(embeddedCliRequiredSize(config) <= CLI_BUFFER_SIZE);
+
+ _cli = embeddedCliNew(config);
+ TU_ASSERT(_cli != NULL);
+
+ _cli->writeChar = cli_write_char;
+
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"cat", "Usage: cat [FILE]...\r\n\tConcatenate FILE(s) to standard output..",
+ true, NULL, cli_cmd_cat});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"cd", "Usage: cd [DIR]...\r\n\tChange the current directory to DIR.",
+ true, NULL, cli_cmd_cd});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
+ cli_cmd_cp});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
+ cli_cmd_dd});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"ls",
+ "Usage: ls [DIR]...\r\n\tList information about the FILEs (the "
+ "current directory by default).",
+ true, NULL, cli_cmd_ls});
+
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"pwd", "Usage: pwd\r\n\tPrint the name of the current working directory.",
+ true, NULL, cli_cmd_pwd});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"mkdir",
+ "Usage: mkdir DIR...\r\n\tCreate the DIRECTORY(ies), if they do not "
+ "already exist..",
+ true, NULL, cli_cmd_mkdir});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"mv", "Usage: mv SOURCE DEST...\r\n\tRename SOURCE to DEST.", true,
+ NULL, cli_cmd_mv});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"rm", "Usage: rm [FILE]...\r\n\tRemove (unlink) the FILE(s).", true,
+ NULL, cli_cmd_rm});
+
+ return true;
+}
+
+void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint32_t count = 1024; // default sectors to read
+ if (embeddedCliGetTokenCount(args) >= 1) {
+ count = (uint32_t)atoi(embeddedCliGetToken(args, 1));
+ if (count == 0) {
+ count = 1024;
+ }
+ }
+
+ // find first mounted MSC device
+ uint8_t dev_addr = 0;
+ for (uint8_t i = 1; i <= CFG_TUH_DEVICE_MAX; i++) {
+ if (tuh_msc_mounted(i)) {
+ dev_addr = i;
+ break;
+ }
+ }
+ if (dev_addr == 0) {
+ printf("no MSC device mounted\r\n");
+ return;
+ }
+
+ const uint8_t lun = 0;
+ const uint32_t block_size = tuh_msc_get_block_size(dev_addr, lun);
+ const uint32_t block_count = tuh_msc_get_block_count(dev_addr, lun);
+ if (count > block_count) {
+ count = block_count;
+ }
+
+ const uint16_t sectors_per_xfer = (uint16_t)(sizeof(rw_buf) / block_size);
+ const uint32_t xfer_count = (count + sectors_per_xfer - 1) / sectors_per_xfer;
+
+ printf("dd: reading %" PRIu32 " sectors (%" PRIu32 " bytes), %u sectors/xfer ...\r\n",
+ count, count * block_size, sectors_per_xfer);
+
+ const uint32_t start_ms = tusb_time_millis_api();
+ const uint8_t pdrv = dev_addr - 1;
+ bool submit_failed = false;
+
+ for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
+ const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
+ _disk_busy[pdrv] = true;
+
+ if (!tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0)) {
+ _disk_busy[pdrv] = false;
+ printf("dd: failed to submit read at sector %" PRIu32 " (%u sectors)\r\n", i, n);
+ submit_failed = true;
+ break;
+ }
+
+ wait_for_disk_io(pdrv);
+ }
+
+ if (submit_failed) {
+ return;
+ }
+
+ const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
+ const uint32_t total_data = count * block_size;
+ // each SCSI transaction has 31-byte CBW + data + 13-byte CSW
+ const uint32_t total_bus = total_data + xfer_count * (31 + 13);
+
+ if (elapsed_ms > 0) {
+ const uint32_t data_kbs = total_data / elapsed_ms; // KB/s (bytes/ms = KB/s)
+ const uint32_t bus_kbs = total_bus / elapsed_ms;
+ printf("dd: %" PRIu32 " bytes in %" PRIu32 " ms = %" PRIu32 " KB/s (bus %" PRIu32 " KB/s)\r\n",
+ total_data, elapsed_ms, data_kbs, bus_kbs);
+ } else {
+ printf("dd: %" PRIu32 " bytes in <1 ms\r\n", total_data);
+ }
+}
+
+void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // need at least 1 argument
+ if (argc == 0) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ for (uint16_t i = 0; i < argc; i++) {
+ FIL *fi = &file1;
+ const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
+
+ if (FR_OK != f_open(fi, fpath, FA_READ)) {
+ printf("%s: No such file or directory\r\n", fpath);
+ } else {
+ UINT count = 0;
+ while ((FR_OK == f_read(fi, rw_buf, sizeof(rw_buf), &count)) && (count > 0)) {
+ for (UINT c = 0; c < count; c++) {
+ const uint8_t ch = rw_buf[c];
+ if (isprint(ch) || iscntrl(ch)) {
+ putchar(ch);
+ } else {
+ putchar('.');
+ }
+ }
+ }
+ }
+
+ f_close(fi);
+ }
+}
+
+void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // only support 1 argument
+ if (argc != 1) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *dpath = args;
+
+ if (FR_OK != f_chdir(dpath)) {
+ printf("%s: No such file or directory\r\n", dpath);
+ return;
+ }
+}
+
+void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+ if (argc != 2) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
+
+ FIL *f_src = &file1;
+ FIL *f_dst = &file2;
+
+ if (FR_OK != f_open(f_src, src, FA_READ)) {
+ printf("cannot stat '%s': No such file or directory\r\n", src);
+ return;
+ }
+
+ if (FR_OK != f_open(f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS)) {
+ printf("cannot create '%s'\r\n", dst);
+ f_close(f_src);
+ return;
+ } else {
+ UINT rd_count = 0;
+ while ((FR_OK == f_read(f_src, rw_buf, sizeof(rw_buf), &rd_count)) && (rd_count > 0)) {
+ UINT wr_count = 0;
+
+ if (FR_OK != f_write(f_dst, rw_buf, rd_count, &wr_count)) {
+ printf("cannot write to '%s'\r\n", dst);
+ break;
+ }
+ }
+ }
+
+ f_close(f_src);
+ f_close(f_dst);
+}
+
+void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // only support 1 argument
+ if (argc > 1) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *dpath = ".";
+ if (argc) {
+ dpath = args;
+ }
+
+ DIR dir;
+ if (FR_OK != f_opendir(&dir, dpath)) {
+ printf("cannot access '%s': No such file or directory\r\n", dpath);
+ return;
+ }
+
+ FILINFO fno;
+ while ((f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0)) {
+ if (fno.fname[0] != '.') // ignore . and .. entry
+ {
+ if (fno.fattrib & AM_DIR) {
+ // directory
+ printf("/%s\r\n", fno.fname);
+ } else {
+ printf("%-40s", fno.fname);
+ if (fno.fsize < 1024) {
+ printf("%" PRIu32 " B\r\n", fno.fsize);
+ } else {
+ printf("%" PRIu32 " KB\r\n", fno.fsize / 1024);
+ }
+ }
+ }
+ }
+
+ f_closedir(&dir);
+}
+
+void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ if (argc != 0) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ char path[256];
+ if (FR_OK != f_getcwd(path, sizeof(path))) {
+ printf("cannot get current working directory\r\n");
+ return;
+ }
+
+ puts(path);
+}
+
+void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // only support 1 argument
+ if (argc != 1) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *dpath = args;
+
+ if (FR_OK != f_mkdir(dpath)) {
+ printf("%s: cannot create this directory\r\n", dpath);
+ return;
+ }
+}
+
+void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+ if (argc != 2) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
+
+ if (FR_OK != f_rename(src, dst)) {
+ printf("cannot mv %s to %s\r\n", src, dst);
+ return;
+ }
+}
+
+void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // need at least 1 argument
+ if (argc == 0) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ for (uint16_t i = 0; i < argc; i++) {
+ const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
+
+ if (FR_OK != f_unlink(fpath)) {
+ printf("cannot remove '%s': No such file or directory\r\n", fpath);
+ }
+ }
+}
diff --git a/examples/host/msc_file_explorer_freertos/src/msc_app.h b/examples/host/msc_file_explorer_freertos/src/msc_app.h
new file mode 100644
index 000000000..eff195b1a
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/msc_app.h
@@ -0,0 +1,34 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 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.
+ */
+#ifndef MSC_APP_H
+#define MSC_APP_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+bool msc_app_init(void);
+
+#endif
diff --git a/examples/host/msc_file_explorer_freertos/src/tusb_config.h b/examples/host/msc_file_explorer_freertos/src/tusb_config.h
new file mode 100644
index 000000000..c3fc4624f
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/tusb_config.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_FREERTOS
+#endif
+
+// Espressif IDF requires "freertos/" prefix in include path
+#ifdef ESP_PLATFORM
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+// #define CFG_TUH_MAX3421 1 // use max3421 as host controller
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB 1 // number of supported hubs
+#define CFG_TUH_MSC 1
+#define CFG_TUH_CDC 0
+#define CFG_TUH_HID 0 // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_VENDOR 0
+
+// max device support (excluding hub device): 1 hub typically has 4 ports
+#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
+
+//------------- MSC -------------//
+#define CFG_TUH_MSC_MAXLUN 4 // typical for most card reader
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/typec/power_delivery/CMakeLists.txt b/examples/typec/power_delivery/CMakeLists.txt
index 837b4996a..728221eb4 100644
--- a/examples/typec/power_delivery/CMakeLists.txt
+++ b/examples/typec/power_delivery/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(power_delivery C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
# Espressif has its own cmake build system
if(FAMILY STREQUAL "espressif")
return()
endif()
-add_executable(${PROJECT})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT} noos)
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/typec/power_delivery/Makefile b/examples/typec/power_delivery/Makefile
index 7fa475da5..7f65c689a 100644
--- a/examples/typec/power_delivery/Makefile
+++ b/examples/typec/power_delivery/Makefile
@@ -1,11 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
-EXAMPLE_SOURCE += $(wildcard src/*.c)
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+EXAMPLE_SOURCE += \
+ src/main.c
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/typec/power_delivery/src/main.c b/examples/typec/power_delivery/src/main.c
index de0db4721..f6191bfe8 100644
--- a/examples/typec/power_delivery/src/main.c
+++ b/examples/typec/power_delivery/src/main.c
@@ -184,7 +184,7 @@ void led_blinking_task(void)
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
diff --git a/examples/typec/power_delivery/src/tusb_config.h b/examples/typec/power_delivery/src/tusb_config.h
index f7cb3cc04..ee013d056 100644
--- a/examples/typec/power_delivery/src/tusb_config.h
+++ b/examples/typec/power_delivery/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -80,4 +80,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */