summaryrefslogtreecommitdiff
path: root/sw/Core/Src/tinyusb
diff options
context:
space:
mode:
Diffstat (limited to 'sw/Core/Src/tinyusb')
-rwxr-xr-xsw/Core/Src/tinyusb/.gitattributes25
-rwxr-xr-xsw/Core/Src/tinyusb/.gitignore27
-rwxr-xr-xsw/Core/Src/tinyusb/CODE_OF_CONDUCT.md76
-rwxr-xr-xsw/Core/Src/tinyusb/CONTRIBUTORS.md89
-rwxr-xr-xsw/Core/Src/tinyusb/LICENSE21
-rwxr-xr-xsw/Core/Src/tinyusb/README.md109
-rwxr-xr-xsw/Core/Src/tinyusb/changelog.md227
-rwxr-xr-xsw/Core/Src/tinyusb/pkg.yml14
-rwxr-xr-xsw/Core/Src/tinyusb/repository.yml12
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/audio/audio.h934
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/audio/audio_device.c1462
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/audio/audio_device.h400
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/bth/bth_device.c255
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/bth/bth_device.h109
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc.h405
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_device.c484
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_device.h261
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_host.c245
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_host.h136
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_rndis.h301
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.c279
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.h63
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.c139
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.h76
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/hid/hid.h1023
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/hid/hid_device.c386
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/hid/hid_device.h379
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/hid/hid_host.c319
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/hid/hid_host.h210
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/midi/midi.h212
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/midi/midi_device.c536
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/midi/midi_device.h176
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/msc/msc.h392
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/msc/msc_device.c709
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/msc/msc_device.h171
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/msc/msc_host.c483
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/msc/msc_host.h131
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/net/net_device.c439
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/net/net_device.h87
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/usbtmc/usbtmc.h316
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.c864
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.h122
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/vendor/vendor_device.c237
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/vendor/vendor_device.h130
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/vendor/vendor_host.c146
-rwxr-xr-xsw/Core/Src/tinyusb/src/class/vendor/vendor_host.h74
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_common.h300
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_compiler.h162
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_error.h75
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_fifo.c669
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_fifo.h141
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_timeout.h80
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_types.h498
-rwxr-xr-xsw/Core/Src/tinyusb/src/common/tusb_verify.h183
-rwxr-xr-xsw/Core/Src/tinyusb/src/device/dcd.h167
-rwxr-xr-xsw/Core/Src/tinyusb/src/device/usbd.c1300
-rwxr-xr-xsw/Core/Src/tinyusb/src/device/usbd.h679
-rwxr-xr-xsw/Core/Src/tinyusb/src/device/usbd_control.c232
-rwxr-xr-xsw/Core/Src/tinyusb/src/device/usbd_pvt.h112
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/hcd.h168
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/hub.c361
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/hub.h195
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/usbh.c965
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/usbh.h130
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/usbh_control.c140
-rwxr-xr-xsw/Core/Src/tinyusb/src/host/usbh_hcd.h112
-rwxr-xr-xsw/Core/Src/tinyusb/src/osal/osal.h104
-rwxr-xr-xsw/Core/Src/tinyusb/src/osal/osal_freertos.h172
-rwxr-xr-xsw/Core/Src/tinyusb/src/osal/osal_mynewt.h174
-rwxr-xr-xsw/Core/Src/tinyusb/src/osal/osal_none.h204
-rwxr-xr-xsw/Core/Src/tinyusb/src/osal/osal_pico.h185
-rwxr-xr-xsw/Core/Src/tinyusb/src/osal/osal_rtthread.h130
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/dialog/da146xx/dcd_da146xx.c1085
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/ehci/ehci.c901
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/ehci/ehci.h460
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/espressif/esp32s2/dcd_esp32s2.c793
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/microchip/samd/dcd_samd.c409
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/microchip/samg/dcd_samg.c456
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c981
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nuvoton/nuc120/dcd_nuc120.c434
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nuvoton/nuc121/dcd_nuc121.c451
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nuvoton/nuc505/dcd_nuc505.c657
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/khci/dcd_khci.c477
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.c582
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.h152
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/hcd_lpc17_40.c47
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c415
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/transdimension/common_transdimension.h136
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c491
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/nxp/transdimension/hcd_transdimension.c127
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/ohci/ohci.c688
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/ohci/ohci.h291
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c532
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/hcd_rp2040.c553
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c294
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.h122
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/renesas/usba/dcd_usba.c736
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/silabs/efm32/dcd_efm32.c931
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/sony/cxd56/dcd_cxd56.c409
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c959
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h407
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/st/synopsys/dcd_synopsys.c1106
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/template/dcd_template.c121
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c642
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.c643
-rwxr-xr-xsw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.h39
-rwxr-xr-xsw/Core/Src/tinyusb/src/tusb.c150
-rwxr-xr-xsw/Core/Src/tinyusb/src/tusb.h132
-rwxr-xr-xsw/Core/Src/tinyusb/src/tusb_config.h120
-rwxr-xr-xsw/Core/Src/tinyusb/src/tusb_option.h290
-rwxr-xr-xsw/Core/Src/tinyusb/src/usb_descriptors.c267
-rwxr-xr-xsw/Core/Src/tinyusb/src/usbmidi.c385
-rwxr-xr-xsw/Core/Src/tinyusb/tinyusb.Doxyfile2384
-rwxr-xr-xsw/Core/Src/tinyusb/version.yml3
114 files changed, 42587 insertions, 0 deletions
diff --git a/sw/Core/Src/tinyusb/.gitattributes b/sw/Core/Src/tinyusb/.gitattributes
new file mode 100755
index 0000000..2342dec
--- /dev/null
+++ b/sw/Core/Src/tinyusb/.gitattributes
@@ -0,0 +1,25 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+*.c text
+*.cpp text
+*.h text
+*.icf text
+*.js text
+*.json text
+*.ld text
+*.md text
+*.mk text
+*.py text
+*.rst text
+*.s text
+*.txt text
+*.xml text
+*.yml text
+
+Makefile text
+
+# Windows-only Visual Studio things
+
+*.sln text eol=crlf
+*.csproj text eol=crlf
diff --git a/sw/Core/Src/tinyusb/.gitignore b/sw/Core/Src/tinyusb/.gitignore
new file mode 100755
index 0000000..093bcc5
--- /dev/null
+++ b/sw/Core/Src/tinyusb/.gitignore
@@ -0,0 +1,27 @@
+html
+latex
+*.d
+*.o
+*.P
+*.map
+*.axf
+*.bin
+*.jlink
+*.emSession
+*.elf
+*.ind
+.env
+.settings/
+.idea/
+/examples/*/*/build*
+test_old/
+tests_obsolete/
+_build
+/examples/*/*/ses
+/examples/*/*/ozone
+/examples/obsolete
+# coverity intermediate files
+cov-int
+# cppcheck build directories
+*-build-dir
+/_bin/
diff --git a/sw/Core/Src/tinyusb/CODE_OF_CONDUCT.md b/sw/Core/Src/tinyusb/CODE_OF_CONDUCT.md
new file mode 100755
index 0000000..0da6ff6
--- /dev/null
+++ b/sw/Core/Src/tinyusb/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at thach@tinyusb.org. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/sw/Core/Src/tinyusb/CONTRIBUTORS.md b/sw/Core/Src/tinyusb/CONTRIBUTORS.md
new file mode 100755
index 0000000..5ce352f
--- /dev/null
+++ b/sw/Core/Src/tinyusb/CONTRIBUTORS.md
@@ -0,0 +1,89 @@
+# TinyUSB contributors (sorted alphabetically)
+
+- **[Adafruit Team](https://github.com/adafruit)**
+ - Main supporter and sponsor for hardware boards and kits
+ - Discussion and suggestion for feature and improvement
+ - Design the project logo
+
+- **[Ha Thach](https://github.com/hathach)**
+ - *Author and maintainer*
+ - Most features development
+
+- **[Hristo Gochkov](https://github.com/me-no-dev)**
+ - Improve ESP32s2 DCD
+
+- **[Jan Dümpelmann](https://github.com/duempel)**
+ - Improve transfer performance for Synopsys DCD for STM32 MCUs
+
+- **[Jeff Epler](https://github.com/jepler)**
+ - Improve MIDI class driver
+
+- **[Jerzy Kasenberg](https://github.com/kasjer)**
+ - Add new DCD port for **Dialog DA1469x**
+ - Add new class driver for **Bluetooth HCI**
+ - Add ISO transfer for STM32 Synopsys, Nordic nRF, Dialog DA1469x
+ - Improve Audio driver and add uac2_headset example
+ - Improve STM32 Synopsys DCD with various PRs
+
+- **[Kamil Tomaszewski](https://github.com/kamtom480)**
+ - Add new DCD port for **Sony CXD56** (spresnese board)
+
+- **[Kay Sievers](https://github.com/kaysievers)**
+ - Improve MIDI driver with packet API
+
+- **[Koji KITAYAMA](https://github.com/kkitayam)**
+ - Add new DCD port for **NXP Kinetis KL25**
+ - Add new DCD port for **Renesas RX63n** with GR-CITRUS board
+
+- **[Nathan Conrad](https://github.com/pigrew)**
+ - Add new DCD port for **STM32 fsdev** Fullspeed device for STM32 L0, F0, F1, F3 etc ...
+ - Add new class driver for **USB Test and Measurement Class (USBTMC)**
+ - Various improvement e.g Zero-length packet, Lint setup
+ - Board support for STM32F070RB Nucleo, STM32F303 Discovery
+
+- **[Peter Lawrence](https://github.com/majbthrd)**
+ - Add new DCD port for **Nuvoton NUC 120, 121, 125, 126, 505**
+ - Add new class driver for **USBNET RNDIS, CDC-ECM**
+ - Add *net_lwip_webserver* example for demonstration of usbnet with lwip
+ - Board support for NuTiny NUC120, NUC121s, NUC125s, NUC126V, NUC505
+ - Improve multiple cdc interfaces API & add cdc_dual_ports example
+
+- **[Rafael Silva](https://github.com/perigoso)**
+ - Add new DCD port for **Silabs EFM32GG12** with SLTB009A board
+
+- **[Raspberry Pi Team](https://github.com/raspberrypi)**
+ - Add new DCD port for **Raspberry Pi RP2040**
+
+- **[Reinhard Panhuber](https://github.com/PanRe)**
+ - Add new class driver for **USB Audio Class 2.0 (UAC2)**
+ - Enhance tu_fifo with unmasked pointer, which better support DMA
+
+- **[Scott Shawcroft](https://github.com/tannewt)**
+ - Add new DCD port for **SAMD21 and SAMD51**
+ - Add new class driver for **Musical Instrument Digital Interface (MIDI)**
+ - Improve USBD control transfer, MSC, CDC class driver
+ - Board support for Metro M0 & M4 express
+ - Write the excellent porting.md documentation
+ - Add initial Makefile
+
+- **[Sean Cross](https://github.com/xobs)**
+ - Add new DCD port for **ValentyUSB eptri** (fomu board)
+
+- **[Sylvain "tnt" Munaut](https://github.com/smunaut)**
+ - Add new class driver for DFU Runtime
+
+- **[Timon Skerutsch](https://github.com/PTS93)**
+ - Add hid_test.js script and extensive test for bi-directional raw HID
+
+- **[Tod E. Kurt](https://github.com/todbot)**
+ - Add hid_test.js script and extensive test for bi-directional raw HID
+
+- **[Uwe Bonnes](https://github.com/UweBonnes)**
+ - Improve STM32 Synopsys highspeed DCD
+
+- **[William D. Jones](https://github.com/cr1901)**
+ - Add new DCD port for **Synopsys DesignWare** for STM32 L4, F2, F4, F7, H7 etc ...
+ - Add new DCD port for **TI MSP430**
+ - Board support for STM32F407 Discovery, STM32H743 Nucleo, pyboard v1.1, msp_exp430f5529lp etc ...
+
+**[Full contributors list](https://github.com/hathach/tinyusb/contributors).**
diff --git a/sw/Core/Src/tinyusb/LICENSE b/sw/Core/Src/tinyusb/LICENSE
new file mode 100755
index 0000000..ddd4ab4
--- /dev/null
+++ b/sw/Core/Src/tinyusb/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2018, hathach (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.
diff --git a/sw/Core/Src/tinyusb/README.md b/sw/Core/Src/tinyusb/README.md
new file mode 100755
index 0000000..ddbd8a7
--- /dev/null
+++ b/sw/Core/Src/tinyusb/README.md
@@ -0,0 +1,109 @@
+# TinyUSB
+
+![TinyUSB](https://user-images.githubusercontent.com/2847802/108847382-a0a6a580-75ad-11eb-96d9-280c79389281.png)
+
+[![Build Status](https://github.com/hathach/tinyusb/workflows/Build/badge.svg)](https://github.com/hathach/tinyusb/actions) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
+
+TinyUSB is an open-source cross-platform USB Host/Device stack for embedded system, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events are deferred then handled in the non-ISR task function.
+
+![tinyusb](https://user-images.githubusercontent.com/249515/49858616-f60c9700-fe27-11e8-8627-e76936352ff7.png)
+
+```
+.
+├── docs # Documentation
+├── examples # Sample with Makefile build support
+├── hw
+│   ├── bsp # Supported boards source files
+│   └── mcu # Low level mcu core & peripheral drivers
+├── lib # Sources from 3rd party such as freeRTOS, fatfs ...
+├── src # All sources files for TinyUSB stack itself.
+├── test # Unit tests for the stack
+└── tools # Files used internally
+```
+
+## Contributors
+
+Special thanks to all the people who spent their precious time and effort to help this project so far. Check out the
+[CONTRIBUTORS.md](CONTRIBUTORS.md) file for the list of all contributors and their awesome work for the stack.
+
+## Supported MCUs
+
+The stack supports the following MCUs:
+
+- **Dialog:** DA1469x
+- **Espressif:** ESP32-S2
+- **MicroChip:** SAMD11, SAMD21, SAMD51, SAME5x, SAMG55
+- **NordicSemi:** nRF52833, nRF52840
+- **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505
+- **NXP:**
+ - iMX RT Series: RT1011, RT1015, RT1021, RT1052, RT1062, RT1064
+ - Kinetis: KL25
+ - LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
+- **Raspberry Pi:** RP2040
+- **Renesas:** RX63N
+- **Silabs:** EFM32GG12
+- **Sony:** CXD56
+- **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 both FullSpeed and HighSpeed
+- **TI:** MSP430
+- **[ValentyUSB](https://github.com/im-tomu/valentyusb)** eptri
+
+[Here is the list of supported Boards](docs/boards.md) that can be used with provided examples.
+
+## Device Stack
+
+Supports multiple device configurations by dynamically changing usb descriptors. Low power functions such like suspend, resume, and remote wakeup. Following device classes are supported:
+
+- USB Audio Class 2.0 (UAC2) still work in progress
+- Bluetooth Host Controller Interface (BTH HCI)
+- Communication Class (CDC)
+- Device Firmware Update (DFU): only Runtinme
+- Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ...
+- Mass Storage Class (MSC): with multiple LUNs
+- Musical Instrument Digital Interface (MIDI)
+- Network with RNDIS, CDC-ECM (work in progress)
+- USB Test and Measurement Class (USBTMC)
+- Vendor-specific class support with generic In & Out endpoints. Can be used with MS OS 2.0 compatible descriptor to load winUSB driver without INF file.
+- [WebUSB](https://github.com/WICG/webusb) with vendor-specific class
+
+## Host Stack
+
+**Most active development is on the Device stack. The Host stack is under rework and largely untested.**
+
+- Human Interface Device (HID): Keyboard, Mouse, Generic
+- Mass Storage Class (MSC)
+- Hub currently only supports 1 level of hub (due to my laziness)
+
+## OS Abstraction layer
+
+TinyUSB is completely thread-safe by pushing all ISR events into a central queue, then process it later in the non-ISR context task function. It also uses semaphore/mutex to access shared resources such as CDC FIFO. Therefore the stack needs to use some of OS's basic APIs. Following OSes are already supported out of the box.
+
+- **No OS** : Disabling USB IRQ is used as way to provide mutex
+- **FreeRTOS**
+- **Mynewt** Due to the newt package build system, Mynewt examples are better to be on its [own repo](https://github.com/hathach/mynewt-tinyusb-example)
+
+## Getting Started
+
+[Here are the details for getting started](docs/getting_started.md) with the stack.
+
+## Porting
+
+Want to help add TinyUSB support for a new MCU? Read [here](docs/porting.md) for an explanation on the low-level API needed by TinyUSB.
+
+## License
+
+MIT license for all TinyUSB sources `src` folder, [Full license is here](LICENSE). However, each file is individually licensed especially those in `lib` and `hw/mcu` folder. Please make sure you understand all the license term for files you use in your project.
+
+## Uses
+
+TinyUSB is currently used by these other projects:
+
+- [Adafruit nRF52 Arduino](https://github.com/adafruit/Adafruit_nRF52_Arduino)
+- [Adafruit nRF52 Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader)
+- [Adafruit SAMD Arduino](https://github.com/adafruit/ArduinoCore-samd)
+- [CircuitPython](https://github.com/adafruit/circuitpython)
+- [Espressif IDF](https://github.com/espressif/esp-idf)
+- [MicroPython](https://github.com/micropython/micropython)
+- [mynewt](https://mynewt.apache.org)
+- [Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk)
+- [TinyUF2 Bootloader](https://github.com/adafruit/tinyuf2)
+- [TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino)
diff --git a/sw/Core/Src/tinyusb/changelog.md b/sw/Core/Src/tinyusb/changelog.md
new file mode 100755
index 0000000..6b17e16
--- /dev/null
+++ b/sw/Core/Src/tinyusb/changelog.md
@@ -0,0 +1,227 @@
+# TinyUSB Changelog
+
+## 0.7.0 - 2020.11.08
+
+### Device Controller Driver
+
+- Added new support for Espressif ESP32-S2
+- Added new support for Dialog DA1469x
+- Enhance STM32 Synopsys
+ - Support bus events disconnection/suspend/resume/wakeup
+ - Improve transfer performance with optimizing xfer and fifo size
+ - Support Highspeed port (OTG_HS) with both internal and external PHY
+ - Support multiple usb ports with rhport=1 is highspeed on selected MCUs e.g H743, F23. It is possible to have OTG_HS to run on Fullspeed PHY (e.g lacking external PHY)
+ - Add ISO transfer, fix odd/even frame
+ - Fix FIFO flush during stall
+ - Implement dcd_edpt_close() API
+ - Support F105, F107
+- Enhance STM32 fsdev
+ - Improve dcd fifo allocation
+ - Fix ISTR race condition
+ - Support remap USB IRQ on supported MCUs
+ - Implement dcd_edpt_close() API
+- Enhance NUC 505: enhance set configure behavior
+- Enhance SAMD
+ - Fix race condition with setup packet
+ - Add SAMD11 option `OPT_MCU_SAMD11`
+ - Add SAME5x option `OPT_MCU_SAME5X`
+- Fix SAMG control data toggle and stall race condition
+- Enhance nRF
+ - Fix hanged when tud_task() is called within critical section (disabled interrupt)
+ - Fix disconnect bus event not submitted
+ - Implement ISO transfer and dcd_edpt_close()
+
+### USB Device
+
+**USBD**
+
+- Add new class driver for **Bluetooth HCI** class driver with example can be found in [mynewt-tinyusb-example](https://github.com/hathach/mynewt-tinyusb-example) since it needs mynewt OS to run with.
+- Fix USBD endpoint usage racing condition with `usbd_edpt_claim()/usbd_edpt_release()`
+- Added `tud_task_event_ready()` and `osal_queue_empty()`. This API is needed to check before enter low power mode with WFI/WFE
+- Rename USB IRQ Handler to `dcd_int_handler()`. Application must define IRQ handler in which it calls this API.
+- Add `dcd_connect()` and `dcd_disconnect()` to enable/disable internal pullup on D+/D- on supported MCUs.
+- Add `usbd_edpt_open()`
+- Remove `dcd_set_config()`
+- Add *OPT_OS_CUMSTOM* as hook for application to overwrite and/or add their own OS implementation
+- Support SET_INTERFACE, GET_INTERFACE request
+- Add Logging for debug with optional uart/rtt/swo printf retarget or `CFG_TUSB_DEBUG_PRINTF` hook
+- Add IAR compiler support
+- Support multiple configuration descriptors. `TUD_CONFIG_DESCRIPTOR()` template has extra config_num as 1st argument
+- Improve USB Highspeed support with actual link speed detection with `dcd_event_bus_reset()`
+- Enhance class driver management
+ - `usbd_driver_open()` add max length argument, and return length of interface (0 for not supported). Return value is used for finding appropriate driver
+ - Add application implemented class driver via `usbd_app_driver_get_cb()`
+ - IAD is handled to assign driver id
+- Added `tud_descriptor_device_qualifier_cb()` callback
+- Optimize `tu_fifo` bulk write/read transfer
+- Forward non-std control request to class driver
+- Let application handle Microsoft OS 1.0 Descriptors (the 0xEE index string)
+- Fix OSAL FreeRTOS yield from ISR
+
+**Class Drivers**
+
+- USBNET: remove ACM-EEM due to lack of support from host
+- USBTMC: fix descriptors when INT EP is disabled
+- CDC:
+ - Send zero length packet for end of data when needed
+ - Add `tud_cdc_tx_complete_cb()` callback
+ - Change tud_cdc_n_write_flush() return number of bytes forced to transfer, and flush when writing enough data to fifo
+- MIDI:
+ - Add packet interface
+ - Add multiple jack descriptors
+ - Fix MIDI driver for sysex
+- DFU Runtime: fix response to SET_INTERFACE and DFU_GETSTATUS request
+- Rename some configure macro to make it clear that those are used directly for endpoint transfer
+ - CFG_TUD_HID_BUFSIZE to `CFG_TUD_HID_EP_BUFSIZE
+ - CFG_TUD_CDC_EPSIZE to CFG_TUD_CDC_EP_BUFSIZE
+ - CFG_TUD_MSC_BUFSIZE to CFG_TUD_MSC_EP_BUFSIZE
+ - CFG_TUD_MIDI_EPSIZE to CFG_TUD_MIDI_EP_BUFSIZE
+- HID:
+ - Fix gamepad template descriptor
+ - Add multiple HID interface API
+ - Add extra comma to HID_REPORT_ID
+
+### USB Host
+
+- Rework USB host stack (still work in progress)
+ - Fix compile error with pipehandle
+ - Rework usbh control and enumeration as non-blocking
+- Improve Hub, MSC, HID host driver
+
+### Examples
+
+- Add new hid_composite_freertos
+- Add new dynamic_configuration to demonstrate how to switch configuration descriptors
+- Add new hid_multiple_interface
+- Enhance `net_lwip_webserver` example
+ - Add multiple configuration: RNDIS for Windows, CDC-ECM for macOS (Linux will work with both)
+ - Update lwip to STABLE-2_1_2_RELEASE for net_lwip_webserver
+- Added new Audio example: audio_test uac2_headsest
+
+### New Boards
+
+- Espressif ESP32-S2: saola_1, kaluga_1
+- STM32: F746 Nucleo, H743 Eval, H743 Nucleo, F723 discovery, stlink v3 mini, STM32L4r5 Nucleo
+- Dialog DA1469x dk pro and dk usb
+- Microchip: Great Scoot Gadgets' LUNA, samd11_xplained, D5035-01, atsamd21 xplained pro
+- nRF: ItsyBitsy nRF52840
+
+## 0.6.0 - 2020.03.30
+
+Added **CONTRIBUTORS.md** to give proper credit for contributors to the stack. Special thanks to [Nathan Conrad](https://github.com/pigrew), [Peter Lawrence](https://github.com/majbthrd) and [William D. Jones](https://github.com/cr1901) and others for spending their precious time to add lots of features and ports for this release.
+
+### Added
+
+**MCUs**
+
+- Added support for Microchip SAMG55
+- Added support for Nordic nRF52833
+- Added support for Nuvoton: NUC120, NUC121/NUC125, NUC126, NUC505
+- Added support for NXP LPC: 51Uxx, 54xxx, 55xx
+- Added support for NXP iMXRT: RT1011, RT1015, RT1021, RT1052, RT1062, RT1064
+- Added support for Sony CXD56 (Spresense)
+- Added support for STM32: L0, F0, F1, F2, F3, F4, F7, H7
+- Added support for TI MSP430
+- Added support for ValentyUSB's eptri
+
+**Class Driver**
+
+- Added DFU Runtime class driver
+- Added Network class driver with RNDIS, CDC-ECM, CDC-EEM (work in progress)
+- Added USBTMC class driver
+- Added WebUSB class driver using vendor-specific class
+- Added multiple instances support for CDC and MIDI
+- Added a handful of unit test with Ceedling.
+- Added LOG support for debugging with CFG_TUSB_DEBUG
+- Added `tud_descriptor_bos_cb()` for BOS descriptor (required for USB 2.1)
+- Added `dcd_edpt0_status_complete()` as optional API for DCD
+
+**Examples**
+
+Following examples are added:
+
+- board_test
+- cdc_dual_ports
+- dfu_rt
+- hid_composite
+- net_lwip_webserver
+- usbtmc
+- webusb_serial
+
+**Boards**
+
+Following boards are added:
+
+- adafruit_clue
+- arduino_nano33_ble
+- circuitplayground_bluefruit
+- circuitplayground_express
+- feather_m0_express
+- feather_nrf52840_sense
+- feather_stm32f405
+- fomu
+- itsybitsy_m0
+- itsybitsy_m4
+- lpcxpresso11u37
+- lpcxpresso1549
+- lpcxpresso51u68
+- lpcxpresso54114
+- lpcxpresso55s69
+- mbed1768
+- mimxrt1010_evk
+- mimxrt1015_evk
+- mimxrt1020_evk
+- mimxrt1050_evkb
+- mimxrt1060_evk
+- mimxrt1064_evk
+- msp_exp430f5529lp
+- ngx4330
+- nrf52840_mdk_dongle
+- nutiny_nuc121s
+- nutiny_nuc125s
+- nutiny_nuc126v
+- nutiny_sdk_nuc120
+- nutiny_sdk_nuc505
+- pca10059
+- pca10100
+- pyboardv11
+- raytac_mdbt50q_rx
+- samg55xplained
+- seeeduino_xiao
+- spresense
+- stm32f070rbnucleo
+- stm32f072disco
+- stm32f103bluepill
+- stm32f207nucleo
+- stm32f401blackpill
+- stm32f411blackpill
+- stm32f411disco
+- stm32f412disco
+- stm32f767nucleo
+- stm32h743nucleo
+- stm32l0538disco
+- stm32l476disco
+- teensy_40
+
+### Changed
+
+- Changed `tud_descriptor_string_cb()` to have additional Language ID argument
+- Merged hal_nrf5x.c into dcd_nrf5x.c
+- Merged dcd_samd21.c and dcd_samd51.c into dcd_samd.c
+- Generalized dcd_stm32f4.c to dcd_synopsys.c
+- Changed cdc_msc_hid to cdc_msc (drop hid) due to limited endpoints number of some MCUs
+- Improved DCD SAMD stability, fix missing setup packet occasionally
+- Improved usbd/usbd_control with proper hanlding of zero-length packet (ZLP)
+- Improved STM32 DCD FSDev
+- Improved STM32 DCD Synopsys
+- Migrated CI from Travis to Github Action
+- Updated nrfx submodule to 2.1.0
+- Fixed mynewt osal queue definition
+- Fixed cdc_msc_freertos example build for all MCUs
+
+## 0.5.0 (Initial Release) - 2019.07.10
+
+First release, device stack works great, host stack works but still need improvement.
+- Special thanks to @adafruit team, especially @tannewt to help out immensely to rework device stack: simplify osal & control transfer, adding SAMD21/SAMD51 ports, writing porting docs, adding MIDI class support etc...
+- Thanks to @cr1901 for adding STM32F4 port.
+- Thanks to @PTS93 and @todbot for HID raw API
diff --git a/sw/Core/Src/tinyusb/pkg.yml b/sw/Core/Src/tinyusb/pkg.yml
new file mode 100755
index 0000000..3314450
--- /dev/null
+++ b/sw/Core/Src/tinyusb/pkg.yml
@@ -0,0 +1,14 @@
+pkg.name: tinyusb
+pkg.description: A silly USB stack for embedded
+pkg.author: "Ha Thach <thach@tinyusb.org>"
+pkg.homepage: "https://github.com/hathach/tinyusb"
+pkg.keywords:
+ - usb
+
+pkg.type: sdk
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+
+pkg.include_dirs:
+ - src
diff --git a/sw/Core/Src/tinyusb/repository.yml b/sw/Core/Src/tinyusb/repository.yml
new file mode 100755
index 0000000..16afbca
--- /dev/null
+++ b/sw/Core/Src/tinyusb/repository.yml
@@ -0,0 +1,12 @@
+repo.name: tinyusb
+repo.versions:
+ "0.0.0": "master"
+ "0.5.0": "0.5.0"
+ "0.6.0": "0.6.0"
+ "0.7.0": "0.7.0"
+ "0.8.0": "0.8.0"
+ "0.9.0": "0.9.0"
+
+ "0-dev": "0.0.0" # master
+ "0-latest": "0.9.0" # latest stable release
+
diff --git a/sw/Core/Src/tinyusb/src/class/audio/audio.h b/sw/Core/Src/tinyusb/src/class/audio/audio.h
new file mode 100755
index 0000000..05e61f8
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/audio/audio.h
@@ -0,0 +1,934 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_Audio Audio
+ * Currently only MIDI subclass is supported
+ * @{ */
+
+#ifndef _TUSB_AUDIO_H__
+#define _TUSB_AUDIO_H__
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Audio Device Class Codes
+
+/// A.2 - Audio Function Subclass Codes
+typedef enum
+{
+ AUDIO_FUNCTION_SUBCLASS_UNDEFINED = 0x00,
+} audio_function_subclass_type_t;
+
+/// A.3 - Audio Function Protocol Codes
+typedef enum
+{
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_function_protocol_code_t;
+
+/// A.5 - Audio Interface Subclass Codes
+typedef enum
+{
+ AUDIO_SUBCLASS_UNDEFINED = 0x00,
+ AUDIO_SUBCLASS_CONTROL , ///< Audio Control
+ AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
+ AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
+} audio_subclass_type_t;
+
+/// A.6 - Audio Interface Protocol Codes
+typedef enum
+{
+ AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_interface_protocol_code_t;
+
+/// A.7 - Audio Function Category Codes
+typedef enum
+{
+ AUDIO_FUNC_UNDEF = 0x00,
+ AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
+ AUDIO_FUNC_HOME_THEATER = 0x02,
+ AUDIO_FUNC_MICROPHONE = 0x03,
+ AUDIO_FUNC_HEADSET = 0x04,
+ AUDIO_FUNC_TELEPHONE = 0x05,
+ AUDIO_FUNC_CONVERTER = 0x06,
+ AUDIO_FUNC_SOUND_RECODER = 0x07,
+ AUDIO_FUNC_IO_BOX = 0x08,
+ AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
+ AUDIO_FUNC_PRO_AUDIO = 0x0A,
+ AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
+ AUDIO_FUNC_CONTROL_PANEL = 0x0C,
+ AUDIO_FUNC_OTHER = 0xFF,
+} audio_function_code_t;
+
+/// A.9 - Audio Class-Specific AC Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
+ AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
+ AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
+ AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
+ AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
+} audio_cs_ac_interface_subtype_t;
+
+/// A.10 - Audio Class-Specific AS Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+ AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
+ AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
+} audio_cs_as_interface_subtype_t;
+
+/// A.11 - Effect Unit Effect Types
+typedef enum
+{
+ AUDIO_EFFECT_TYPE_UNDEF = 0x00,
+ AUDIO_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
+ AUDIO_EFFECT_TYPE_REVERBERATION = 0x02,
+ AUDIO_EFFECT_TYPE_MOD_DELAY = 0x03,
+ AUDIO_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
+} audio_effect_unit_effect_type_t;
+
+/// A.12 - Processing Unit Process Types
+typedef enum
+{
+ AUDIO_PROCESS_TYPE_UNDEF = 0x00,
+ AUDIO_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
+ AUDIO_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
+ AUDIO_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
+} audio_processing_unit_process_type_t;
+
+/// A.13 - Audio Class-Specific EP Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio_cs_ep_subtype_t;
+
+/// A.14 - Audio Class-Specific Request Codes
+typedef enum
+{
+ AUDIO_CS_REQ_UNDEF = 0x00,
+ AUDIO_CS_REQ_CUR = 0x01,
+ AUDIO_CS_REQ_RANGE = 0x02,
+ AUDIO_CS_REQ_MEM = 0x03,
+} audio_cs_req_t;
+
+/// A.17 - Control Selector Codes
+
+/// A.17.1 - Clock Source Control Selectors
+typedef enum
+{
+ AUDIO_CS_CTRL_UNDEF = 0x00,
+ AUDIO_CS_CTRL_SAM_FREQ = 0x01,
+ AUDIO_CS_CTRL_CLK_VALID = 0x02,
+} audio_clock_src_control_selector_t;
+
+/// A.17.2 - Clock Selector Control Selectors
+typedef enum
+{
+ AUDIO_CX_CTRL_UNDEF = 0x00,
+ AUDIO_CX_CTRL_CONTROL = 0x01,
+} audio_clock_sel_control_selector_t;
+
+/// A.17.3 - Clock Multiplier Control Selectors
+typedef enum
+{
+ AUDIO_CM_CTRL_UNDEF = 0x00,
+ AUDIO_CM_CTRL_NUMERATOR_CONTROL = 0x01,
+ AUDIO_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
+} audio_clock_mul_control_selector_t;
+
+/// A.17.4 - Terminal Control Selectors
+typedef enum
+{
+ AUDIO_TE_CTRL_UNDEF = 0x00,
+ AUDIO_TE_CTRL_COPY_PROTECT = 0x01,
+ AUDIO_TE_CTRL_CONNECTOR = 0x02,
+ AUDIO_TE_CTRL_OVERLOAD = 0x03,
+ AUDIO_TE_CTRL_CLUSTER = 0x04,
+ AUDIO_TE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_TE_CTRL_OVERFLOW = 0x06,
+ AUDIO_TE_CTRL_LATENCY = 0x07,
+} audio_terminal_control_selector_t;
+
+/// A.17.5 - Mixer Control Selectors
+typedef enum
+{
+ AUDIO_MU_CTRL_UNDEF = 0x00,
+ AUDIO_MU_CTRL_MIXER = 0x01,
+ AUDIO_MU_CTRL_CLUSTER = 0x02,
+ AUDIO_MU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_MU_CTRL_OVERFLOW = 0x04,
+ AUDIO_MU_CTRL_LATENCY = 0x05,
+} audio_mixer_control_selector_t;
+
+/// A.17.6 - Selector Control Selectors
+typedef enum
+{
+ AUDIO_SU_CTRL_UNDEF = 0x00,
+ AUDIO_SU_CTRL_SELECTOR = 0x01,
+ AUDIO_SU_CTRL_LATENCY = 0x02,
+} audio_sel_control_selector_t;
+
+/// A.17.7 - Feature Unit Control Selectors
+typedef enum
+{
+ AUDIO_FU_CTRL_UNDEF = 0x00,
+ AUDIO_FU_CTRL_MUTE = 0x01,
+ AUDIO_FU_CTRL_VOLUME = 0x02,
+ AUDIO_FU_CTRL_BASS = 0x03,
+ AUDIO_FU_CTRL_MID = 0x04,
+ AUDIO_FU_CTRL_TREBLE = 0x05,
+ AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO_FU_CTRL_AGC = 0x07,
+ AUDIO_FU_CTRL_DELAY = 0x08,
+ AUDIO_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO_FU_CTRL_LOUDNESS = 0x0A,
+ AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
+ AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
+ AUDIO_FU_CTRL_INVERTER = 0x0D,
+ AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
+ AUDIO_FU_CTRL_OVERVLOW = 0x0F,
+ AUDIO_FU_CTRL_LATENCY = 0x10,
+} audio_feature_unit_control_selector_t;
+
+/// A.17.8 Effect Unit Control Selectors
+
+/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_PE_CTRL_UNDEF = 0x00,
+ AUDIO_PE_CTRL_ENABLE = 0x01,
+ AUDIO_PE_CTRL_CENTERFREQ = 0x02,
+ AUDIO_PE_CTRL_QFACTOR = 0x03,
+ AUDIO_PE_CTRL_GAIN = 0x04,
+ AUDIO_PE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_PE_CTRL_OVERFLOW = 0x06,
+ AUDIO_PE_CTRL_LATENCY = 0x07,
+} audio_parametric_equalizer_control_selector_t;
+
+/// A.17.8.2 Reverberation Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_RV_CTRL_UNDEF = 0x00,
+ AUDIO_RV_CTRL_ENABLE = 0x01,
+ AUDIO_RV_CTRL_TYPE = 0x02,
+ AUDIO_RV_CTRL_LEVEL = 0x03,
+ AUDIO_RV_CTRL_TIME = 0x04,
+ AUDIO_RV_CTRL_FEEDBACK = 0x05,
+ AUDIO_RV_CTRL_PREDELAY = 0x06,
+ AUDIO_RV_CTRL_DENSITY = 0x07,
+ AUDIO_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
+ AUDIO_RV_CTRL_UNDERFLOW = 0x09,
+ AUDIO_RV_CTRL_OVERFLOW = 0x0A,
+ AUDIO_RV_CTRL_LATENCY = 0x0B,
+} audio_reverberation_effect_control_selector_t;
+
+/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_MD_CTRL_UNDEF = 0x00,
+ AUDIO_MD_CTRL_ENABLE = 0x01,
+ AUDIO_MD_CTRL_BALANCE = 0x02,
+ AUDIO_MD_CTRL_RATE = 0x03,
+ AUDIO_MD_CTRL_DEPTH = 0x04,
+ AUDIO_MD_CTRL_TIME = 0x05,
+ AUDIO_MD_CTRL_FEEDBACK = 0x06,
+ AUDIO_MD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MD_CTRL_LATENCY = 0x09,
+} audio_modulation_delay_control_selector_t;
+
+/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_DR_CTRL_UNDEF = 0x00,
+ AUDIO_DR_CTRL_ENABLE = 0x01,
+ AUDIO_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO_DR_CTRL_RELEASE_TIME = 0x06,
+ AUDIO_DR_CTRL_UNDERFLOW = 0x07,
+ AUDIO_DR_CTRL_OVERFLOW = 0x08,
+ AUDIO_DR_CTRL_LATENCY = 0x09,
+} audio_dynamic_range_compression_control_selector_t;
+
+/// A.17.9 Processing Unit Control Selectors
+
+/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_UD_CTRL_UNDEF = 0x00,
+ AUDIO_UD_CTRL_ENABLE = 0x01,
+ AUDIO_UD_CTRL_MODE_SELECT = 0x02,
+ AUDIO_UD_CTRL_CLUSTER = 0x03,
+ AUDIO_UD_CTRL_UNDERFLOW = 0x04,
+ AUDIO_UD_CTRL_OVERFLOW = 0x05,
+ AUDIO_UD_CTRL_LATENCY = 0x06,
+} audio_up_down_mix_control_selector_t;
+
+/// A.17.9.2 Dolby Prologic ™ Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_DP_CTRL_UNDEF = 0x00,
+ AUDIO_DP_CTRL_ENABLE = 0x01,
+ AUDIO_DP_CTRL_MODE_SELECT = 0x02,
+ AUDIO_DP_CTRL_CLUSTER = 0x03,
+ AUDIO_DP_CTRL_UNDERFLOW = 0x04,
+ AUDIO_DP_CTRL_OVERFLOW = 0x05,
+ AUDIO_DP_CTRL_LATENCY = 0x06,
+} audio_dolby_prologic_control_selector_t;
+
+/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_ST_EXT_CTRL_UNDEF = 0x00,
+ AUDIO_ST_EXT_CTRL_ENABLE = 0x01,
+ AUDIO_ST_EXT_CTRL_WIDTH = 0x02,
+ AUDIO_ST_EXT_CTRL_UNDERFLOW = 0x03,
+ AUDIO_ST_EXT_CTRL_OVERFLOW = 0x04,
+ AUDIO_ST_EXT_CTRL_LATENCY = 0x05,
+} audio_stereo_extender_control_selector_t;
+
+/// A.17.10 Extension Unit Control Selectors
+typedef enum
+{
+ AUDIO_XU_CTRL_UNDEF = 0x00,
+ AUDIO_XU_CTRL_ENABLE = 0x01,
+ AUDIO_XU_CTRL_CLUSTER = 0x02,
+ AUDIO_XU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_XU_CTRL_OVERFLOW = 0x04,
+ AUDIO_XU_CTRL_LATENCY = 0x05,
+} audio_extension_unit_control_selector_t;
+
+/// A.17.11 AudioStreaming Interface Control Selectors
+typedef enum
+{
+ AUDIO_AS_CTRL_UNDEF = 0x00,
+ AUDIO_AS_CTRL_ACT_ALT_SETTING = 0x01,
+ AUDIO_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
+ AUDIO_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
+} audio_audiostreaming_interface_control_selector_t;
+
+/// A.17.12 Encoder Control Selectors
+typedef enum
+{
+ AUDIO_EN_CTRL_UNDEF = 0x00,
+ AUDIO_EN_CTRL_BIT_RATE = 0x01,
+ AUDIO_EN_CTRL_QUALITY = 0x02,
+ AUDIO_EN_CTRL_VBR = 0x03,
+ AUDIO_EN_CTRL_TYPE = 0x04,
+ AUDIO_EN_CTRL_UNDERFLOW = 0x05,
+ AUDIO_EN_CTRL_OVERFLOW = 0x06,
+ AUDIO_EN_CTRL_ENCODER_ERROR = 0x07,
+ AUDIO_EN_CTRL_PARAM1 = 0x08,
+ AUDIO_EN_CTRL_PARAM2 = 0x09,
+ AUDIO_EN_CTRL_PARAM3 = 0x0A,
+ AUDIO_EN_CTRL_PARAM4 = 0x0B,
+ AUDIO_EN_CTRL_PARAM5 = 0x0C,
+ AUDIO_EN_CTRL_PARAM6 = 0x0D,
+ AUDIO_EN_CTRL_PARAM7 = 0x0E,
+ AUDIO_EN_CTRL_PARAM8 = 0x0F,
+} audio_encoder_control_selector_t;
+
+/// A.17.13 Decoder Control Selectors
+
+/// A.17.13.1 MPEG Decoder Control Selectors
+typedef enum
+{
+ AUDIO_MPD_CTRL_UNDEF = 0x00,
+ AUDIO_MPD_CTRL_DUAL_CHANNEL = 0x01,
+ AUDIO_MPD_CTRL_SECOND_STEREO = 0x02,
+ AUDIO_MPD_CTRL_MULTILINGUAL = 0x03,
+ AUDIO_MPD_CTRL_DYN_RANGE = 0x04,
+ AUDIO_MPD_CTRL_SCALING = 0x05,
+ AUDIO_MPD_CTRL_HILO_SCALING = 0x06,
+ AUDIO_MPD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MPD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MPD_CTRL_DECODER_ERROR = 0x09,
+} audio_MPEG_decoder_control_selector_t;
+
+/// A.17.13.2 AC-3 Decoder Control Selectors
+typedef enum
+{
+ AUDIO_AD_CTRL_UNDEF = 0x00,
+ AUDIO_AD_CTRL_MODE = 0x01,
+ AUDIO_AD_CTRL_DYN_RANGE = 0x02,
+ AUDIO_AD_CTRL_SCALING = 0x03,
+ AUDIO_AD_CTRL_HILO_SCALING = 0x04,
+ AUDIO_AD_CTRL_UNDERFLOW = 0x05,
+ AUDIO_AD_CTRL_OVERFLOW = 0x06,
+ AUDIO_AD_CTRL_DECODER_ERROR = 0x07,
+} audio_AC3_decoder_control_selector_t;
+
+/// A.17.13.3 WMA Decoder Control Selectors
+typedef enum
+{
+ AUDIO_WD_CTRL_UNDEF = 0x00,
+ AUDIO_WD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_WD_CTRL_OVERFLOW = 0x02,
+ AUDIO_WD_CTRL_DECODER_ERROR = 0x03,
+} audio_WMA_decoder_control_selector_t;
+
+/// A.17.13.4 DTS Decoder Control Selectors
+typedef enum
+{
+ AUDIO_DD_CTRL_UNDEF = 0x00,
+ AUDIO_DD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_DD_CTRL_OVERFLOW = 0x02,
+ AUDIO_DD_CTRL_DECODER_ERROR = 0x03,
+} audio_DTS_decoder_control_selector_t;
+
+/// A.17.14 Endpoint Control Selectors
+typedef enum
+{
+ AUDIO_EP_CTRL_UNDEF = 0x00,
+ AUDIO_EP_CTRL_PITCH = 0x01,
+ AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
+ AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
+} audio_EP_control_selector_t;
+
+/// Terminal Types
+
+/// 2.1 - Audio Class-Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
+ AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
+ AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
+} audio_terminal_type_t;
+
+/// 2.2 - Audio Class-Input Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
+ AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
+ AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
+ AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
+ AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
+ AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
+ AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
+} audio_terminal_input_type_t;
+
+/// 2.3 - Audio Class-Output Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
+ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
+ AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
+ AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
+ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
+ AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
+ AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
+ AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
+} audio_terminal_output_type_t;
+
+/// Rest is yet to be implemented
+
+/// Additional Audio Device Class Codes - Source: Audio Data Formats
+
+/// A.1 - Audio Class-Format Type Codes UAC2
+//typedef enum
+//{
+// AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
+// AUDIO_FORMAT_TYPE_I = 0x01,
+// AUDIO_FORMAT_TYPE_II = 0x02,
+// AUDIO_FORMAT_TYPE_III = 0x03,
+// AUDIO_FORMAT_TYPE_IV = 0x04,
+// AUDIO_EXT_FORMAT_TYPE_I = 0x81,
+// AUDIO_EXT_FORMAT_TYPE_II = 0x82,
+// AUDIO_EXT_FORMAT_TYPE_III = 0x83,
+//} audio_format_type_t;
+
+#define AUDIO_FORMAT_TYPE_UNDEFINED 0x00
+#define AUDIO_FORMAT_TYPE_I 0x01
+#define AUDIO_FORMAT_TYPE_II 0x02
+#define AUDIO_FORMAT_TYPE_III 0x03
+#define AUDIO_FORMAT_TYPE_IV 0x04
+#define AUDIO_EXT_FORMAT_TYPE_I 0x81
+#define AUDIO_EXT_FORMAT_TYPE_II 0x82
+#define AUDIO_EXT_FORMAT_TYPE_III 0x83
+
+/// A.2.1 - Audio Class-Audio Data Format Type I UAC2
+//typedef enum
+//{
+// AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
+// AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
+// AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
+// AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
+// AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
+// AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x100000000,
+//} audio_data_format_type_I_t;
+
+#define AUDIO_DATA_FORMAT_TYPE_I_PCM ((uint32_t) (1 << 0))
+#define AUDIO_DATA_FORMAT_TYPE_I_PCM8 ((uint32_t) (1 << 1))
+#define AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT ((uint32_t) (1 << 2))
+#define AUDIO_DATA_FORMAT_TYPE_I_ALAW ((uint32_t) (1 << 3))
+#define AUDIO_DATA_FORMAT_TYPE_I_MULAW ((uint32_t) (1 << 4))
+#define AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA 0x100000000
+
+/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
+
+/// Isochronous End Point Attributes
+typedef enum
+{
+ TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
+ TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
+ TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
+ TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C,
+ TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
+ TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
+ TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
+} tusb_iso_ep_attribute_t;
+
+/// Audio Class-Control Values UAC2
+typedef enum
+{
+ AUDIO_CTRL_NONE = 0x00, ///< No Host access
+ AUDIO_CTRL_R = 0x01, ///< Host read access only
+ AUDIO_CTRL_RW = 0x03, ///< Host read write access
+} audio_control_t;
+
+/// Audio Class-Specific AC Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
+} audio_cs_ac_interface_control_pos_t;
+
+/// Audio Class-Specific AS Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
+ AUDIO_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
+} audio_cs_as_interface_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Attributes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+} audio_cs_as_iso_data_ep_attribute_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
+} audio_cs_as_iso_data_ep_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio_cs_as_iso_data_ep_lock_delay_unit_t;
+
+/// Audio Class-Clock Source Attributes UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
+ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
+ AUDIO_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
+ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
+ AUDIO_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
+} audio_clock_source_attribute_t;
+
+/// Audio Class-Clock Source Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
+} audio_clock_source_control_pos_t;
+
+/// Audio Class-Clock Selector Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SELECTOR_CTRL_POS = 0,
+} audio_clock_selector_control_pos_t;
+
+/// Audio Class-Clock Multiplier Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
+ AUDIO_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
+} audio_clock_multiplier_control_pos_t;
+
+/// Audio Class-Input Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_IN_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_IN_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_IN_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_IN_TERM_CTRL_CLUSTER_POS = 6,
+ AUDIO_IN_TERM_CTRL_UNDERFLOW_POS = 8,
+ AUDIO_IN_TERM_CTRL_OVERFLOW_POS = 10,
+} audio_terminal_input_control_pos_t;
+
+/// Audio Class-Output Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_OUT_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_OUT_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_OUT_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
+ AUDIO_OUT_TERM_CTRL_OVERFLOW_POS = 8,
+} audio_terminal_output_control_pos_t;
+
+/// Audio Class-Feature Unit Controls UAC2
+typedef enum
+{
+ AUDIO_FEATURE_UNIT_CTRL_MUTE_POS = 0,
+ AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_POS = 4,
+ AUDIO_FEATURE_UNIT_CTRL_MID_POS = 6,
+ AUDIO_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
+ AUDIO_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
+ AUDIO_FEATURE_UNIT_CTRL_AGC_POS = 12,
+ AUDIO_FEATURE_UNIT_CTRL_DELAY_POS = 14,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
+ AUDIO_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
+ AUDIO_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
+ AUDIO_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
+ AUDIO_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
+} audio_feature_unit_control_pos_t;
+
+/// Audio Class-Audio Channel Configuration UAC2
+typedef enum
+{
+ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
+ AUDIO_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
+ AUDIO_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
+ AUDIO_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
+ AUDIO_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
+ AUDIO_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
+ AUDIO_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
+ AUDIO_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
+ AUDIO_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
+ AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
+ AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000,
+} audio_channel_config_t;
+
+/// AUDIO Channel Cluster Descriptor (4.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bNrChannels; ///< Number of channels currently connected.
+ audio_channel_config_t bmChannelConfig; ///< Bitmap according to 'audio_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location.
+} audio_desc_channel_cluster_t;
+
+/// AUDIO Class-Specific AC Interface Header Descriptor (4.7.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER.
+ uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200).
+ uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t.
+ uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
+ uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
+} audio_desc_cs_ac_interface_t;
+
+/// AUDIO Clock Source Descriptor (4.7.2.1)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t.
+ uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t.
+ uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity.
+} audio_desc_clock_source_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for ONE pin
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1.
+ uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected..
+ uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity.
+} audio_desc_clock_selector_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for multiple pins
+#define audio_desc_clock_selector_n_t(source_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; \
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bClockID ; \
+ uint8_t bNrInPins ; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID ; \
+ } sourceID[source_num] ; \
+ uint8_t bmControls ; \
+ uint8_t iClockSource ; \
+}
+
+/// AUDIO Clock Multiplier Descriptor (4.7.2.3)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected.
+ uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
+} audio_desc_clock_multiplier_t;
+
+/// AUDIO Input Terminal Descriptor(4.7.2.4)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 17.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
+ uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
+ uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
+ uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
+} audio_desc_input_terminal_t;
+
+/// AUDIO Output Terminal Descriptor(4.7.2.5)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 12.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types.
+ uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected.
+ uint16_t bmControls ; ///< See: audio_terminal_output_type_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal.
+} audio_desc_output_terminal_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for ONE channel
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 14.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT.
+ uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected.
+ struct TU_ATTR_PACKED {
+ uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1.
+ } controls[2] ;
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit.
+} audio_desc_feature_unit_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for multiple channels
+#define audio_desc_feature_unit_n_t(ch_num)\
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; /* 6+(ch_num+1)*4 */\
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bUnitID ; \
+ uint8_t bSourceID ; \
+ struct TU_ATTR_PACKED { \
+ uint32_t bmaControls ; \
+ } controls[ch_num+1] ; \
+ uint8_t iTerminal ; \
+}
+
+/// AUDIO Class-Specific AS Interface Descriptor(4.9.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 16.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected.
+ uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t.
+ uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t.
+ uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t.
+ uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel.
+} audio_desc_cs_as_interface_t;
+
+/// AUDIO Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 6.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I.
+ uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4.
+ uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot.
+} audio_desc_type_I_format_t;
+
+/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t.
+ uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t.
+ uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t.
+ uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
+} audio_desc_cs_as_iso_data_ep_t;
+
+//// 5.2.3 Control Request Parameter Block Layout
+
+// 5.2.3.1 1-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int8_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_1_t;
+
+// 5.2.3.2 2-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_2_t;
+
+// 5.2.3.3 4-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_4_t;
+
+// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the one below for predefined lengths - or if you know what you are doing do what you like
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_1_t;
+
+// 5.2.3.2 2-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_2_t;
+
+// 5.2.3.3 4-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_4_t;
+
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+#define audio_control_range_1_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges] ; \
+}
+
+ /// 5.2.3.2 2-byte Control RANGE Parameter Block
+#define audio_control_range_2_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
+
+ // 5.2.3.3 4-byte Control RANGE Parameter Block
+#define audio_control_range_4_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
+
+ /** @} */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+
+ /** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/audio/audio_device.c b/sw/Core/Src/tinyusb/src/class/audio/audio_device.c
new file mode 100755
index 0000000..8581925
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/audio/audio_device.c
@@ -0,0 +1,1462 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg
+ *
+ * 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.
+ */
+
+/*
+ * This driver supports at most one out EP, one in EP, one control EP, and one feedback EP and one alternative interface other than zero. Hence, only one input terminal and one output terminal are support, if you need more adjust the driver!
+ * It supports multiple TX and RX channels.
+ *
+ * In case you need more alternate interfaces, you need to define additional defines for this specific alternate interface. Just define them and set them in the set_interface function.
+ *
+ * */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "audio_device.h"
+#include "class/audio/audio.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
+#define CFG_TUD_AUDIO_TX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_TX
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#ifndef CFG_TUD_AUDIO_RX_FIFO_COUNT
+#define CFG_TUD_AUDIO_RX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_RX
+#endif
+#endif
+
+typedef struct
+{
+ uint8_t rhport;
+ uint8_t const * p_desc; // Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+ uint8_t ep_in; // Outgoing (out of uC) audio data EP.
+ uint16_t epin_buf_cnt; // Count filling status of EP in buffer - this is a shared state currently and is intended to be removed once EP buffers can be implemented as FIFOs!
+ uint8_t ep_in_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to output terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ uint8_t ep_out; // Incoming (into uC) audio data EP.
+ uint8_t ep_out_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to input terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint8_t ep_fb; // Feedback EP.
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ uint8_t ep_int_ctr; // Audio control interrupt EP.
+#endif
+
+#if CFG_TUD_AUDIO_N_AS_INT
+ uint8_t altSetting[CFG_TUD_AUDIO_N_AS_INT]; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
+#endif
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+
+ // Buffer for control requests
+ CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SIZE];
+
+ // FIFO
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+ tu_fifo_t tx_ff[CFG_TUD_AUDIO_TX_FIFO_COUNT];
+ CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_TX_FIFO_COUNT][CFG_TUD_AUDIO_TX_FIFO_SIZE];
+#if CFG_FIFO_MUTEX
+ osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_TX_FIFO_COUNT];
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+ tu_fifo_t rx_ff[CFG_TUD_AUDIO_RX_FIFO_COUNT];
+ CFG_TUSB_MEM_ALIGN uint8_t rx_ff_buf[CFG_TUD_AUDIO_RX_FIFO_COUNT][CFG_TUD_AUDIO_RX_FIFO_SIZE];
+#if CFG_FIFO_MUTEX
+ osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_RX_FIFO_COUNT];
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ tu_fifo_t int_ctr_ff;
+ CFG_TUSB_MEM_ALIGN uint8_t int_ctr_ff_buf[CFG_TUD_AUDIO_INT_CTR_BUFSIZE];
+#if CFG_FIFO_MUTEX
+ osal_mutex_def_t int_ctr_ff_mutex;
+#endif
+#endif
+
+ // Endpoint Transfer buffers
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_AUDIO_EPSIZE_OUT]; // Bigger makes no sense for isochronous EP's (but technically possible here)
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_AUDIO_EPSIZE_IN]; // Bigger makes no sense for isochronous EP's (but technically possible here)
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN];
+#endif
+
+} audiod_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(audiod_interface_t, ctrl_buf)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION audiod_interface_t _audiod_itf[CFG_TUD_AUDIO];
+
+extern const uint16_t tud_audio_desc_lengths[];
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio);
+#endif
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+
+static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int);
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *idxDriver);
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver);
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver);
+
+bool tud_audio_n_mounted(uint8_t itf)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ if (audio->ep_out == 0)
+ {
+ return false;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+ if (audio->ep_in == 0)
+ {
+ return false;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ if (audio->ep_int_ctr == 0)
+ {
+ return false;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (audio->ep_fb == 0)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+uint16_t tud_audio_n_available(uint8_t itf, uint8_t channelId)
+{
+ TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX);
+ return tu_fifo_count(&_audiod_itf[itf].rx_ff[channelId]);
+}
+
+uint16_t tud_audio_n_read(uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize)
+{
+ TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX);
+ return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[channelId], buffer, bufsize);
+}
+
+void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId)
+{
+ TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, );
+ tu_fifo_clear(&_audiod_itf[itf].rx_ff[channelId]);
+}
+#else
+uint16_t tud_audio_n_available(uint8_t itf)
+{
+ return tu_fifo_count(&_audiod_itf[itf].rx_ff[0]);
+}
+
+uint16_t tud_audio_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
+{
+ return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[0], buffer, bufsize);
+}
+
+void tud_audio_n_read_flush (uint8_t itf)
+{
+ tu_fifo_clear(&_audiod_itf[itf].rx_ff[0]);
+}
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+uint16_t tud_audio_int_ctr_n_available(uint8_t itf)
+{
+ return tu_fifo_count(&_audiod_itf[itf].int_ctr_ff);
+}
+
+uint16_t tud_audio_int_ctr_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
+{
+ return tu_fifo_read_n(&_audiod_itf[itf].int_ctr_ff, buffer, bufsize);
+}
+
+void tud_audio_int_ctr_n_read_flush (uint8_t itf)
+{
+ tu_fifo_clear(&_audiod_itf[itf].int_ctr_ff);
+}
+
+#endif
+
+// This function is called once something is received by USB and is responsible for decoding received stream into audio channels.
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_RX_FIFO_SIZE = 0.
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+
+static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t* buffer, uint16_t bufsize)
+{
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_RX)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL DECODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_I_RX)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+
+#if CFG_TUD_AUDIO_RX_FIFO_SIZE
+ TU_VERIFY(audio_rx_done_type_I_pcm_ff_cb(rhport, audio, buffer, bufsize));
+#else
+#error YOUR DECODING AND BUFFERING IS REQUIRED HERE!
+#endif
+ break;
+
+ default:
+ // DESIRED CFG_TUD_AUDIO_FORMAT_TYPE_I_RX NOT IMPLEMENTED!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_RX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+
+ // Call a weak callback here - a possibility for user to get informed RX was completed
+ if (tud_audio_rx_done_cb) TU_VERIFY(tud_audio_rx_done_cb(rhport, buffer, bufsize));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_EPSIZE_OUT
+
+// The following functions are used in case CFG_TUD_AUDIO_RX_FIFO_SIZE != 0
+#if CFG_TUD_AUDIO_RX_FIFO_SIZE
+#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize)
+{
+ (void) rhport;
+
+ // We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel
+ if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0)
+ {
+ return false;
+ }
+
+ uint8_t chId = 0;
+ uint16_t cnt;
+#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 1
+ uint8_t sample = 0;
+#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 2
+ uint16_t sample = 0;
+#else
+ uint32_t sample = 0;
+#endif
+
+ for(cnt = 0; cnt < bufsize; cnt += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX)
+ {
+ // Let alignment problems be handled by memcpy
+ memcpy(&sample, &buffer[cnt], CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX);
+ if(tu_fifo_write_n(&audio->rx_ff[chId++], &sample, CFG_TUD_AUDIO_RX_ITEMSIZE) != CFG_TUD_AUDIO_RX_ITEMSIZE)
+ {
+ // Buffer overflow
+ return false;
+ }
+
+ if (chId == CFG_TUD_AUDIO_N_CHANNELS_RX)
+ {
+ chId = 0;
+ }
+ }
+ return true;
+}
+#else
+static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *audio, uint8_t *buffer, uint16_t bufsize)
+{
+ (void) rhport;
+
+ // We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel
+ if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0)
+ {
+ return false;
+ }
+
+ tu_fifo_write_n(&audio->rx_ff[0], buffer, bufsize);
+ return true;
+}
+#endif // CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+#endif //CFG_TUD_AUDIO_RX_FIFO_SIZE
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+/**
+ * \brief Write data to EP in buffer
+ *
+ * Write data to buffer. If it is full, new data can be inserted once a transmit was scheduled. See audiod_tx_done_cb().
+ * If TX FIFOs are used, this function is not available in order to not let the user mess up the encoding process.
+ *
+ * \param[in] itf: Index of audio function interface
+ * \param[in] data: Pointer to data array to be copied from
+ * \param[in] len: # of array elements to copy
+ * \return Number of bytes actually written
+ */
+#if CFG_TUD_AUDIO_EPSIZE_IN
+#if !CFG_TUD_AUDIO_TX_FIFO_SIZE
+/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
+uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ // THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if audiod_tx_done_cb() is executed in between!
+
+ // FOR SINGLE THREADED OPERATION:
+ // AS LONG AS THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
+
+ // Determine free space
+ uint16_t free = CFG_TUD_AUDIO_EPSIZE_IN - audio->epin_buf_cnt;
+
+ // Clip length if needed
+ if (len > free) len = free;
+
+ // Write data
+ memcpy((void *) &audio->epin_buf[audio->epin_buf_cnt], data, len);
+
+ audio->epin_buf_cnt += len;
+
+ // Return number of bytes written
+ return len;
+}
+*/
+
+#else
+
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT == 1
+uint16_t tud_audio_n_write(uint8_t itf, void const* data, uint16_t len)
+{
+ {
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL)
+ {
+ return 0;
+ }
+ return tu_fifo_write_n(&audio->tx_ff[0], data, len);
+ }
+}
+#else
+uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, uint16_t len)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ return tu_fifo_write_n(&audio->tx_ff[channelId], data, len);
+}
+#endif
+
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied);
+
+uint16_t tud_audio_n_write_flush(uint8_t itf)
+{
+ audiod_interface_t *audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ uint16_t n_bytes_copied;
+ TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio, &n_bytes_copied));
+ return n_bytes_copied;
+}
+
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+uint32_t tud_audio_int_ctr_n_write(uint8_t itf, uint8_t const* buffer, uint32_t bufsize)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ return tu_fifo_write_n(&audio->int_ctr_ff, buffer, bufsize);
+}
+#endif
+
+
+// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_TX_FIFO_SIZE = 0 and use tud_audio_n_write_ep_in_buffer() (NOT IMPLEMENTED SO FAR).
+
+// n_bytes_copied - Informs caller how many bytes were loaded. In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is available for current frame.
+#if CFG_TUD_AUDIO_EPSIZE_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied)
+{
+ uint8_t idxDriver, idxItf;
+ uint8_t const *dummy2;
+
+ // If a callback is used determine current alternate setting of
+ if (tud_audio_tx_done_pre_load_cb || tud_audio_tx_done_post_load_cb)
+ {
+ // Find index of audio streaming interface and index of interface
+ TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, &idxDriver, &idxItf, &dummy2));
+ }
+
+ // Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer (in case FIFOs are used) or
+ // if no FIFOs are used the user may use this call back to load its data into the EP in buffer by use of tud_audio_n_write_ep_in_buffer().
+ if (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idxDriver, audio->ep_in, audio->altSetting[idxItf]));
+
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_TX)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL ENCODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_I_TX)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+
+ TU_VERIFY(audiod_tx_done_type_I_pcm_ff_cb(rhport, audio));
+
+ break;
+
+ default:
+ // YOUR ENCODING IS REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_TX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+#endif
+
+ // THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if tud_audio_n_write_ep_in_buffer() is executed in between!
+
+ // THIS IS NOT SOLVED SO FAR!
+
+ // FOR SINGLE THREADED OPERATION:
+ // THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT SO IT DOES NOT INTERRUPT tud_audio_n_write_ep_in_buffer()! AS LONG AS tud_audio_n_write_ep_in_buffer() IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
+
+ // Schedule transmit
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->epin_buf, audio->epin_buf_cnt));
+
+ // Inform how many bytes were copied
+ *n_bytes_copied = audio->epin_buf_cnt;
+
+ // Declare EP in buffer empty
+ audio->epin_buf_cnt = 0;
+
+ // TO HERE
+
+ // Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame
+ if (tud_audio_tx_done_post_load_cb) TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, *n_bytes_copied, idxDriver, audio->ep_in, audio->altSetting[idxItf]));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_EPSIZE_IN
+
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
+static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
+{
+ // We encode directly into IN EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
+
+ // Determine amount of samples
+ uint16_t const nEndpointSampleCapacity = CFG_TUD_AUDIO_EPSIZE_IN / CFG_TUD_AUDIO_N_CHANNELS_TX / CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+ uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]) / CFG_TUD_AUDIO_TX_ITEMSIZE;
+ uint16_t nBytesToSend;
+ uint8_t cntChannel;
+
+ for (cntChannel = 1; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
+ {
+ uint16_t const count = tu_fifo_count(&audio->tx_ff[cntChannel]);
+ if (count / CFG_TUD_AUDIO_TX_ITEMSIZE < nSamplesPerChannelToSend)
+ {
+ nSamplesPerChannelToSend = count * CFG_TUD_AUDIO_TX_ITEMSIZE;
+ }
+ }
+
+ // Check if there is enough
+ if (nSamplesPerChannelToSend == 0)
+ {
+ audio->epin_buf_cnt = 0;
+ return true;
+ }
+
+ // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
+ nSamplesPerChannelToSend = tu_min16(nSamplesPerChannelToSend, nEndpointSampleCapacity);
+ nBytesToSend = nSamplesPerChannelToSend * CFG_TUD_AUDIO_N_CHANNELS_TX * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+
+ // Encode
+ uint16_t cntSample;
+ uint8_t * pBuff = audio->epin_buf;
+#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
+ uint8_t sample;
+#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
+ uint16_t sample;
+#else
+ uint32_t sample;
+#endif
+
+ // TODO: Big endianess handling
+ for (cntSample = 0; cntSample < nSamplesPerChannelToSend; cntSample++)
+ {
+ for (cntChannel = 0; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
+ {
+ // Get sample from buffer
+ tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_TX_ITEMSIZE);
+
+ // Put it into EP's buffer - Let alignment problems be handled by memcpy
+ memcpy(pBuff, &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX);
+
+ // Advance pointer
+ pBuff += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+ }
+ }
+
+ audio->epin_buf_cnt = nBytesToSend;
+
+ return true;
+}
+
+#else
+static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
+{
+ // We encode directly into IN EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
+
+ // Determine amount of samples
+ uint16_t nByteCount = tu_fifo_count(&audio->tx_ff[0]);
+
+ nByteCount = tu_min16(nByteCount, CFG_TUD_AUDIO_EPSIZE_IN);
+
+ // Check if there is enough
+ if (nByteCount == 0)
+ {
+ return true;
+ }
+
+ nByteCount = tu_fifo_read_n(&audio->tx_ff[0], audio->epin_buf, nByteCount);
+ audio->epin_buf_cnt = nByteCount;
+
+ return true;
+}
+#endif // CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
+
+#endif //CFG_TUD_AUDIO_TX_FIFO_SIZE
+
+// This function is called once a transmit of an feedback packet was successfully completed. Here, we get the next feedback value to be sent
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static bool audio_fb_send(uint8_t rhport, audiod_interface_t *audio)
+{
+ uint8_t fb[4];
+ uint16_t len;
+
+ if (audio->fb_val == 0)
+ {
+ len = 0;
+ return true;
+ }
+ else
+ {
+ len = 4;
+ // Here we need to return the feedback value
+ if (rhport == 0)
+ {
+ // For FS format is 10.14
+ fb[0] = (audio->fb_val >> 2) & 0xFF;
+ fb[1] = (audio->fb_val >> 10) & 0xFF;
+ fb[2] = (audio->fb_val >> 18) & 0xFF;
+ // 4th byte is needed to work correctly with MS Windows
+ fb[3] = 0;
+ }
+ else
+ {
+ // For HS format is 16.16
+ fb[0] = (audio->fb_val >> 0) & 0xFF;
+ fb[1] = (audio->fb_val >> 8) & 0xFF;
+ fb[2] = (audio->fb_val >> 16) & 0xFF;
+ fb[3] = (audio->fb_val >> 24) & 0xFF;
+ }
+ return usbd_edpt_xfer(rhport, audio->ep_fb, fb, len);
+ }
+
+}
+
+//static uint16_t audio_fb_done_cb(uint8_t rhport, audiod_interface_t* audio)
+//{
+// (void) rhport;
+// (void) audio;
+//
+// if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
+// return 0;
+//}
+
+#endif
+
+// This function is called once a transmit of an interrupt control packet was successfully completed. Here, we get the remaining bytes to send
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+static bool audio_int_ctr_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied)
+{
+ // We write directly into the EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_int_ctr));
+
+ // TODO: Big endianess handling
+ uint16_t cnt = tu_fifo_read_n(audio->int_ctr_ff, audio->ep_int_ctr_buf, CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN);
+
+ if (cnt > 0)
+ {
+ // Schedule transmit
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_int_ctr, audio->ep_int_ctr_buf, cnt));
+ }
+
+ *n_bytes_copied = cnt;
+
+ if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, n_bytes_copied));
+
+ return true;
+}
+#endif
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void audiod_init(void)
+{
+ tu_memclr(_audiod_itf, sizeof(_audiod_itf));
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ audiod_interface_t* audio = &_audiod_itf[i];
+
+ // Initialize TX FIFOs if required
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
+ {
+ tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->tx_ff[cnt], osal_mutex_create(&audio->tx_ff_mutex[cnt]));
+#endif
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
+ {
+ tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->rx_ff[cnt], osal_mutex_create(&audio->rx_ff_mutex[cnt]));
+#endif
+ }
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+ tu_fifo_config(&audio->int_ctr_ff, &audio->int_ctr_ff_buf, CFG_TUD_AUDIO_INT_CTR_BUFSIZE, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->int_ctr_ff, osal_mutex_create(&audio->int_ctr_ff_mutex));
+#endif
+#endif
+ }
+}
+
+void audiod_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ audiod_interface_t* audio = &_audiod_itf[i];
+ tu_memclr(audio, ITF_MEM_RESET_SIZE);
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
+ {
+ tu_fifo_clear(&audio->tx_ff[cnt]);
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
+ {
+ tu_fifo_clear(&audio->rx_ff[cnt]);
+ }
+#endif
+ }
+}
+
+uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void) max_len;
+
+ TU_VERIFY ( TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+
+ // Verify version is correct - this check can be omitted
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+
+ // Verify interrupt control EP is enabled if demanded by descriptor - this should be best some static check however - this check can be omitted
+ if (itf_desc->bNumEndpoints == 1) // 0 or 1 EPs are allowed
+ {
+ TU_VERIFY(CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0);
+ }
+
+ // Alternate setting MUST be zero - this check can be omitted
+ TU_VERIFY(itf_desc->bAlternateSetting == 0);
+
+ // Find available audio driver interface
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (!_audiod_itf[i].p_desc)
+ {
+ _audiod_itf[i].p_desc = (uint8_t const *)itf_desc; // Save pointer to AC descriptor which is by specification always the first one
+ _audiod_itf[i].rhport = rhport;
+ break;
+ }
+ }
+
+ // Verify we found a free one
+ TU_ASSERT( i < CFG_TUD_AUDIO );
+
+ // This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
+ // TODO: Find a way to find end of current audio function and avoid necessity of tud_audio_desc_lengths - since now max_length is available we could do this surely somehow
+ uint16_t drv_len = tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+
+ return drv_len;
+}
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+#if CFG_TUD_AUDIO_N_AS_INT > 0
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+
+ // Find index of audio streaming interface
+ uint8_t idxDriver, idxItf;
+ uint8_t const *dummy;
+
+ TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &dummy));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_itf[idxDriver].altSetting[idxItf], 1));
+
+ TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_itf[idxDriver].altSetting[idxItf]);
+
+ return true;
+
+#else
+ (void) rhport;
+ (void) p_request;
+ return false;
+#endif
+}
+
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Here we need to do the following:
+
+ // 1. Find the audio driver assigned to the given interface to be set
+ // Since one audio driver interface has to be able to cover an unknown number of interfaces (AC, AS + its alternate settings), the best memory efficient way to solve this is to always search through the descriptors.
+ // The audio driver is mapped to an audio function by a reference pointer to the corresponding AC interface of this audio function which serves as a starting point for searching
+
+ // 2. Close EPs which are currently open
+ // To do so it is not necessary to know the current active alternate interface since we already save the current EP addresses - we simply close them
+
+ // 3. Open new EP
+
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
+
+ TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt);
+
+ // Find index of audio streaming interface and index of interface
+ uint8_t idxDriver, idxItf;
+ uint8_t const *p_desc;
+ TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &p_desc));
+
+ // Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
+#if CFG_TUD_AUDIO_EPSIZE_IN > 0
+ if (_audiod_itf[idxDriver].ep_in_as_intf_num == itf)
+ {
+ _audiod_itf[idxDriver].ep_in_as_intf_num = 0;
+ usbd_edpt_close(rhport, _audiod_itf[idxDriver].ep_in);
+
+ // Invoke callback - can be used to stop data sampling
+ if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+
+ _audiod_itf[idxDriver].ep_in = 0; // Necessary?
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ if (_audiod_itf[idxDriver].ep_out_as_intf_num == itf)
+ {
+ _audiod_itf[idxDriver].ep_out_as_intf_num = 0;
+ usbd_edpt_close(rhport, _audiod_itf[idxDriver].ep_out);
+ _audiod_itf[idxDriver].ep_out = 0; // Necessary?
+
+ // Close corresponding feedback EP
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ usbd_edpt_close(rhport, _audiod_itf[idxDriver].ep_fb);
+ _audiod_itf[idxDriver].ep_fb = 0; // Necessary?
+#endif
+ }
+#endif
+
+ // Save current alternative interface setting
+ _audiod_itf[idxDriver].altSetting[idxItf] = alt;
+
+ // Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface
+ // Get pointer at end
+ uint8_t const *p_desc_end = _audiod_itf[idxDriver].p_desc + tud_audio_desc_lengths[idxDriver] - TUD_AUDIO_DESC_IAD_LEN;
+
+ // p_desc starts at required interface with alternate setting zero
+ while (p_desc < p_desc_end)
+ {
+ // Find correct interface
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
+ {
+ // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
+ uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
+ while (foundEPs < nEps && p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
+ {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *)p_desc));
+
+ uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ // We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
+ usbd_edpt_clear_stall(rhport, ep_addr);
+
+#if CFG_TUD_AUDIO_EPSIZE_IN > 0
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && ((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.usage == 0x00) // Check if usage is data EP
+ {
+ // Save address
+ _audiod_itf[idxDriver].ep_in = ep_addr;
+ _audiod_itf[idxDriver].ep_in_as_intf_num = itf;
+
+ // Invoke callback - can be used to trigger data sampling if not already running
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+
+ // Schedule first transmit - in case no sample data is available a ZLP is loaded
+ uint16_t n_bytes_copied;
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_itf[idxDriver], &n_bytes_copied));
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) // Checking usage not necessary
+ {
+ // Save address
+ _audiod_itf[idxDriver].ep_out = ep_addr;
+ _audiod_itf[idxDriver].ep_out_as_intf_num = itf;
+
+ // Invoke callback
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+
+ // Prepare for incoming data
+ TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].epout_buf, CFG_TUD_AUDIO_EPSIZE_OUT), false);
+ }
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && ((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.usage == 1) // Check if usage is explicit data feedback
+ {
+ _audiod_itf[idxDriver].ep_fb = ep_addr;
+
+ // Invoke callback
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ }
+#endif
+
+#endif
+ foundEPs += 1;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ TU_VERIFY(foundEPs == nEps);
+
+ // We are done - abort loop
+ break;
+ }
+
+ // Moving forward
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ tud_control_status(rhport, p_request);
+
+ return true;
+}
+
+// Invoked when class request DATA stage is finished.
+// return false to stall control EP (e.g Host send non-sense DATA)
+static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ // Handle audio class specific set requests
+ if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT)
+ {
+ uint8_t idxDriver;
+
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ if (entityID != 0)
+ {
+ if (tud_audio_set_req_entity_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver));
+
+ // Invoke callback
+ return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No entity set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ else
+ {
+ if (tud_audio_set_req_itf_cb)
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver));
+
+ // Invoke callback
+ return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No interface set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ if (tud_audio_set_req_ep_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver));
+
+ // Invoke callback
+ return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No EP set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+
+ // Unknown/Unsupported recipient
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+ return true;
+}
+
+// Handle class control request
+// return false to stall control endpoint (e.g unsupported request)
+static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Handle standard requests - standard set requests usually have no data stage so we also handle set requests here
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
+ {
+ switch (p_request->bRequest)
+ {
+ case TUSB_REQ_GET_INTERFACE:
+ return audiod_get_interface(rhport, p_request);
+
+ case TUSB_REQ_SET_INTERFACE:
+ return audiod_set_interface(rhport, p_request);
+
+ // Unknown/Unsupported request
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+
+ // Handle class requests
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
+ {
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t idxDriver;
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_entity_cb)
+ {
+ return tud_audio_get_req_entity_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No entity get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_itf_cb)
+ {
+ return tud_audio_get_req_itf_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No interface get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_ep_cb)
+ {
+ return tud_audio_get_req_ep_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No EP get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
+ TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf, CFG_TUD_AUDIO_CTRL_BUF_SIZE));
+ return true;
+ }
+
+ // There went something wrong - unsupported control request type
+ TU_BREAKPOINT();
+ return false;
+}
+
+bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ return audiod_control_request(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ return audiod_control_complete(rhport, request);
+ }
+
+ return true;
+}
+
+bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+ (void) xferred_bytes;
+
+ // Search for interface belonging to given end point address and proceed as required
+ uint8_t idxDriver;
+ for (idxDriver = 0; idxDriver < CFG_TUD_AUDIO; idxDriver++)
+ {
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+ // Data transmission of control interrupt finished
+ if (_audiod_itf[idxDriver].ep_int_ctr == ep_addr)
+ {
+ // According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
+ // In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
+ // In case of an erroneous transmission a retransmission is conducted - this is taken care of by PHY ???
+
+ // Load new data
+ uint16 *n_bytes_copied;
+ TU_VERIFY(audio_int_ctr_done_cb(rhport, &_audiod_itf[idxDriver], n_bytes_copied));
+
+ if (*n_bytes_copied == 0 && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN)))
+ {
+ // There is no data left to send, a ZLP should be sent if
+ // xferred_bytes is multiple of EP size and not zero
+ return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
+ }
+ }
+
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+
+ // Data transmission of audio packet finished
+ if (_audiod_itf[idxDriver].ep_in == ep_addr)
+ {
+ // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
+ // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
+ // This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP.
+
+ // Check if there is data to load into EPs buffer - if not load it with ZLP
+ // Be aware - we as a device are not able to know if the host polls for data with a faster rate as we stated this in the descriptors. Therefore we always have to put something into the EPs buffer. However, once we did that, there is no way of aborting this or replacing what we put into the buffer before!
+ // This is the only place where we can fill something into the EPs buffer!
+
+ // Load new data
+ uint16_t n_bytes_copied;
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_itf[idxDriver], &n_bytes_copied));
+
+ // Transmission of ZLP is done by audiod_tx_done_cb()
+ return true;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+
+ // New audio packet received
+ if (_audiod_itf[idxDriver].ep_out == ep_addr)
+ {
+ // Save into buffer - do whatever has to be done
+ TU_VERIFY(audio_rx_done_cb(rhport, &_audiod_itf[idxDriver], _audiod_itf[idxDriver].epout_buf, xferred_bytes));
+
+ // prepare for next transmission
+ TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].epout_buf, CFG_TUD_AUDIO_EPSIZE_OUT), false);
+
+ return true;
+ }
+
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Transmission of feedback EP finished
+ if (_audiod_itf[idxDriver].ep_fb == ep_addr)
+ {
+ if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
+
+ return audio_fb_send(rhport, &_audiod_itf[idxDriver]);
+ }
+#endif
+#endif
+ }
+
+ return false;
+
+}
+
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len)
+{
+ // Handles only sending of data not receiving
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false;
+
+ // Get corresponding driver index
+ uint8_t idxDriver;
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver));
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver));
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver));
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // Crop length
+ if (len > CFG_TUD_AUDIO_CTRL_BUF_SIZE) len = CFG_TUD_AUDIO_CTRL_BUF_SIZE;
+
+ // Copy into buffer
+ memcpy((void *)_audiod_itf[idxDriver].ctrl_buf, data, (size_t)len);
+
+ // Schedule transmit
+ return tud_control_xfer(rhport, p_request, (void*)_audiod_itf[idxDriver].ctrl_buf, len);
+}
+
+// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function
+// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
+// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
+static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int)
+{
+ // Loop over audio driver interfaces
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_itf[i].p_desc)
+ {
+ // Get pointer at end
+ uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN;
+
+ // Advance past AC descriptors
+ uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ uint8_t tmp = 0;
+ while (p_desc < p_desc_end)
+ {
+ // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf)
+ {
+ *idxItf = tmp;
+ *idxDriver = i;
+ *pp_desc_int = p_desc;
+ return true;
+ }
+
+ // Increase index, bytes read, and pointer
+ tmp++;
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+
+ return false;
+}
+
+// Verify an entity with the given ID exists and returns also the corresponding driver index
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *idxDriver)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ // Look for the correct driver by checking if the unique standard AC interface number fits
+ if (_audiod_itf[i].p_desc && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf)
+ {
+ // Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
+ uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc); // Points to CS AC descriptor
+ uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc;
+ p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
+
+ while (p_desc < p_desc_end)
+ {
+ if (p_desc[3] == entityID) // Entity IDs are always at offset 3
+ {
+ *idxDriver = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_itf[i].p_desc)
+ {
+ // Get pointer at beginning and end
+ uint8_t const *p_desc = _audiod_itf[i].p_desc;
+ uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf)
+ {
+ *idxDriver = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_itf[i].p_desc)
+ {
+ // Get pointer at end
+ uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i];
+
+ // Advance past AC descriptors - EP we look for are streaming EPs
+ uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
+ {
+ *idxDriver = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+bool tud_audio_fb_set(uint8_t rhport, uint32_t feedback)
+{
+ audiod_interface_t *audio = &_audiod_itf[0];
+
+ audio->fb_val = feedback;
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_fb), true);
+
+ return audio_fb_send(rhport, audio);
+}
+#endif
+
+#endif //TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO
diff --git a/sw/Core/Src/tinyusb/src/class/audio/audio_device.h b/sw/Core/Src/tinyusb/src/class/audio/audio_device.h
new file mode 100755
index 0000000..5061501
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/audio/audio_device.h
@@ -0,0 +1,400 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * 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_AUDIO_DEVICE_H_
+#define _TUSB_AUDIO_DEVICE_H_
+
+#include "assert.h"
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+
+#include "audio.h"
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just waste a few bytes)
+#ifndef CFG_TUD_AUDIO_N_AS_INT
+#define CFG_TUD_AUDIO_N_AS_INT 0
+#endif
+
+// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
+#ifndef CFG_TUD_AUDIO_CTRL_BUF_SIZE
+#error You must define an audio class control request buffer size!
+#endif
+
+// Use of TX/RX FIFOs - If sizes are not zero, audio.c implements FIFOs for RX and TX (whatever defined).
+// For RX: the input stream gets decoded into its corresponding channels, where for each channel a FIFO is setup to hold its data -> see: audio_rx_done_cb().
+// For TX: the output stream is composed from CFG_TUD_AUDIO_N_CHANNELS_TX channels, where for each channel a FIFO is defined.
+// Further, it implements encoding and decoding of the individual channels (parameterized by the defines below).
+// If you don't use the FIFOs you need to handle encoding and decoding on your own in audio_rx_done_cb() and audio_tx_done_cb(). This, however, allows for optimizations.
+
+#ifndef CFG_TUD_AUDIO_TX_FIFO_SIZE
+#define CFG_TUD_AUDIO_TX_FIFO_SIZE 0 // Buffer size per channel
+#endif
+
+#ifndef CFG_TUD_AUDIO_RX_FIFO_SIZE
+#define CFG_TUD_AUDIO_RX_FIFO_SIZE 0 // Buffer size per channel
+#endif
+
+// End point sizes - Limits: Full Speed <= 1023, High Speed <= 1024
+#ifndef CFG_TUD_AUDIO_EPSIZE_IN
+#define CFG_TUD_AUDIO_EPSIZE_IN 0 // TX
+#endif
+
+#ifndef CFG_TUD_AUDIO_EPSIZE_OUT
+#define CFG_TUD_AUDIO_EPSIZE_OUT 0 // RX
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback
+#endif
+
+#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+#ifndef CFG_TUD_AUDIO_INT_CTR_BUFSIZE
+#define CFG_TUD_AUDIO_INT_CTR_BUFSIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
+#endif
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_CHANNELS_TX
+#define CFG_TUD_AUDIO_N_CHANNELS_TX 1
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_CHANNELS_RX
+#define CFG_TUD_AUDIO_N_CHANNELS_RX 1
+#endif
+
+// Audio data format types
+#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_TX
+#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, an encoding function has to be implemented in audio_device.c
+#endif
+
+#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_RX
+#define CFG_TUD_AUDIO_FORMAT_TYPE_RX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, a decoding function has to be implemented in audio_device.c
+#endif
+
+// Audio data format type I specifications
+#if CFG_TUD_AUDIO_FORMAT_TYPE_TX == AUDIO_FORMAT_TYPE_I
+
+// Type definitions - for possible formats see: audio_data_format_type_I_t and further in UAC2 specifications.
+#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_TX
+#define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX AUDIO_DATA_FORMAT_TYPE_I_PCM
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX // bSubslotSize
+#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
+#endif
+
+#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE
+#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
+#define CFG_TUD_AUDIO_TX_ITEMSIZE 1
+#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
+#define CFG_TUD_AUDIO_TX_ITEMSIZE 2
+#else
+#define CFG_TUD_AUDIO_TX_ITEMSIZE 4
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_TX_ITEMSIZE < CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX
+#error FIFO element size (ITEMSIZE) must not be smaller then sample size
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_FORMAT_TYPE_RX == AUDIO_FORMAT_TYPE_I
+
+#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_RX
+#define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX AUDIO_DATA_FORMAT_TYPE_I_PCM
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX // bSubslotSize
+#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 1
+#endif
+
+#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 1
+#define CFG_TUD_AUDIO_RX_ITEMSIZE 1
+#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 2
+#define CFG_TUD_AUDIO_RX_ITEMSIZE 2
+#else
+#define CFG_TUD_AUDIO_RX_ITEMSIZE 4
+#endif
+
+#endif
+
+//static_assert(sizeof(tud_audio_desc_lengths) != CFG_TUD_AUDIO, "Supply audio function descriptor pack length!");
+
+// Supported types of this driver:
+// AUDIO_DATA_FORMAT_TYPE_I_PCM - Required definitions: CFG_TUD_AUDIO_N_CHANNELS and CFG_TUD_AUDIO_BYTES_PER_CHANNEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \addtogroup AUDIO_Serial Serial
+ * @{
+ * \defgroup AUDIO_Serial_Device Device
+ * @{ */
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+// CFG_TUD_AUDIO > 1
+//--------------------------------------------------------------------+
+bool tud_audio_n_mounted (uint8_t itf);
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+uint16_t tud_audio_n_available (uint8_t itf, uint8_t channelId);
+uint16_t tud_audio_n_read (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
+void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId);
+#else
+uint16_t tud_audio_n_available (uint8_t itf);
+uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
+void tud_audio_n_read_flush (uint8_t itf);
+#endif
+#endif
+
+/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
+#if CFG_TUD_AUDIO_EPSIZE_IN && !CFG_TUD_AUDIO_TX_FIFO_SIZE
+uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
+#endif
+*/
+
+#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
+#define CFG_TUD_AUDIO_TX_FIFO_COUNT 1
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
+uint16_t tud_audio_n_write (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
+#else
+uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
+#endif
+uint16_t tud_audio_n_write_flush(uint8_t itf);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+uint16_t tud_audio_int_ctr_n_available (uint8_t itf);
+uint16_t tud_audio_int_ctr_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
+void tud_audio_int_ctr_n_read_flush (uint8_t itf);
+uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Interface0)
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted (void);
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+static inline uint16_t tud_audio_available (void);
+static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
+static inline void tud_audio_read_flush (void);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
+static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize);
+#else
+static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t bufsize);
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+static inline uint32_t tud_audio_int_ctr_available (void);
+static inline uint32_t tud_audio_int_ctr_read (void* buffer, uint32_t bufsize);
+static inline void tud_audio_int_ctr_read_flush (void);
+static inline uint32_t tud_audio_int_ctr_write (uint8_t const* buffer, uint32_t bufsize);
+#endif
+
+// Buffer control EP data and schedule a transmit
+// This function is intended to be used if you do not have a persistent buffer or memory location available (e.g. non-local variables) and need to answer onto a
+// get request. This function buffers your answer request frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it.
+// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer pointer in pointing. If you already have such
+// available you may directly use 'tud_control_xfer(...)'. In this case data does not need to be copied into an additional buffer and you save some time.
+// If the request's wLength is zero, a status packet is sent instead.
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
+TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+TU_ATTR_WEAK bool tud_audio_rx_done_cb(uint8_t rhport, uint8_t * buffer, uint16_t bufsize);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT > 0 && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
+// User code should call this function with feedback value in 16.16 format for FS and HS.
+// Value will be corrected for FS to 10.14 format automatically.
+// (see Universal Serial Bus Specification Revision 2.0 5.12.4.2).
+// Feedback value will be sent at FB endpoint interval till it's changed.
+bool tud_audio_fb_set(uint8_t rhport, uint32_t feedback);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t * n_bytes_copied);
+#endif
+
+// Invoked when audio set interface request received
+TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio set interface request received which closes an EP
+TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific set request received for an EP
+TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an interface
+TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an entity
+TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific get request received for an EP
+TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an interface
+TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an entity
+TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted(void)
+{
+ return tud_audio_n_mounted(0);
+}
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE && CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
+static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
+{
+ return tud_audio_n_write(0, channelId, buffer, n_bytes);
+}
+#else
+static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
+{
+ return tud_audio_n_write(0, buffer, n_bytes);
+}
+#endif
+
+static inline uint16_t tud_audio_write_flush (void) // Short version if only one audio function is used
+{
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE
+ return tud_audio_n_write_flush(0);
+#else
+ return 0;
+#endif
+}
+#endif // CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+static inline uint16_t tud_audio_available(uint8_t channelId)
+{
+ return tud_audio_n_available(0, channelId);
+}
+
+static inline uint16_t tud_audio_read(uint8_t channelId, void* buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read(0, channelId, buffer, bufsize);
+}
+
+static inline void tud_audio_read_flush(uint8_t channelId)
+{
+ tud_audio_n_read_flush(0, channelId);
+}
+#else
+static inline uint16_t tud_audio_available(void)
+{
+ return tud_audio_n_available(0);
+}
+
+static inline uint16_t tud_audio_read(void *buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read(0, buffer, bufsize);
+}
+
+static inline void tud_audio_read_flush(void)
+{
+ tud_audio_n_read_flush(0);
+}
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+static inline uint16_t tud_audio_int_ctr_available(void)
+{
+ return tud_audio_int_ctr_n_available(0);
+}
+
+static inline uint16_t tud_audio_int_ctr_read(void* buffer, uint16_t bufsize)
+{
+ return tud_audio_int_ctr_n_read(0, buffer, bufsize);
+}
+
+static inline void tud_audio_int_ctr_read_flush(void)
+{
+ return tud_audio_int_ctr_n_read_flush(0);
+}
+
+static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t bufsize)
+{
+ return tud_audio_int_ctr_n_write(0, buffer, bufsize);
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void audiod_init (void);
+void audiod_reset (uint8_t rhport);
+uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool audiod_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_AUDIO_DEVICE_H_ */
+
+/** @} */
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/bth/bth_device.c b/sw/Core/Src/tinyusb/src/class/bth/bth_device.c
new file mode 100755
index 0000000..481dc13
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/bth/bth_device.c
@@ -0,0 +1,255 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_BTH)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "bth_device.h"
+#include <common/tusb_types.h>
+#include <device/usbd_pvt.h>
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_ev;
+ uint8_t ep_acl_in;
+ uint8_t ep_acl_out;
+ uint8_t ep_voice[2]; // Not used yet
+ uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT];
+
+ // Endpoint Transfer buffer
+ CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd;
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE];
+
+} btd_interface_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf;
+
+static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
+{
+ // skip if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(TUD_OPT_RHPORT, ep));
+
+ TU_ASSERT(usbd_edpt_xfer(TUD_OPT_RHPORT, ep, data, len));
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+bool tud_bt_event_send(void *event, uint16_t event_len)
+{
+ return bt_tx_data(_btd_itf.ep_ev, event, event_len);
+}
+
+bool tud_bt_acl_data_send(void *event, uint16_t event_len)
+{
+ return bt_tx_data(_btd_itf.ep_acl_in, event, event_len);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void btd_init(void)
+{
+ tu_memclr(&_btd_itf, sizeof(_btd_itf));
+}
+
+void btd_reset(uint8_t rhport)
+{
+ (void)rhport;
+}
+
+uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+{
+ tusb_desc_endpoint_t const *desc_ep;
+ uint16_t drv_len = 0;
+ // Size of single alternative of ISO interface
+ const uint16_t iso_alt_itf_size = sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t);
+ // Size of hci interface
+ const uint16_t hci_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t);
+ // Ensure this is BT Primary Controller
+ TU_VERIFY(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass &&
+ TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass &&
+ TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0);
+
+ // Distinguish interface by number of endpoints, as both interface have same class, subclass and protocol
+ if (itf_desc->bNumEndpoints == 3 && max_len >= hci_itf_size)
+ {
+ _btd_itf.itf_num = itf_desc->bInterfaceNumber;
+
+ desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ _btd_itf.ep_ev = desc_ep->bEndpointAddress;
+
+ // Open endpoint pair
+ TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(desc_ep), 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out,
+ &_btd_itf.ep_acl_in), 0);
+
+ // Prepare for incoming data from host
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
+
+ drv_len = hci_itf_size;
+ }
+ else if (itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size)
+ {
+ uint8_t dir;
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
+ TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
+ // Store endpoint size for alternative
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
+ // Store endpoint size for alternative
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+ drv_len += iso_alt_itf_size;
+
+ for (int i = 1; i < CFG_TUD_BTH_ISO_ALT_COUNT && drv_len + iso_alt_itf_size <= max_len; ++i) {
+ // Make sure rest of alternatives matches
+ itf_desc = (tusb_desc_interface_t const *)tu_desc_next(desc_ep);
+ if (itf_desc->bDescriptorType != TUSB_DESC_INTERFACE ||
+ TUSB_CLASS_WIRELESS_CONTROLLER != itf_desc->bInterfaceClass ||
+ TUD_BT_APP_SUBCLASS != itf_desc->bInterfaceSubClass ||
+ TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol)
+ {
+ // Not an Iso interface instance
+ break;
+ }
+ TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ // Verify that alternative endpoint are same as first ones
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
+ _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ // Verify that alternative endpoint are same as first ones
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
+ _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+ drv_len += iso_alt_itf_size;
+ }
+ }
+
+ return drv_len;
+}
+
+// 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 btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
+{
+ (void)rhport;
+
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
+ request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
+ {
+ // HCI command packet addressing for single function Primary Controllers
+ TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
+ }
+ else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
+ {
+ if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex)
+ {
+ // TODO: Set interface it would involve changing size of endpoint size
+ }
+ else
+ {
+ // HCI command packet for Primary Controller function in a composite device
+ TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == _btd_itf.itf_num);
+ }
+ }
+ else return false;
+
+ return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, request->wLength);
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ // Handle class request only
+ TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, request->wLength);
+ }
+
+ return true;
+}
+
+bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void)result;
+
+ // received new data from host
+ if (ep_addr == _btd_itf.ep_acl_out)
+ {
+ if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_itf.epout_buf, xferred_bytes);
+
+ // prepare for next data
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
+ }
+ else if (ep_addr == _btd_itf.ep_ev)
+ {
+ if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes);
+ }
+ else if (ep_addr == _btd_itf.ep_acl_in)
+ {
+ if (tud_bt_acl_data_sent_cb) tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes);
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/bth/bth_device.h b/sw/Core/Src/tinyusb/src/class/bth/bth_device.h
new file mode 100755
index 0000000..1b90d09
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/bth/bth_device.h
@@ -0,0 +1,109 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jerzy Kasenberg
+ *
+ * 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_BTH_DEVICE_H_
+#define _TUSB_BTH_DEVICE_H_
+
+#include <common/tusb_common.h>
+#include <device/usbd.h>
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+#ifndef CFG_TUD_BTH_EVENT_EPSIZE
+#define CFG_TUD_BTH_EVENT_EPSIZE 16
+#endif
+#ifndef CFG_TUD_BTH_DATA_EPSIZE
+#define CFG_TUD_BTH_DATA_EPSIZE 64
+#endif
+
+typedef struct TU_ATTR_PACKED
+{
+ uint16_t op_code;
+ uint8_t param_length;
+ uint8_t param[255];
+} bt_hci_cmd_t;
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when HCI command was received over USB from Bluetooth host.
+// Detailed format is described in Bluetooth core specification Vol 2,
+// Part E, 5.4.1.
+// Length of the command is from 3 bytes (2 bytes for OpCode,
+// 1 byte for parameter total length) to 258.
+TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len);
+
+// Invoked when ACL data was received over USB from Bluetooth host.
+// Detailed format is described in Bluetooth core specification Vol 2,
+// Part E, 5.4.2.
+// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags
+// and 16 bits for data total length) to endpoint size.
+TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len);
+
+// Called when event sent with tud_bt_event_send() was delivered to BT stack.
+// Controller can release/reuse buffer with Event packet at this point.
+TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes);
+
+// Called when ACL data that was sent with tud_bt_acl_data_send()
+// was delivered to BT stack.
+// Controller can release/reuse buffer with ACL packet at this point.
+TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes);
+
+// Bluetooth controller calls this function when it wants to send even packet
+// as described in Bluetooth core specification Vol 2, Part E, 5.4.4.
+// Event has at least 2 bytes, first is Event code second contains parameter
+// total length. Controller can release/reuse event memory after
+// tud_bt_event_sent_cb() is called.
+bool tud_bt_event_send(void *event, uint16_t event_len);
+
+// Bluetooth controller calls this to send ACL data packet
+// as described in Bluetooth core specification Vol 2, Part E, 5.4.2
+// Minimum length is 4 bytes, (12 bits for Handle, 4 bits for flags
+// and 16 bits for data total length). Upper limit is not limited
+// to endpoint size since buffer is allocate by controller
+// and must not be reused till tud_bt_acl_data_sent_cb() is called.
+bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void btd_init (void);
+void btd_reset (uint8_t rhport);
+uint16_t btd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool btd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
+bool btd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_BTH_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc.h b/sw/Core/Src/tinyusb/src/class/cdc/cdc.h
new file mode 100755
index 0000000..f59bf0f
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc.h
@@ -0,0 +1,405 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_CDC Communication Device Class (CDC)
+ * Currently only Abstract Control Model subclass is supported
+ * @{ */
+
+#ifndef _TUSB_CDC_H__
+#define _TUSB_CDC_H__
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/** \defgroup ClassDriver_CDC_Common Common Definitions
+ * @{ */
+
+// TODO remove
+/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In)
+typedef enum
+{
+ CDC_PIPE_NOTIFICATION , ///< Notification pipe
+ CDC_PIPE_DATA_IN , ///< Data in pipe
+ CDC_PIPE_DATA_OUT , ///< Data out pipe
+ CDC_PIPE_ERROR , ///< Invalid Pipe ID
+}cdc_pipeid_t;
+
+//--------------------------------------------------------------------+
+// CDC Communication Interface Class
+//--------------------------------------------------------------------+
+
+/// Communication Interface Subclass Codes
+typedef enum
+{
+ CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2]
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL , ///< Abstract Control Model [USBPSTN1.2]
+ CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2]
+ CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2]
+ CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2]
+ CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2]
+ CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2]
+ CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1]
+ CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1]
+ CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1]
+ CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1]
+ CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0]
+} cdc_comm_sublcass_type_t;
+
+/// Communication Interface Protocol Codes
+typedef enum
+{
+ CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol
+ CDC_COMM_PROTOCOL_ATCOMMAND , ///< AT Commands: V.250 etc
+ CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 , ///< AT Commands defined by PCCA-101
+ CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O
+ CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 , ///< AT Commands defined by GSM 07.07
+ CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 , ///< AT Commands defined by 3GPP 27.007
+ CDC_COMM_PROTOCOL_ATCOMMAND_CDMA , ///< AT Commands defined by TIA for CDMA
+ CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model
+} cdc_comm_protocol_type_t;
+
+//------------- SubType Descriptor in COMM Functional Descriptor -------------//
+/// Communication Interface SubType Descriptor
+typedef enum
+{
+ CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface.
+ CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor.
+ CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor.
+ CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor.
+ CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor.
+ CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor.
+ CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor
+ CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor
+ CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor
+ CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor
+ CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor
+ CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor
+ CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor
+ CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor
+ CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor
+ CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor
+ CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor
+ CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor
+ CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor
+ CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor
+ CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor
+ CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor
+ CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor
+ CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor
+ CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor
+ CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor
+}cdc_func_desc_type_t;
+
+//--------------------------------------------------------------------+
+// CDC Data Interface Class
+//--------------------------------------------------------------------+
+
+// SUBCLASS code of Data Interface is not used and should/must be zero
+/// Data Interface Protocol Codes
+typedef enum{
+ CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI
+ CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC
+ CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent
+ CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol
+ CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931
+ CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol
+ CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures
+ CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control
+ CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN
+ CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands
+ CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack.
+ CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface
+}cdc_data_protocol_type_t;
+
+//--------------------------------------------------------------------+
+// Management Element Request (Control Endpoint)
+//--------------------------------------------------------------------+
+
+/// Communication Interface Management Element Request Codes
+typedef enum
+{
+ CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
+ CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
+
+ CDC_REQUEST_SET_COMM_FEATURE = 0x02,
+ CDC_REQUEST_GET_COMM_FEATURE = 0x03,
+ CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04,
+
+ CDC_REQUEST_SET_AUX_LINE_STATE = 0x10,
+ CDC_REQUEST_SET_HOOK_STATE = 0x11,
+ CDC_REQUEST_PULSE_SETUP = 0x12,
+ CDC_REQUEST_SEND_PULSE = 0x13,
+ CDC_REQUEST_SET_PULSE_TIME = 0x14,
+ CDC_REQUEST_RING_AUX_JACK = 0x15,
+
+ CDC_REQUEST_SET_LINE_CODING = 0x20,
+ CDC_REQUEST_GET_LINE_CODING = 0x21,
+ CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22,
+ CDC_REQUEST_SEND_BREAK = 0x23,
+
+ CDC_REQUEST_SET_RINGER_PARMS = 0x30,
+ CDC_REQUEST_GET_RINGER_PARMS = 0x31,
+ CDC_REQUEST_SET_OPERATION_PARMS = 0x32,
+ CDC_REQUEST_GET_OPERATION_PARMS = 0x33,
+ CDC_REQUEST_SET_LINE_PARMS = 0x34,
+ CDC_REQUEST_GET_LINE_PARMS = 0x35,
+ CDC_REQUEST_DIAL_DIGITS = 0x36,
+ CDC_REQUEST_SET_UNIT_PARAMETER = 0x37,
+ CDC_REQUEST_GET_UNIT_PARAMETER = 0x38,
+ CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39,
+ CDC_REQUEST_GET_PROFILE = 0x3A,
+
+ CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40,
+ CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41,
+ CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42,
+ CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43,
+ CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44,
+
+ CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50,
+ CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51,
+ CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52,
+ CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
+
+ CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
+}cdc_management_request_t;
+
+//--------------------------------------------------------------------+
+// Management Elemenent Notification (Notification Endpoint)
+//--------------------------------------------------------------------+
+
+/// Communication Interface Management Element Notification Codes
+typedef enum
+{
+ NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
+ RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
+
+ AUX_JACK_HOOK_STATE = 0x08,
+ RING_DETECT = 0x09,
+
+ SERIAL_STATE = 0x20,
+
+ CALL_STATE_CHANGE = 0x28,
+ LINE_STATE_CHANGE = 0x29,
+ CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred
+ MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40,
+}cdc_notification_request_t;
+
+//--------------------------------------------------------------------+
+// Class Specific Functional Descriptor (Communication Interface)
+//--------------------------------------------------------------------+
+
+/// Header Functional Descriptor (Communication Interface)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_
+ uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal
+}cdc_desc_func_header_t;
+
+/// Union Functional Descriptor (Communication Interface)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ uint8_t bControlInterface ; ///< Interface number of Communication Interface
+ uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface
+}cdc_desc_func_union_t;
+
+#define cdc_desc_func_union_n_t(no_slave)\
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ;\
+ uint8_t bDescriptorType ;\
+ uint8_t bDescriptorSubType ;\
+ uint8_t bControlInterface ;\
+ uint8_t bSubordinateInterface[no_slave] ;\
+}
+
+/// Country Selection Functional Descriptor (Communication Interface)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes.
+ uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country.
+}cdc_desc_func_country_selection_t;
+
+#define cdc_desc_func_country_selection_n_t(no_country) \
+ struct TU_ATTR_PACKED {\
+ uint8_t bLength ;\
+ uint8_t bDescriptorType ;\
+ uint8_t bDescriptorSubType ;\
+ uint8_t iCountryCodeRelDate ;\
+ uint16_t wCountryCode[no_country] ;\
+}
+
+//--------------------------------------------------------------------+
+// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS
+//--------------------------------------------------------------------+
+
+/// \brief Call Management Functional Descriptor
+/// \details This functional descriptor describes the processing of calls for the Communications Class interface.
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+
+ struct {
+ uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface.
+ uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself.
+ uint8_t TU_RESERVED : 6;
+ } bmCapabilities;
+
+ uint8_t bDataInterface;
+}cdc_desc_func_call_management_t;
+
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature.
+ uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State.
+ uint8_t support_send_break : 1; ///< Device supports the request Send_Break
+ uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection.
+ uint8_t TU_RESERVED : 4;
+}cdc_acm_capability_t;
+
+TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler");
+
+/// \brief Abstract Control Management Functional Descriptor
+/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ cdc_acm_capability_t bmCapabilities ;
+}cdc_desc_func_acm_t;
+
+/// \brief Direct Line Management Functional Descriptor
+/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ struct {
+ uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit.
+ uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State.
+ uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time.
+ uint8_t TU_RESERVED : 5;
+ } bmCapabilities;
+}cdc_desc_func_direct_line_management_t;
+
+/// \brief Telephone Ringer Functional Descriptor
+/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface,
+/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ uint8_t bRingerVolSteps ;
+ uint8_t bNumRingerPatterns ;
+}cdc_desc_func_telephone_ringer_t;
+
+/// \brief Telephone Operational Modes Functional Descriptor
+/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by
+/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ struct {
+ uint8_t simple_mode : 1;
+ uint8_t standalone_mode : 1;
+ uint8_t computer_centric_mode : 1;
+ uint8_t TU_RESERVED : 5;
+ } bmCapabilities;
+}cdc_desc_func_telephone_operational_modes_t;
+
+/// \brief Telephone Call and Line State Reporting Capabilities Descriptor
+/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a
+/// telephone device to report optional call and line states.
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
+ struct {
+ uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone
+ uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states.
+ uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information.
+ uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns.
+ uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line.
+ uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification
+ uint32_t TU_RESERVED : 26;
+ } bmCapabilities;
+}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
+
+static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc)
+{
+ return p_desc[2];
+}
+
+//--------------------------------------------------------------------+
+// Requests
+//--------------------------------------------------------------------+
+typedef struct TU_ATTR_PACKED
+{
+ uint32_t bit_rate;
+ uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
+ uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
+ uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
+} cdc_line_coding_t;
+
+TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR.
+ uint16_t half_duplex_carrier_control : 1;
+ uint16_t : 14;
+} cdc_line_control_state_t;
+
+TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");
+
+/** @} */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_device.c b/sw/Core/Src/tinyusb/src/class/cdc/cdc_device.c
new file mode 100755
index 0000000..c24edb7
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_device.c
@@ -0,0 +1,484 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC)
+
+#include "cdc_device.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+enum
+{
+ BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
+};
+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_notif;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+ uint8_t line_state;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+ char wanted_char;
+ cdc_line_coding_t line_coding;
+
+ // FIFO
+ tu_fifo_t rx_ff;
+ tu_fifo_t tx_ff;
+
+ uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
+
+#if CFG_FIFO_MUTEX
+ osal_mutex_def_t rx_ff_mutex;
+ osal_mutex_def_t tx_ff_mutex;
+#endif
+
+ // Endpoint Transfer buffer
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
+
+}cdcd_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+
+static void _prep_out_transaction (cdcd_interface_t* p_cdc)
+{
+ uint8_t const rhport = TUD_OPT_RHPORT;
+ uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
+
+ // Prepare for incoming data but only allow what we can store in the ring buffer.
+ // TODO Actually we can still carry out the transfer, keeping count of received bytes
+ // and slowly move it to the FIFO when read().
+ // This pre-check reduces endpoint claiming
+ TU_VERIFY(available >= sizeof(p_cdc->epout_buf), );
+
+ // claim endpoint
+ TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out), );
+
+ // fifo can be changed before endpoint is claimed
+ available = tu_fifo_remaining(&p_cdc->rx_ff);
+
+ if ( available >= sizeof(p_cdc->epout_buf) )
+ {
+ usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, p_cdc->ep_out);
+ }
+}
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+bool tud_cdc_n_connected(uint8_t itf)
+{
+ // DTR (bit 0) active is considered as connected
+ return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
+}
+
+uint8_t tud_cdc_n_get_line_state (uint8_t itf)
+{
+ return _cdcd_itf[itf].line_state;
+}
+
+void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
+{
+ (*coding) = _cdcd_itf[itf].line_coding;
+}
+
+void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
+{
+ _cdcd_itf[itf].wanted_char = wanted;
+}
+
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+uint32_t tud_cdc_n_available(uint8_t itf)
+{
+ return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
+}
+
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, bufsize);
+ _prep_out_transaction(p_cdc);
+ return num_read;
+}
+
+bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr)
+{
+ return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr);
+}
+
+void tud_cdc_n_read_flush (uint8_t itf)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ tu_fifo_clear(&p_cdc->rx_ff);
+ _prep_out_transaction(p_cdc);
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, bufsize);
+
+ // flush if queue more than packet size
+ if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE )
+ {
+ tud_cdc_n_write_flush(itf);
+ }
+
+ return ret;
+}
+
+uint32_t tud_cdc_n_write_flush (uint8_t itf)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+
+ // Skip if usb is not ready yet
+ TU_VERIFY( tud_ready(), 0 );
+
+ // No data to send
+ if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
+
+ uint8_t const rhport = TUD_OPT_RHPORT;
+
+ // Claim the endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
+
+ // Pull data from FIFO
+ uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
+
+ if ( count )
+ {
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+ return count;
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ // Note: data is dropped if terminal is not connected
+ usbd_edpt_release(rhport, p_cdc->ep_in);
+ return 0;
+ }
+}
+
+uint32_t tud_cdc_n_write_available (uint8_t itf)
+{
+ return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
+}
+
+bool tud_cdc_n_write_clear (uint8_t itf)
+{
+ return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void cdcd_init(void)
+{
+ tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
+
+ for(uint8_t i=0; i<CFG_TUD_CDC; i++)
+ {
+ cdcd_interface_t* p_cdc = &_cdcd_itf[i];
+
+ p_cdc->wanted_char = -1;
+
+ // default line coding is : stop bit = 1, parity = none, data bits = 8
+ p_cdc->line_coding.bit_rate = 115200;
+ p_cdc->line_coding.stop_bits = 0;
+ p_cdc->line_coding.parity = 0;
+ p_cdc->line_coding.data_bits = 8;
+
+ // Config RX fifo
+ tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
+
+ // Config TX fifo as overwritable at initialization and will be changed to non-overwritable
+ // if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal.
+ // In this way, the most current data is prioritized.
+ tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
+
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
+#endif
+ }
+}
+
+void cdcd_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_CDC; i++)
+ {
+ cdcd_interface_t* p_cdc = &_cdcd_itf[i];
+
+ tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
+ tu_fifo_clear(&p_cdc->rx_ff);
+ tu_fifo_clear(&p_cdc->tx_ff);
+ tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
+ }
+}
+
+uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ // Only support ACM subclass
+ TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
+
+ // Note: 0xFF can be used with RNDIS
+ TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA), 0);
+
+ // Find available interface
+ cdcd_interface_t * p_cdc = NULL;
+ for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
+ {
+ if ( _cdcd_itf[cdc_id].ep_in == 0 )
+ {
+ p_cdc = &_cdcd_itf[cdc_id];
+ break;
+ }
+ }
+ TU_ASSERT(p_cdc, 0);
+
+ //------------- Control Interface -------------//
+ p_cdc->itf_num = itf_desc->bInterfaceNumber;
+
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const * p_desc = tu_desc_next( itf_desc );
+
+ // Communication Functional Descriptors
+ while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
+ {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+ {
+ // notification endpoint if any
+ TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+
+ p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface (if any) -------------//
+ if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
+ {
+ // next to endpoint descriptor
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+
+ // Open endpoint pair
+ TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
+
+ drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ }
+
+ // Prepare for incoming data
+ _prep_out_transaction(p_cdc);
+
+ return drv_len;
+}
+
+// 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 cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // Handle class request only
+ TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ uint8_t itf = 0;
+ cdcd_interface_t* p_cdc = _cdcd_itf;
+
+ // Identify which interface to use
+ for ( ; ; itf++, p_cdc++)
+ {
+ if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
+
+ if ( p_cdc->itf_num == request->wIndex ) break;
+ }
+
+ switch ( request->bRequest )
+ {
+ case CDC_REQUEST_SET_LINE_CODING:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ TU_LOG2(" Set Line Coding\r\n");
+ tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ }
+ else if ( stage == CONTROL_STAGE_ACK)
+ {
+ if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ }
+ break;
+
+ case CDC_REQUEST_GET_LINE_CODING:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ TU_LOG2(" Get Line Coding\r\n");
+ tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ }
+ break;
+
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ tud_control_status(rhport, request);
+ }
+ else if (stage == CONTROL_STAGE_ACK)
+ {
+ // CDC PSTN v1.2 section 6.3.12
+ // Bit 0: Indicates if DTE is present or not.
+ // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
+ // Bit 1: Carrier control for half-duplex modems.
+ // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
+ bool const dtr = tu_bit_test(request->wValue, 0);
+ bool const rts = tu_bit_test(request->wValue, 1);
+
+ p_cdc->line_state = (uint8_t) request->wValue;
+
+ // Disable fifo overwriting if DTR bit is set
+ tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
+
+ TU_LOG2(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
+
+ // Invoke callback
+ if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
+ }
+ break;
+ case CDC_REQUEST_SEND_BREAK:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ tud_control_status(rhport, request);
+ }
+ else if (stage == CONTROL_STAGE_ACK)
+ {
+ TU_LOG2(" Send Break\r\n");
+ if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue);
+ }
+ break;
+
+ default: return false; // stall unsupported request
+ }
+
+ return true;
+}
+
+bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+
+ uint8_t itf;
+ cdcd_interface_t* p_cdc;
+
+ // Identify which interface to use
+ for (itf = 0; itf < CFG_TUD_CDC; itf++)
+ {
+ p_cdc = &_cdcd_itf[itf];
+ if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break;
+ }
+ TU_ASSERT(itf < CFG_TUD_CDC);
+
+ // Received new data
+ if ( ep_addr == p_cdc->ep_out )
+ {
+ tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, xferred_bytes);
+
+ // Check for wanted char and invoke callback if needed
+ if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) )
+ {
+ for ( uint32_t i = 0; i < xferred_bytes; i++ )
+ {
+ if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) )
+ {
+ tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
+ }
+ }
+ }
+
+ // invoke receive callback (if there is still data)
+ if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
+
+ // prepare for OUT transaction
+ _prep_out_transaction(p_cdc);
+ }
+
+ // Data sent to host, we continue to fetch from tx fifo to send.
+ // Note: This will cause incorrect baudrate set in line coding.
+ // Though maybe the baudrate is not really important !!!
+ if ( ep_addr == p_cdc->ep_in )
+ {
+ // invoke transmit callback to possibly refill tx fifo
+ if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf);
+
+ if ( 0 == tud_cdc_n_write_flush(itf) )
+ {
+ // If there is no data left, a ZLP should be sent if
+ // xferred_bytes is multiple of EP Packet size and not zero
+ if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
+ {
+ if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
+ {
+ usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
+ }
+ }
+ }
+ }
+
+ // nothing to do with notif endpoint for now
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_device.h b/sw/Core/Src/tinyusb/src/class/cdc/cdc_device.h
new file mode 100755
index 0000000..0885922
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_device.h
@@ -0,0 +1,261 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_CDC_DEVICE_H_
+#define _TUSB_CDC_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+#include "cdc.h"
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE)
+ #warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE
+#endif
+
+#ifndef CFG_TUD_CDC_EP_BUFSIZE
+ #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/** \addtogroup CDC_Serial Serial
+ * @{
+ * \defgroup CDC_Serial_Device Device
+ * @{ */
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Ports)
+// CFG_TUD_CDC > 1
+//--------------------------------------------------------------------+
+
+// Check if terminal is connected to this port
+bool tud_cdc_n_connected (uint8_t itf);
+
+// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+uint8_t tud_cdc_n_get_line_state (uint8_t itf);
+
+// Get current line encoding: bit rate, stop bits parity etc ..
+void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding);
+
+// Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving
+void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted);
+
+// Get the number of bytes available for reading
+uint32_t tud_cdc_n_available (uint8_t itf);
+
+// Read received bytes
+uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
+
+// Read a byte, return -1 if there is none
+static inline
+int32_t tud_cdc_n_read_char (uint8_t itf);
+
+// Clear the received FIFO
+void tud_cdc_n_read_flush (uint8_t itf);
+
+// Get a byte from FIFO at the specified position without removing it
+bool tud_cdc_n_peek (uint8_t itf, int pos, uint8_t* u8);
+
+// Write bytes to TX FIFO, data may remain in the FIFO for a while
+uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
+
+// Write a byte
+static inline
+uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
+
+// Write a null-terminated string
+static inline
+uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
+
+// Force sending data if possible, return number of forced bytes
+uint32_t tud_cdc_n_write_flush (uint8_t itf);
+
+// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
+uint32_t tud_cdc_n_write_available (uint8_t itf);
+
+// Clear the transmit FIFO
+bool tud_cdc_n_write_clear (uint8_t itf);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+static inline bool tud_cdc_connected (void);
+static inline uint8_t tud_cdc_get_line_state (void);
+static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding);
+static inline void tud_cdc_set_wanted_char (char wanted);
+
+static inline uint32_t tud_cdc_available (void);
+static inline int32_t tud_cdc_read_char (void);
+static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize);
+static inline void tud_cdc_read_flush (void);
+static inline bool tud_cdc_peek (int pos, uint8_t* u8);
+
+static inline uint32_t tud_cdc_write_char (char ch);
+static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize);
+static inline uint32_t tud_cdc_write_str (char const* str);
+static inline uint32_t tud_cdc_write_flush (void);
+static inline uint32_t tud_cdc_write_available (void);
+static inline bool tud_cdc_write_clear (void);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
+
+// Invoked when received `wanted_char`
+TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
+
+// Invoked when space becomes available in TX buffer
+TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf);
+
+// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
+TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
+
+// Invoked when line coding is change via SET_LINE_CODING
+TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
+
+// Invoked when received send break
+TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+static inline int32_t tud_cdc_n_read_char (uint8_t itf)
+{
+ uint8_t ch;
+ return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
+}
+
+static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch)
+{
+ return tud_cdc_n_write(itf, &ch, 1);
+}
+
+static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str)
+{
+ return tud_cdc_n_write(itf, str, strlen(str));
+}
+
+static inline bool tud_cdc_connected (void)
+{
+ return tud_cdc_n_connected(0);
+}
+
+static inline uint8_t tud_cdc_get_line_state (void)
+{
+ return tud_cdc_n_get_line_state(0);
+}
+
+static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding)
+{
+ tud_cdc_n_get_line_coding(0, coding);
+}
+
+static inline void tud_cdc_set_wanted_char (char wanted)
+{
+ tud_cdc_n_set_wanted_char(0, wanted);
+}
+
+static inline uint32_t tud_cdc_available (void)
+{
+ return tud_cdc_n_available(0);
+}
+
+static inline int32_t tud_cdc_read_char (void)
+{
+ return tud_cdc_n_read_char(0);
+}
+
+static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize)
+{
+ return tud_cdc_n_read(0, buffer, bufsize);
+}
+
+static inline void tud_cdc_read_flush (void)
+{
+ tud_cdc_n_read_flush(0);
+}
+
+static inline bool tud_cdc_peek (int pos, uint8_t* u8)
+{
+ return tud_cdc_n_peek(0, pos, u8);
+}
+
+static inline uint32_t tud_cdc_write_char (char ch)
+{
+ return tud_cdc_n_write_char(0, ch);
+}
+
+static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize)
+{
+ return tud_cdc_n_write(0, buffer, bufsize);
+}
+
+static inline uint32_t tud_cdc_write_str (char const* str)
+{
+ return tud_cdc_n_write_str(0, str);
+}
+
+static inline uint32_t tud_cdc_write_flush (void)
+{
+ return tud_cdc_n_write_flush(0);
+}
+
+static inline uint32_t tud_cdc_write_available(void)
+{
+ return tud_cdc_n_write_available(0);
+}
+
+static inline bool tud_cdc_write_clear(void)
+{
+ return tud_cdc_n_write_clear(0);
+}
+
+/** @} */
+/** @} */
+
+//--------------------------------------------------------------------+
+// INTERNAL USBD-CLASS DRIVER API
+//--------------------------------------------------------------------+
+void cdcd_init (void);
+void cdcd_reset (uint8_t rhport);
+uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool cdcd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_host.c b/sw/Core/Src/tinyusb/src/class/cdc/cdc_host.c
new file mode 100755
index 0000000..a897fb5
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_host.c
@@ -0,0 +1,245 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC)
+
+#include "common/tusb_common.h"
+#include "cdc_host.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct {
+ uint8_t itf_num;
+ uint8_t itf_protocol;
+
+ uint8_t ep_notif;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ cdc_acm_capability_t acm_capability;
+
+} cdch_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX];
+
+static inline cdch_data_t* get_itf(uint8_t dev_addr)
+{
+ return &cdch_data[dev_addr-1];
+}
+
+bool tuh_cdc_mounted(uint8_t dev_addr)
+{
+ cdch_data_t* cdc = get_itf(dev_addr);
+ return cdc->ep_in && cdc->ep_out;
+}
+
+bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid)
+{
+ if ( !tuh_cdc_mounted(dev_addr) ) return false;
+
+ cdch_data_t const * p_cdc = get_itf(dev_addr);
+
+ switch (pipeid)
+ {
+ case CDC_PIPE_NOTIFICATION:
+ return hcd_edpt_busy(dev_addr, p_cdc->ep_notif );
+
+ case CDC_PIPE_DATA_IN:
+ return hcd_edpt_busy(dev_addr, p_cdc->ep_in );
+
+ case CDC_PIPE_DATA_OUT:
+ return hcd_edpt_busy(dev_addr, p_cdc->ep_out );
+
+ default:
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------+
+// APPLICATION API (parameter validation needed)
+//--------------------------------------------------------------------+
+bool tuh_cdc_serial_is_mounted(uint8_t dev_addr)
+{
+ // TODO consider all AT Command as serial candidate
+ return tuh_cdc_mounted(dev_addr) &&
+ (cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA);
+}
+
+bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify)
+{
+ TU_VERIFY( tuh_cdc_mounted(dev_addr) );
+ TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA);
+
+ uint8_t const ep_out = cdch_data[dev_addr-1].ep_out;
+ if ( hcd_edpt_busy(dev_addr, ep_out) ) return false;
+
+ return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify);
+}
+
+bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify)
+{
+ TU_VERIFY( tuh_cdc_mounted(dev_addr) );
+ TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA);
+
+ uint8_t const ep_in = cdch_data[dev_addr-1].ep_in;
+ if ( hcd_edpt_busy(dev_addr, ep_in) ) return false;
+
+ return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify);
+}
+
+bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_complete_cb_t complete_cb)
+{
+ cdch_data_t const * p_cdc = get_itf(dev_addr);
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
+ .wValue = (rts ? 2 : 0) | (dtr ? 1 : 0),
+ .wIndex = p_cdc->itf_num,
+ .wLength = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, complete_cb) );
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBH-CLASS DRIVER API
+//--------------------------------------------------------------------+
+void cdch_init(void)
+{
+ tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
+}
+
+bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+{
+ // Only support ACM
+ TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
+
+ // Only support AT commands, no protocol and vendor specific commands.
+ TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
+ 0xff == itf_desc->bInterfaceProtocol);
+
+ uint8_t const * p_desc;
+ cdch_data_t * p_cdc;
+
+ p_desc = tu_desc_next(itf_desc);
+ p_cdc = get_itf(dev_addr);
+
+ p_cdc->itf_num = itf_desc->bInterfaceNumber;
+ p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
+
+ //------------- Communication Interface -------------//
+ (*p_length) = sizeof(tusb_desc_interface_t);
+
+ // Communication Functional Descriptors
+ while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] )
+ {
+ if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
+ {
+ // save ACM bmCapabilities
+ p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+ }
+
+ (*p_length) += p_desc[DESC_OFFSET_LEN];
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
+ {
+ // notification endpoint
+ tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc;
+
+ TU_ASSERT( usbh_edpt_open(rhport, dev_addr, ep_desc) );
+ p_cdc->ep_notif = ep_desc->bEndpointAddress;
+
+ (*p_length) += p_desc[DESC_OFFSET_LEN];
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface (if any) -------------//
+ if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
+ {
+ (*p_length) += p_desc[DESC_OFFSET_LEN];
+ p_desc = tu_desc_next(p_desc);
+
+ // data endpoints expected to be in pairs
+ for(uint32_t i=0; i<2; i++)
+ {
+ tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
+ TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
+
+ TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
+
+ if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
+ {
+ p_cdc->ep_in = ep_desc->bEndpointAddress;
+ }else
+ {
+ p_cdc->ep_out = ep_desc->bEndpointAddress;
+ }
+
+ (*p_length) += p_desc[DESC_OFFSET_LEN];
+ p_desc = tu_desc_next( p_desc );
+ }
+ }
+
+ return true;
+}
+
+bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ (void) dev_addr; (void) itf_num;
+ return true;
+}
+
+bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+{
+ (void) ep_addr;
+ tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes );
+ return true;
+}
+
+void cdch_close(uint8_t dev_addr)
+{
+ cdch_data_t * p_cdc = get_itf(dev_addr);
+ tu_memclr(p_cdc, sizeof(cdch_data_t));
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_host.h b/sw/Core/Src/tinyusb/src/class/cdc/cdc_host.h
new file mode 100755
index 0000000..66c2f07
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_host.h
@@ -0,0 +1,136 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_CDC_HOST_H_
+#define _TUSB_CDC_HOST_H_
+
+#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "cdc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// CDC APPLICATION PUBLIC API
+//--------------------------------------------------------------------+
+/** \ingroup ClassDriver_CDC Communication Device Class (CDC)
+ * \addtogroup CDC_Serial Serial
+ * @{
+ * \defgroup CDC_Serial_Host Host
+ * @{ */
+
+bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_complete_cb_t complete_cb);
+
+static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_control_complete_cb_t complete_cb)
+{
+ return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb);
+}
+
+static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_control_complete_cb_t complete_cb)
+{
+ return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb);
+}
+
+/** \brief Check if device support CDC Serial interface or not
+ * \param[in] dev_addr device address
+ * \retval true if device supports
+ * \retval false if device does not support or is not mounted
+ */
+bool tuh_cdc_serial_is_mounted(uint8_t dev_addr);
+
+/** \brief Check if the interface is currently busy or not
+ * \param[in] dev_addr device address
+ * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe.
+ * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device
+ * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device
+ * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer
+ * can be scheduled. User needs to make sure the corresponding interface is mounted
+ * (by \ref tuh_cdc_serial_is_mounted) before calling this function.
+ */
+bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid);
+
+/** \brief Perform USB OUT transfer to device
+ * \param[in] dev_addr device address
+ * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
+ * \param[in] length Number of bytes to be transferred via USB bus
+ * \retval TUSB_ERROR_NONE on success
+ * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
+ * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
+ * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
+ * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
+ * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
+ */
+bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify);
+
+/** \brief Perform USB IN transfer to get data from device
+ * \param[in] dev_addr device address
+ * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
+ * \param[in] length Number of bytes to be transferred via USB bus
+ * \retval TUSB_ERROR_NONE on success
+ * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
+ * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
+ * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
+ * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
+ * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
+ */
+bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify);
+
+//--------------------------------------------------------------------+
+// CDC APPLICATION CALLBACKS
+//--------------------------------------------------------------------+
+
+/** \brief Callback function that is invoked when an transferring event occurred
+ * \param[in] dev_addr Address of device
+ * \param[in] event an value from \ref xfer_result_t
+ * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe
+ * \param[in] xferred_bytes Number of bytes transferred via USB bus
+ * \note event can be one of following
+ * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
+ * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
+ * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
+ * \note
+ */
+void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes);
+
+/// @} // group CDC_Serial_Host
+/// @}
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void cdch_init(void);
+bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
+bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void cdch_close(uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_HOST_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis.h b/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis.h
new file mode 100755
index 0000000..e0f129f
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis.h
@@ -0,0 +1,301 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup ClassDriver_CDC Communication Device Class (CDC)
+ * \defgroup CDC_RNDIS Remote Network Driver Interface Specification (RNDIS)
+ * @{
+ * \defgroup CDC_RNDIS_Common Common Definitions
+ * @{ */
+
+#ifndef _TUSB_CDC_RNDIS_H_
+#define _TUSB_CDC_RNDIS_H_
+
+#include "cdc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef __CC_ARM
+#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
+#endif
+
+/// RNDIS Message Types
+typedef enum
+{
+ RNDIS_MSG_PACKET = 0x00000001UL, ///< The host and device use this to send network data to one another.
+
+ RNDIS_MSG_INITIALIZE = 0x00000002UL, ///< Sent by the host to initialize the device.
+ RNDIS_MSG_INITIALIZE_CMPLT = 0x80000002UL, ///< Device response to an initialize message.
+
+ RNDIS_MSG_HALT = 0x00000003UL, ///< Sent by the host to halt the device. This does not have a response. It is optional for the device to send this message to the host.
+
+ RNDIS_MSG_QUERY = 0x00000004UL, ///< Sent by the host to send a query OID.
+ RNDIS_MSG_QUERY_CMPLT = 0x80000004UL, ///< Device response to a query OID.
+
+ RNDIS_MSG_SET = 0x00000005UL, ///< Sent by the host to send a set OID.
+ RNDIS_MSG_SET_CMPLT = 0x80000005UL, ///< Device response to a set OID.
+
+ RNDIS_MSG_RESET = 0x00000006UL, ///< Sent by the host to perform a soft reset on the device.
+ RNDIS_MSG_RESET_CMPLT = 0x80000006UL, ///< Device response to reset message.
+
+ RNDIS_MSG_INDICATE_STATUS = 0x00000007UL, ///< Sent by the device to indicate its status or an error when an unrecognized message is received.
+
+ RNDIS_MSG_KEEP_ALIVE = 0x00000008UL, ///< During idle periods, sent every few seconds by the host to check that the device is still responsive. It is optional for the device to send this message to check if the host is active.
+ RNDIS_MSG_KEEP_ALIVE_CMPLT = 0x80000008UL ///< The device response to a keepalivemessage. The host can respond with this message to a keepalive message from the device when the device implements the optional KeepAliveTimer.
+}rndis_msg_type_t;
+
+/// RNDIS Message Status Values
+typedef enum
+{
+ RNDIS_STATUS_SUCCESS = 0x00000000UL, ///< Success
+ RNDIS_STATUS_FAILURE = 0xC0000001UL, ///< Unspecified error
+ RNDIS_STATUS_INVALID_DATA = 0xC0010015UL, ///< Invalid data error
+ RNDIS_STATUS_NOT_SUPPORTED = 0xC00000BBUL, ///< Unsupported request error
+ RNDIS_STATUS_MEDIA_CONNECT = 0x4001000BUL, ///< Device is connected to a network medium.
+ RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000CUL ///< Device is disconnected from the medium.
+}rndis_msg_status_t;
+
+#ifdef __CC_ARM
+#pragma diag_default 66 // return Keil 66 to normal severity
+#endif
+
+//--------------------------------------------------------------------+
+// MESSAGE STRUCTURE
+//--------------------------------------------------------------------+
+
+//------------- Initialize -------------//
+/// \brief Initialize Message
+/// \details This message MUST be sent by the host to initialize the device.
+typedef struct {
+ uint32_t type ; ///< Message type, must be \ref RNDIS_MSG_INITIALIZE
+ uint32_t length ; ///< Message length in bytes, must be 0x18
+ uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device.
+ uint32_t major_version ; ///< The major version of the RNDIS Protocol implemented by the host.
+ uint32_t minor_version ; ///< The minor version of the RNDIS Protocol implemented by the host
+ uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the host expects to receive from the device.
+}rndis_msg_initialize_t;
+
+/// \brief Initialize Complete Message
+/// \details This message MUST be sent by the device in response to an initialize message.
+typedef struct {
+ uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_INITIALIZE_CMPLT
+ uint32_t length ; ///< Message length in bytes, must be 0x30
+ uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_initialize_t to which this message is a response.
+ uint32_t status ; ///< The initialization status of the device, has value from \ref rndis_msg_status_t
+ uint32_t major_version ; ///< the highest-numbered RNDIS Protocol version supported by the device.
+ uint32_t minor_version ; ///< the highest-numbered RNDIS Protocol version supported by the device.
+ uint32_t device_flags ; ///< MUST be set to 0x000000010. Other values are reserved for future use.
+ uint32_t medium ; ///< is 0x00 for RNDIS_MEDIUM_802_3
+ uint32_t max_packet_per_xfer ; ///< The maximum number of concatenated \ref RNDIS_MSG_PACKET messages that the device can handle in a single bus transfer to it. This value MUST be at least 1.
+ uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the device expects to receive from the host.
+ uint32_t packet_alignment_factor ; ///< The byte alignment the device expects for each RNDIS message that is part of a multimessage transfer to it. The value is specified as an exponent of 2; for example, the host uses 2<SUP>{PacketAlignmentFactor}</SUP> as the alignment value.
+ uint32_t reserved[2] ;
+} rndis_msg_initialize_cmplt_t;
+
+//------------- Query -------------//
+/// \brief Query Message
+/// \details This message MUST be sent by the host to query an OID.
+typedef struct {
+ uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY
+ uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer
+ uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device.
+ uint32_t oid ; ///< The integer value of the host operating system-defined identifier, for the parameter of the device being queried for.
+ uint32_t buffer_length ; ///< The length, in bytes, of the input data required for the OID query. This MUST be set to 0 when there is no input data associated with the OID.
+ uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the input data for the query is located in the message. This value MUST be set to 0 when there is no input data associated with the OID.
+ uint32_t reserved ;
+ uint8_t oid_buffer[] ; ///< Flexible array contains the input data supplied by the host, required for the OID query request processing by the device, as per the host NDIS specification.
+} rndis_msg_query_t, rndis_msg_set_t;
+
+TU_VERIFY_STATIC(sizeof(rndis_msg_query_t) == 28, "Make sure flexible array member does not affect layout");
+
+/// \brief Query Complete Message
+/// \details This message MUST be sent by the device in response to a query OID message.
+typedef struct {
+ uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY_CMPLT
+ uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer
+ uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_query_t to which this message is a response.
+ uint32_t status ; ///< The status of processing for the query request, has value from \ref rndis_msg_status_t.
+ uint32_t buffer_length ; ///< The length, in bytes, of the data in the response to the query. This MUST be set to 0 when there is no OIDInputBuffer.
+ uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the response data for the query is located in the message. This MUST be set to 0 when there is no \ref oid_buffer.
+ uint8_t oid_buffer[] ; ///< Flexible array member contains the response data to the OID query request as specified by the host.
+} rndis_msg_query_cmplt_t;
+
+TU_VERIFY_STATIC(sizeof(rndis_msg_query_cmplt_t) == 24, "Make sure flexible array member does not affect layout");
+
+//------------- Reset -------------//
+/// \brief Reset Message
+/// \details This message MUST be sent by the host to perform a soft reset on the device.
+typedef struct {
+ uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET
+ uint32_t length ; ///< Message length in bytes, MUST be 0x06
+ uint32_t reserved ;
+} rndis_msg_reset_t;
+
+/// \brief Reset Complete Message
+/// \details This message MUST be sent by the device in response to a reset message.
+typedef struct {
+ uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET_CMPLT
+ uint32_t length ; ///< Message length in bytes, MUST be 0x10
+ uint32_t status ; ///< The status of processing for the \ref rndis_msg_reset_t, has value from \ref rndis_msg_status_t.
+ uint32_t addressing_reset ; ///< This field indicates whether the addressing information, which is the multicast address list or packet filter, has been lost during the reset operation. This MUST be set to 0x00000001 if the device requires that the host to resend addressing information or MUST be set to zero otherwise.
+} rndis_msg_reset_cmplt_t;
+
+//typedef struct {
+// uint32_t type;
+// uint32_t length;
+// uint32_t status;
+// uint32_t buffer_length;
+// uint32_t buffer_offset;
+// uint32_t diagnostic_status; // optional
+// uint32_t diagnostic_error_offset; // optional
+// uint32_t status_buffer[0]; // optional
+//} rndis_msg_indicate_status_t;
+
+/// \brief Keep Alive Message
+/// \details This message MUST be sent by the host to check that device is still responsive. It is optional for the device to send this message to check if the host is active
+typedef struct {
+ uint32_t type ; ///< Message Type
+ uint32_t length ; ///< Message length in bytes, MUST be 0x10
+ uint32_t request_id ;
+} rndis_msg_keep_alive_t, rndis_msg_halt_t;
+
+/// \brief Set Complete Message
+/// \brief This message MUST be sent in response to a the request message
+typedef struct {
+ uint32_t type ; ///< Message Type
+ uint32_t length ; ///< Message length in bytes, MUST be 0x10
+ uint32_t request_id ; ///< must be the same as requesting message
+ uint32_t status ; ///< The status of processing for the request message request by the device to which this message is the response.
+} rndis_msg_set_cmplt_t, rndis_msg_keep_alive_cmplt_t;
+
+/// \brief Packet Data Message
+/// \brief This message MUST be used by the host and the device to send network data to one another.
+typedef struct {
+ uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_PACKET
+ uint32_t length ; ///< Message length in bytes, The total length of this RNDIS message including the header, payload, and padding.
+ uint32_t data_offset ; ///< Specifies the offset, in bytes, from the start of this \a data_offset field of this message to the start of the data. This MUST be an integer multiple of 4.
+ uint32_t data_length ; ///< Specifies the number of bytes in the payload of this message.
+ uint32_t out_of_band_data_offet ; ///< Specifies the offset, in bytes, of the first out-of-band data record from the start of the DataOffset field in this message. MUST be an integer multiple of 4 when out-of-band data is present or set to 0 otherwise. When there are multiple out-ofband data records, each subsequent record MUST immediately follow the previous out-of-band data record.
+ uint32_t out_of_band_data_length ; ///< Specifies, in bytes, the total length of the out-of-band data.
+ uint32_t num_out_of_band_data_elements ; ///< Specifies the number of out-of-band records in this message.
+ uint32_t per_packet_info_offset ; ///< Specifies the offset, in bytes, of the start of per-packet-info data record from the start of the \a data_offset field in this message. MUST be an integer multiple of 4 when per-packet-info data record is present or MUST be set to 0 otherwise. When there are multiple per-packet-info data records, each subsequent record MUST immediately follow the previous record.
+ uint32_t per_packet_info_length ; ///< Specifies, in bytes, the total length of per-packetinformation contained in this message.
+ uint32_t reserved[2] ;
+ uint32_t payload[0] ; ///< Network data contained in this message.
+
+ // uint8_t padding[0]
+ // Additional bytes of zeros added at the end of the message to comply with
+ // the internal and external padding requirements. Internal padding SHOULD be as per the
+ // specification of the out-of-band data record and per-packet-info data record. The external
+ //padding size SHOULD be determined based on the PacketAlignmentFactor field specification
+ //in REMOTE_NDIS_INITIALIZE_CMPLT message by the device, when multiple
+ //REMOTE_NDIS_PACKET_MSG messages are bundled together in a single bus-native message.
+ //In this case, all but the very last REMOTE_NDIS_PACKET_MSG MUST respect the
+ //PacketAlignmentFactor field.
+
+ // rndis_msg_packet_t [0] : (optional) more packet if multiple packet per bus transaction is supported
+} rndis_msg_packet_t;
+
+
+typedef struct {
+ uint32_t size ; ///< Length, in bytes, of this header and appended data and padding. This value MUST be an integer multiple of 4.
+ uint32_t type ; ///< MUST be as per host operating system specification.
+ uint32_t offset ; ///< The byte offset from the beginning of this record to the beginning of data.
+ uint32_t data[0] ; ///< Flexible array contains data
+} rndis_msg_out_of_band_data_t, rndis_msg_per_packet_info_t;
+
+//--------------------------------------------------------------------+
+// NDIS Object ID
+//--------------------------------------------------------------------+
+
+/// NDIS Object ID
+typedef enum
+{
+ //------------- General Required OIDs -------------//
+ RNDIS_OID_GEN_SUPPORTED_LIST = 0x00010101, ///< List of supported OIDs
+ RNDIS_OID_GEN_HARDWARE_STATUS = 0x00010102, ///< Hardware status
+ RNDIS_OID_GEN_MEDIA_SUPPORTED = 0x00010103, ///< Media types supported (encoded)
+ RNDIS_OID_GEN_MEDIA_IN_USE = 0x00010104, ///< Media types in use (encoded)
+ RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, ///<
+ RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, ///< Maximum frame size in bytes
+ RNDIS_OID_GEN_LINK_SPEED = 0x00010107, ///< Link speed in units of 100 bps
+ RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, ///< Transmit buffer space
+ RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, ///< Receive buffer space
+ RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010A, ///< Minimum amount of storage, in bytes, that a single packet occupies in the transmit buffer space of the NIC
+ RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010B, ///< Amount of storage, in bytes, that a single packet occupies in the receive buffer space of the NIC
+ RNDIS_OID_GEN_VENDOR_ID = 0x0001010C, ///< Vendor NIC code
+ RNDIS_OID_GEN_VENDOR_DESCRIPTION = 0x0001010D, ///< Vendor network card description
+ RNDIS_OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E, ///< Current packet filter (encoded)
+ RNDIS_OID_GEN_CURRENT_LOOKAHEAD = 0x0001010F, ///< Current lookahead size in bytes
+ RNDIS_OID_GEN_DRIVER_VERSION = 0x00010110, ///< NDIS version number used by the driver
+ RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, ///< Maximum total packet length in bytes
+ RNDIS_OID_GEN_PROTOCOL_OPTIONS = 0x00010112, ///< Optional protocol flags (encoded)
+ RNDIS_OID_GEN_MAC_OPTIONS = 0x00010113, ///< Optional NIC flags (encoded)
+ RNDIS_OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, ///< Whether the NIC is connected to the network
+ RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, ///< The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction
+
+ //------------- General Optional OIDs -------------//
+ RNDIS_OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, ///< Vendor-assigned version number of the driver
+ RNDIS_OID_GEN_SUPPORTED_GUIDS = 0x00010117, ///< The custom GUIDs (Globally Unique Identifier) supported by the miniport driver
+ RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, ///< List of network-layer addresses associated with the binding between a transport and the driver
+ RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, ///< Size of packets' additional headers
+ RNDIS_OID_GEN_MEDIA_CAPABILITIES = 0x00010201, ///<
+ RNDIS_OID_GEN_PHYSICAL_MEDIUM = 0x00010202, ///< Physical media supported by the miniport driver (encoded)
+
+ //------------- 802.3 Objects (Ethernet) -------------//
+ RNDIS_OID_802_3_PERMANENT_ADDRESS = 0x01010101, ///< Permanent station address
+ RNDIS_OID_802_3_CURRENT_ADDRESS = 0x01010102, ///< Current station address
+ RNDIS_OID_802_3_MULTICAST_LIST = 0x01010103, ///< Current multicast address list
+ RNDIS_OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, ///< Maximum size of multicast address list
+} rndis_oid_type_t;
+
+/// RNDIS Packet Filter Bits \ref RNDIS_OID_GEN_CURRENT_PACKET_FILTER.
+typedef enum
+{
+ RNDIS_PACKET_TYPE_DIRECTED = 0x00000001, ///< Directed packets. Directed packets contain a destination address equal to the station address of the NIC.
+ RNDIS_PACKET_TYPE_MULTICAST = 0x00000002, ///< Multicast address packets sent to addresses in the multicast address list.
+ RNDIS_PACKET_TYPE_ALL_MULTICAST = 0x00000004, ///< All multicast address packets, not just the ones enumerated in the multicast address list.
+ RNDIS_PACKET_TYPE_BROADCAST = 0x00000008, ///< Broadcast packets.
+ RNDIS_PACKET_TYPE_SOURCE_ROUTING = 0x00000010, ///< All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge.
+ RNDIS_PACKET_TYPE_PROMISCUOUS = 0x00000020, ///< Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not.
+ RNDIS_PACKET_TYPE_SMT = 0x00000040, ///< SMT packets that an FDDI NIC receives.
+ RNDIS_PACKET_TYPE_ALL_LOCAL = 0x00000080, ///< All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle.
+ RNDIS_PACKET_TYPE_GROUP = 0x00001000, ///< Packets sent to the current group address.
+ RNDIS_PACKET_TYPE_ALL_FUNCTIONAL = 0x00002000, ///< All functional address packets, not just the ones in the current functional address.
+ RNDIS_PACKET_TYPE_FUNCTIONAL = 0x00004000, ///< Functional address packets sent to addresses included in the current functional address.
+ RNDIS_PACKET_TYPE_MAC_FRAME = 0x00008000, ///< NIC driver frames that a Token Ring NIC receives.
+ RNDIS_PACKET_TYPE_NO_LOCAL = 0x00010000,
+} rndis_packet_filter_type_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_RNDIS_H_ */
+
+/** @} */
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.c b/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.c
new file mode 100755
index 0000000..767b917
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.c
@@ -0,0 +1,279 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "common/tusb_common.h"
+#include "cdc_host.h"
+#include "cdc_rndis_host.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
+
+CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUSB_HOST_DEVICE_MAX][8];
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
+
+static rndish_data_t rndish_data[CFG_TUSB_HOST_DEVICE_MAX];
+
+// TODO Microsoft requires message length for any get command must be at least 4096 bytes
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+static tusb_error_t rndis_body_subtask(void);
+static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
+ uint8_t * p_mess, uint32_t mess_length,
+ uint8_t *p_response );
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
+{
+ TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
+ TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA);
+
+ memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6);
+
+ return TUSB_ERROR_NONE;
+}
+
+//--------------------------------------------------------------------+
+// IMPLEMENTATION
+//--------------------------------------------------------------------+
+
+// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
+// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
+// forever loop cannot have any return at all.
+OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;)
+{
+ OSAL_TASK_BEGIN
+ rndis_body_subtask();
+ OSAL_TASK_END
+}
+
+static tusb_error_t rndis_body_subtask(void)
+{
+ static uint8_t relative_addr;
+
+ OSAL_SUBTASK_BEGIN
+
+ for (relative_addr = 0; relative_addr < CFG_TUSB_HOST_DEVICE_MAX; relative_addr++)
+ {
+
+ }
+
+ osal_task_delay(100);
+
+ OSAL_SUBTASK_END
+}
+
+//--------------------------------------------------------------------+
+// RNDIS-CDC Driver API
+//--------------------------------------------------------------------+
+void rndish_init(void)
+{
+ tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
+
+ //------------- Task creation -------------//
+
+ //------------- semaphore creation for notificaiton pipe -------------//
+ for(uint8_t i=0; i<CFG_TUSB_HOST_DEVICE_MAX; i++)
+ {
+ rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
+ }
+}
+
+void rndish_close(uint8_t dev_addr)
+{
+ osal_semaphore_reset( rndish_data[dev_addr-1].sem_notification_hdl );
+// tu_memclr(&rndish_data[dev_addr-1], sizeof(rndish_data_t)); TODO need to move semaphore & its handle out before memclr
+}
+
+
+static rndis_msg_initialize_t const msg_init =
+{
+ .type = RNDIS_MSG_INITIALIZE,
+ .length = sizeof(rndis_msg_initialize_t),
+ .request_id = 1, // TODO should use some magic number
+ .major_version = 1,
+ .minor_version = 0,
+ .max_xfer_size = 0x4000 // TODO mimic windows
+};
+
+static rndis_msg_query_t const msg_query_permanent_addr =
+{
+ .type = RNDIS_MSG_QUERY,
+ .length = sizeof(rndis_msg_query_t)+6,
+ .request_id = 1,
+ .oid = RNDIS_OID_802_3_PERMANENT_ADDRESS,
+ .buffer_length = 6,
+ .buffer_offset = 20,
+};
+
+static rndis_msg_set_t const msg_set_packet_filter =
+{
+ .type = RNDIS_MSG_SET,
+ .length = sizeof(rndis_msg_set_t)+4,
+ .request_id = 1,
+ .oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
+ .buffer_length = 4,
+ .buffer_offset = 20,
+};
+
+tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
+{
+ tusb_error_t error;
+
+ OSAL_SUBTASK_BEGIN
+
+ //------------- Message Initialize -------------//
+ memcpy(msg_payload, &msg_init, sizeof(rndis_msg_initialize_t));
+ STASK_INVOKE(
+ send_message_get_response_subtask( dev_addr, p_cdc,
+ msg_payload, sizeof(rndis_msg_initialize_t),
+ msg_payload),
+ error
+ );
+ if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
+
+ // TODO currently not support multiple data packets per xfer
+ rndis_msg_initialize_cmplt_t * const p_init_cmpt = (rndis_msg_initialize_cmplt_t *) msg_payload;
+ STASK_ASSERT(p_init_cmpt->type == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS &&
+ p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX);
+ rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size;
+
+ //------------- Message Query 802.3 Permanent Address -------------//
+ memcpy(msg_payload, &msg_query_permanent_addr, sizeof(rndis_msg_query_t));
+ tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address
+
+ STASK_INVOKE(
+ send_message_get_response_subtask( dev_addr, p_cdc,
+ msg_payload, sizeof(rndis_msg_query_t) + 6,
+ msg_payload),
+ error
+ );
+ if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
+
+ rndis_msg_query_cmplt_t * const p_query_cmpt = (rndis_msg_query_cmplt_t *) msg_payload;
+ STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS);
+ memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6);
+
+ //------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------//
+ memcpy(msg_payload, &msg_set_packet_filter, sizeof(rndis_msg_set_t));
+ tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags
+ ((rndis_msg_set_t*) msg_payload)->oid_buffer[0] = (RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_MULTICAST | RNDIS_PACKET_TYPE_BROADCAST);
+
+ STASK_INVOKE(
+ send_message_get_response_subtask( dev_addr, p_cdc,
+ msg_payload, sizeof(rndis_msg_set_t) + 4,
+ msg_payload),
+ error
+ );
+ if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
+
+ rndis_msg_set_cmplt_t * const p_set_cmpt = (rndis_msg_set_cmplt_t *) msg_payload;
+ STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS);
+
+ tusbh_cdc_rndis_mounted_cb(dev_addr);
+
+ OSAL_SUBTASK_END
+}
+
+void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
+{
+ if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) )
+ {
+ osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl );
+ }
+}
+
+//--------------------------------------------------------------------+
+// INTERNAL & HELPER
+//--------------------------------------------------------------------+
+static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
+ uint8_t * p_mess, uint32_t mess_length,
+ uint8_t *p_response)
+{
+ tusb_error_t error;
+
+ OSAL_SUBTASK_BEGIN
+
+ //------------- Send RNDIS Control Message -------------//
+ STASK_INVOKE(
+ usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
+ CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number,
+ mess_length, p_mess),
+ error
+ );
+ if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
+
+ //------------- waiting for Response Available notification -------------//
+ (void) hcd_pipe_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8, true);
+ osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error);
+ if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
+ STASK_ASSERT(msg_notification[dev_addr-1][0] == 1);
+
+ //------------- Get RNDIS Message Initialize Complete -------------//
+ STASK_INVOKE(
+ usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
+ CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number,
+ RNDIS_MSG_PAYLOAD_MAX, p_response),
+ error
+ );
+ if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
+
+ OSAL_SUBTASK_END
+}
+
+//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
+//{
+// tusb_error_t error;
+//
+// OSAL_SUBTASK_BEGIN
+//
+// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t)
+// {
+// .type = RNDIS_MSG_INITIALIZE,
+// .length = sizeof(rndis_msg_initialize_t),
+// .request_id = 1, // TODO should use some magic number
+// .major_version = 1,
+// .minor_version = 0,
+// .max_xfer_size = 0x4000 // TODO mimic windows
+// };
+//
+//
+//
+// OSAL_SUBTASK_END
+//}
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.h b/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.h
new file mode 100755
index 0000000..170cb3b
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/cdc/cdc_rndis_host.h
@@ -0,0 +1,63 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup CDC_RNDIS
+ * \defgroup CDC_RNSID_Host Host
+ * @{ */
+
+#ifndef _TUSB_CDC_RNDIS_HOST_H_
+#define _TUSB_CDC_RNDIS_HOST_H_
+
+#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "cdc_rndis.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// INTERNAL RNDIS-CDC Driver API
+//--------------------------------------------------------------------+
+typedef struct {
+ OSAL_SEM_DEF(semaphore_notification);
+ osal_semaphore_handle_t sem_notification_hdl; // used to wait on notification pipe
+ uint32_t max_xfer_size; // got from device's msg initialize complete
+ uint8_t mac_address[6];
+}rndish_data_t;
+
+void rndish_init(void);
+tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc);
+void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
+void rndish_close(uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_RNDIS_HOST_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.c b/sw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.c
new file mode 100755
index 0000000..5700615
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.c
@@ -0,0 +1,139 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME)
+
+#include "dfu_rt_device.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef enum {
+ DFU_REQUEST_DETACH = 0,
+ DFU_REQUEST_DNLOAD = 1,
+ DFU_REQUEST_UPLOAD = 2,
+ DFU_REQUEST_GETSTATUS = 3,
+ DFU_REQUEST_CLRSTATUS = 4,
+ DFU_REQUEST_GETSTATE = 5,
+ DFU_REQUEST_ABORT = 6,
+} dfu_requests_t;
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t status;
+ uint8_t poll_timeout[3];
+ uint8_t state;
+ uint8_t istring;
+} dfu_status_t;
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void dfu_rtd_init(void)
+{
+}
+
+void dfu_rtd_reset(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void) rhport;
+ (void) max_len;
+
+ // Ensure this is DFU Runtime
+ TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS &&
+ itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT, 0);
+
+ uint8_t const * p_desc = tu_desc_next( itf_desc );
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+
+ if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
+ {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ return drv_len;
+}
+
+// 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 dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // nothing to do with DATA and ACK stage
+ if ( stage != CONTROL_STAGE_SETUP ) return true;
+
+ TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
+
+ // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
+ if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
+ TUSB_REQ_SET_INTERFACE == request->bRequest )
+ {
+ tud_control_status(rhport, request);
+ return true;
+ }
+
+ // Handle class request only from here
+ TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ switch ( request->bRequest )
+ {
+ case DFU_REQUEST_DETACH:
+ tud_control_status(rhport, request);
+ tud_dfu_runtime_reboot_to_dfu_cb();
+ break;
+
+ case DFU_REQUEST_GETSTATUS:
+ {
+ // status = OK, poll timeout = 0, state = app idle, istring = 0
+ uint8_t status_response[6] = { 0, 0, 0, 0, 0, 0 };
+ tud_control_xfer(rhport, request, status_response, sizeof(status_response));
+ }
+ break;
+
+ default: return false; // stall unsupported request
+ }
+
+ return true;
+}
+
+bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) rhport;
+ (void) ep_addr;
+ (void) result;
+ (void) xferred_bytes;
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.h b/sw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.h
new file mode 100755
index 0000000..cff43d0
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/dfu/dfu_rt_device.h
@@ -0,0 +1,76 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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_DFU_RT_DEVICE_H_
+#define _TUSB_DFU_RT_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+//--------------------------------------------------------------------+
+// Common Definitions
+//--------------------------------------------------------------------+
+
+// DFU Protocol
+typedef enum
+{
+ DFU_PROTOCOL_RT = 1,
+ DFU_PROTOCOL_DFU = 2,
+} dfu_protocol_type_t;
+
+// DFU Descriptor Type
+typedef enum
+{
+ DFU_DESC_FUNCTIONAL = 0x21,
+} dfu_descriptor_type_t;
+
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+TU_ATTR_WEAK void tud_dfu_runtime_reboot_to_dfu_cb(void);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void dfu_rtd_init(void);
+void dfu_rtd_reset(uint8_t rhport);
+uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_DFU_RT_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/hid/hid.h b/sw/Core/Src/tinyusb/src/class/hid/hid.h
new file mode 100755
index 0000000..351a4d6
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/hid/hid.h
@@ -0,0 +1,1023 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_HID Human Interface Device (HID)
+ * @{ */
+
+#ifndef _TUSB_HID_H_
+#define _TUSB_HID_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Common Definitions
+//--------------------------------------------------------------------+
+/** \defgroup ClassDriver_HID_Common Common Definitions
+ * @{ */
+
+ /// USB HID Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */
+ uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */
+
+ uint16_t bcdHID; /**< Numeric expression identifying the HID Class Specification release */
+ uint8_t bCountryCode; /**< Numeric expression identifying country code of the localized hardware. */
+ uint8_t bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */
+
+ uint8_t bReportType; /**< Type of HID class report. */
+ uint16_t wReportLength; /**< the total size of the Report descriptor. */
+} tusb_hid_descriptor_hid_t;
+
+/// HID Subclass
+typedef enum
+{
+ HID_SUBCLASS_NONE = 0, ///< No Subclass
+ HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass
+}hid_subclass_type_t;
+
+/// HID Protocol
+typedef enum
+{
+ HID_PROTOCOL_NONE = 0, ///< None
+ HID_PROTOCOL_KEYBOARD = 1, ///< Keyboard
+ HID_PROTOCOL_MOUSE = 2 ///< Mouse
+}hid_protocol_type_t;
+
+/// HID Descriptor Type
+typedef enum
+{
+ HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor
+ HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor
+ HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor
+}hid_descriptor_type_t;
+
+/// HID Request Report Type
+typedef enum
+{
+ HID_REPORT_TYPE_INVALID = 0,
+ HID_REPORT_TYPE_INPUT, ///< Input
+ HID_REPORT_TYPE_OUTPUT, ///< Output
+ HID_REPORT_TYPE_FEATURE ///< Feature
+}hid_report_type_t;
+
+/// HID Class Specific Control Request
+typedef enum
+{
+ HID_REQ_CONTROL_GET_REPORT = 0x01, ///< Get Report
+ HID_REQ_CONTROL_GET_IDLE = 0x02, ///< Get Idle
+ HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol
+ HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report
+ HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle
+ HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol
+}hid_request_type_t;
+
+/// HID Country Code
+typedef enum
+{
+ HID_LOCAL_NotSupported = 0 , ///< NotSupported
+ HID_LOCAL_Arabic , ///< Arabic
+ HID_LOCAL_Belgian , ///< Belgian
+ HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual
+ HID_LOCAL_Canadian_French , ///< Canadian_French
+ HID_LOCAL_Czech_Republic , ///< Czech_Republic
+ HID_LOCAL_Danish , ///< Danish
+ HID_LOCAL_Finnish , ///< Finnish
+ HID_LOCAL_French , ///< French
+ HID_LOCAL_German , ///< German
+ HID_LOCAL_Greek , ///< Greek
+ HID_LOCAL_Hebrew , ///< Hebrew
+ HID_LOCAL_Hungary , ///< Hungary
+ HID_LOCAL_International , ///< International
+ HID_LOCAL_Italian , ///< Italian
+ HID_LOCAL_Japan_Katakana , ///< Japan_Katakana
+ HID_LOCAL_Korean , ///< Korean
+ HID_LOCAL_Latin_American , ///< Latin_American
+ HID_LOCAL_Netherlands_Dutch , ///< Netherlands/Dutch
+ HID_LOCAL_Norwegian , ///< Norwegian
+ HID_LOCAL_Persian_Farsi , ///< Persian (Farsi)
+ HID_LOCAL_Poland , ///< Poland
+ HID_LOCAL_Portuguese , ///< Portuguese
+ HID_LOCAL_Russia , ///< Russia
+ HID_LOCAL_Slovakia , ///< Slovakia
+ HID_LOCAL_Spanish , ///< Spanish
+ HID_LOCAL_Swedish , ///< Swedish
+ HID_LOCAL_Swiss_French , ///< Swiss/French
+ HID_LOCAL_Swiss_German , ///< Swiss/German
+ HID_LOCAL_Switzerland , ///< Switzerland
+ HID_LOCAL_Taiwan , ///< Taiwan
+ HID_LOCAL_Turkish_Q , ///< Turkish-Q
+ HID_LOCAL_UK , ///< UK
+ HID_LOCAL_US , ///< US
+ HID_LOCAL_Yugoslavia , ///< Yugoslavia
+ HID_LOCAL_Turkish_F ///< Turkish-F
+} hid_country_code_t;
+
+/** @} */
+
+//--------------------------------------------------------------------+
+// GAMEPAD
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Gamepad Gamepad
+ * @{ */
+
+/* From https://www.kernel.org/doc/html/latest/input/gamepad.html
+ ____________________________ __
+ / [__ZL__] [__ZR__] \ |
+ / [__ TL __] [__ TR __] \ | Front Triggers
+ __/________________________________\__ __|
+ / _ \ |
+ / /\ __ (N) \ |
+ / || __ |MO| __ _ _ \ | Main Pad
+ | <===DP===> |SE| |ST| (W) -|- (E) | |
+ \ || ___ ___ _ / |
+ /\ \/ / \ / \ (S) /\ __|
+ / \________ | LS | ____ | RS | ________/ \ |
+| / \ \___/ / \ \___/ / \ | | Control Sticks
+| / \_____/ \_____/ \ | __|
+| / \ |
+ \_____/ \_____/
+
+ |________|______| |______|___________|
+ D-Pad Left Right Action Pad
+ Stick Stick
+
+ |_____________|
+ Menu Pad
+
+ Most gamepads have the following features:
+ - Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST.
+ - D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right.
+ - Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START.
+ - Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also
+ provide a digital button if you press them.
+ - Triggers are located on the upper-side of the pad in vertical direction. The upper buttons
+ are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right.
+ - Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors.
+ */
+
+/// HID Gamepad Protocol Report.
+typedef struct TU_ATTR_PACKED
+{
+ int8_t x; ///< Delta x movement of left analog-stick
+ int8_t y; ///< Delta y movement of left analog-stick
+ int8_t z; ///< Delta z movement of right analog-joystick
+ int8_t rz; ///< Delta Rz movement of right analog-joystick
+ int8_t rx; ///< Delta Rx movement of analog left trigger
+ int8_t ry; ///< Delta Ry movement of analog right trigger
+ uint8_t hat; ///< Buttons mask for currently pressed buttons in the DPad/hat
+ uint16_t buttons; ///< Buttons mask for currently pressed buttons
+}hid_gamepad_report_t;
+
+/// Standard Gamepad Buttons Bitmap (from Linux input event codes)
+typedef enum
+{
+ GAMEPAD_BUTTON_A = TU_BIT(0), ///< A/South button
+ GAMEPAD_BUTTON_B = TU_BIT(1), ///< B/East button
+ GAMEPAD_BUTTON_C = TU_BIT(2), ///< C button
+ GAMEPAD_BUTTON_X = TU_BIT(3), ///< X/North button
+ GAMEPAD_BUTTON_Y = TU_BIT(4), ///< Y/West button
+ GAMEPAD_BUTTON_Z = TU_BIT(5), ///< Z button
+ GAMEPAD_BUTTON_TL = TU_BIT(6), ///< L1 button
+ GAMEPAD_BUTTON_TR = TU_BIT(7), ///< R1 button
+ GAMEPAD_BUTTON_TL2 = TU_BIT(8), ///< L2 button
+ GAMEPAD_BUTTON_TR2 = TU_BIT(9), ///< R2 button
+ GAMEPAD_BUTTON_SELECT = TU_BIT(10), ///< Select button
+ GAMEPAD_BUTTON_START = TU_BIT(11), ///< Start button
+ GAMEPAD_BUTTON_MODE = TU_BIT(12), ///< Mode button
+ GAMEPAD_BUTTON_THUMBL = TU_BIT(13), ///< L3 button
+ GAMEPAD_BUTTON_THUMBR = TU_BIT(14), ///< R3 button
+//GAMEPAD_BUTTON_ = TU_BIT(15), ///< Undefined button
+}hid_gamepad_button_bm_t;
+
+/// Standard Gamepad HAT/DPAD Buttons (from Linux input event codes)
+typedef enum
+{
+ GAMEPAD_HAT_CENTERED = 0, ///< DPAD_CENTERED
+ GAMEPAD_HAT_UP = 1, ///< DPAD_UP
+ GAMEPAD_HAT_UP_RIGHT = 2, ///< DPAD_UP_RIGHT
+ GAMEPAD_HAT_RIGHT = 3, ///< DPAD_RIGHT
+ GAMEPAD_HAT_DOWN_RIGHT = 4, ///< DPAD_DOWN_RIGHT
+ GAMEPAD_HAT_DOWN = 5, ///< DPAD_DOWN
+ GAMEPAD_HAT_DOWN_LEFT = 6, ///< DPAD_DOWN_LEFT
+ GAMEPAD_HAT_LEFT = 7, ///< DPAD_LEFT
+ GAMEPAD_HAT_UP_LEFT = 8, ///< DPAD_UP_LEFT
+}hid_gamepad_hat_t;
+
+/// @}
+
+//--------------------------------------------------------------------+
+// MOUSE
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Mouse Mouse
+ * @{ */
+
+/// Standard HID Boot Protocol Mouse Report.
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
+ int8_t x; /**< Current delta x movement of the mouse. */
+ int8_t y; /**< Current delta y movement on the mouse. */
+ int8_t wheel; /**< Current delta wheel movement on the mouse. */
+ int8_t pan; // using AC Pan
+} hid_mouse_report_t;
+
+/// Standard Mouse Buttons Bitmap
+typedef enum
+{
+ MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button
+ MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button
+ MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button
+ MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button,
+ MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button,
+}hid_mouse_button_bm_t;
+
+/// @}
+
+//--------------------------------------------------------------------+
+// Keyboard
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Keyboard Keyboard
+ * @{ */
+
+/// Standard HID Boot Protocol Keyboard Report.
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFIER_* masks). */
+ uint8_t reserved; /**< Reserved for OEM use, always set to 0. */
+ uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */
+} hid_keyboard_report_t;
+
+/// Keyboard modifier codes bitmap
+typedef enum
+{
+ KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control
+ KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift
+ KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt
+ KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window
+ KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control
+ KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift
+ KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt
+ KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window
+}hid_keyboard_modifier_bm_t;
+
+typedef enum
+{
+ KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED
+ KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED
+ KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED
+ KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode
+ KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode
+}hid_keyboard_led_bm_t;
+
+/// @}
+
+//--------------------------------------------------------------------+
+// HID KEYCODE
+//--------------------------------------------------------------------+
+#define HID_KEY_NONE 0x00
+#define HID_KEY_A 0x04
+#define HID_KEY_B 0x05
+#define HID_KEY_C 0x06
+#define HID_KEY_D 0x07
+#define HID_KEY_E 0x08
+#define HID_KEY_F 0x09
+#define HID_KEY_G 0x0A
+#define HID_KEY_H 0x0B
+#define HID_KEY_I 0x0C
+#define HID_KEY_J 0x0D
+#define HID_KEY_K 0x0E
+#define HID_KEY_L 0x0F
+#define HID_KEY_M 0x10
+#define HID_KEY_N 0x11
+#define HID_KEY_O 0x12
+#define HID_KEY_P 0x13
+#define HID_KEY_Q 0x14
+#define HID_KEY_R 0x15
+#define HID_KEY_S 0x16
+#define HID_KEY_T 0x17
+#define HID_KEY_U 0x18
+#define HID_KEY_V 0x19
+#define HID_KEY_W 0x1A
+#define HID_KEY_X 0x1B
+#define HID_KEY_Y 0x1C
+#define HID_KEY_Z 0x1D
+#define HID_KEY_1 0x1E
+#define HID_KEY_2 0x1F
+#define HID_KEY_3 0x20
+#define HID_KEY_4 0x21
+#define HID_KEY_5 0x22
+#define HID_KEY_6 0x23
+#define HID_KEY_7 0x24
+#define HID_KEY_8 0x25
+#define HID_KEY_9 0x26
+#define HID_KEY_0 0x27
+#define HID_KEY_ENTER 0x28
+#define HID_KEY_ESCAPE 0x29
+#define HID_KEY_BACKSPACE 0x2A
+#define HID_KEY_TAB 0x2B
+#define HID_KEY_SPACE 0x2C
+#define HID_KEY_MINUS 0x2D
+#define HID_KEY_EQUAL 0x2E
+#define HID_KEY_BRACKET_LEFT 0x2F
+#define HID_KEY_BRACKET_RIGHT 0x30
+#define HID_KEY_BACKSLASH 0x31
+#define HID_KEY_EUROPE_1 0x32
+#define HID_KEY_SEMICOLON 0x33
+#define HID_KEY_APOSTROPHE 0x34
+#define HID_KEY_GRAVE 0x35
+#define HID_KEY_COMMA 0x36
+#define HID_KEY_PERIOD 0x37
+#define HID_KEY_SLASH 0x38
+#define HID_KEY_CAPS_LOCK 0x39
+#define HID_KEY_F1 0x3A
+#define HID_KEY_F2 0x3B
+#define HID_KEY_F3 0x3C
+#define HID_KEY_F4 0x3D
+#define HID_KEY_F5 0x3E
+#define HID_KEY_F6 0x3F
+#define HID_KEY_F7 0x40
+#define HID_KEY_F8 0x41
+#define HID_KEY_F9 0x42
+#define HID_KEY_F10 0x43
+#define HID_KEY_F11 0x44
+#define HID_KEY_F12 0x45
+#define HID_KEY_PRINT_SCREEN 0x46
+#define HID_KEY_SCROLL_LOCK 0x47
+#define HID_KEY_PAUSE 0x48
+#define HID_KEY_INSERT 0x49
+#define HID_KEY_HOME 0x4A
+#define HID_KEY_PAGE_UP 0x4B
+#define HID_KEY_DELETE 0x4C
+#define HID_KEY_END 0x4D
+#define HID_KEY_PAGE_DOWN 0x4E
+#define HID_KEY_ARROW_RIGHT 0x4F
+#define HID_KEY_ARROW_LEFT 0x50
+#define HID_KEY_ARROW_DOWN 0x51
+#define HID_KEY_ARROW_UP 0x52
+#define HID_KEY_NUM_LOCK 0x53
+#define HID_KEY_KEYPAD_DIVIDE 0x54
+#define HID_KEY_KEYPAD_MULTIPLY 0x55
+#define HID_KEY_KEYPAD_SUBTRACT 0x56
+#define HID_KEY_KEYPAD_ADD 0x57
+#define HID_KEY_KEYPAD_ENTER 0x58
+#define HID_KEY_KEYPAD_1 0x59
+#define HID_KEY_KEYPAD_2 0x5A
+#define HID_KEY_KEYPAD_3 0x5B
+#define HID_KEY_KEYPAD_4 0x5C
+#define HID_KEY_KEYPAD_5 0x5D
+#define HID_KEY_KEYPAD_6 0x5E
+#define HID_KEY_KEYPAD_7 0x5F
+#define HID_KEY_KEYPAD_8 0x60
+#define HID_KEY_KEYPAD_9 0x61
+#define HID_KEY_KEYPAD_0 0x62
+#define HID_KEY_KEYPAD_DECIMAL 0x63
+#define HID_KEY_EUROPE_2 0x64
+#define HID_KEY_APPLICATION 0x65
+#define HID_KEY_POWER 0x66
+#define HID_KEY_KEYPAD_EQUAL 0x67
+#define HID_KEY_F13 0x68
+#define HID_KEY_F14 0x69
+#define HID_KEY_F15 0x6A
+#define HID_KEY_F16 0x6B
+#define HID_KEY_F17 0x6C
+#define HID_KEY_F18 0x6D
+#define HID_KEY_F19 0x6E
+#define HID_KEY_F20 0x6F
+#define HID_KEY_F21 0x70
+#define HID_KEY_F22 0x71
+#define HID_KEY_F23 0x72
+#define HID_KEY_F24 0x73
+#define HID_KEY_EXECUTE 0x74
+#define HID_KEY_HELP 0x75
+#define HID_KEY_MENU 0x76
+#define HID_KEY_SELECT 0x77
+#define HID_KEY_STOP 0x78
+#define HID_KEY_AGAIN 0x79
+#define HID_KEY_UNDO 0x7A
+#define HID_KEY_CUT 0x7B
+#define HID_KEY_COPY 0x7C
+#define HID_KEY_PASTE 0x7D
+#define HID_KEY_FIND 0x7E
+#define HID_KEY_MUTE 0x7F
+#define HID_KEY_VOLUME_UP 0x80
+#define HID_KEY_VOLUME_DOWN 0x81
+#define HID_KEY_LOCKING_CAPS_LOCK 0x82
+#define HID_KEY_LOCKING_NUM_LOCK 0x83
+#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
+#define HID_KEY_KEYPAD_COMMA 0x85
+#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
+#define HID_KEY_KANJI1 0x87
+#define HID_KEY_KANJI2 0x88
+#define HID_KEY_KANJI3 0x89
+#define HID_KEY_KANJI4 0x8A
+#define HID_KEY_KANJI5 0x8B
+#define HID_KEY_KANJI6 0x8C
+#define HID_KEY_KANJI7 0x8D
+#define HID_KEY_KANJI8 0x8E
+#define HID_KEY_KANJI9 0x8F
+#define HID_KEY_LANG1 0x90
+#define HID_KEY_LANG2 0x91
+#define HID_KEY_LANG3 0x92
+#define HID_KEY_LANG4 0x93
+#define HID_KEY_LANG5 0x94
+#define HID_KEY_LANG6 0x95
+#define HID_KEY_LANG7 0x96
+#define HID_KEY_LANG8 0x97
+#define HID_KEY_LANG9 0x98
+#define HID_KEY_ALTERNATE_ERASE 0x99
+#define HID_KEY_SYSREQ_ATTENTION 0x9A
+#define HID_KEY_CANCEL 0x9B
+#define HID_KEY_CLEAR 0x9C
+#define HID_KEY_PRIOR 0x9D
+#define HID_KEY_RETURN 0x9E
+#define HID_KEY_SEPARATOR 0x9F
+#define HID_KEY_OUT 0xA0
+#define HID_KEY_OPER 0xA1
+#define HID_KEY_CLEAR_AGAIN 0xA2
+#define HID_KEY_CRSEL_PROPS 0xA3
+#define HID_KEY_EXSEL 0xA4
+// RESERVED 0xA5-DF
+#define HID_KEY_CONTROL_LEFT 0xE0
+#define HID_KEY_SHIFT_LEFT 0xE1
+#define HID_KEY_ALT_LEFT 0xE2
+#define HID_KEY_GUI_LEFT 0xE3
+#define HID_KEY_CONTROL_RIGHT 0xE4
+#define HID_KEY_SHIFT_RIGHT 0xE5
+#define HID_KEY_ALT_RIGHT 0xE6
+#define HID_KEY_GUI_RIGHT 0xE7
+
+
+//--------------------------------------------------------------------+
+// REPORT DESCRIPTOR
+//--------------------------------------------------------------------+
+//------------- ITEM & TAG -------------//
+#define HID_REPORT_DATA_0(data)
+#define HID_REPORT_DATA_1(data) , data
+#define HID_REPORT_DATA_2(data) , U16_TO_U8S_LE(data)
+#define HID_REPORT_DATA_3(data) , U32_TO_U8S_LE(data)
+
+#define HID_REPORT_ITEM(data, tag, type, size) \
+ (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data)
+
+#define RI_TYPE_MAIN 0
+#define RI_TYPE_GLOBAL 1
+#define RI_TYPE_LOCAL 2
+
+//------------- MAIN ITEMS 6.2.2.4 -------------//
+#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1)
+#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1)
+#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1)
+#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1)
+#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0)
+
+//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------//
+#define HID_DATA (0<<0)
+#define HID_CONSTANT (1<<0)
+
+#define HID_ARRAY (0<<1)
+#define HID_VARIABLE (1<<1)
+
+#define HID_ABSOLUTE (0<<2)
+#define HID_RELATIVE (1<<2)
+
+#define HID_WRAP_NO (0<<3)
+#define HID_WRAP (1<<3)
+
+#define HID_LINEAR (0<<4)
+#define HID_NONLINEAR (1<<4)
+
+#define HID_PREFERRED_STATE (0<<5)
+#define HID_PREFERRED_NO (1<<5)
+
+#define HID_NO_NULL_POSITION (0<<6)
+#define HID_NULL_STATE (1<<6)
+
+#define HID_NON_VOLATILE (0<<7)
+#define HID_VOLATILE (1<<7)
+
+#define HID_BITFIELD (0<<8)
+#define HID_BUFFERED_BYTES (1<<8)
+
+//------------- COLLECTION ITEM 6.2.2.6 -------------//
+enum {
+ HID_COLLECTION_PHYSICAL = 0,
+ HID_COLLECTION_APPLICATION,
+ HID_COLLECTION_LOGICAL,
+ HID_COLLECTION_REPORT,
+ HID_COLLECTION_NAMED_ARRAY,
+ HID_COLLECTION_USAGE_SWITCH,
+ HID_COLLECTION_USAGE_MODIFIER
+};
+
+//------------- GLOBAL ITEMS 6.2.2.7 -------------//
+#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1)
+#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n)
+
+#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1)
+#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n)
+
+#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1)
+#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n)
+
+#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1)
+#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n)
+
+#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1)
+#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n)
+
+#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1)
+#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n)
+
+#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1)
+#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n)
+
+#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1)
+#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n)
+
+#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1),
+#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n),
+
+#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1)
+#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n)
+
+#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0)
+#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0)
+
+//------------- LOCAL ITEMS 6.2.2.8 -------------//
+#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1)
+#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n)
+
+#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1)
+#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n)
+
+#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1)
+#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n)
+
+//--------------------------------------------------------------------+
+// Usage Table
+//--------------------------------------------------------------------+
+
+/// HID Usage Table - Table 1: Usage Page Summary
+enum {
+ HID_USAGE_PAGE_DESKTOP = 0x01,
+ HID_USAGE_PAGE_SIMULATE = 0x02,
+ HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
+ HID_USAGE_PAGE_SPORT = 0x04,
+ HID_USAGE_PAGE_GAME = 0x05,
+ HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
+ HID_USAGE_PAGE_KEYBOARD = 0x07,
+ HID_USAGE_PAGE_LED = 0x08,
+ HID_USAGE_PAGE_BUTTON = 0x09,
+ HID_USAGE_PAGE_ORDINAL = 0x0a,
+ HID_USAGE_PAGE_TELEPHONY = 0x0b,
+ HID_USAGE_PAGE_CONSUMER = 0x0c,
+ HID_USAGE_PAGE_DIGITIZER = 0x0d,
+ HID_USAGE_PAGE_PID = 0x0f,
+ HID_USAGE_PAGE_UNICODE = 0x10,
+ HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
+ HID_USAGE_PAGE_MEDICAL = 0x40,
+ HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83
+ HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
+ HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
+ HID_USAGE_PAGE_SCALE = 0x8d,
+ HID_USAGE_PAGE_MSR = 0x8e,
+ HID_USAGE_PAGE_CAMERA = 0x90,
+ HID_USAGE_PAGE_ARCADE = 0x91,
+ HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
+};
+
+/// HID Usage Table - Table 6: Generic Desktop Page
+enum {
+ HID_USAGE_DESKTOP_POINTER = 0x01,
+ HID_USAGE_DESKTOP_MOUSE = 0x02,
+ HID_USAGE_DESKTOP_JOYSTICK = 0x04,
+ HID_USAGE_DESKTOP_GAMEPAD = 0x05,
+ HID_USAGE_DESKTOP_KEYBOARD = 0x06,
+ HID_USAGE_DESKTOP_KEYPAD = 0x07,
+ HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08,
+ HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09,
+ HID_USAGE_DESKTOP_X = 0x30,
+ HID_USAGE_DESKTOP_Y = 0x31,
+ HID_USAGE_DESKTOP_Z = 0x32,
+ HID_USAGE_DESKTOP_RX = 0x33,
+ HID_USAGE_DESKTOP_RY = 0x34,
+ HID_USAGE_DESKTOP_RZ = 0x35,
+ HID_USAGE_DESKTOP_SLIDER = 0x36,
+ HID_USAGE_DESKTOP_DIAL = 0x37,
+ HID_USAGE_DESKTOP_WHEEL = 0x38,
+ HID_USAGE_DESKTOP_HAT_SWITCH = 0x39,
+ HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a,
+ HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b,
+ HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c,
+ HID_USAGE_DESKTOP_START = 0x3d,
+ HID_USAGE_DESKTOP_SELECT = 0x3e,
+ HID_USAGE_DESKTOP_VX = 0x40,
+ HID_USAGE_DESKTOP_VY = 0x41,
+ HID_USAGE_DESKTOP_VZ = 0x42,
+ HID_USAGE_DESKTOP_VBRX = 0x43,
+ HID_USAGE_DESKTOP_VBRY = 0x44,
+ HID_USAGE_DESKTOP_VBRZ = 0x45,
+ HID_USAGE_DESKTOP_VNO = 0x46,
+ HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47,
+ HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48,
+ HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80,
+ HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81,
+ HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82,
+ HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83,
+ HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84,
+ HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85,
+ HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C,
+ HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D,
+ HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E,
+ HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F,
+ HID_USAGE_DESKTOP_DPAD_UP = 0x90,
+ HID_USAGE_DESKTOP_DPAD_DOWN = 0x91,
+ HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92,
+ HID_USAGE_DESKTOP_DPAD_LEFT = 0x93,
+ HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0,
+ HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1,
+ HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2,
+ HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3,
+ HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4,
+ HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5,
+ HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6,
+ HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7,
+ HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6,
+ HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7
+};
+
+
+/// HID Usage Table: Consumer Page (0x0C)
+/// Only contains controls that supported by Windows (whole list is too long)
+enum
+{
+ // Generic Control
+ HID_USAGE_CONSUMER_CONTROL = 0x0001,
+
+ // Power Control
+ HID_USAGE_CONSUMER_POWER = 0x0030,
+ HID_USAGE_CONSUMER_RESET = 0x0031,
+ HID_USAGE_CONSUMER_SLEEP = 0x0032,
+
+ // Screen Brightness
+ HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F,
+ HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070,
+
+ // These HID usages operate only on mobile systems (battery powered) and
+ // require Windows 8 (build 8302 or greater).
+ HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C,
+ HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6,
+ HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7,
+ HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8,
+
+ // Media Control
+ HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD,
+ HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5,
+ HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6,
+ HID_USAGE_CONSUMER_STOP = 0x00B7,
+ HID_USAGE_CONSUMER_VOLUME = 0x00E0,
+ HID_USAGE_CONSUMER_MUTE = 0x00E2,
+ HID_USAGE_CONSUMER_BASS = 0x00E3,
+ HID_USAGE_CONSUMER_TREBLE = 0x00E4,
+ HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5,
+ HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9,
+ HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA,
+ HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152,
+ HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153,
+ HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154,
+ HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155,
+
+ // Application Launcher
+ HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183,
+ HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A,
+ HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192,
+ HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194,
+
+ // Browser/Explorer Specific
+ HID_USAGE_CONSUMER_AC_SEARCH = 0x0221,
+ HID_USAGE_CONSUMER_AC_HOME = 0x0223,
+ HID_USAGE_CONSUMER_AC_BACK = 0x0224,
+ HID_USAGE_CONSUMER_AC_FORWARD = 0x0225,
+ HID_USAGE_CONSUMER_AC_STOP = 0x0226,
+ HID_USAGE_CONSUMER_AC_REFRESH = 0x0227,
+ HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A,
+
+ // Mouse Horizontal scroll
+ HID_USAGE_CONSUMER_AC_PAN = 0x0238,
+};
+
+/*--------------------------------------------------------------------
+ * ASCII to KEYCODE Conversion
+ * Expand to array of [128][2] (shift, keycode)
+ *
+ * Usage: example to convert input chr into keyboard report (modifier + keycode)
+ *
+ * uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
+ *
+ * uint8_t keycode[6] = { 0 };
+ * uint8_t modifier = 0;
+ *
+ * if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
+ * keycode[0] = conv_table[chr][1];
+ * tud_hid_keyboard_report(report_id, modifier, keycode);
+ *
+ *--------------------------------------------------------------------*/
+#define HID_ASCII_TO_KEYCODE \
+ {0, 0 }, /* 0x00 Null */ \
+ {0, 0 }, /* 0x01 */ \
+ {0, 0 }, /* 0x02 */ \
+ {0, 0 }, /* 0x03 */ \
+ {0, 0 }, /* 0x04 */ \
+ {0, 0 }, /* 0x05 */ \
+ {0, 0 }, /* 0x06 */ \
+ {0, 0 }, /* 0x07 */ \
+ {0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \
+ {0, HID_KEY_TAB }, /* 0x09 Tab */ \
+ {0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \
+ {0, 0 }, /* 0x0B */ \
+ {0, 0 }, /* 0x0C */ \
+ {0, HID_KEY_RETURN }, /* 0x0D CR */ \
+ {0, 0 }, /* 0x0E */ \
+ {0, 0 }, /* 0x0F */ \
+ {0, 0 }, /* 0x10 */ \
+ {0, 0 }, /* 0x11 */ \
+ {0, 0 }, /* 0x12 */ \
+ {0, 0 }, /* 0x13 */ \
+ {0, 0 }, /* 0x14 */ \
+ {0, 0 }, /* 0x15 */ \
+ {0, 0 }, /* 0x16 */ \
+ {0, 0 }, /* 0x17 */ \
+ {0, 0 }, /* 0x18 */ \
+ {0, 0 }, /* 0x19 */ \
+ {0, 0 }, /* 0x1A */ \
+ {0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \
+ {0, 0 }, /* 0x1C */ \
+ {0, 0 }, /* 0x1D */ \
+ {0, 0 }, /* 0x1E */ \
+ {0, 0 }, /* 0x1F */ \
+ \
+ {0, HID_KEY_SPACE }, /* 0x20 */ \
+ {1, HID_KEY_1 }, /* 0x21 ! */ \
+ {1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \
+ {1, HID_KEY_3 }, /* 0x23 # */ \
+ {1, HID_KEY_4 }, /* 0x24 $ */ \
+ {1, HID_KEY_5 }, /* 0x25 % */ \
+ {1, HID_KEY_7 }, /* 0x26 & */ \
+ {0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \
+ {1, HID_KEY_9 }, /* 0x28 ( */ \
+ {1, HID_KEY_0 }, /* 0x29 ) */ \
+ {1, HID_KEY_8 }, /* 0x2A * */ \
+ {1, HID_KEY_EQUAL }, /* 0x2B + */ \
+ {0, HID_KEY_COMMA }, /* 0x2C , */ \
+ {0, HID_KEY_MINUS }, /* 0x2D - */ \
+ {0, HID_KEY_PERIOD }, /* 0x2E . */ \
+ {0, HID_KEY_SLASH }, /* 0x2F / */ \
+ {0, HID_KEY_0 }, /* 0x30 0 */ \
+ {0, HID_KEY_1 }, /* 0x31 1 */ \
+ {0, HID_KEY_2 }, /* 0x32 2 */ \
+ {0, HID_KEY_3 }, /* 0x33 3 */ \
+ {0, HID_KEY_4 }, /* 0x34 4 */ \
+ {0, HID_KEY_5 }, /* 0x35 5 */ \
+ {0, HID_KEY_6 }, /* 0x36 6 */ \
+ {0, HID_KEY_7 }, /* 0x37 7 */ \
+ {0, HID_KEY_8 }, /* 0x38 8 */ \
+ {0, HID_KEY_9 }, /* 0x39 9 */ \
+ {1, HID_KEY_SEMICOLON }, /* 0x3A : */ \
+ {0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \
+ {1, HID_KEY_COMMA }, /* 0x3C < */ \
+ {0, HID_KEY_EQUAL }, /* 0x3D = */ \
+ {1, HID_KEY_PERIOD }, /* 0x3E > */ \
+ {1, HID_KEY_SLASH }, /* 0x3F ? */ \
+ \
+ {1, HID_KEY_2 }, /* 0x40 @ */ \
+ {1, HID_KEY_A }, /* 0x41 A */ \
+ {1, HID_KEY_B }, /* 0x42 B */ \
+ {1, HID_KEY_C }, /* 0x43 C */ \
+ {1, HID_KEY_D }, /* 0x44 D */ \
+ {1, HID_KEY_E }, /* 0x45 E */ \
+ {1, HID_KEY_F }, /* 0x46 F */ \
+ {1, HID_KEY_G }, /* 0x47 G */ \
+ {1, HID_KEY_H }, /* 0x48 H */ \
+ {1, HID_KEY_I }, /* 0x49 I */ \
+ {1, HID_KEY_J }, /* 0x4A J */ \
+ {1, HID_KEY_K }, /* 0x4B K */ \
+ {1, HID_KEY_L }, /* 0x4C L */ \
+ {1, HID_KEY_M }, /* 0x4D M */ \
+ {1, HID_KEY_N }, /* 0x4E N */ \
+ {1, HID_KEY_O }, /* 0x4F O */ \
+ {1, HID_KEY_P }, /* 0x50 P */ \
+ {1, HID_KEY_Q }, /* 0x51 Q */ \
+ {1, HID_KEY_R }, /* 0x52 R */ \
+ {1, HID_KEY_S }, /* 0x53 S */ \
+ {1, HID_KEY_T }, /* 0x55 T */ \
+ {1, HID_KEY_U }, /* 0x55 U */ \
+ {1, HID_KEY_V }, /* 0x56 V */ \
+ {1, HID_KEY_W }, /* 0x57 W */ \
+ {1, HID_KEY_X }, /* 0x58 X */ \
+ {1, HID_KEY_Y }, /* 0x59 Y */ \
+ {1, HID_KEY_Z }, /* 0x5A Z */ \
+ {0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \
+ {0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \
+ {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \
+ {1, HID_KEY_6 }, /* 0x5E ^ */ \
+ {1, HID_KEY_MINUS }, /* 0x5F _ */ \
+ \
+ {0, HID_KEY_GRAVE }, /* 0x60 ` */ \
+ {0, HID_KEY_A }, /* 0x61 a */ \
+ {0, HID_KEY_B }, /* 0x62 b */ \
+ {0, HID_KEY_C }, /* 0x63 c */ \
+ {0, HID_KEY_D }, /* 0x66 d */ \
+ {0, HID_KEY_E }, /* 0x65 e */ \
+ {0, HID_KEY_F }, /* 0x66 f */ \
+ {0, HID_KEY_G }, /* 0x67 g */ \
+ {0, HID_KEY_H }, /* 0x68 h */ \
+ {0, HID_KEY_I }, /* 0x69 i */ \
+ {0, HID_KEY_J }, /* 0x6A j */ \
+ {0, HID_KEY_K }, /* 0x6B k */ \
+ {0, HID_KEY_L }, /* 0x6C l */ \
+ {0, HID_KEY_M }, /* 0x6D m */ \
+ {0, HID_KEY_N }, /* 0x6E n */ \
+ {0, HID_KEY_O }, /* 0x6F o */ \
+ {0, HID_KEY_P }, /* 0x70 p */ \
+ {0, HID_KEY_Q }, /* 0x71 q */ \
+ {0, HID_KEY_R }, /* 0x72 r */ \
+ {0, HID_KEY_S }, /* 0x73 s */ \
+ {0, HID_KEY_T }, /* 0x75 t */ \
+ {0, HID_KEY_U }, /* 0x75 u */ \
+ {0, HID_KEY_V }, /* 0x76 v */ \
+ {0, HID_KEY_W }, /* 0x77 w */ \
+ {0, HID_KEY_X }, /* 0x78 x */ \
+ {0, HID_KEY_Y }, /* 0x79 y */ \
+ {0, HID_KEY_Z }, /* 0x7A z */ \
+ {1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \
+ {1, HID_KEY_BACKSLASH }, /* 0x7C | */ \
+ {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \
+ {1, HID_KEY_GRAVE }, /* 0x7E ~ */ \
+ {0, HID_KEY_DELETE } /* 0x7F Delete */ \
+
+/*--------------------------------------------------------------------
+ * KEYCODE to Ascii Conversion
+ * Expand to array of [128][2] (ascii without shift, ascii with shift)
+ *
+ * Usage: example to convert ascii from keycode (key) and shift modifier (shift).
+ * Here we assume key < 128 ( printable )
+ *
+ * uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII };
+ * char ch = shift ? conv_table[chr][1] : conv_table[chr][0];
+ *
+ *--------------------------------------------------------------------*/
+#define HID_KEYCODE_TO_ASCII \
+ {0 , 0 }, /* 0x00 */ \
+ {0 , 0 }, /* 0x01 */ \
+ {0 , 0 }, /* 0x02 */ \
+ {0 , 0 }, /* 0x03 */ \
+ {'a' , 'A' }, /* 0x04 */ \
+ {'b' , 'B' }, /* 0x05 */ \
+ {'c' , 'C' }, /* 0x06 */ \
+ {'d' , 'D' }, /* 0x07 */ \
+ {'e' , 'E' }, /* 0x08 */ \
+ {'f' , 'F' }, /* 0x09 */ \
+ {'g' , 'G' }, /* 0x0a */ \
+ {'h' , 'H' }, /* 0x0b */ \
+ {'i' , 'I' }, /* 0x0c */ \
+ {'j' , 'J' }, /* 0x0d */ \
+ {'k' , 'K' }, /* 0x0e */ \
+ {'l' , 'L' }, /* 0x0f */ \
+ {'m' , 'M' }, /* 0x10 */ \
+ {'n' , 'N' }, /* 0x11 */ \
+ {'o' , 'O' }, /* 0x12 */ \
+ {'p' , 'P' }, /* 0x13 */ \
+ {'q' , 'Q' }, /* 0x14 */ \
+ {'r' , 'R' }, /* 0x15 */ \
+ {'s' , 'S' }, /* 0x16 */ \
+ {'t' , 'T' }, /* 0x17 */ \
+ {'u' , 'U' }, /* 0x18 */ \
+ {'v' , 'V' }, /* 0x19 */ \
+ {'w' , 'W' }, /* 0x1a */ \
+ {'x' , 'X' }, /* 0x1b */ \
+ {'y' , 'Y' }, /* 0x1c */ \
+ {'z' , 'Z' }, /* 0x1d */ \
+ {'1' , '!' }, /* 0x1e */ \
+ {'2' , '@' }, /* 0x1f */ \
+ {'3' , '#' }, /* 0x20 */ \
+ {'4' , '$' }, /* 0x21 */ \
+ {'5' , '%' }, /* 0x22 */ \
+ {'6' , '^' }, /* 0x23 */ \
+ {'7' , '&' }, /* 0x24 */ \
+ {'8' , '*' }, /* 0x25 */ \
+ {'9' , '(' }, /* 0x26 */ \
+ {'0' , ')' }, /* 0x27 */ \
+ {'\r' , '\r' }, /* 0x28 */ \
+ {'\x1b', '\x1b' }, /* 0x29 */ \
+ {'\b' , '\b' }, /* 0x2a */ \
+ {'\t' , '\t' }, /* 0x2b */ \
+ {' ' , ' ' }, /* 0x2c */ \
+ {'-' , '_' }, /* 0x2d */ \
+ {'=' , '+' }, /* 0x2e */ \
+ {'[' , '{' }, /* 0x2f */ \
+ {']' , '}' }, /* 0x30 */ \
+ {'\\' , '|' }, /* 0x31 */ \
+ {'#' , '~' }, /* 0x32 */ \
+ {';' , ':' }, /* 0x33 */ \
+ {'\'' , '\"' }, /* 0x34 */ \
+ {'`' , '~' }, /* 0x35 */ \
+ {',' , '<' }, /* 0x36 */ \
+ {'.' , '>' }, /* 0x37 */ \
+ {'/' , '?' }, /* 0x38 */ \
+ \
+ {0 , 0 }, /* 0x39 */ \
+ {0 , 0 }, /* 0x3a */ \
+ {0 , 0 }, /* 0x3b */ \
+ {0 , 0 }, /* 0x3c */ \
+ {0 , 0 }, /* 0x3d */ \
+ {0 , 0 }, /* 0x3e */ \
+ {0 , 0 }, /* 0x3f */ \
+ {0 , 0 }, /* 0x40 */ \
+ {0 , 0 }, /* 0x41 */ \
+ {0 , 0 }, /* 0x42 */ \
+ {0 , 0 }, /* 0x43 */ \
+ {0 , 0 }, /* 0x44 */ \
+ {0 , 0 }, /* 0x45 */ \
+ {0 , 0 }, /* 0x46 */ \
+ {0 , 0 }, /* 0x47 */ \
+ {0 , 0 }, /* 0x48 */ \
+ {0 , 0 }, /* 0x49 */ \
+ {0 , 0 }, /* 0x4a */ \
+ {0 , 0 }, /* 0x4b */ \
+ {0 , 0 }, /* 0x4c */ \
+ {0 , 0 }, /* 0x4d */ \
+ {0 , 0 }, /* 0x4e */ \
+ {0 , 0 }, /* 0x4f */ \
+ {0 , 0 }, /* 0x50 */ \
+ {0 , 0 }, /* 0x51 */ \
+ {0 , 0 }, /* 0x52 */ \
+ {0 , 0 }, /* 0x53 */ \
+ \
+ {'/' , '/' }, /* 0x54 */ \
+ {'*' , '*' }, /* 0x55 */ \
+ {'-' , '-' }, /* 0x56 */ \
+ {'+' , '+' }, /* 0x57 */ \
+ {'\r' , '\r' }, /* 0x58 */ \
+ {'1' , 0 }, /* 0x59 */ \
+ {'2' , 0 }, /* 0x5a */ \
+ {'3' , 0 }, /* 0x5b */ \
+ {'4' , 0 }, /* 0x5c */ \
+ {'5' , '5' }, /* 0x5d */ \
+ {'6' , 0 }, /* 0x5e */ \
+ {'7' , 0 }, /* 0x5f */ \
+ {'8' , 0 }, /* 0x60 */ \
+ {'9' , 0 }, /* 0x61 */ \
+ {'0' , 0 }, /* 0x62 */ \
+ {'0' , 0 }, /* 0x63 */ \
+ {'=' , '=' }, /* 0x67 */ \
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_HID_H__ */
+
+/// @}
diff --git a/sw/Core/Src/tinyusb/src/class/hid/hid_device.c b/sw/Core/Src/tinyusb/src/class/hid/hid_device.c
new file mode 100755
index 0000000..18d35bc
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/hid/hid_device.c
@@ -0,0 +1,386 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_HID)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "common/tusb_common.h"
+#include "hid_device.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out; // optional Out endpoint
+ uint8_t boot_protocol; // Boot mouse or keyboard
+ bool boot_mode; // default = false (Report)
+ uint8_t idle_rate; // up to application to handle idle rate
+ uint16_t report_desc_len;
+
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
+
+ tusb_hid_descriptor_hid_t const * hid_descriptor;
+} hidd_interface_t;
+
+CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
+
+/*------------- Helpers -------------*/
+static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
+{
+ for (uint8_t i=0; i < CFG_TUD_HID; i++ )
+ {
+ if ( itf_num == _hidd_itf[i].itf_num ) return i;
+ }
+
+ return 0xFF;
+}
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+bool tud_hid_n_ready(uint8_t itf)
+{
+ uint8_t const ep_in = _hidd_itf[itf].ep_in;
+ return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in);
+}
+
+bool tud_hid_n_report(uint8_t itf, uint8_t report_id, void const* report, uint8_t len)
+{
+ uint8_t const rhport = 0;
+ hidd_interface_t * p_hid = &_hidd_itf[itf];
+
+ // claim endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
+
+ // prepare data
+ if (report_id)
+ {
+ len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1);
+
+ p_hid->epin_buf[0] = report_id;
+ memcpy(p_hid->epin_buf+1, report, len);
+ len++;
+ }else
+ {
+ // If report id = 0, skip ID field
+ len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE);
+ memcpy(p_hid->epin_buf, report, len);
+ }
+
+ return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
+}
+
+bool tud_hid_n_boot_mode(uint8_t itf)
+{
+ return _hidd_itf[itf].boot_mode;
+}
+
+bool tud_hid_n_keyboard_report(uint8_t itf, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
+{
+ hid_keyboard_report_t report;
+
+ report.modifier = modifier;
+
+ if ( keycode )
+ {
+ memcpy(report.keycode, keycode, 6);
+ }else
+ {
+ tu_memclr(report.keycode, 6);
+ }
+
+ return tud_hid_n_report(itf, report_id, &report, sizeof(report));
+}
+
+bool tud_hid_n_mouse_report(uint8_t itf, uint8_t report_id,
+ uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
+{
+ hid_mouse_report_t report =
+ {
+ .buttons = buttons,
+ .x = x,
+ .y = y,
+ .wheel = vertical,
+ .pan = horizontal
+ };
+
+ return tud_hid_n_report(itf, report_id, &report, sizeof(report));
+}
+
+bool tud_hid_n_gamepad_report(uint8_t itf, uint8_t report_id,
+ int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint16_t buttons)
+{
+ hid_gamepad_report_t report =
+ {
+ .x = x,
+ .y = y,
+ .z = z,
+ .rz = rz,
+ .rx = rx,
+ .ry = ry,
+ .hat = hat,
+ .buttons = buttons,
+ };
+
+ return tud_hid_n_report(itf, report_id, &report, sizeof(report));
+}
+
+//--------------------------------------------------------------------+
+// USBD-CLASS API
+//--------------------------------------------------------------------+
+void hidd_init(void)
+{
+ hidd_reset(TUD_OPT_RHPORT);
+}
+
+void hidd_reset(uint8_t rhport)
+{
+ (void) rhport;
+ tu_memclr(_hidd_itf, sizeof(_hidd_itf));
+}
+
+uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
+{
+ TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
+
+ // len = interface + hid + n*endpoints
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ TU_ASSERT(max_len >= drv_len, 0);
+
+ // Find available interface
+ hidd_interface_t * p_hid = NULL;
+ uint8_t hid_id;
+ for(hid_id=0; hid_id<CFG_TUD_HID; hid_id++)
+ {
+ if ( _hidd_itf[hid_id].ep_in == 0 )
+ {
+ p_hid = &_hidd_itf[hid_id];
+ break;
+ }
+ }
+ TU_ASSERT(p_hid, 0);
+
+ uint8_t const *p_desc = (uint8_t const *) desc_itf;
+
+ //------------- HID descriptor -------------//
+ p_desc = tu_desc_next(p_desc);
+ p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
+ TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType, 0);
+
+ //------------- Endpoint Descriptor -------------//
+ p_desc = tu_desc_next(p_desc);
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
+
+ if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->boot_protocol = desc_itf->bInterfaceProtocol;
+
+ p_hid->boot_mode = false; // default mode is REPORT
+ p_hid->itf_num = desc_itf->bInterfaceNumber;
+
+ // Use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&p_hid->report_desc_len, (uint8_t*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength), 2);
+
+ // Prepare for output endpoint
+ if (p_hid->ep_out)
+ {
+ if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
+ {
+ TU_LOG1_FAILED();
+ TU_BREAKPOINT();
+ }
+ }
+
+ return drv_len;
+}
+
+// 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 hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
+
+ uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
+ TU_VERIFY(hid_itf < CFG_TUD_HID);
+
+ hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
+
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
+ {
+ //------------- STD Request -------------//
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ uint8_t const desc_type = tu_u16_high(request->wValue);
+ //uint8_t const desc_index = tu_u16_low (request->wValue);
+
+ if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
+ {
+ TU_VERIFY(p_hid->hid_descriptor != NULL);
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
+ }
+ else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
+ {
+ uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
+ tud_control_xfer(rhport, request, (void*) desc_report, p_hid->report_desc_len);
+ }
+ else
+ {
+ return false; // stall unsupported request
+ }
+ }
+ }
+ else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
+ {
+ //------------- Class Specific Request -------------//
+ switch( request->bRequest )
+ {
+ case HID_REQ_CONTROL_GET_REPORT:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ uint8_t const report_type = tu_u16_high(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
+
+ uint16_t xferlen = tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, p_hid->epin_buf, request->wLength);
+ TU_ASSERT( xferlen > 0 );
+
+ tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
+ }
+ break;
+
+ case HID_REQ_CONTROL_SET_REPORT:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
+ tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ uint8_t const report_type = tu_u16_high(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
+
+ tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, p_hid->epout_buf, request->wLength);
+ }
+ break;
+
+ case HID_REQ_CONTROL_SET_IDLE:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ p_hid->idle_rate = tu_u16_high(request->wValue);
+ if ( tud_hid_set_idle_cb )
+ {
+ // stall request if callback return false
+ TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
+ }
+
+ tud_control_status(rhport, request);
+ }
+ break;
+
+ case HID_REQ_CONTROL_GET_IDLE:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // TODO idle rate of report
+ tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
+ }
+ break;
+
+ case HID_REQ_CONTROL_GET_PROTOCOL:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // 0 is Boot, 1 is Report protocol
+ uint8_t protocol = (uint8_t)(1-p_hid->boot_mode);
+ tud_control_xfer(rhport, request, &protocol, 1);
+ }
+ break;
+
+ case HID_REQ_CONTROL_SET_PROTOCOL:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // 0 is Boot, 1 is Report protocol
+ p_hid->boot_mode = 1 - request->wValue;
+ tud_control_status(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ if (tud_hid_boot_mode_cb)
+ {
+ tud_hid_boot_mode_cb(hid_itf, p_hid->boot_mode);
+ }
+ }
+ break;
+
+ default: return false; // stall unsupported request
+ }
+ }else
+ {
+ return false; // stall unsupported request
+ }
+
+ return true;
+}
+
+bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+
+ uint8_t itf = 0;
+ hidd_interface_t * p_hid = _hidd_itf;
+
+ // Identify which interface to use
+ for (itf = 0; itf < CFG_TUD_HID; itf++)
+ {
+ p_hid = &_hidd_itf[itf];
+ if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
+ }
+ TU_ASSERT(itf < CFG_TUD_HID);
+
+ // Sent report successfully
+ if (ep_addr == p_hid->ep_in)
+ {
+ if (tud_hid_report_complete_cb)
+ {
+ tud_hid_report_complete_cb(itf, p_hid->epin_buf, (uint8_t) xferred_bytes);
+ }
+ }
+ // Received report
+ else if (ep_addr == p_hid->ep_out)
+ {
+ tud_hid_set_report_cb(itf, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/hid/hid_device.h b/sw/Core/Src/tinyusb/src/class/hid/hid_device.h
new file mode 100755
index 0000000..0c59d5d
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/hid/hid_device.h
@@ -0,0 +1,379 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_HID_DEVICE_H_
+#define _TUSB_HID_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+#include "hid.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Default Configure & Validation
+//--------------------------------------------------------------------+
+
+#if !defined(CFG_TUD_HID_EP_BUFSIZE) & defined(CFG_TUD_HID_BUFSIZE)
+ // TODO warn user to use new name later on
+ // #warning CFG_TUD_HID_BUFSIZE is renamed to CFG_TUD_HID_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_HID_EP_BUFSIZE CFG_TUD_HID_BUFSIZE
+#endif
+
+#ifndef CFG_TUD_HID_EP_BUFSIZE
+ #define CFG_TUD_HID_EP_BUFSIZE 64
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Ports)
+// CFG_TUD_HID > 1
+//--------------------------------------------------------------------+
+
+// Check if the interface is ready to use
+bool tud_hid_n_ready(uint8_t itf);
+
+// Check if current mode is Boot (true) or Report (false)
+bool tud_hid_n_boot_mode(uint8_t itf);
+
+// Send report to host
+bool tud_hid_n_report(uint8_t itf, uint8_t report_id, void const* report, uint8_t len);
+
+// KEYBOARD: convenient helper to send keyboard report if application
+// use template layout report as defined by hid_keyboard_report_t
+bool tud_hid_n_keyboard_report(uint8_t itf, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+
+// MOUSE: convenient helper to send mouse report if application
+// use template layout report as defined by hid_mouse_report_t
+bool tud_hid_n_mouse_report(uint8_t itf, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+
+// Gamepad: convenient helper to send mouse report if application
+// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
+bool tud_hid_n_gamepad_report(uint8_t itf, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint16_t buttons);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+static inline bool tud_hid_ready(void);
+static inline bool tud_hid_boot_mode(void);
+static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
+static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+
+//--------------------------------------------------------------------+
+// Callbacks (Weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received GET HID REPORT DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf);
+
+// 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 itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
+
+// 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 itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
+
+// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report )
+TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t itf, uint8_t boot_mode);
+
+// Invoked when received SET_IDLE request. return false will stall the request
+// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
+// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
+TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t itf, uint8_t idle_rate);
+
+// 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
+TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t itf, uint8_t const* report, uint8_t len);
+
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+static inline bool tud_hid_ready(void)
+{
+ return tud_hid_n_ready(0);
+}
+
+static inline bool tud_hid_boot_mode(void)
+{
+ return tud_hid_n_boot_mode(0);
+}
+
+static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
+{
+ return tud_hid_n_report(0, report_id, report, len);
+}
+
+static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
+{
+ return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
+}
+
+static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
+{
+ return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+/* --------------------------------------------------------------------+
+ * HID Report Descriptor Template
+ *
+ * Convenient for declaring popular HID device (keyboard, mouse, consumer,
+ * gamepad etc...). Templates take "HID_REPORT_ID(n)" as input, leave
+ * empty if multiple reports is not used
+ *
+ * - Only 1 report: no parameter
+ * uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() };
+ *
+ * - Multiple Reports: "HID_REPORT_ID(ID)" must be passed to template
+ * uint8_t const report_desc[] =
+ * {
+ * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1) ) ,
+ * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2) )
+ * };
+ *--------------------------------------------------------------------*/
+
+// Keyboard Report Descriptor Template
+#define TUD_HID_REPORT_DESC_KEYBOARD(...) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
+ HID_USAGE_MIN ( 224 ) ,\
+ HID_USAGE_MAX ( 231 ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX ( 1 ) ,\
+ HID_REPORT_COUNT ( 8 ) ,\
+ HID_REPORT_SIZE ( 1 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 8 bit reserved */ \
+ HID_REPORT_COUNT ( 1 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_CONSTANT ) ,\
+ /* 6-byte Keycodes */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
+ HID_USAGE_MIN ( 0 ) ,\
+ HID_USAGE_MAX ( 255 ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX ( 255 ) ,\
+ HID_REPORT_COUNT ( 6 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
+ /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\
+ HID_USAGE_MIN ( 1 ) ,\
+ HID_USAGE_MAX ( 5 ) ,\
+ HID_REPORT_COUNT ( 5 ) ,\
+ HID_REPORT_SIZE ( 1 ) ,\
+ HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* led padding */ \
+ HID_REPORT_COUNT ( 1 ) ,\
+ HID_REPORT_SIZE ( 3 ) ,\
+ HID_OUTPUT ( HID_CONSTANT ) ,\
+ HID_COLLECTION_END \
+
+// Mouse Report Descriptor Template
+#define TUD_HID_REPORT_DESC_MOUSE(...) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
+ HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
+ HID_USAGE_MIN ( 1 ) ,\
+ HID_USAGE_MAX ( 5 ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX ( 1 ) ,\
+ /* Left, Right, Middle, Backward, Forward buttons */ \
+ HID_REPORT_COUNT( 5 ) ,\
+ HID_REPORT_SIZE ( 1 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 3 bit padding */ \
+ HID_REPORT_COUNT( 1 ) ,\
+ HID_REPORT_SIZE ( 3 ) ,\
+ HID_INPUT ( HID_CONSTANT ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ /* X, Y position [-127, 127] */ \
+ HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
+ HID_LOGICAL_MIN ( 0x81 ) ,\
+ HID_LOGICAL_MAX ( 0x7f ) ,\
+ HID_REPORT_COUNT( 2 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
+ /* Verital wheel scroll [-127, 127] */ \
+ HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
+ HID_LOGICAL_MIN ( 0x81 ) ,\
+ HID_LOGICAL_MAX ( 0x7f ) ,\
+ HID_REPORT_COUNT( 1 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
+ /* Horizontal wheel scroll [-127, 127] */ \
+ HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
+ HID_LOGICAL_MIN ( 0x81 ), \
+ HID_LOGICAL_MAX ( 0x7f ), \
+ HID_REPORT_COUNT( 1 ), \
+ HID_REPORT_SIZE ( 8 ), \
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
+ HID_COLLECTION_END , \
+ HID_COLLECTION_END \
+
+// Consumer Control Report Descriptor Template
+#define TUD_HID_REPORT_DESC_CONSUMER(...) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
+ HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ HID_LOGICAL_MIN ( 0x00 ) ,\
+ HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\
+ HID_USAGE_MIN ( 0x00 ) ,\
+ HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\
+ HID_REPORT_COUNT ( 1 ) ,\
+ HID_REPORT_SIZE ( 16 ) ,\
+ HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
+ HID_COLLECTION_END \
+
+/* System Control Report Descriptor Template
+ * 0x00 - do nothing
+ * 0x01 - Power Off
+ * 0x02 - Standby
+ * 0x04 - Wake Host
+ */
+#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ /* 2 bit system power control */ \
+ HID_LOGICAL_MIN ( 1 ) ,\
+ HID_LOGICAL_MAX ( 3 ) ,\
+ HID_REPORT_COUNT ( 1 ) ,\
+ HID_REPORT_SIZE ( 2 ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\
+ HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
+ /* 6 bit padding */ \
+ HID_REPORT_COUNT ( 1 ) ,\
+ HID_REPORT_SIZE ( 6 ) ,\
+ HID_INPUT ( HID_CONSTANT ) ,\
+ HID_COLLECTION_END \
+
+// Gamepad Report Descriptor Template
+// with 16 buttons, 2 joysticks and 1 hat/dpad with following layout
+// | X | Y | Z | Rz | Rx | Ry (1 byte each) | hat/DPAD (1 byte) | Button Map (2 bytes) |
+#define TUD_HID_REPORT_DESC_GAMEPAD(...) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ /* 8 bit X, Y, Z, Rz, Rx, Ry (min -127, max 127 ) */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_RX ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_RY ) ,\
+ HID_LOGICAL_MIN ( 0x81 ) ,\
+ HID_LOGICAL_MAX ( 0x7f ) ,\
+ HID_REPORT_COUNT ( 6 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 8 bit DPad/Hat Button Map */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_HAT_SWITCH ) ,\
+ HID_LOGICAL_MIN ( 1 ) ,\
+ HID_LOGICAL_MAX ( 8 ) ,\
+ HID_PHYSICAL_MIN ( 0 ) ,\
+ HID_PHYSICAL_MAX_N ( 315, 2 ) ,\
+ HID_REPORT_COUNT ( 1 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 16 bit Button Map */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
+ HID_USAGE_MIN ( 1 ) ,\
+ HID_USAGE_MAX ( 16 ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX ( 1 ) ,\
+ HID_REPORT_COUNT ( 16 ) ,\
+ HID_REPORT_SIZE ( 1 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ HID_COLLECTION_END \
+
+// HID Generic Input & Output
+// - 1st parameter is report size (mandatory)
+// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
+#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \
+ HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
+ HID_USAGE ( 0x01 ),\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ /* Input */ \
+ HID_USAGE ( 0x02 ),\
+ HID_LOGICAL_MIN ( 0x00 ),\
+ HID_LOGICAL_MAX ( 0xff ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT( report_size ),\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ /* Output */ \
+ HID_USAGE ( 0x03 ),\
+ HID_LOGICAL_MIN ( 0x00 ),\
+ HID_LOGICAL_MAX ( 0xff ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT( report_size ),\
+ HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END \
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void hidd_init (void);
+void hidd_reset (uint8_t rhport);
+uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_HID_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/hid/hid_host.c b/sw/Core/Src/tinyusb/src/class/hid/hid_host.c
new file mode 100755
index 0000000..64d1b8e
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/hid/hid_host.c
@@ -0,0 +1,319 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_HOST_ENABLED && HOST_CLASS_HID)
+
+#include "common/tusb_common.h"
+#include "hid_host.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
+ bool valid;
+
+ uint16_t report_size;
+}hidh_interface_t;
+
+//--------------------------------------------------------------------+
+// HID Interface common functions
+//--------------------------------------------------------------------+
+static inline bool hidh_interface_open(uint8_t rhport, uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_t *p_hid)
+{
+ TU_ASSERT( usbh_edpt_open(rhport, dev_addr, p_endpoint_desc) );
+
+ p_hid->ep_in = p_endpoint_desc->bEndpointAddress;
+ p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor
+ p_hid->itf_num = interface_number;
+ p_hid->valid = true;
+
+ return true;
+}
+
+static inline void hidh_interface_close(hidh_interface_t *p_hid)
+{
+ tu_memclr(p_hid, sizeof(hidh_interface_t));
+}
+
+// called from public API need to validate parameters
+tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_interface_t *p_hid)
+{
+ //------------- parameters validation -------------//
+ // TODO change to use is configured function
+ TU_ASSERT(TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY);
+ TU_VERIFY(report, TUSB_ERROR_INVALID_PARA);
+ TU_ASSERT(!hcd_edpt_busy(dev_addr, p_hid->ep_in), TUSB_ERROR_INTERFACE_IS_BUSY);
+
+ TU_ASSERT( usbh_edpt_xfer(dev_addr, p_hid->ep_in, report, p_hid->report_size) ) ;
+
+ return TUSB_ERROR_NONE;
+}
+
+//--------------------------------------------------------------------+
+// KEYBOARD
+//--------------------------------------------------------------------+
+#if CFG_TUH_HID_KEYBOARD
+
+static hidh_interface_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
+
+//------------- KEYBOARD PUBLIC API (parameter validation required) -------------//
+bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr)
+{
+ return tuh_device_is_configured(dev_addr) && (keyboardh_data[dev_addr-1].ep_in != 0);
+}
+
+tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report)
+{
+ return hidh_interface_get_report(dev_addr, p_report, &keyboardh_data[dev_addr-1]);
+}
+
+bool tuh_hid_keyboard_is_busy(uint8_t dev_addr)
+{
+ return tuh_hid_keyboard_is_mounted(dev_addr) && hcd_edpt_busy(dev_addr, keyboardh_data[dev_addr-1].ep_in);
+}
+
+#endif
+
+//--------------------------------------------------------------------+
+// MOUSE
+//--------------------------------------------------------------------+
+#if CFG_TUH_HID_MOUSE
+
+static hidh_interface_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
+
+//------------- Public API -------------//
+bool tuh_hid_mouse_is_mounted(uint8_t dev_addr)
+{
+ return tuh_device_is_configured(dev_addr) && (mouseh_data[dev_addr-1].ep_in != 0);
+}
+
+bool tuh_hid_mouse_is_busy(uint8_t dev_addr)
+{
+ return tuh_hid_mouse_is_mounted(dev_addr) && hcd_edpt_busy(dev_addr, mouseh_data[dev_addr-1].ep_in);
+}
+
+tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report)
+{
+ return hidh_interface_get_report(dev_addr, report, &mouseh_data[dev_addr-1]);
+}
+
+#endif
+
+//--------------------------------------------------------------------+
+// GENERIC
+//--------------------------------------------------------------------+
+#if CFG_TUSB_HOST_HID_GENERIC
+
+//STATIC_ struct {
+// hidh_interface_info_t
+//} generic_data[CFG_TUSB_HOST_DEVICE_MAX];
+
+#endif
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API (don't require to verify parameters)
+//--------------------------------------------------------------------+
+void hidh_init(void)
+{
+#if CFG_TUH_HID_KEYBOARD
+ tu_memclr(&keyboardh_data, sizeof(hidh_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
+#endif
+
+#if CFG_TUH_HID_MOUSE
+ tu_memclr(&mouseh_data, sizeof(hidh_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
+#endif
+
+#if CFG_TUSB_HOST_HID_GENERIC
+ hidh_generic_init();
+#endif
+}
+
+#if 0
+CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256];
+#endif
+
+bool hidh_open_subtask(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
+{
+ uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
+
+ //------------- HID descriptor -------------//
+ p_desc += p_desc[DESC_OFFSET_LEN];
+ tusb_hid_descriptor_hid_t const *p_desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
+ TU_ASSERT(HID_DESC_TYPE_HID == p_desc_hid->bDescriptorType, TUSB_ERROR_INVALID_PARA);
+
+ //------------- Endpoint Descriptor -------------//
+ p_desc += p_desc[DESC_OFFSET_LEN];
+ tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA);
+
+ if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass )
+ {
+ #if CFG_TUH_HID_KEYBOARD
+ if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol)
+ {
+ TU_ASSERT( hidh_interface_open(rhport, dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) );
+ TU_LOG2_HEX(keyboardh_data[dev_addr-1].ep_in);
+ } else
+ #endif
+
+ #if CFG_TUH_HID_MOUSE
+ if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol)
+ {
+ TU_ASSERT ( hidh_interface_open(rhport, dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) );
+ TU_LOG2_HEX(mouseh_data[dev_addr-1].ep_in);
+ } else
+ #endif
+
+ {
+ // Not supported protocol
+ return false;
+ }
+ }else
+ {
+ // Not supported subclass
+ return false;
+ }
+
+ *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t);
+
+ return true;
+}
+
+bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+#if 0
+ //------------- Get Report Descriptor TODO HID parser -------------//
+ if ( p_desc_hid->bNumDescriptors )
+ {
+ STASK_INVOKE(
+ usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_INTERFACE),
+ TUSB_REQ_GET_DESCRIPTOR, (p_desc_hid->bReportType << 8), 0,
+ p_desc_hid->wReportLength, report_descriptor ),
+ error
+ );
+ (void) error; // if error in getting report descriptor --> treating like there is none
+ }
+#endif
+
+#if 0
+ // SET IDLE = 0 request
+ // Device can stall if not support this request
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_IDLE,
+ .wValue = 0, // idle_rate = 0
+ .wIndex = p_interface_desc->bInterfaceNumber,
+ .wLength = 0
+ };
+
+ // stall is a valid response for SET_IDLE, therefore we could ignore result of this request
+ tuh_control_xfer(dev_addr, &request, NULL, NULL);
+#endif
+
+ usbh_driver_set_config_complete(dev_addr, itf_num);
+
+#if CFG_TUH_HID_KEYBOARD
+ if (( keyboardh_data[dev_addr-1].itf_num == itf_num) && keyboardh_data[dev_addr-1].valid)
+ {
+ tuh_hid_keyboard_mounted_cb(dev_addr);
+ }
+#endif
+
+#if CFG_TUH_HID_MOUSE
+ if (( mouseh_data[dev_addr-1].ep_in == itf_num ) && mouseh_data[dev_addr-1].valid)
+ {
+ tuh_hid_mouse_mounted_cb(dev_addr);
+ }
+#endif
+
+ return true;
+}
+
+bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+{
+ (void) xferred_bytes; // TODO may need to use this para later
+
+#if CFG_TUH_HID_KEYBOARD
+ if ( ep_addr == keyboardh_data[dev_addr-1].ep_in )
+ {
+ tuh_hid_keyboard_isr(dev_addr, event);
+ return true;
+ }
+#endif
+
+#if CFG_TUH_HID_MOUSE
+ if ( ep_addr == mouseh_data[dev_addr-1].ep_in )
+ {
+ tuh_hid_mouse_isr(dev_addr, event);
+ return true;
+ }
+#endif
+
+#if CFG_TUSB_HOST_HID_GENERIC
+
+#endif
+
+ return true;
+}
+
+void hidh_close(uint8_t dev_addr)
+{
+#if CFG_TUH_HID_KEYBOARD
+ if ( keyboardh_data[dev_addr-1].ep_in != 0 )
+ {
+ hidh_interface_close(&keyboardh_data[dev_addr-1]);
+ tuh_hid_keyboard_unmounted_cb(dev_addr);
+ }
+#endif
+
+#if CFG_TUH_HID_MOUSE
+ if( mouseh_data[dev_addr-1].ep_in != 0 )
+ {
+ hidh_interface_close(&mouseh_data[dev_addr-1]);
+ tuh_hid_mouse_unmounted_cb( dev_addr );
+ }
+#endif
+
+#if CFG_TUSB_HOST_HID_GENERIC
+ hidh_generic_close(dev_addr);
+#endif
+}
+
+
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/hid/hid_host.h b/sw/Core/Src/tinyusb/src/class/hid/hid_host.h
new file mode 100755
index 0000000..5c77398
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/hid/hid_host.h
@@ -0,0 +1,210 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \addtogroup ClassDriver_HID
+ * @{ */
+
+#ifndef _TUSB_HID_HOST_H_
+#define _TUSB_HID_HOST_H_
+
+#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "hid.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// KEYBOARD Application API
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Keyboard Keyboard
+ * @{ */
+
+/** \defgroup Keyboard_Host Host
+ * The interface API includes status checking function, data transferring function and callback functions
+ * @{ */
+
+extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if build failed without KEYBOARD enabled
+
+/** \brief Check if device supports Keyboard interface or not
+ * \param[in] dev_addr device address
+ * \retval true if device supports Keyboard interface
+ * \retval false if device does not support Keyboard interface or is not mounted
+ */
+bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr);
+
+/** \brief Check if the interface is currently busy or not
+ * \param[in] dev_addr device address
+ * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
+ * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
+ * \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report.
+ * Alternatively, asynchronous event API can be used
+ */
+bool tuh_hid_keyboard_is_busy(uint8_t dev_addr);
+
+/** \brief Perform a get report from Keyboard interface
+ * \param[in] dev_addr device address
+ * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION)
+ * \returns \ref tusb_error_t type to indicate success or error condition.
+ * \retval TUSB_ERROR_NONE on success
+ * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
+ * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
+ * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
+ * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
+ */
+tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report);
+
+//------------- Application Callback -------------//
+/** \brief Callback function that is invoked when an transferring event occurred
+ * \param[in] dev_addr Address of device
+ * \param[in] event an value from \ref xfer_result_t
+ * \note event can be one of following
+ * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
+ * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
+ * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
+ * \note Application should schedule the next report by calling \ref tuh_hid_keyboard_get_report within this callback
+ */
+void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event);
+
+/** \brief Callback function that will be invoked when a device with Keyboard interface is mounted
+ * \param[in] dev_addr Address of newly mounted device
+ * \note This callback should be used by Application to set-up interface-related data
+ */
+void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr);
+
+/** \brief Callback function that will be invoked when a device with Keyboard interface is unmounted
+ * \param[in] dev_addr Address of newly unmounted device
+ * \note This callback should be used by Application to tear-down interface-related data
+ */
+void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr);
+
+/** @} */ // Keyboard_Host
+/** @} */ // ClassDriver_HID_Keyboard
+
+//--------------------------------------------------------------------+
+// MOUSE Application API
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Mouse Mouse
+ * @{ */
+
+/** \defgroup Mouse_Host Host
+ * The interface API includes status checking function, data transferring function and callback functions
+ * @{ */
+
+/** \brief Check if device supports Mouse interface or not
+ * \param[in] dev_addr device address
+ * \retval true if device supports Mouse interface
+ * \retval false if device does not support Mouse interface or is not mounted
+ */
+bool tuh_hid_mouse_is_mounted(uint8_t dev_addr);
+
+/** \brief Check if the interface is currently busy or not
+ * \param[in] dev_addr device address
+ * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
+ * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
+ * \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report.
+ * Alternatively, asynchronous event API can be used
+ */
+bool tuh_hid_mouse_is_busy(uint8_t dev_addr);
+
+/** \brief Perform a get report from Mouse interface
+ * \param[in] dev_addr device address
+ * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION)
+ * \returns \ref tusb_error_t type to indicate success or error condition.
+ * \retval TUSB_ERROR_NONE on success
+ * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
+ * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
+ * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
+ * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
+ */
+tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report);
+
+//------------- Application Callback -------------//
+/** \brief Callback function that is invoked when an transferring event occurred
+ * \param[in] dev_addr Address of device
+ * \param[in] event an value from \ref xfer_result_t
+ * \note event can be one of following
+ * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
+ * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
+ * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
+ * \note Application should schedule the next report by calling \ref tuh_hid_mouse_get_report within this callback
+ */
+void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event);
+
+/** \brief Callback function that will be invoked when a device with Mouse interface is mounted
+ * \param[in] dev_addr Address of newly mounted device
+ * \note This callback should be used by Application to set-up interface-related data
+ */
+void tuh_hid_mouse_mounted_cb(uint8_t dev_addr);
+
+/** \brief Callback function that will be invoked when a device with Mouse interface is unmounted
+ * \param[in] dev_addr Address of newly unmounted device
+ * \note This callback should be used by Application to tear-down interface-related data
+ */
+void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr);
+
+/** @} */ // Mouse_Host
+/** @} */ // ClassDriver_HID_Mouse
+
+//--------------------------------------------------------------------+
+// GENERIC Application API
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Generic Generic (not supported yet)
+ * @{ */
+
+/** \defgroup Generic_Host Host
+ * The interface API includes status checking function, data transferring function and callback functions
+ * @{ */
+
+bool tuh_hid_generic_is_mounted(uint8_t dev_addr);
+tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
+tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
+tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr);
+tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr);
+
+//------------- Application Callback -------------//
+void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event);
+
+/** @} */ // Generic_Host
+/** @} */ // ClassDriver_HID_Generic
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void hidh_init(void);
+bool hidh_open_subtask(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
+bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void hidh_close(uint8_t dev_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_HID_HOST_H_ */
+
+/** @} */ // ClassDriver_HID
diff --git a/sw/Core/Src/tinyusb/src/class/midi/midi.h b/sw/Core/Src/tinyusb/src/class/midi/midi.h
new file mode 100755
index 0000000..74dc417
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/midi/midi.h
@@ -0,0 +1,212 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_CDC Communication Device Class (CDC)
+ * Currently only Abstract Control Model subclass is supported
+ * @{ */
+
+#ifndef _TUSB_MIDI_H__
+#define _TUSB_MIDI_H__
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Specific Descriptor
+//--------------------------------------------------------------------+
+
+typedef enum
+{
+ MIDI_CS_INTERFACE_HEADER = 0x01,
+ MIDI_CS_INTERFACE_IN_JACK = 0x02,
+ MIDI_CS_INTERFACE_OUT_JACK = 0x03,
+ MIDI_CS_INTERFACE_ELEMENT = 0x04,
+} midi_cs_interface_subtype_t;
+
+typedef enum
+{
+ MIDI_CS_ENDPOINT_GENERAL = 0x01
+} midi_cs_endpoint_subtype_t;
+
+typedef enum
+{
+ MIDI_JACK_EMBEDDED = 0x01,
+ MIDI_JACK_EXTERNAL = 0x02
+} midi_jack_type_t;
+
+typedef enum
+{
+ MIDI_CIN_MISC = 0,
+ MIDI_CIN_CABLE_EVENT = 1,
+ MIDI_CIN_SYSCOM_2BYTE = 2, // 2 byte system common message e.g MTC, SongSelect
+ MIDI_CIN_SYSCOM_3BYTE = 3, // 3 byte system common message e.g SPP
+ MIDI_CIN_SYSEX_START = 4, // SysEx starts or continue
+ MIDI_CIN_SYSEX_END_1BYTE = 5, // SysEx ends with 1 data, or 1 byte system common message
+ MIDI_CIN_SYSEX_END_2BYTE = 6, // SysEx ends with 2 data
+ MIDI_CIN_SYSEX_END_3BYTE = 7, // SysEx ends with 3 data
+ MIDI_CIN_NOTE_ON = 8,
+ MIDI_CIN_NOTE_OFF = 9,
+ MIDI_CIN_POLY_KEYPRESS = 10,
+ MIDI_CIN_CONTROL_CHANGE = 11,
+ MIDI_CIN_PROGRAM_CHANGE = 12,
+ MIDI_CIN_CHANNEL_PRESSURE = 13,
+ MIDI_CIN_PITCH_BEND_CHANGE = 14,
+ MIDI_CIN_1BYTE_DATA = 15
+} midi_code_index_number_t;
+
+// MIDI 1.0 status byte
+enum
+{
+ //------------- System Exclusive -------------//
+ MIDI_STATUS_SYSEX_START = 0xF0,
+ MIDI_STATUS_SYSEX_END = 0xF7,
+
+ //------------- System Common -------------//
+ MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME = 0xF1,
+ MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER = 0xF2,
+ MIDI_STATUS_SYSCOM_SONG_SELECT = 0xF3,
+ // F4, F5 is undefined
+ MIDI_STATUS_SYSCOM_TUNE_REQUEST = 0xF6,
+
+ //------------- System RealTime -------------//
+ MIDI_STATUS_SYSREAL_TIMING_CLOCK = 0xF8,
+ // 0xF9 is undefined
+ MIDI_STATUS_SYSREAL_START = 0xFA,
+ MIDI_STATUS_SYSREAL_CONTINUE = 0xFB,
+ MIDI_STATUS_SYSREAL_STOP = 0xFC,
+ // 0xFD is undefined
+ MIDI_STATUS_SYSREAL_ACTIVE_SENSING = 0xFE,
+ MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF,
+};
+
+/// MIDI Interface Header Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType
+ uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
+ uint16_t wTotalLength ;
+} midi_desc_header_t;
+
+/// MIDI In Jack Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType
+ uint8_t bJackType ; ///< Embedded or External
+ uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
+ uint8_t iJack ; ///< string descriptor
+} midi_desc_in_jack_t;
+
+
+/// MIDI Out Jack Descriptor with single pin
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType
+ uint8_t bJackType ; ///< Embedded or External
+ uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
+ uint8_t bNrInputPins;
+
+ uint8_t baSourceID;
+ uint8_t baSourcePin;
+
+ uint8_t iJack ; ///< string descriptor
+} midi_desc_out_jack_t ;
+
+/// MIDI Out Jack Descriptor with multiple pins
+#define midi_desc_out_jack_n_t(input_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; \
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bJackType ; \
+ uint8_t bJackID ; \
+ uint8_t bNrInputPins ; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID; \
+ uint8_t baSourcePin; \
+ } pins[input_num]; \
+ uint8_t iJack ; \
+ }
+
+/// MIDI Element Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType
+ uint8_t bElementID;
+
+ uint8_t bNrInputPins;
+ uint8_t baSourceID;
+ uint8_t baSourcePin;
+
+ uint8_t bNrOutputPins;
+ uint8_t bInTerminalLink;
+ uint8_t bOutTerminalLink;
+ uint8_t bElCapsSize;
+
+ uint16_t bmElementCaps;
+ uint8_t iElement;
+} midi_desc_element_t;
+
+/// MIDI Element Descriptor with multiple pins
+#define midi_desc_element_n_t(input_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bElementID; \
+ uint8_t bNrInputPins; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID; \
+ uint8_t baSourcePin; \
+ } pins[input_num]; \
+ uint8_t bNrOutputPins; \
+ uint8_t bInTerminalLink; \
+ uint8_t bOutTerminalLink; \
+ uint8_t bElCapsSize; \
+ uint16_t bmElementCaps; \
+ uint8_t iElement; \
+ }
+
+/** @} */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/midi/midi_device.c b/sw/Core/Src/tinyusb/src/class/midi/midi_device.c
new file mode 100755
index 0000000..9ccbb73
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/midi/midi_device.c
@@ -0,0 +1,536 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MIDI)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "midi_device.h"
+#include "class/audio/audio.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+typedef struct
+{
+ uint8_t buffer[4];
+ uint8_t index;
+ uint8_t total;
+}midid_stream_t;
+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ // For Stream read()/write() API
+ // Messages are always 4 bytes long, queue them for reading and writing so the
+ // callers can use the Stream interface with single-byte read/write calls.
+ midid_stream_t stream_write;
+ midid_stream_t stream_read;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+ // FIFO
+ tu_fifo_t rx_ff;
+ tu_fifo_t tx_ff;
+ uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
+
+ #if CFG_FIFO_MUTEX
+ osal_mutex_def_t rx_ff_mutex;
+ osal_mutex_def_t tx_ff_mutex;
+ #endif
+
+ // Endpoint Transfer buffer
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE];
+
+} midid_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI];
+
+bool tud_midi_n_mounted (uint8_t itf)
+{
+ midid_interface_t* midi = &_midid_itf[itf];
+ return midi->ep_in && midi->ep_out;
+}
+
+static void _prep_out_transaction (midid_interface_t* p_midi)
+{
+ uint8_t const rhport = TUD_OPT_RHPORT;
+ uint16_t available = tu_fifo_remaining(&p_midi->rx_ff);
+
+ // Prepare for incoming data but only allow what we can store in the ring buffer.
+ // TODO Actually we can still carry out the transfer, keeping count of received bytes
+ // and slowly move it to the FIFO when read().
+ // This pre-check reduces endpoint claiming
+ TU_VERIFY(available >= sizeof(p_midi->epout_buf), );
+
+ // claim endpoint
+ TU_VERIFY(usbd_edpt_claim(rhport, p_midi->ep_out), );
+
+ // fifo can be changed before endpoint is claimed
+ available = tu_fifo_remaining(&p_midi->rx_ff);
+
+ if ( available >= sizeof(p_midi->epout_buf) ) {
+ usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, sizeof(p_midi->epout_buf));
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, p_midi->ep_out);
+ }
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
+{
+ (void) cable_num;
+ return tu_fifo_count(&_midid_itf[itf].rx_ff);
+}
+
+uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
+{
+ (void) cable_num;
+ TU_VERIFY(bufsize, 0);
+
+ uint8_t* buf8 = (uint8_t*) buffer;
+
+ midid_interface_t* midi = &_midid_itf[itf];
+ midid_stream_t* stream = &midi->stream_read;
+
+ uint32_t total_read = 0;
+ while( bufsize )
+ {
+ // Get new packet from fifo, then set packet expected bytes
+ if ( stream->total == 0 )
+ {
+ // return if there is no more data from fifo
+ if ( !tud_midi_n_packet_read(itf, stream->buffer) ) return total_read;
+
+ uint8_t const code_index = stream->buffer[0] & 0x0f;
+
+ // MIDI 1.0 Table 4-1: Code Index Number Classifications
+ switch(code_index)
+ {
+ case MIDI_CIN_MISC:
+ case MIDI_CIN_CABLE_EVENT:
+ // These are reserved and unused, possibly issue somewhere, skip this packet
+ return 0;
+ break;
+
+ case MIDI_CIN_SYSEX_END_1BYTE:
+ case MIDI_CIN_1BYTE_DATA:
+ stream->total = 1;
+ break;
+
+ case MIDI_CIN_SYSCOM_2BYTE :
+ case MIDI_CIN_SYSEX_END_2BYTE :
+ case MIDI_CIN_PROGRAM_CHANGE :
+ case MIDI_CIN_CHANNEL_PRESSURE :
+ stream->total = 2;
+ break;
+
+ default:
+ stream->total = 3;
+ break;
+ }
+ }
+
+ // Copy data up to bufsize
+ uint32_t const count = tu_min32(stream->total - stream->index, bufsize);
+
+ // Skip the header (1st byte) in the buffer
+ memcpy(buf8, stream->buffer + 1 + stream->index, count);
+
+ total_read += count;
+ stream->index += count;
+ buf8 += count;
+ bufsize -= count;
+
+ // complete current event packet, reset stream
+ if ( stream->total == stream->index )
+ {
+ stream->index = 0;
+ stream->total = 0;
+ }
+ }
+
+ return total_read;
+}
+
+bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
+{
+ midid_interface_t* p_midi = &_midid_itf[itf];
+ uint32_t num_read = tu_fifo_read_n(&p_midi->rx_ff, packet, 4);
+ _prep_out_transaction(p_midi);
+ return (num_read == 4);
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+static uint32_t write_flush(midid_interface_t* midi)
+{
+ // No data to send
+ if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
+
+ uint8_t const rhport = TUD_OPT_RHPORT;
+
+ // skip if previous transfer not complete
+ TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
+
+ uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
+
+ if (count)
+ {
+ TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
+ return count;
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, midi->ep_in);
+ return 0;
+ }
+}
+
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
+{
+ midid_interface_t* midi = &_midid_itf[itf];
+ TU_VERIFY(midi->itf_num, 0);
+
+ midid_stream_t* stream = &midi->stream_write;
+
+ uint32_t total_written = 0;
+ uint32_t i = 0;
+ while ( i < bufsize )
+ {
+ uint8_t const data = buffer[i];
+
+ if ( stream->index == 0 )
+ {
+ // new event packet
+
+ uint8_t const msg = data >> 4;
+
+ stream->index = 2;
+ stream->buffer[1] = data;
+
+ // Check to see if we're still in a SysEx transmit.
+ if ( stream->buffer[0] == MIDI_CIN_SYSEX_START )
+ {
+ if ( data == MIDI_STATUS_SYSEX_END )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ stream->total = 2;
+ }
+ else
+ {
+ stream->total = 4;
+ }
+ }
+ else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE )
+ {
+ // Channel Voice Messages
+ stream->buffer[0] = (cable_num << 4) | msg;
+ stream->total = 4;
+ }
+ else if ( msg == 0xf )
+ {
+ // System message
+ if ( data == MIDI_STATUS_SYSEX_START )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_START;
+ stream->total = 4;
+ }
+ else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE;
+ stream->total = 3;
+ }
+ else if ( data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
+ stream->total = 4;
+ }
+ else
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ stream->total = 2;
+ }
+ }
+ else
+ {
+ // Pack individual bytes if we don't support packing them into words.
+ stream->buffer[0] = cable_num << 4 | 0xf;
+ stream->buffer[2] = 0;
+ stream->buffer[3] = 0;
+ stream->index = 2;
+ stream->total = 2;
+ }
+ }
+ else
+ {
+ // On-going (buffering) packet
+
+ TU_ASSERT(stream->index < 4, total_written);
+ stream->buffer[stream->index] = data;
+ stream->index++;
+
+ // See if this byte ends a SysEx.
+ if ( stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1);
+ stream->total = stream->index;
+ }
+ }
+
+ // Send out packet
+ if ( stream->index == stream->total )
+ {
+ // zeroes unused bytes
+ for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0;
+
+ uint16_t const count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
+
+ // complete current event packet, reset stream
+ stream->index = stream->total = 0;
+
+ // fifo overflow, here we assume FIFO is multiple of 4 and didn't check remaining before writing
+ if ( count != 4 ) break;
+
+ // updated written if succeeded
+ total_written = i;
+ }
+
+ i++;
+ }
+
+ write_flush(midi);
+
+ return total_written;
+}
+
+bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
+{
+ midid_interface_t* midi = &_midid_itf[itf];
+ if (midi->itf_num == 0) {
+ return 0;
+ }
+
+ if (tu_fifo_remaining(&midi->tx_ff) < 4) return false;
+
+ tu_fifo_write_n(&midi->tx_ff, packet, 4);
+ write_flush(midi);
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void midid_init(void)
+{
+ tu_memclr(_midid_itf, sizeof(_midid_itf));
+
+ for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
+ {
+ midid_interface_t* midi = &_midid_itf[i];
+
+ // config fifo
+ tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, false); // true, true
+ tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
+
+ #if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex));
+ tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex));
+ #endif
+ }
+}
+
+void midid_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
+ {
+ midid_interface_t* midi = &_midid_itf[i];
+ tu_memclr(midi, ITF_MEM_RESET_SIZE);
+ tu_fifo_clear(&midi->rx_ff);
+ tu_fifo_clear(&midi->tx_ff);
+ }
+}
+
+uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
+{
+ // 1st Interface is Audio Control v1
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
+
+ uint16_t drv_len = tu_desc_len(desc_itf);
+ uint8_t const * p_desc = tu_desc_next(desc_itf);
+
+ // Skip Class Specific descriptors
+ while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
+ {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // 2nd Interface is MIDI Streaming
+ TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+ tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
+
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
+ AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0);
+
+ // Find available interface
+ midid_interface_t * p_midi = NULL;
+ for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
+ {
+ if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 )
+ {
+ p_midi = &_midid_itf[i];
+ break;
+ }
+ }
+
+ p_midi->itf_num = desc_midi->bInterfaceNumber;
+
+ // next descriptor
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+
+ // Find and open endpoint descriptors
+ uint8_t found_endpoints = 0;
+ while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
+ {
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+ {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
+ uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
+ {
+ p_midi->ep_in = ep_addr;
+ } else {
+ p_midi->ep_out = ep_addr;
+ }
+
+ // Class Specific MIDI Stream endpoint descriptor
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+
+ found_endpoints += 1;
+ }
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Prepare for incoming data
+ _prep_out_transaction(p_midi);
+
+ return drv_len;
+}
+
+// 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 midid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ (void) rhport;
+ (void) stage;
+ (void) request;
+
+ // driver doesn't support any request yet
+ return false;
+}
+
+bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+ (void) rhport;
+
+ uint8_t itf;
+ midid_interface_t* p_midi;
+
+ // Identify which interface to use
+ for (itf = 0; itf < CFG_TUD_MIDI; itf++)
+ {
+ p_midi = &_midid_itf[itf];
+ if ( ( ep_addr == p_midi->ep_out ) || ( ep_addr == p_midi->ep_in ) ) break;
+ }
+ TU_ASSERT(itf < CFG_TUD_MIDI);
+
+ // receive new data
+ if ( ep_addr == p_midi->ep_out )
+ {
+ tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, xferred_bytes);
+
+ // invoke receive callback if available
+ if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
+
+ // prepare for next
+ // TODO for now ep_out is not used by public API therefore there is no race condition,
+ // and does not need to claim like ep_in
+ _prep_out_transaction(p_midi);
+ }
+ else if ( ep_addr == p_midi->ep_in )
+ {
+ if (0 == write_flush(p_midi))
+ {
+ // If there is no data left, a ZLP should be sent if
+ // xferred_bytes is multiple of EP size and not zero
+ if ( !tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
+ {
+ if ( usbd_edpt_claim(rhport, p_midi->ep_in) )
+ {
+ usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/midi/midi_device.h b/sw/Core/Src/tinyusb/src/class/midi/midi_device.h
new file mode 100755
index 0000000..67f0393
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/midi/midi_device.h
@@ -0,0 +1,176 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_MIDI_DEVICE_H_
+#define _TUSB_MIDI_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+
+#include "class/audio/audio.h"
+#include "midi.h"
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE)
+ #warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE
+#endif
+
+#ifndef CFG_TUD_MIDI_EP_BUFSIZE
+ #define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/** \addtogroup MIDI_Serial Serial
+ * @{
+ * \defgroup MIDI_Serial_Device Device
+ * @{ */
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+// CFG_TUD_MIDI > 1
+//--------------------------------------------------------------------+
+
+// Check if midi interface is mounted
+bool tud_midi_n_mounted (uint8_t itf);
+
+// Get the number of bytes available for reading
+uint32_t tud_midi_n_available (uint8_t itf, uint8_t cable_num);
+
+// Read byte stream (legacy)
+uint32_t tud_midi_n_stream_read (uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize);
+
+// Write byte Stream (legacy)
+uint32_t tud_midi_n_stream_write (uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize);
+
+// Read event packet (4 bytes)
+bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4]);
+
+// Write event packet (4 bytes)
+bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]);
+
+//--------------------------------------------------------------------+
+// Application API (Single Interface)
+//--------------------------------------------------------------------+
+static inline bool tud_midi_mounted (void);
+static inline uint32_t tud_midi_available (void);
+
+static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize);
+static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize);
+
+static inline bool tud_midi_packet_read (uint8_t packet[4]);
+static inline bool tud_midi_packet_write (uint8_t const packet[4]);
+
+//------------- Deprecated API name -------------//
+// TODO remove after 0.10.0 release
+
+TU_ATTR_DEPRECATED("tud_midi_read() is renamed to tud_midi_stream_read()")
+static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize)
+{
+ return tud_midi_stream_read(buffer, bufsize);
+}
+
+TU_ATTR_DEPRECATED("tud_midi_write() is renamed to tud_midi_stream_write()")
+static inline uint32_t tud_midi_write(uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
+{
+ return tud_midi_stream_write(cable_num, buffer, bufsize);
+}
+
+
+TU_ATTR_DEPRECATED("tud_midi_send() is renamed to tud_midi_packet_write()")
+static inline bool tud_midi_send(uint8_t packet[4])
+{
+ return tud_midi_packet_write(packet);
+}
+
+TU_ATTR_DEPRECATED("tud_midi_receive() is renamed to tud_midi_packet_read()")
+static inline bool tud_midi_receive(uint8_t packet[4])
+{
+ return tud_midi_packet_read(packet);
+}
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline bool tud_midi_mounted (void)
+{
+ return tud_midi_n_mounted(0);
+}
+
+static inline uint32_t tud_midi_available (void)
+{
+ return tud_midi_n_available(0, 0);
+}
+
+static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize)
+{
+ return tud_midi_n_stream_read(0, 0, buffer, bufsize);
+}
+
+static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
+{
+ return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
+}
+
+static inline bool tud_midi_packet_read (uint8_t packet[4])
+{
+ return tud_midi_n_packet_read(0, packet);
+}
+
+static inline bool tud_midi_packet_write (uint8_t const packet[4])
+{
+ return tud_midi_n_packet_write(0, packet);
+}
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void midid_init (void);
+void midid_reset (uint8_t rhport);
+uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_MIDI_DEVICE_H_ */
+
+/** @} */
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/class/msc/msc.h b/sw/Core/Src/tinyusb/src/class/msc/msc.h
new file mode 100755
index 0000000..0bdc006
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/msc/msc.h
@@ -0,0 +1,392 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_MSC MassStorage (MSC)
+ * @{ */
+
+/** \defgroup ClassDriver_MSC_Common Common Definitions
+ * @{ */
+
+#ifndef _TUSB_MSC_H_
+#define _TUSB_MSC_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Mass Storage Class Constant
+//--------------------------------------------------------------------+
+/// MassStorage Subclass
+typedef enum
+{
+ MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D
+ MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device
+ MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device
+ MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device
+ MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device
+ MSC_SUBCLASS_SCSI ///< SCSI transparent command set
+}msc_subclass_type_t;
+
+enum {
+ MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian)
+ MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian)
+};
+
+/// \brief MassStorage Protocol.
+/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy
+typedef enum
+{
+ MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt)
+ MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt)
+ MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport
+}msc_protocol_type_t;
+
+/// MassStorage Class-Specific Control Request
+typedef enum
+{
+ MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15
+ MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host.
+}msc_request_type_t;
+
+/// \brief Command Block Status Values
+/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed
+/// successfully. A non-zero value shall indicate a failure during command execution according to the following
+typedef enum
+{
+ MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED
+ MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED
+ MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR
+}msc_csw_status_t;
+
+/// Command Block Wrapper
+typedef struct TU_ATTR_PACKED
+{
+ uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW.
+ uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW.
+ uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags.
+ uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host.
+ uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero.
+ uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16
+ uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block
+}msc_cbw_t;
+
+TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct");
+
+/// Command Status Wrapper
+typedef struct TU_ATTR_PACKED
+{
+ uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
+ uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
+ uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
+ uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t
+}msc_csw_t;
+
+TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct");
+
+//--------------------------------------------------------------------+
+// SCSI Constant
+//--------------------------------------------------------------------+
+
+/// SCSI Command Operation Code
+typedef enum
+{
+ SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation.
+ SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device.
+ SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters.
+ SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command.
+ SCSI_CMD_START_STOP_UNIT = 0x1B,
+ SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E,
+ SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device.
+ SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device.
+ SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed
+ SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer.
+ SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them.
+}scsi_cmd_type_t;
+
+/// SCSI Sense Key
+typedef enum
+{
+ SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command
+ SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive.
+ SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed.
+ SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition.
+ SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test.
+ SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters
+ SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset.
+ SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed.
+ SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key.
+ SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command.
+ SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison.
+ SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium.
+ SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium.
+}scsi_sense_key_type_t;
+
+//--------------------------------------------------------------------+
+// SCSI Primary Command (SPC-4)
+//--------------------------------------------------------------------+
+
+/// SCSI Test Unit Ready Command
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
+ uint8_t lun ; ///< Logical Unit
+ uint8_t reserved[3] ;
+ uint8_t control ;
+} scsi_test_unit_ready_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct");
+
+/// SCSI Inquiry Command
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
+ uint8_t reserved1 ;
+ uint8_t page_code ;
+ uint8_t reserved2 ;
+ uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred.
+ uint8_t control ;
+} scsi_inquiry_t, scsi_request_sense_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct");
+
+/// SCSI Inquiry Response Data
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t peripheral_device_type : 5;
+ uint8_t peripheral_qualifier : 3;
+
+ uint8_t : 7;
+ uint8_t is_removable : 1;
+
+ uint8_t version;
+
+ uint8_t response_data_format : 4;
+ uint8_t hierarchical_support : 1;
+ uint8_t normal_aca : 1;
+ uint8_t : 2;
+
+ uint8_t additional_length;
+
+ uint8_t protect : 1;
+ uint8_t : 2;
+ uint8_t third_party_copy : 1;
+ uint8_t target_port_group_support : 2;
+ uint8_t access_control_coordinator : 1;
+ uint8_t scc_support : 1;
+
+ uint8_t addr16 : 1;
+ uint8_t : 3;
+ uint8_t multi_port : 1;
+ uint8_t : 1; // vendor specific
+ uint8_t enclosure_service : 1;
+ uint8_t : 1;
+
+ uint8_t : 1; // vendor specific
+ uint8_t cmd_que : 1;
+ uint8_t : 2;
+ uint8_t sync : 1;
+ uint8_t wbus16 : 1;
+ uint8_t : 2;
+
+ uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product.
+ uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor.
+ uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor.
+} scsi_inquiry_resp_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct");
+
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
+ uint8_t valid : 1;
+
+ uint8_t reserved;
+
+ uint8_t sense_key : 4;
+ uint8_t : 1;
+ uint8_t ili : 1; ///< Incorrect length indicator
+ uint8_t end_of_medium : 1;
+ uint8_t filemark : 1;
+
+ uint32_t information;
+ uint8_t add_sense_len;
+ uint32_t command_specific_info;
+ uint8_t add_sense_code;
+ uint8_t add_sense_qualifier;
+ uint8_t field_replaceable_unit_code;
+
+ uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout
+
+} scsi_sense_fixed_resp_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
+
+ uint8_t : 3;
+ uint8_t disable_block_descriptor : 1;
+ uint8_t : 4;
+
+ uint8_t page_code : 6;
+ uint8_t page_control : 2;
+
+ uint8_t subpage_code;
+ uint8_t alloc_length;
+ uint8_t control;
+} scsi_mode_sense6_t;
+
+TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct");
+
+// This is only a Mode parameter header(6).
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t data_len;
+ uint8_t medium_type;
+
+ uint8_t reserved : 7;
+ bool write_protected : 1;
+
+ uint8_t block_descriptor_len;
+} scsi_mode_sense6_resp_t;
+
+TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
+ uint8_t reserved[3];
+ uint8_t prohibit_removal;
+ uint8_t control;
+} scsi_prevent_allow_medium_removal_t;
+
+TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code;
+
+ uint8_t immded : 1;
+ uint8_t : 7;
+
+ uint8_t TU_RESERVED;
+
+ uint8_t power_condition_mod : 4;
+ uint8_t : 4;
+
+ uint8_t start : 1;
+ uint8_t load_eject : 1;
+ uint8_t no_flush : 1;
+ uint8_t : 1;
+ uint8_t power_condition : 4;
+
+ uint8_t control;
+} scsi_start_stop_unit_t;
+
+TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct");
+
+//--------------------------------------------------------------------+
+// SCSI MMC
+//--------------------------------------------------------------------+
+/// SCSI Read Format Capacity: Write Capacity
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code;
+ uint8_t reserved[6];
+ uint16_t alloc_length;
+ uint8_t control;
+} scsi_read_format_capacity_t;
+
+TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
+
+typedef struct TU_ATTR_PACKED{
+ uint8_t reserved[3];
+ uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
+
+ uint32_t block_num; /// Number of Logical Blocks
+ uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present
+
+ uint8_t reserved2;
+ uint16_t block_size_u16;
+
+} scsi_read_format_capacity_data_t;
+
+TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct");
+
+//--------------------------------------------------------------------+
+// SCSI Block Command (SBC-3)
+// NOTE: All data in SCSI command are in Big Endian
+//--------------------------------------------------------------------+
+
+/// SCSI Read Capacity 10 Command: Read Capacity
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
+ uint8_t reserved1 ;
+ uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
+ uint16_t reserved2 ;
+ uint8_t partial_medium_indicator ;
+ uint8_t control ;
+} scsi_read_capacity10_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct");
+
+/// SCSI Read Capacity 10 Response Data
+typedef struct {
+ uint32_t last_lba ; ///< The last Logical Block Address of the device
+ uint32_t block_size ; ///< Block size in bytes
+} scsi_read_capacity10_resp_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct");
+
+/// SCSI Read 10 Command
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t cmd_code ; ///< SCSI OpCode
+ uint8_t reserved ; // has LUN according to wiki
+ uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
+ uint8_t reserved2 ;
+ uint16_t block_count ; ///< Number of Blocks used by this command
+ uint8_t control ;
+} scsi_read10_t, scsi_write10_t;
+
+TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct");
+TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_MSC_H_ */
+
+/// @}
+/// @}
diff --git a/sw/Core/Src/tinyusb/src/class/msc/msc_device.c b/sw/Core/Src/tinyusb/src/class/msc/msc_device.c
new file mode 100755
index 0000000..194f4d3
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/msc/msc_device.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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC)
+
+#include "common/tusb_common.h"
+#include "msc_device.h"
+#include "device/usbd_pvt.h"
+#include "device/dcd.h" // for faking dcd_event_xfer_complete
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+enum
+{
+ MSC_STAGE_CMD = 0,
+ MSC_STAGE_DATA,
+ MSC_STAGE_STATUS,
+ MSC_STAGE_STATUS_SENT
+};
+
+typedef struct
+{
+ // TODO optimize alignment
+ CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
+ CFG_TUSB_MEM_ALIGN msc_csw_t csw;
+
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ // Bulk Only Transfer (BOT) Protocol
+ uint8_t stage;
+ uint32_t total_len;
+ uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
+
+ // Sense Response Data
+ uint8_t sense_key;
+ uint8_t add_sense_code;
+ uint8_t add_sense_qualifier;
+}mscd_interface_t;
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
+static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
+static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
+
+static inline uint32_t rdwr10_get_lba(uint8_t const command[])
+{
+ // read10 & write10 has the same format
+ scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
+
+ // copy first to prevent mis-aligned access
+ uint32_t lba;
+ // use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&lba, (uint8_t*) p_rdwr10 + offsetof(scsi_write10_t, lba), 4);
+
+ // lba is in Big Endian format
+ return tu_ntohl(lba);
+}
+
+static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
+{
+ // read10 & write10 has the same format
+ scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
+
+ // copy first to prevent mis-aligned access
+ uint16_t block_count;
+ // use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&block_count, (uint8_t*) p_rdwr10 + offsetof(scsi_write10_t, block_count), 2);
+
+ return tu_ntohs(block_count);
+}
+
+//--------------------------------------------------------------------+
+// Debug
+//--------------------------------------------------------------------+
+#if CFG_TUSB_DEBUG >= 2
+
+static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
+{
+ { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
+ { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
+ { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
+ { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" },
+ { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" },
+ { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" },
+ { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" },
+ { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" },
+ { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" },
+ { .key = SCSI_CMD_READ_10 , .data = "Read10" },
+ { .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
+};
+
+static tu_lookup_table_t const _msc_scsi_cmd_table =
+{
+ .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
+ .items = _msc_scsi_cmd_lookup
+};
+
+#endif
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
+{
+ (void) lun;
+
+ _mscd_itf.sense_key = sense_key;
+ _mscd_itf.add_sense_code = add_sense_code;
+ _mscd_itf.add_sense_qualifier = add_sense_qualifier;
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void mscd_init(void)
+{
+ tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
+}
+
+void mscd_reset(uint8_t rhport)
+{
+ (void) rhport;
+ tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
+}
+
+uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ // only support SCSI's BOT protocol
+ TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
+ MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
+ MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
+
+ // msc driver length is fixed
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+
+ // Max length mus be at least 1 interface + 2 endpoints
+ TU_ASSERT(max_len >= drv_len, 0);
+
+ mscd_interface_t * p_msc = &_mscd_itf;
+ p_msc->itf_num = itf_desc->bInterfaceNumber;
+
+ // Open endpoint pair
+ TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
+
+ // Prepare for Command Block Wrapper
+ if ( !usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) )
+ {
+ TU_LOG1_FAILED();
+ TU_BREAKPOINT();
+ }
+
+ return drv_len;
+}
+
+// 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 mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request)
+{
+ // nothing to do with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
+ // Handle class request only
+ TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ switch ( p_request->bRequest )
+ {
+ case MSC_REQ_RESET:
+ // TODO: Actually reset interface.
+ tud_control_status(rhport, p_request);
+ break;
+
+ case MSC_REQ_GET_MAX_LUN:
+ {
+ uint8_t maxlun = 1;
+ if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
+ TU_VERIFY(maxlun);
+
+ // MAX LUN is minus 1 by specs
+ maxlun--;
+
+ tud_control_xfer(rhport, p_request, &maxlun, 1);
+ }
+ break;
+
+ default: return false; // stall unsupported request
+ }
+
+ return true;
+}
+
+bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+{
+ mscd_interface_t* p_msc = &_mscd_itf;
+ msc_cbw_t const * p_cbw = &p_msc->cbw;
+ msc_csw_t * p_csw = &p_msc->csw;
+
+ switch (p_msc->stage)
+ {
+ case MSC_STAGE_CMD:
+ //------------- new CBW received -------------//
+ // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
+ if(ep_addr != p_msc->ep_out) return true;
+
+ TU_ASSERT( event == XFER_RESULT_SUCCESS &&
+ xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
+
+ TU_LOG2(" SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
+ // TU_LOG2_MEM(p_cbw, xferred_bytes, 2);
+
+ p_csw->signature = MSC_CSW_SIGNATURE;
+ p_csw->tag = p_cbw->tag;
+ p_csw->data_residue = 0;
+
+ /*------------- Parse command and prepare DATA -------------*/
+ p_msc->stage = MSC_STAGE_DATA;
+ p_msc->total_len = p_cbw->total_bytes;
+ p_msc->xferred_len = 0;
+
+ if (SCSI_CMD_READ_10 == p_cbw->command[0])
+ {
+ proc_read10_cmd(rhport, p_msc);
+ }
+ else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
+ {
+ proc_write10_cmd(rhport, p_msc);
+ }
+ else
+ {
+ // For other SCSI commands
+ // 1. OUT : queue transfer (invoke app callback after done)
+ // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
+ if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) )
+ {
+ // queue transfer
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
+ }else
+ {
+ int32_t resplen;
+
+ // First process if it is a built-in commands
+ resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
+
+ // Not built-in, invoke user callback
+ if ( (resplen < 0) && (p_msc->sense_key == 0) )
+ {
+ resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
+ }
+
+ if ( resplen < 0 )
+ {
+ p_msc->total_len = 0;
+ p_csw->status = MSC_CSW_STATUS_FAILED;
+ p_msc->stage = MSC_STAGE_STATUS;
+
+ // failed but senskey is not set: default to Illegal Request
+ if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+ // Stall bulk In if needed
+ if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in);
+ }
+ else
+ {
+ p_msc->total_len = (uint32_t) resplen;
+ p_csw->status = MSC_CSW_STATUS_PASSED;
+
+ if (p_msc->total_len)
+ {
+ TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
+ }else
+ {
+ p_msc->stage = MSC_STAGE_STATUS;
+ }
+ }
+ }
+ }
+ break;
+
+ case MSC_STAGE_DATA:
+ TU_LOG2(" SCSI Data\r\n");
+ //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2);
+
+ // OUT transfer, invoke callback if needed
+ if ( !tu_bit_test(p_cbw->dir, 7) )
+ {
+ if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] )
+ {
+ int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
+
+ if ( cb_result < 0 )
+ {
+ p_csw->status = MSC_CSW_STATUS_FAILED;
+ tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
+ }else
+ {
+ p_csw->status = MSC_CSW_STATUS_PASSED;
+ }
+ }
+ else
+ {
+ uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command);
+
+ // Adjust lba with transferred bytes
+ uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
+
+ // Application can consume smaller bytes
+ int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes);
+
+ if ( nbytes < 0 )
+ {
+ // negative means error -> skip to status phase, status in CSW set to failed
+ p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
+ p_csw->status = MSC_CSW_STATUS_FAILED;
+ p_msc->stage = MSC_STAGE_STATUS;
+
+ tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
+ break;
+ }else
+ {
+ // Application consume less than what we got (including zero)
+ if ( nbytes < (int32_t) xferred_bytes )
+ {
+ if ( nbytes > 0 )
+ {
+ p_msc->xferred_len += nbytes;
+ memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
+ }
+
+ // simulate an transfer complete with adjusted parameters --> this driver callback will fired again
+ dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
+
+ return true; // skip the rest
+ }
+ else
+ {
+ // Application consume all bytes in our buffer. Nothing to do, process with normal flow
+ }
+ }
+ }
+ }
+
+ // Accumulate data so far
+ p_msc->xferred_len += xferred_bytes;
+
+ if ( p_msc->xferred_len >= p_msc->total_len )
+ {
+ // Data Stage is complete
+ p_msc->stage = MSC_STAGE_STATUS;
+ }
+ else
+ {
+ // READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
+ // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_EP_BUFSIZE
+ if (SCSI_CMD_READ_10 == p_cbw->command[0])
+ {
+ proc_read10_cmd(rhport, p_msc);
+ }
+ else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
+ {
+ proc_write10_cmd(rhport, p_msc);
+ }else
+ {
+ // No other command take more than one transfer yet -> unlikely error
+ TU_BREAKPOINT();
+ }
+ }
+ break;
+
+ case MSC_STAGE_STATUS:
+ // processed immediately after this switch, supposedly to be empty
+ break;
+
+ case MSC_STAGE_STATUS_SENT:
+ // Wait for the Status phase to complete
+ if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
+ {
+ TU_LOG2(" SCSI Status: %u\r\n", p_csw->status);
+ // TU_LOG2_MEM(p_csw, xferred_bytes, 2);
+
+ // Invoke complete callback if defined
+ // Note: There is racing issue with samd51 + qspi flash testing with arduino
+ // if complete_cb() is invoked after queuing the status.
+ switch(p_cbw->command[0])
+ {
+ case SCSI_CMD_READ_10:
+ if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
+ break;
+
+ case SCSI_CMD_WRITE_10:
+ if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
+ break;
+
+ default:
+ if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
+ break;
+ }
+
+ // Move to default CMD stage
+ p_msc->stage = MSC_STAGE_CMD;
+
+ // Queue for the next CBW
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
+ }
+ break;
+
+ default : break;
+ }
+
+ if ( p_msc->stage == MSC_STAGE_STATUS )
+ {
+ // Either endpoints is stalled, need to wait until it is cleared by host
+ if ( usbd_edpt_stalled(rhport, p_msc->ep_in) || usbd_edpt_stalled(rhport, p_msc->ep_out) )
+ {
+ // simulate an transfer complete with adjusted parameters --> this driver callback will fired again
+ // and response with status phase after halted endpoints are cleared.
+ // note: use ep_out to prevent confusing with STATUS complete
+ dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false);
+ }
+ else
+ {
+ // Move to Status Sent stage
+ p_msc->stage = MSC_STAGE_STATUS_SENT;
+
+ // Send SCSI Status
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ }
+ }
+
+ return true;
+}
+
+/*------------------------------------------------------------------*/
+/* SCSI Command Process
+ *------------------------------------------------------------------*/
+
+// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
+// In case of a failed status, sense key must be set for reason of failure
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
+{
+ (void) bufsize; // TODO refractor later
+ int32_t resplen;
+
+ switch ( scsi_cmd[0] )
+ {
+ case SCSI_CMD_TEST_UNIT_READY:
+ resplen = 0;
+ if ( !tud_msc_test_unit_ready_cb(lun) )
+ {
+ // Failed status response
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ break;
+
+ case SCSI_CMD_START_STOP_UNIT:
+ resplen = 0;
+
+ if (tud_msc_start_stop_cb)
+ {
+ scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
+ if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
+ {
+ // Failed status response
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ }
+ break;
+
+ case SCSI_CMD_READ_CAPACITY_10:
+ {
+ uint32_t block_count;
+ uint32_t block_size;
+ uint16_t block_size_u16;
+
+ tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
+ block_size = (uint32_t) block_size_u16;
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ scsi_read_capacity10_resp_t read_capa10;
+
+ read_capa10.last_lba = tu_htonl(block_count-1);
+ read_capa10.block_size = tu_htonl(block_size);
+
+ resplen = sizeof(read_capa10);
+ memcpy(buffer, &read_capa10, resplen);
+ }
+ }
+ break;
+
+ case SCSI_CMD_READ_FORMAT_CAPACITY:
+ {
+ scsi_read_format_capacity_data_t read_fmt_capa =
+ {
+ .list_length = 8,
+ .block_num = 0,
+ .descriptor_type = 2, // formatted media
+ .block_size_u16 = 0
+ };
+
+ uint32_t block_count;
+ uint16_t block_size;
+
+ tud_msc_capacity_cb(lun, &block_count, &block_size);
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ read_fmt_capa.block_num = tu_htonl(block_count);
+ read_fmt_capa.block_size_u16 = tu_htons(block_size);
+
+ resplen = sizeof(read_fmt_capa);
+ memcpy(buffer, &read_fmt_capa, resplen);
+ }
+ }
+ break;
+
+ case SCSI_CMD_INQUIRY:
+ {
+ scsi_inquiry_resp_t inquiry_rsp =
+ {
+ .is_removable = 1,
+ .version = 2,
+ .response_data_format = 2,
+ };
+
+ // vendor_id, product_id, product_rev is space padded string
+ memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
+ memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
+ memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
+
+ tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
+
+ resplen = sizeof(inquiry_rsp);
+ memcpy(buffer, &inquiry_rsp, resplen);
+ }
+ break;
+
+ case SCSI_CMD_MODE_SENSE_6:
+ {
+ scsi_mode_sense6_resp_t mode_resp =
+ {
+ .data_len = 3,
+ .medium_type = 0,
+ .write_protected = false,
+ .reserved = 0,
+ .block_descriptor_len = 0 // no block descriptor are included
+ };
+
+ bool writable = true;
+ if (tud_msc_is_writable_cb) {
+ writable = tud_msc_is_writable_cb(lun);
+ }
+ mode_resp.write_protected = !writable;
+
+ resplen = sizeof(mode_resp);
+ memcpy(buffer, &mode_resp, resplen);
+ }
+ break;
+
+ case SCSI_CMD_REQUEST_SENSE:
+ {
+ scsi_sense_fixed_resp_t sense_rsp =
+ {
+ .response_code = 0x70,
+ .valid = 1
+ };
+
+ sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+
+ sense_rsp.sense_key = _mscd_itf.sense_key;
+ sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
+ sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
+
+ resplen = sizeof(sense_rsp);
+ memcpy(buffer, &sense_rsp, resplen);
+
+ // Clear sense data after copy
+ tud_msc_set_sense(lun, 0, 0, 0);
+ }
+ break;
+
+ default: resplen = -1; break;
+ }
+
+ return resplen;
+}
+
+static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
+{
+ msc_cbw_t const * p_cbw = &p_msc->cbw;
+ msc_csw_t * p_csw = &p_msc->csw;
+
+ uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command);
+ TU_ASSERT(block_cnt, ); // prevent div by zero
+
+ uint16_t const block_sz = p_cbw->total_bytes / block_cnt;
+ TU_ASSERT(block_sz, ); // prevent div by zero
+
+ // Adjust lba with transferred bytes
+ uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
+
+ // remaining bytes capped at class buffer
+ int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
+
+ // Application can consume smaller bytes
+ nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes);
+
+ if ( nbytes < 0 )
+ {
+ // negative means error -> pipe is stalled & status in CSW set to failed
+ p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
+ p_csw->status = MSC_CSW_STATUS_FAILED;
+
+ tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
+ usbd_edpt_stall(rhport, p_msc->ep_in);
+ }
+ else if ( nbytes == 0 )
+ {
+ // zero means not ready -> simulate an transfer complete so that this driver callback will fired again
+ dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
+ }
+ else
+ {
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), );
+ }
+}
+
+static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
+{
+ msc_cbw_t const * p_cbw = &p_msc->cbw;
+ bool writable = true;
+ if (tud_msc_is_writable_cb) {
+ writable = tud_msc_is_writable_cb(p_cbw->lun);
+ }
+ if (!writable) {
+ msc_csw_t* p_csw = &p_msc->csw;
+ p_csw->data_residue = p_cbw->total_bytes;
+ p_csw->status = MSC_CSW_STATUS_FAILED;
+
+ tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected
+ usbd_edpt_stall(rhport, p_msc->ep_out);
+ return;
+ }
+
+ // remaining bytes capped at class buffer
+ int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
+
+ // Write10 callback will be called later when usb transfer complete
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/msc/msc_device.h b/sw/Core/Src/tinyusb/src/class/msc/msc_device.h
new file mode 100755
index 0000000..469f2f2
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/msc/msc_device.h
@@ -0,0 +1,171 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_MSC_DEVICE_H_
+#define _TUSB_MSC_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+#include "msc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+#if !defined(CFG_TUD_MSC_EP_BUFSIZE) & defined(CFG_TUD_MSC_BUFSIZE)
+ // TODO warn user to use new name later on
+ // #warning CFG_TUD_MSC_BUFSIZE is renamed to CFG_TUD_MSC_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_MSC_EP_BUFSIZE CFG_TUD_MSC_BUFSIZE
+#endif
+
+#ifndef CFG_TUD_MSC_EP_BUFSIZE
+ #error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better
+#endif
+
+TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct");
+
+/** \addtogroup ClassDriver_MSC
+ * @{
+ * \defgroup MSC_Device Device
+ * @{ */
+
+bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
+
+//--------------------------------------------------------------------+
+// Application Callbacks (WEAK is optional)
+//--------------------------------------------------------------------+
+
+/**
+ * Invoked when received \ref SCSI_CMD_READ_10 command
+ * \param[in] lun Logical unit number
+ * \param[in] lba Logical Block Address to be read
+ * \param[in] offset Byte offset from LBA
+ * \param[out] buffer Buffer which application need to update with the response data.
+ * \param[in] bufsize Requested bytes
+ *
+ * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer
+ * this amount first and invoked this again for remaining data.
+ *
+ * \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete.
+ * tinyusb will invoke this callback with the same parameters again some time later.
+ *
+ * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding
+ * endpoint and return failed status in command status wrapper phase.
+ */
+int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
+
+/**
+ * Invoked when received \ref SCSI_CMD_WRITE_10 command
+ * \param[in] lun Logical unit number
+ * \param[in] lba Logical Block Address to be write
+ * \param[in] offset Byte offset from LBA
+ * \param[out] buffer Buffer which holds written data.
+ * \param[in] bufsize Requested bytes
+ *
+ * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with
+ * other work and invoked this again with adjusted parameters.
+ *
+ * \retval zero Indicate application is not ready yet e.g disk I/O is not complete.
+ * Tinyusb will invoke this callback with the same parameters again some time later.
+ *
+ * \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding
+ * endpoint and return failed status in command status wrapper phase.
+ */
+int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
+
+// Invoked when received SCSI_CMD_INQUIRY
+// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]);
+
+// 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);
+
+// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
+// Application update block count and block size
+void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
+
+/**
+ * Invoked when received an SCSI command not in built-in list below.
+ * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
+ * - READ10 and WRITE10 has their own callbacks
+ *
+ * \param[in] lun Logical unit number
+ * \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
+ * \param[out] buffer Buffer for SCSI Data Stage.
+ * - For INPUT: application must fill this with response.
+ * - For OUTPUT it holds the Data from host
+ * \param[in] bufsize Buffer's length.
+ *
+ * \return Actual bytes processed, can be zero for no-data command.
+ * \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
+ * endpoint and return failed status in command status wrapper phase.
+ */
+int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
+
+/*------------- Optional callbacks -------------*/
+
+// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
+TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
+
+// Invoked when Read10 command is complete
+TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
+
+// Invoke when Write10 command is complete, can be used to flush flash caching
+TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
+
+// Invoked when command in tud_msc_scsi_cb is complete
+TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
+
+// Hook to make a mass storage device read-only. TODO remove
+TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
+
+/** @} */
+/** @} */
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void mscd_init (void);
+void mscd_reset (uint8_t rhport);
+uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
+bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_MSC_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/msc/msc_host.c b/sw/Core/Src/tinyusb/src/class/msc/msc_host.c
new file mode 100755
index 0000000..34dbec2
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/msc/msc_host.c
@@ -0,0 +1,483 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "common/tusb_common.h"
+#include "msc_host.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+enum
+{
+ MSC_STAGE_IDLE = 0,
+ MSC_STAGE_CMD,
+ MSC_STAGE_DATA,
+ MSC_STAGE_STATUS,
+};
+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ uint8_t max_lun;
+
+ volatile bool configured; // Receive SET_CONFIGURE
+ volatile bool mounted; // Enumeration is complete
+
+ struct {
+ uint32_t block_size;
+ uint32_t block_count;
+ } capacity[CFG_TUH_MSC_MAXLUN];
+
+ //------------- SCSI -------------//
+ uint8_t stage;
+ void* buffer;
+ tuh_msc_complete_cb_t complete_cb;
+
+ msc_cbw_t cbw;
+ msc_csw_t csw;
+}msch_interface_t;
+
+CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUSB_HOST_DEVICE_MAX];
+
+// buffer used to read scsi information when mounted
+// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
+
+static inline msch_interface_t* get_itf(uint8_t dev_addr)
+{
+ return &_msch_itf[dev_addr-1];
+}
+
+//--------------------------------------------------------------------+
+// PUBLIC API
+//--------------------------------------------------------------------+
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->max_lun;
+}
+
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->capacity[lun].block_count;
+}
+
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->capacity[lun].block_size;
+}
+
+bool tuh_msc_mounted(uint8_t dev_addr)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->mounted;
+}
+
+bool tuh_msc_ready(uint8_t dev_addr)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->mounted && !hcd_edpt_busy(dev_addr, p_msc->ep_in);
+}
+
+//--------------------------------------------------------------------+
+// PUBLIC API: SCSI COMMAND
+//--------------------------------------------------------------------+
+static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
+{
+ tu_memclr(cbw, sizeof(msc_cbw_t));
+ cbw->signature = MSC_CBW_SIGNATURE;
+ cbw->tag = 0x54555342; // TUSB
+ cbw->lun = lun;
+}
+
+bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
+
+ // TODO claim endpoint
+
+ p_msc->cbw = *cbw;
+ p_msc->stage = MSC_STAGE_CMD;
+ p_msc->buffer = data;
+ p_msc->complete_cb = complete_cb;
+
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
+
+ return true;
+}
+
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read_capacity10_t);
+ cbw.command[0] = SCSI_CMD_READ_CAPACITY_10;
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
+}
+
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_inquiry_t);
+
+ scsi_inquiry_t const cmd_inquiry =
+ {
+ .cmd_code = SCSI_CMD_INQUIRY,
+ .alloc_length = sizeof(scsi_inquiry_resp_t)
+ };
+ memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
+}
+
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = 0;
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
+ cbw.command[1] = lun; // according to wiki TODO need verification
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb);
+}
+
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb)
+{
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = 18; // TODO sense response
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_request_sense_t);
+
+ scsi_request_sense_t const cmd_request_sense =
+ {
+ .cmd_code = SCSI_CMD_REQUEST_SENSE,
+ .alloc_length = 18
+ };
+
+ memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, resposne, complete_cb);
+}
+
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read10_t);
+
+ scsi_read10_t const cmd_read10 =
+ {
+ .cmd_code = SCSI_CMD_READ_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
+ };
+
+ memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb);
+}
+
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_write10_t);
+
+ scsi_write10_t const cmd_write10 =
+ {
+ .cmd_code = SCSI_CMD_WRITE_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
+ };
+
+ memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, (void*) buffer, complete_cb);
+}
+
+#if 0
+// MSC interface Reset (not used now)
+bool tuh_msc_reset(uint8_t dev_addr)
+{
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = MSC_REQ_RESET,
+ .wValue = 0,
+ .wIndex = p_msc->itf_num,
+ .wLength = 0
+ };
+ TU_ASSERT( usbh_control_xfer( dev_addr, &new_request, NULL ) );
+}
+#endif
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API (don't require to verify parameters)
+//--------------------------------------------------------------------+
+void msch_init(void)
+{
+ tu_memclr(_msch_itf, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
+}
+
+void msch_close(uint8_t dev_addr)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ tu_memclr(p_msc, sizeof(msch_interface_t));
+ tuh_msc_unmount_cb(dev_addr); // invoke Application Callback
+}
+
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ msc_cbw_t const * cbw = &p_msc->cbw;
+ msc_csw_t * csw = &p_msc->csw;
+
+ switch (p_msc->stage)
+ {
+ case MSC_STAGE_CMD:
+ // Must be Command Block
+ TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
+
+ if ( cbw->total_bytes && p_msc->buffer )
+ {
+ // Data stage if any
+ p_msc->stage = MSC_STAGE_DATA;
+
+ uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, cbw->total_bytes));
+ }else
+ {
+ // Status stage
+ p_msc->stage = MSC_STAGE_STATUS;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ }
+ break;
+
+ case MSC_STAGE_DATA:
+ // Status stage
+ p_msc->stage = MSC_STAGE_STATUS;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ break;
+
+ case MSC_STAGE_STATUS:
+ // SCSI op is complete
+ p_msc->stage = MSC_STAGE_IDLE;
+
+ if (p_msc->complete_cb) p_msc->complete_cb(dev_addr, cbw, csw);
+ break;
+
+ // unknown state
+ default: break;
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// MSC Enumeration
+//--------------------------------------------------------------------+
+
+static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+{
+ TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
+ MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
+
+ msch_interface_t* p_msc = get_itf(dev_addr);
+
+ //------------- Open Data Pipe -------------//
+ tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ for(uint32_t i=0; i<2; i++)
+ {
+ TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
+ TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
+
+ if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
+ {
+ p_msc->ep_in = ep_desc->bEndpointAddress;
+ }else
+ {
+ p_msc->ep_out = ep_desc->bEndpointAddress;
+ }
+
+ ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
+ }
+
+ p_msc->itf_num = itf_desc->bInterfaceNumber;
+ (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+
+ return true;
+}
+
+bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_ASSERT(p_msc->itf_num == itf_num);
+
+ p_msc->configured = true;
+
+ //------------- Get Max Lun -------------//
+ TU_LOG2("MSC Get Max Lun\r\n");
+ tusb_control_request_t request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = MSC_REQ_GET_MAX_LUN,
+ .wValue = 0,
+ .wIndex = itf_num,
+ .wLength = 1
+ };
+ TU_ASSERT(tuh_control_xfer(dev_addr, &request, &p_msc->max_lun, config_get_maxlun_complete));
+
+ return true;
+}
+
+static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+
+ msch_interface_t* p_msc = get_itf(dev_addr);
+
+ // STALL means zero
+ p_msc->max_lun = (XFER_RESULT_SUCCESS == result) ? _msch_buffer[0] : 0;
+ p_msc->max_lun++; // MAX LUN is minus 1 by specs
+
+ // TODO multiple LUN support
+ TU_LOG2("SCSI Test Unit Ready\r\n");
+ uint8_t const lun = 0;
+ tuh_msc_test_unit_ready(dev_addr, lun, config_test_unit_ready_complete);
+
+ return true;
+}
+
+static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
+{
+ if (csw->status == 0)
+ {
+ // Unit is ready, read its capacity
+ TU_LOG2("SCSI Read Capacity\r\n");
+ tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) _msch_buffer, config_read_capacity_complete);
+ }else
+ {
+ // Note: During enumeration, some device fails Test Unit Ready and require a few retries
+ // with Request Sense to start working !!
+ // TODO limit number of retries
+ TU_LOG2("SCSI Request Sense\r\n");
+ TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete));
+ }
+
+ return true;
+}
+
+static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
+{
+ TU_ASSERT(csw->status == 0);
+ TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete));
+ return true;
+}
+
+static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
+{
+ TU_ASSERT(csw->status == 0);
+
+ msch_interface_t* p_msc = get_itf(dev_addr);
+
+ // Capacity response field: Block size and Last LBA are both Big-Endian
+ scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) _msch_buffer;
+ p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
+ p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
+
+ // Mark enumeration is complete
+ p_msc->mounted = true;
+ tuh_msc_mount_cb(dev_addr);
+
+ usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/msc/msc_host.h b/sw/Core/Src/tinyusb/src/class/msc/msc_host.h
new file mode 100755
index 0000000..8116e72
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/msc/msc_host.h
@@ -0,0 +1,131 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_MSC_HOST_H_
+#define _TUSB_MSC_HOST_H_
+
+#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "msc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUH_MSC_MAXLUN
+#define CFG_TUH_MSC_MAXLUN 4
+#endif
+
+
+/** \addtogroup ClassDriver_MSC
+ * @{
+ * \defgroup MSC_Host Host
+ * The interface API includes status checking function, data transferring function and callback functions
+ * @{ */
+
+typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Check if device supports MassStorage interface.
+// This function true after tuh_msc_mounted_cb() and false after tuh_msc_unmounted_cb()
+bool tuh_msc_mounted(uint8_t dev_addr);
+
+// Check if the interface is currently ready or busy transferring data
+bool tuh_msc_ready(uint8_t dev_addr);
+
+// Get Max Lun
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr);
+
+// Get number of block
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun);
+
+// Get block size in bytes
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
+
+// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
+// Complete callback is invoked when SCSI op is complete.
+// return true if success, false if there is already pending operation.
+bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb);
+
+// Perform SCSI Inquiry command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb);
+
+// Perform SCSI Test Unit Ready command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb);
+
+// Perform SCSI Request Sense 10 command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb);
+
+// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb);
+
+// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb);
+
+// Perform SCSI Read Capacity 10 command
+// Complete callback is invoked when SCSI op is complete.
+// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
+// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb);
+
+//------------- Application Callback -------------//
+
+// Invoked when a device with MassStorage interface is mounted
+void tuh_msc_mount_cb(uint8_t dev_addr);
+
+// Invoked when a device with MassStorage interface is unmounted
+void tuh_msc_unmount_cb(uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+
+void msch_init(void);
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
+bool msch_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void msch_close(uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_MSC_HOST_H_ */
+
+/// @}
+/// @}
diff --git a/sw/Core/Src/tinyusb/src/class/net/net_device.c b/sw/Core/Src/tinyusb/src/class/net/net_device.c
new file mode 100755
index 0000000..ce11312
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/net/net_device.c
@@ -0,0 +1,439 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NET )
+
+#include "net_device.h"
+#include "device/usbd_pvt.h"
+#include "rndis_protocol.h"
+
+void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
+ uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
+
+ uint8_t ep_notif;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ bool ecm_mode;
+
+ // Endpoint descriptor use to open/close when receving SetInterface
+ // TODO since configuration descriptor may not be long-lived memory, we should
+ // keep a copy of endpoint attribute instead
+ uint8_t const * ecm_desc_epdata;
+
+} netd_interface_t;
+
+#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
+#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
+
+struct ecm_notify_struct
+{
+ tusb_control_request_t header;
+ uint32_t downlink, uplink;
+};
+
+static const struct ecm_notify_struct ecm_notify_nc =
+{
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
+ .wValue = 1 /* Connected */,
+ .wLength = 0,
+ },
+};
+
+static const struct ecm_notify_struct ecm_notify_csc =
+{
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
+ .wLength = 8,
+ },
+ .downlink = 9728000,
+ .uplink = 9728000,
+};
+
+// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static union
+{
+ uint8_t rndis_buf[120];
+ struct ecm_notify_struct ecm_buf;
+} notify;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// TODO remove CFG_TUSB_MEM_SECTION
+CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf;
+
+static bool can_xmit;
+
+void tud_network_recv_renew(void)
+{
+ usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received));
+}
+
+static void do_in_xfer(uint8_t *buf, uint16_t len)
+{
+ can_xmit = false;
+ usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len);
+}
+
+void netd_report(uint8_t *buf, uint16_t len)
+{
+ usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void netd_init(void)
+{
+ tu_memclr(&_netd_itf, sizeof(_netd_itf));
+}
+
+void netd_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ netd_init();
+}
+
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
+ TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
+ TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
+
+ bool const is_ecm = (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
+ 0x00 == itf_desc->bInterfaceProtocol);
+
+ TU_VERIFY(is_rndis || is_ecm, 0);
+
+ // confirm interface hasn't already been allocated
+ TU_ASSERT(0 == _netd_itf.ep_notif, 0);
+
+ // sanity check the descriptor
+ _netd_itf.ecm_mode = is_ecm;
+
+ //------------- Management Interface -------------//
+ _netd_itf.itf_num = itf_desc->bInterfaceNumber;
+
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const * p_desc = tu_desc_next( itf_desc );
+
+ // Communication Functional Descriptors
+ while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
+ {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // notification endpoint (if any)
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+ {
+ TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+
+ _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface -------------//
+ // - RNDIS Data followed immediately by a pair of endpoints
+ // - CDC-ECM data interface has 2 alternate settings
+ // - 0 : zero endpoints for inactive (default)
+ // - 1 : IN & OUT endpoints for active networking
+ TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+
+ do
+ {
+ tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+ TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
+
+ // Pair of endpoints
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+
+ if ( _netd_itf.ecm_mode )
+ {
+ // ECM by default is in-active, save the endpoint attribute
+ // to open later when received setInterface
+ _netd_itf.ecm_desc_epdata = p_desc;
+ }else
+ {
+ // Open endpoint pair for RNDIS
+ TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
+
+ tud_network_init_cb();
+
+ // we are ready to transmit a packet
+ can_xmit = true;
+
+ // prepare for incoming packets
+ tud_network_recv_renew();
+ }
+
+ drv_len += 2*sizeof(tusb_desc_endpoint_t);
+
+ return drv_len;
+}
+
+static void ecm_report(bool nc)
+{
+ notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
+ notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
+ netd_report((uint8_t *)&notify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
+}
+
+// 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 netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ switch ( request->bmRequestType_bit.type )
+ {
+ case TUSB_REQ_TYPE_STANDARD:
+ switch ( request->bRequest )
+ {
+ case TUSB_REQ_GET_INTERFACE:
+ {
+ uint8_t const req_itfnum = (uint8_t) request->wIndex;
+ TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
+
+ tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
+ }
+ break;
+
+ case TUSB_REQ_SET_INTERFACE:
+ {
+ uint8_t const req_itfnum = (uint8_t) request->wIndex;
+ uint8_t const req_alt = (uint8_t) request->wValue;
+
+ // Only valid for Data Interface with Alternate is either 0 or 1
+ TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
+
+ // ACM-ECM only: qequest to enable/disable network activities
+ TU_VERIFY(_netd_itf.ecm_mode);
+
+ _netd_itf.itf_data_alt = req_alt;
+
+ if ( _netd_itf.itf_data_alt )
+ {
+ // TODO since we don't actually close endpoint
+ // hack here to not re-open it
+ if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
+ {
+ TU_ASSERT(_netd_itf.ecm_desc_epdata);
+ TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
+
+ // TODO should be merge with RNDIS's after endpoint opened
+ // Also should have opposite callback for application to disable network !!
+ tud_network_init_cb();
+ can_xmit = true; // we are ready to transmit a packet
+ tud_network_recv_renew(); // prepare for incoming packets
+ }
+ }else
+ {
+ // TODO close the endpoint pair
+ // For now pretend that we did, this should have no harm since host won't try to
+ // communicate with the endpoints again
+ // _netd_itf.ep_in = _netd_itf.ep_out = 0
+ }
+
+ tud_control_status(rhport, request);
+ }
+ break;
+
+ // unsupported request
+ default: return false;
+ }
+ break;
+
+ case TUSB_REQ_TYPE_CLASS:
+ TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+
+ if (_netd_itf.ecm_mode)
+ {
+ /* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
+ if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
+ {
+ tud_control_xfer(rhport, request, NULL, 0);
+ ecm_report(true);
+ }
+ }
+ else
+ {
+ if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
+ uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
+ TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
+ tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
+ }
+ else
+ {
+ tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf));
+ }
+ }
+ break;
+
+ // unsupported request
+ default: return false;
+ }
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ // Handle RNDIS class control OUT only
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
+ request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
+ _netd_itf.itf_num == request->wIndex)
+ {
+ if ( !_netd_itf.ecm_mode )
+ {
+ rndis_class_set_handler(notify.rndis_buf, request->wLength);
+ }
+ }
+ }
+
+ return true;
+}
+
+static void handle_incoming_packet(uint32_t len)
+{
+ uint8_t *pnt = received;
+ uint32_t size = 0;
+
+ if (_netd_itf.ecm_mode)
+ {
+ size = len;
+ }
+ else
+ {
+ rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
+ if (len >= sizeof(rndis_data_packet_t))
+ if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
+ if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
+ {
+ pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
+ size = r->DataLength;
+ }
+ }
+
+ if (!tud_network_recv_cb(pnt, size))
+ {
+ /* if a buffer was never handled by user code, we must renew on the user's behalf */
+ tud_network_recv_renew();
+ }
+}
+
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) rhport;
+ (void) result;
+
+ /* new packet received */
+ if ( ep_addr == _netd_itf.ep_out )
+ {
+ handle_incoming_packet(xferred_bytes);
+ }
+
+ /* data transmission finished */
+ if ( ep_addr == _netd_itf.ep_in )
+ {
+ /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
+
+ if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) )
+ {
+ do_in_xfer(NULL, 0); /* a ZLP is needed */
+ }
+ else
+ {
+ /* we're finally finished */
+ can_xmit = true;
+ }
+ }
+
+ if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
+ {
+ if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
+ }
+
+ return true;
+}
+
+bool tud_network_can_xmit(void)
+{
+ return can_xmit;
+}
+
+void tud_network_xmit(void *ref, uint16_t arg)
+{
+ uint8_t *data;
+ uint16_t len;
+
+ if (!can_xmit)
+ return;
+
+ len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
+ data = transmitted + len;
+
+ len += tud_network_xmit_cb(data, ref, arg);
+
+ if (!_netd_itf.ecm_mode)
+ {
+ rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
+ memset(hdr, 0, sizeof(rndis_data_packet_t));
+ hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
+ hdr->MessageLength = len;
+ hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset);
+ hdr->DataLength = len - sizeof(rndis_data_packet_t);
+ }
+
+ do_in_xfer(transmitted, len);
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/net/net_device.h b/sw/Core/Src/tinyusb/src/class/net/net_device.h
new file mode 100755
index 0000000..38c47d6
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/net/net_device.h
@@ -0,0 +1,87 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_NET_DEVICE_H_
+#define _TUSB_NET_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+#include "class/cdc/cdc.h"
+
+/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
+#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+/* Maximum Tranmission Unit (in bytes) of the network, including Ethernet header */
+#ifndef CFG_TUD_NET_MTU
+#define CFG_TUD_NET_MTU 1514
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// client must provide this: initialize any network state back to the beginning
+void tud_network_init_cb(void);
+
+// client must provide this: return false if the packet buffer was not accepted
+bool tud_network_recv_cb(const uint8_t *src, uint16_t size);
+
+// client must provide this: copy from network stack packet pointer to dst
+uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg);
+
+// client must provide this: 48-bit MAC address
+// TODO removed later since it is not part of tinyusb stack
+extern const uint8_t tud_network_mac_address[6];
+
+// indicate to network driver that client has finished with the packet provided to network_recv_cb()
+void tud_network_recv_renew(void);
+
+// poll network driver for its ability to accept another packet to transmit
+bool tud_network_can_xmit(void);
+
+// if network_can_xmit() returns true, network_xmit() can be called once
+void tud_network_xmit(void *ref, uint16_t arg);
+
+//--------------------------------------------------------------------+
+// INTERNAL USBD-CLASS DRIVER API
+//--------------------------------------------------------------------+
+void netd_init (void);
+void netd_reset (uint8_t rhport);
+uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void netd_report (uint8_t *buf, uint16_t len);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_NET_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc.h b/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc.h
new file mode 100755
index 0000000..7d7005c
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc.h
@@ -0,0 +1,316 @@
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 N Conrad
+ *
+ * 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_USBTMC_H__
+#define _TUSB_USBTMC_H__
+
+#include "common/tusb_common.h"
+
+
+/* Implements USBTMC Revision 1.0, April 14, 2003
+
+ String descriptors must have a "LANGID=0x409"/US English string.
+ Characters must be 0x20 (' ') to 0x7E ('~') ASCII,
+ But MUST not contain: "/:?\*
+ Also must not have leading or trailing space (' ')
+ Device descriptor must state USB version 0x0200 or greater
+
+ If USB488DeviceCapabilites.D2 = 1 (SR1), then there must be a INT endpoint.
+*/
+
+#define USBTMC_VERSION 0x0100
+#define USBTMC_488_VERSION 0x0100
+
+typedef enum {
+ USBTMC_MSGID_DEV_DEP_MSG_OUT = 1u,
+ USBTMC_MSGID_DEV_DEP_MSG_IN = 2u,
+ USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT = 126u,
+ USBTMC_MSGID_VENDOR_SPECIFIC_IN = 127u,
+ USBTMC_MSGID_USB488_TRIGGER = 128u,
+} usbtmc_msgid_enum;
+
+/// \brief Message header (For BULK OUT and BULK IN); 4 bytes
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t MsgID ; ///< Message type ID (usbtmc_msgid_enum)
+ uint8_t bTag ; ///< Transfer ID 1<=bTag<=255
+ uint8_t bTagInverse ; ///< Complement of the tag
+ uint8_t _reserved ; ///< Must be 0x00
+} usbtmc_msg_header_t;
+
+typedef struct TU_ATTR_PACKED
+{
+ usbtmc_msg_header_t header;
+ uint8_t data[8];
+} usbtmc_msg_generic_t;
+
+/* Uses on the bulk-out endpoint: */
+// Next 8 bytes are message-specific
+typedef struct TU_ATTR_PACKED {
+ usbtmc_msg_header_t header ; ///< Header
+ uint32_t TransferSize ; ///< Transfer size; LSB first
+ struct TU_ATTR_PACKED
+ {
+ unsigned int EOM : 1 ; ///< EOM set on last byte
+ } bmTransferAttributes;
+ uint8_t _reserved[3];
+} usbtmc_msg_request_dev_dep_out;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_out) == 12u, "struct wrong length");
+
+// Next 8 bytes are message-specific
+typedef struct TU_ATTR_PACKED
+{
+ usbtmc_msg_header_t header ; ///< Header
+ uint32_t TransferSize ; ///< Transfer size; LSB first
+ struct TU_ATTR_PACKED
+ {
+ unsigned int TermCharEnabled : 1 ; ///< "The Bulk-IN transfer must terminate on the specified TermChar."; CAPABILITIES must list TermChar
+ } bmTransferAttributes;
+ uint8_t TermChar;
+ uint8_t _reserved[2];
+} usbtmc_msg_request_dev_dep_in;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_in) == 12u, "struct wrong length");
+
+/* Bulk-in headers */
+
+typedef struct TU_ATTR_PACKED
+{
+ usbtmc_msg_header_t header;
+ uint32_t TransferSize;
+ struct TU_ATTR_PACKED
+ {
+ uint8_t EOM: 1; ///< Last byte of transfer is the end of the message
+ uint8_t UsingTermChar: 1; ///< Support TermChar && Request.TermCharEnabled && last char in transfer is TermChar
+ } bmTransferAttributes;
+ uint8_t _reserved[3];
+} usbtmc_msg_dev_dep_msg_in_header_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_dev_dep_msg_in_header_t) == 12u, "struct wrong length");
+
+/* Unsupported vendor things.... Are these ever used?*/
+
+typedef struct TU_ATTR_PACKED
+{
+ usbtmc_msg_header_t header ; ///< Header
+ uint32_t TransferSize ; ///< Transfer size; LSB first
+ uint8_t _reserved[4];
+} usbtmc_msg_request_vendor_specific_out;
+
+
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_out) == 12u, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
+ usbtmc_msg_header_t header ; ///< Header
+ uint32_t TransferSize ; ///< Transfer size; LSB first
+ uint8_t _reserved[4];
+} usbtmc_msg_request_vendor_specific_in;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_in) == 12u, "struct wrong length");
+
+// Control request type should use tusb_control_request_t
+
+/*
+typedef struct TU_ATTR_PACKED {
+ struct {
+ unsigned int Recipient : 5 ; ///< EOM set on last byte
+ unsigned int Type : 2 ; ///< EOM set on last byte
+ unsigned int DirectionToHost : 1 ; ///< 0 is OUT, 1 is IN
+ } bmRequestType;
+ uint8_t bRequest ; ///< If bmRequestType.Type = Class, see usmtmc_request_type_enum
+ uint16_t wValue ;
+ uint16_t wIndex ;
+ uint16_t wLength ; // Number of bytes in data stage
+} usbtmc_class_specific_control_req;
+
+*/
+// bulk-in protocol errors
+enum {
+ USBTMC_BULK_IN_ERR_INCOMPLETE_HEADER = 1u,
+ USBTMC_BULK_IN_ERR_UNSUPPORTED = 2u,
+ USBTMC_BULK_IN_ERR_BAD_PARAMETER = 3u,
+ USBTMC_BULK_IN_ERR_DATA_TOO_SHORT = 4u,
+ USBTMC_BULK_IN_ERR_DATA_TOO_LONG = 5u,
+};
+// bult-in halt errors
+enum {
+ USBTMC_BULK_IN_ERR = 1u, ///< receives a USBTMC command message that expects a response while a
+ /// Bulk-IN transfer is in progress
+};
+
+typedef enum {
+ USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT = 1u,
+ USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS = 2u,
+ USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN = 3u,
+ USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS = 4u,
+ USBTMC_bREQUEST_INITIATE_CLEAR = 5u,
+ USBTMC_bREQUEST_CHECK_CLEAR_STATUS = 6u,
+ USBTMC_bREQUEST_GET_CAPABILITIES = 7u,
+
+ USBTMC_bREQUEST_INDICATOR_PULSE = 64u, // Optional
+
+ /****** USBTMC 488 *************/
+ USB488_bREQUEST_READ_STATUS_BYTE = 128u,
+ USB488_bREQUEST_REN_CONTROL = 160u,
+ USB488_bREQUEST_GO_TO_LOCAL = 161u,
+ USB488_bREQUEST_LOCAL_LOCKOUT = 162u,
+
+} usmtmc_request_type_enum;
+
+typedef enum {
+ USBTMC_STATUS_SUCCESS = 0x01,
+ USBTMC_STATUS_PENDING = 0x02,
+ USBTMC_STATUS_FAILED = 0x80,
+ USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS = 0x81,
+ USBTMC_STATUS_SPLIT_NOT_IN_PROGRESS = 0x82,
+ USBTMC_STATUS_SPLIT_IN_PROGRESS = 0x83
+} usbtmc_status_enum;
+
+/************************************************************
+ * Control Responses
+ */
+
+typedef struct TU_ATTR_PACKED {
+ uint8_t USBTMC_status; ///< usbtmc_status_enum
+ uint8_t _reserved;
+ uint16_t bcdUSBTMC; ///< USBTMC_VERSION
+
+ struct TU_ATTR_PACKED
+ {
+ unsigned int listenOnly :1;
+ unsigned int talkOnly :1;
+ unsigned int supportsIndicatorPulse :1;
+ } bmIntfcCapabilities;
+ struct TU_ATTR_PACKED
+ {
+ unsigned int canEndBulkInOnTermChar :1;
+ } bmDevCapabilities;
+ uint8_t _reserved2[6];
+ uint8_t _reserved3[12];
+} usbtmc_response_capabilities_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_t) == 0x18, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t USBTMC_status;
+ struct TU_ATTR_PACKED
+ {
+ unsigned int BulkInFifoBytes :1;
+ } bmClear;
+} usbtmc_get_clear_status_rsp_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
+
+// Used for both abort bulk IN and bulk OUT
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t USBTMC_status;
+ uint8_t bTag;
+} usbtmc_initiate_abort_rsp_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
+
+// Used for both check_abort_bulk_in_status and check_abort_bulk_out_status
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t USBTMC_status;
+ struct TU_ATTR_PACKED
+ {
+ unsigned int BulkInFifoBytes : 1; ///< Has queued data or a short packet that is queued
+ } bmAbortBulkIn;
+ uint8_t _reserved[2]; ///< Must be zero
+ uint32_t NBYTES_RXD_TXD;
+} usbtmc_check_abort_bulk_rsp_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_check_abort_bulk_rsp_t) == 8u, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t USBTMC_status; ///< usbtmc_status_enum
+ uint8_t _reserved;
+ uint16_t bcdUSBTMC; ///< USBTMC_VERSION
+
+ struct TU_ATTR_PACKED
+ {
+ unsigned int listenOnly :1;
+ unsigned int talkOnly :1;
+ unsigned int supportsIndicatorPulse :1;
+ } bmIntfcCapabilities;
+
+ struct TU_ATTR_PACKED
+ {
+ unsigned int canEndBulkInOnTermChar :1;
+ } bmDevCapabilities;
+
+ uint8_t _reserved2[6];
+ uint16_t bcdUSB488;
+
+ struct TU_ATTR_PACKED
+ {
+ unsigned int is488_2 :1;
+ unsigned int supportsREN_GTL_LLO :1;
+ unsigned int supportsTrigger :1;
+ } bmIntfcCapabilities488;
+
+ struct TU_ATTR_PACKED
+ {
+ unsigned int SCPI :1;
+ unsigned int SR1 :1;
+ unsigned int RL1 :1;
+ unsigned int DT1 :1;
+ } bmDevCapabilities488;
+ uint8_t _reserved3[8];
+} usbtmc_response_capabilities_488_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_488_t) == 0x18, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t USBTMC_status;
+ uint8_t bTag;
+ uint8_t statusByte;
+} usbtmc_read_stb_rsp_488_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
+ struct TU_ATTR_PACKED
+ {
+ unsigned int bTag : 7;
+ unsigned int one : 1;
+ } bNotify1;
+ uint8_t StatusByte;
+} usbtmc_read_stb_interrupt_488_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_interrupt_488_t) == 2u, "struct wrong length");
+
+#endif
+
diff --git a/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.c b/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.c
new file mode 100755
index 0000000..d5f8fe4
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.c
@@ -0,0 +1,864 @@
+/*
+ * usbtmc.c
+ *
+ * Created on: Sep 9, 2019
+ * Author: nconrad
+ */
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Nathan Conrad
+ *
+ * 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.
+ */
+
+/*
+ * This library is not fully reentrant, though it is reentrant from the view
+ * of either the application layer or the USB stack. Due to its locking,
+ * it is not safe to call its functions from interrupts.
+ *
+ * The one exception is that its functions may not be called from the application
+ * until the USB stack is initialized. This should not be a problem since the
+ * device shouldn't be sending messages until it receives a request from the
+ * host.
+ */
+
+
+/*
+ * In the case of single-CPU "no OS", this task is never preempted other than by
+ * interrupts, and the USBTMC code isn't called by interrupts, so all is OK. For "no OS",
+ * the mutex structure's main effect is to disable the USB interrupts.
+ * With an OS, this class driver uses the OSAL to perform locking. The code uses a single lock
+ * and does not call outside of this class with a lock held, so deadlocks won't happen.
+ */
+
+//Limitations:
+// "vendor-specific" commands are not handled.
+// Dealing with "termchar" must be handled by the application layer,
+// though additional error checking is does in this module.
+// talkOnly and listenOnly are NOT supported. They're not permitted
+// in USB488, anyway.
+
+/* Supported:
+ *
+ * Notification pulse
+ * Trigger
+ * Read status byte (both by interrupt endpoint and control message)
+ *
+ */
+
+
+// TODO:
+// USBTMC 3.2.2 error conditions not strictly followed
+// No local lock-out, REN, or GTL.
+// Clear message available status byte at the correct time? (488 4.3.1.3)
+
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_USBTMC)
+
+#include <string.h>
+#include "usbtmc.h"
+#include "usbtmc_device.h"
+#include "device/usbd.h"
+#include "osal/osal.h"
+
+// FIXME: I shouldn't need to include _pvt headers, but it is necessary for usbd_edpt_xfer, _stall, and _busy
+#include "device/usbd_pvt.h"
+
+#ifdef xDEBUG
+#include "uart_util.h"
+static char logMsg[150];
+#endif
+
+/*
+ * The state machine does not allow simultaneous reading and writing. This is
+ * consistent with USBTMC.
+ */
+
+typedef enum
+{
+ STATE_CLOSED, // Endpoints have not yet been opened since USB reset
+ STATE_NAK, // Bulk-out endpoint is in NAK state.
+ STATE_IDLE, // Bulk-out endpoint is waiting for CMD.
+ STATE_RCV, // Bulk-out is receiving DEV_DEP message
+ STATE_TX_REQUESTED,
+ STATE_TX_INITIATED,
+ STATE_TX_SHORTED,
+ STATE_CLEARING,
+ STATE_ABORTING_BULK_IN,
+ STATE_ABORTING_BULK_IN_SHORTED, // aborting, and short packet has been queued for transmission
+ STATE_ABORTING_BULK_IN_ABORTED, // aborting, and short packet has been transmitted
+ STATE_ABORTING_BULK_OUT,
+ STATE_NUM_STATES
+} usbtmcd_state_enum;
+
+#if (CFG_TUD_USBTMC_ENABLE_488)
+ typedef usbtmc_response_capabilities_488_t usbtmc_capabilities_specific_t;
+#else
+ typedef usbtmc_response_capabilities_t usbtmc_capabilities_specific_t;
+#endif
+
+
+typedef struct
+{
+ volatile usbtmcd_state_enum state;
+
+ uint8_t itf_id;
+ uint8_t rhport;
+ uint8_t ep_bulk_in;
+ uint8_t ep_bulk_out;
+ uint8_t ep_int_in;
+ // IN buffer is only used for first packet, not the remainder
+ // in order to deal with prepending header
+ CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE];
+ // OUT buffer receives one packet at a time
+ CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE];
+ uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
+ uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
+
+ uint8_t lastBulkOutTag; // used for aborts (mostly)
+ uint8_t lastBulkInTag; // used for aborts (mostly)
+
+ uint8_t const * devInBuffer; // pointer to application-layer used for transmissions
+
+ usbtmc_capabilities_specific_t const * capabilities;
+} usbtmc_interface_state_t;
+
+CFG_TUSB_MEM_SECTION static usbtmc_interface_state_t usbtmc_state =
+{
+ .itf_id = 0xFF,
+};
+
+// We need all headers to fit in a single packet in this implementation.
+TU_VERIFY_STATIC(USBTMCD_MAX_PACKET_SIZE >= 32u,"USBTMC dev EP packet size too small");
+TU_VERIFY_STATIC(
+ (sizeof(usbtmc_state.ep_bulk_in_buf) % USBTMCD_MAX_PACKET_SIZE) == 0,
+ "packet buffer must be a multiple of the packet size");
+
+static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len);
+static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen);
+
+static uint8_t termChar;
+static uint8_t termCharRequested = false;
+
+
+osal_mutex_def_t usbtmcLockBuffer;
+static osal_mutex_t usbtmcLock;
+
+// Our own private lock, mostly for the state variable.
+#define criticalEnter() do {osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
+#define criticalLeave() do {osal_mutex_unlock(usbtmcLock); } while (0)
+
+bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
+{
+ bool ret = true;
+ criticalEnter();
+ usbtmcd_state_enum oldState = usbtmc_state.state;
+ if (oldState == expectedState)
+ {
+ usbtmc_state.state = newState;
+ }
+ else
+ {
+ ret = false;
+ }
+ criticalLeave();
+ return ret;
+}
+
+// called from app
+// We keep a reference to the buffer, so it MUST not change until the app is
+// notified that the transfer is complete.
+// length of data is specified in the hdr.
+
+// We can't just send the whole thing at once because we need to concatanate the
+// header with the data.
+bool tud_usbtmc_transmit_dev_msg_data(
+ const void * data, size_t len,
+ bool endOfMessage,
+ bool usingTermChar)
+{
+ const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
+
+#ifndef NDEBUG
+ TU_ASSERT(len > 0u);
+ TU_ASSERT(len <= usbtmc_state.transfer_size_remaining);
+ TU_ASSERT(usbtmc_state.transfer_size_sent == 0u);
+ if(usingTermChar)
+ {
+ TU_ASSERT(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
+ TU_ASSERT(termCharRequested);
+ TU_ASSERT(((uint8_t*)data)[len-1u] == termChar);
+ }
+#endif
+
+ TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
+ usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_state.ep_bulk_in_buf;
+ tu_varclr(hdr);
+ hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
+ hdr->header.bTag = usbtmc_state.lastBulkInTag;
+ hdr->header.bTagInverse = (uint8_t)~(usbtmc_state.lastBulkInTag);
+ hdr->TransferSize = len;
+ hdr->bmTransferAttributes.EOM = endOfMessage;
+ hdr->bmTransferAttributes.UsingTermChar = usingTermChar;
+
+ // Copy in the header
+ const size_t headerLen = sizeof(*hdr);
+ const size_t dataLen = ((headerLen + hdr->TransferSize) <= txBufLen) ?
+ len : (txBufLen - headerLen);
+ const size_t packetLen = headerLen + dataLen;
+
+ memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + headerLen, data, dataLen);
+ usbtmc_state.transfer_size_remaining = len - dataLen;
+ usbtmc_state.transfer_size_sent = dataLen;
+ usbtmc_state.devInBuffer = (uint8_t*)data + (dataLen);
+
+ bool stateChanged =
+ atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
+ TU_VERIFY(stateChanged);
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen));
+ return true;
+}
+
+void usbtmcd_init_cb(void)
+{
+ usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
+#ifndef NDEBUG
+# if CFG_TUD_USBTMC_ENABLE_488
+ if(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger)
+ TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,);
+ // Per USB488 spec: table 8
+ TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly,);
+ TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly,);
+# endif
+ if(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse)
+ TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,);
+#endif
+
+ usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
+}
+
+uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void)rhport;
+
+ uint16_t drv_len;
+ uint8_t const * p_desc;
+ uint8_t found_endpoints = 0;
+
+ TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS , 0);
+ TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS, 0);
+
+#ifndef NDEBUG
+ // Only 2 or 3 endpoints are allowed for USBTMC.
+ TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3), 0);
+#endif
+
+ TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
+
+ // Interface
+ drv_len = 0u;
+ p_desc = (uint8_t const *) itf_desc;
+
+ usbtmc_state.itf_id = itf_desc->bInterfaceNumber;
+ usbtmc_state.rhport = rhport;
+
+ while (found_endpoints < itf_desc->bNumEndpoints && drv_len <= max_len)
+ {
+ if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
+ {
+ tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc;
+ switch(ep_desc->bmAttributes.xfer) {
+ case TUSB_XFER_BULK:
+ TU_ASSERT(ep_desc->wMaxPacketSize.size == USBTMCD_MAX_PACKET_SIZE, 0);
+ if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
+ {
+ usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress;
+ } else {
+ usbtmc_state.ep_bulk_out = ep_desc->bEndpointAddress;
+ }
+
+ break;
+ case TUSB_XFER_INTERRUPT:
+#ifndef NDEBUG
+ TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN, 0);
+ TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
+#endif
+ usbtmc_state.ep_int_in = ep_desc->bEndpointAddress;
+ break;
+ default:
+ TU_ASSERT(false, 0);
+ }
+ TU_ASSERT( usbd_edpt_open(rhport, ep_desc), 0);
+ found_endpoints++;
+ }
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // bulk endpoints are required, but interrupt IN is optional
+#ifndef NDEBUG
+ TU_ASSERT(usbtmc_state.ep_bulk_in != 0, 0);
+ TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
+ if (itf_desc->bNumEndpoints == 2)
+ {
+ TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
+ }
+ else if (itf_desc->bNumEndpoints == 3)
+ {
+ TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
+ }
+#if (CFG_TUD_USBTMC_ENABLE_488)
+ if(usbtmc_state.capabilities->bmIntfcCapabilities488.is488_2 ||
+ usbtmc_state.capabilities->bmDevCapabilities488.SR1)
+ {
+ TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
+ }
+#endif
+#endif
+ atomicChangeState(STATE_CLOSED, STATE_NAK);
+ tud_usbtmc_open_cb(itf_desc->iInterface);
+
+ return drv_len;
+}
+// Tell USBTMC class to set its bulk-in EP to ACK so that it can
+// receive USBTMC commands.
+// Returns false if it was already in an ACK state or is busy
+// processing a command (such as a clear). Returns true if it was
+// in the NAK state and successfully transitioned to the ACK wait
+// state.
+bool tud_usbtmc_start_bus_read()
+{
+ usbtmcd_state_enum oldState = usbtmc_state.state;
+ switch(oldState)
+ {
+ // These may transition to IDLE
+ case STATE_NAK:
+ case STATE_ABORTING_BULK_IN_ABORTED:
+ TU_VERIFY(atomicChangeState(oldState, STATE_IDLE));
+ break;
+ // When receiving, let it remain receiving
+ case STATE_RCV:
+ break;
+ default:
+ TU_VERIFY(false);
+ }
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64));
+ return true;
+}
+
+void usbtmcd_reset_cb(uint8_t rhport)
+{
+ (void)rhport;
+ usbtmc_capabilities_specific_t const * capabilities = tud_usbtmc_get_capabilities_cb();
+
+ criticalEnter();
+ tu_varclr(&usbtmc_state);
+ usbtmc_state.capabilities = capabilities;
+ usbtmc_state.itf_id = 0xFFu;
+ criticalLeave();
+}
+
+static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len)
+{
+ (void)rhport;
+ // return true upon failure, as we can assume error is being handled elsewhere.
+ TU_VERIFY(atomicChangeState(STATE_IDLE, STATE_RCV), true);
+ usbtmc_state.transfer_size_sent = 0u;
+
+ // must be a header, should have been confirmed before calling here.
+ usbtmc_msg_request_dev_dep_out *msg = (usbtmc_msg_request_dev_dep_out*)data;
+ usbtmc_state.transfer_size_remaining = msg->TransferSize;
+ TU_VERIFY(tud_usbtmc_msgBulkOut_start_cb(msg));
+
+ TU_VERIFY(handle_devMsgOut(rhport, (uint8_t*)data + sizeof(*msg), len - sizeof(*msg), len));
+ usbtmc_state.lastBulkOutTag = msg->header.bTag;
+ return true;
+}
+
+static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen)
+{
+ (void)rhport;
+ // return true upon failure, as we can assume error is being handled elsewhere.
+ TU_VERIFY(usbtmc_state.state == STATE_RCV,true);
+
+ bool shortPacket = (packetLen < USBTMCD_MAX_PACKET_SIZE);
+
+ // Packet is to be considered complete when we get enough data or at a short packet.
+ bool atEnd = false;
+ if(len >= usbtmc_state.transfer_size_remaining || shortPacket)
+ {
+ atEnd = true;
+ TU_VERIFY(atomicChangeState(STATE_RCV, STATE_NAK));
+ }
+
+ len = tu_min32(len, usbtmc_state.transfer_size_remaining);
+
+ usbtmc_state.transfer_size_remaining -= len;
+ usbtmc_state.transfer_size_sent += len;
+
+ // App may (should?) call the wait_for_bus() command at this point
+ if(!tud_usbtmc_msg_data_cb(data, len, atEnd))
+ {
+ // TODO: Go to an error state upon failure other than just stalling the EP?
+ return false;
+ }
+
+
+ return true;
+}
+
+static bool handle_devMsgIn(void *data, size_t len)
+{
+ TU_VERIFY(len == sizeof(usbtmc_msg_request_dev_dep_in));
+ usbtmc_msg_request_dev_dep_in *msg = (usbtmc_msg_request_dev_dep_in*)data;
+ bool stateChanged = atomicChangeState(STATE_IDLE, STATE_TX_REQUESTED);
+ TU_VERIFY(stateChanged);
+ usbtmc_state.lastBulkInTag = msg->header.bTag;
+ usbtmc_state.transfer_size_remaining = msg->TransferSize;
+ usbtmc_state.transfer_size_sent = 0u;
+
+ termCharRequested = msg->bmTransferAttributes.TermCharEnabled;
+ termChar = msg->TermChar;
+
+ if(termCharRequested)
+ TU_VERIFY(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
+
+ TU_VERIFY(tud_usbtmc_msgBulkIn_request_cb(msg));
+ return true;
+}
+
+bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ //uart_tx_str_sync("TMC XFER CB\r\n");
+ if(usbtmc_state.state == STATE_CLEARING) {
+ return true; /* I think we can ignore everything here */
+ }
+
+ if(ep_addr == usbtmc_state.ep_bulk_out)
+ {
+ usbtmc_msg_generic_t *msg = NULL;
+
+ switch(usbtmc_state.state)
+ {
+ case STATE_IDLE:
+ TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
+ msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf);
+ uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
+ TU_VERIFY(msg->header.bTag == invInvTag);
+ TU_VERIFY(msg->header.bTag != 0x00);
+
+ switch(msg->header.MsgID) {
+ case USBTMC_MSGID_DEV_DEP_MSG_OUT:
+ if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
+ {
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ TU_VERIFY(false);
+ }
+ break;
+
+ case USBTMC_MSGID_DEV_DEP_MSG_IN:
+ TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
+ break;
+
+#if (CFG_TUD_USBTMC_ENABLE_488)
+ case USBTMC_MSGID_USB488_TRIGGER:
+ // Spec says we halt the EP if we didn't declare we support it.
+ TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger);
+ TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
+
+ break;
+#endif
+ case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
+ case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
+ default:
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ TU_VERIFY(false);
+ return false;
+ }
+ return true;
+
+ case STATE_RCV:
+ if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes))
+ {
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ TU_VERIFY(false);
+ }
+ return true;
+
+ case STATE_ABORTING_BULK_OUT:
+ TU_VERIFY(false);
+ return false; // Should be stalled by now, shouldn't have received a packet.
+ case STATE_TX_REQUESTED:
+ case STATE_TX_INITIATED:
+ case STATE_ABORTING_BULK_IN:
+ case STATE_ABORTING_BULK_IN_SHORTED:
+ case STATE_ABORTING_BULK_IN_ABORTED:
+ default:
+
+ TU_VERIFY(false);
+ }
+ }
+ else if(ep_addr == usbtmc_state.ep_bulk_in)
+ {
+ switch(usbtmc_state.state) {
+ case STATE_TX_SHORTED:
+ TU_VERIFY(atomicChangeState(STATE_TX_SHORTED, STATE_NAK));
+ TU_VERIFY(tud_usbtmc_msgBulkIn_complete_cb());
+ break;
+
+ case STATE_TX_INITIATED:
+ if(usbtmc_state.transfer_size_remaining >=sizeof(usbtmc_state.ep_bulk_in_buf))
+ {
+ // FIXME! This removes const below!
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
+ (void*)usbtmc_state.devInBuffer,sizeof(usbtmc_state.ep_bulk_in_buf)));
+ usbtmc_state.devInBuffer += sizeof(usbtmc_state.ep_bulk_in_buf);
+ usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.ep_bulk_in_buf);
+ usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.ep_bulk_in_buf);
+ }
+ else // last packet
+ {
+ size_t packetLen = usbtmc_state.transfer_size_remaining;
+ memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
+ usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.transfer_size_remaining);
+ usbtmc_state.transfer_size_remaining = 0;
+ usbtmc_state.devInBuffer = NULL;
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)packetLen));
+ if(((packetLen % USBTMCD_MAX_PACKET_SIZE) != 0) || (packetLen == 0 ))
+ {
+ usbtmc_state.state = STATE_TX_SHORTED;
+ }
+ }
+ return true;
+ case STATE_ABORTING_BULK_IN:
+ // need to send short packet (ZLP?)
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
+ usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
+ return true;
+ case STATE_ABORTING_BULK_IN_SHORTED:
+ /* Done. :)*/
+ usbtmc_state.state = STATE_ABORTING_BULK_IN_ABORTED;
+ return true;
+ default:
+ TU_ASSERT(false);
+ return false;
+ }
+ }
+ else if (ep_addr == usbtmc_state.ep_int_in) {
+ // Good?
+ return true;
+ }
+ return false;
+}
+
+// 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 usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // nothing to do with DATA and ACK stage
+ if ( stage != CONTROL_STAGE_SETUP ) return true;
+
+ uint8_t tmcStatusCode = USBTMC_STATUS_FAILED;
+#if (CFG_TUD_USBTMC_ENABLE_488)
+ uint8_t bTag;
+#endif
+
+ if((request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) &&
+ (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT) &&
+ (request->bRequest == TUSB_REQ_CLEAR_FEATURE) &&
+ (request->wValue == TUSB_REQ_FEATURE_EDPT_HALT))
+ {
+ uint32_t ep_addr = (request->wIndex);
+
+ if(ep_addr == usbtmc_state.ep_bulk_out)
+ {
+ criticalEnter();
+ usbtmc_state.state = STATE_NAK; // USBD core has placed EP in NAK state for us
+ criticalLeave();
+ tud_usbtmc_bulkOut_clearFeature_cb();
+ }
+ else if (ep_addr == usbtmc_state.ep_bulk_in)
+ {
+ tud_usbtmc_bulkIn_clearFeature_cb();
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+
+ // Otherwise, we only handle class requests.
+ if(request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS)
+ {
+ return false;
+ }
+
+ // Verification that we own the interface is unneeded since it's been routed to us specifically.
+
+ switch(request->bRequest)
+ {
+ // USBTMC required requests
+ case USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT:
+ {
+ usbtmc_initiate_abort_rsp_t rsp = {
+ .bTag = usbtmc_state.lastBulkOutTag,
+ };
+ TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(rsp));
+ TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
+
+ // wValue is the requested bTag to abort
+ if(usbtmc_state.state != STATE_RCV)
+ {
+ rsp.USBTMC_status = USBTMC_STATUS_FAILED;
+ }
+ else if(usbtmc_state.lastBulkOutTag == (request->wValue & 0x7Fu))
+ {
+ rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
+ }
+ else
+ {
+ rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
+ // Check if we've queued a short packet
+ criticalEnter();
+ usbtmc_state.state = STATE_ABORTING_BULK_OUT;
+ criticalLeave();
+ TU_VERIFY(tud_usbtmc_initiate_abort_bulk_out_cb(&(rsp.USBTMC_status)));
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ }
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
+ return true;
+ }
+
+ case USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS:
+ {
+ usbtmc_check_abort_bulk_rsp_t rsp = {
+ .USBTMC_status = USBTMC_STATUS_SUCCESS,
+ .NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent
+ };
+ TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
+ TU_VERIFY(request->wLength == sizeof(rsp));
+ TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
+ TU_VERIFY(tud_usbtmc_check_abort_bulk_out_cb(&rsp));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
+ return true;
+ }
+
+ case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN:
+ {
+ usbtmc_initiate_abort_rsp_t rsp = {
+ .bTag = usbtmc_state.lastBulkInTag,
+ };
+ TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(rsp));
+ TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_in);
+ // wValue is the requested bTag to abort
+ if((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED) &&
+ usbtmc_state.lastBulkInTag == (request->wValue & 0x7Fu))
+ {
+ rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
+ usbtmc_state.transfer_size_remaining = 0u;
+ // Check if we've queued a short packet
+ criticalEnter();
+ usbtmc_state.state = ((usbtmc_state.transfer_size_sent % USBTMCD_MAX_PACKET_SIZE) == 0) ?
+ STATE_ABORTING_BULK_IN : STATE_ABORTING_BULK_IN_SHORTED;
+ criticalLeave();
+ if(usbtmc_state.transfer_size_sent == 0)
+ {
+ // Send short packet, nothing is in the buffer yet
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
+ usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
+ }
+ TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
+ }
+ else if((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED))
+ { // FIXME: Unsure how to check if the OUT endpoint fifo is non-empty....
+ rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
+ }
+ else
+ {
+ rsp.USBTMC_status = USBTMC_STATUS_FAILED;
+ }
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
+ return true;
+ }
+
+ case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS:
+ {
+ TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
+ TU_VERIFY(request->wLength == 8u);
+
+ usbtmc_check_abort_bulk_rsp_t rsp =
+ {
+ .USBTMC_status = USBTMC_STATUS_FAILED,
+ .bmAbortBulkIn =
+ {
+ .BulkInFifoBytes = (usbtmc_state.state != STATE_ABORTING_BULK_IN_ABORTED)
+ },
+ .NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent,
+ };
+ TU_VERIFY(tud_usbtmc_check_abort_bulk_in_cb(&rsp));
+ criticalEnter();
+ switch(usbtmc_state.state)
+ {
+ case STATE_ABORTING_BULK_IN_ABORTED:
+ rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
+ usbtmc_state.state = STATE_IDLE;
+ break;
+ case STATE_ABORTING_BULK_IN:
+ case STATE_ABORTING_BULK_OUT:
+ rsp.USBTMC_status = USBTMC_STATUS_PENDING;
+ break;
+ default:
+ break;
+ }
+ criticalLeave();
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
+
+ return true;
+ }
+
+ case USBTMC_bREQUEST_INITIATE_CLEAR:
+ {
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
+ // After receiving an INITIATE_CLEAR request, the device must Halt the Bulk-OUT endpoint, queue the
+ // control endpoint response shown in Table 31, and clear all input buffers and output buffers.
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ usbtmc_state.transfer_size_remaining = 0;
+ criticalEnter();
+ usbtmc_state.state = STATE_CLEARING;
+ criticalLeave();
+ TU_VERIFY(tud_usbtmc_initiate_clear_cb(&tmcStatusCode));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode,sizeof(tmcStatusCode)));
+ return true;
+ }
+
+ case USBTMC_bREQUEST_CHECK_CLEAR_STATUS:
+ {
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
+ TU_VERIFY(request->wLength == sizeof(clearStatusRsp));
+
+ if(usbd_edpt_busy(rhport, usbtmc_state.ep_bulk_in))
+ {
+ // Stuff stuck in TX buffer?
+ clearStatusRsp.bmClear.BulkInFifoBytes = 1;
+ clearStatusRsp.USBTMC_status = USBTMC_STATUS_PENDING;
+ }
+ else
+ {
+ // Let app check if it's clear
+ TU_VERIFY(tud_usbtmc_check_clear_cb(&clearStatusRsp));
+ }
+ if(clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS)
+ {
+ criticalEnter();
+ usbtmc_state.state = STATE_IDLE;
+ criticalLeave();
+ }
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&clearStatusRsp,sizeof(clearStatusRsp)));
+ return true;
+ }
+
+ case USBTMC_bREQUEST_GET_CAPABILITIES:
+ {
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(*(usbtmc_state.capabilities)));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)usbtmc_state.capabilities, sizeof(*usbtmc_state.capabilities)));
+ return true;
+ }
+ // USBTMC Optional Requests
+
+ case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional
+ {
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
+ TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse);
+ TU_VERIFY(tud_usbtmc_indicator_pulse_cb(request, &tmcStatusCode));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode, sizeof(tmcStatusCode)));
+ return true;
+ }
+#if (CFG_TUD_USBTMC_ENABLE_488)
+
+ // USB488 required requests
+ case USB488_bREQUEST_READ_STATUS_BYTE:
+ {
+ usbtmc_read_stb_rsp_488_t rsp;
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(rsp)); // in,class,interface
+
+ bTag = request->wValue & 0x7F;
+ TU_VERIFY(request->bmRequestType == 0xA1);
+ TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero
+ TU_VERIFY(bTag >= 0x02 && bTag <= 127);
+ TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
+ TU_VERIFY(request->wLength == 0x0003);
+ rsp.bTag = (uint8_t)bTag;
+ if(usbtmc_state.ep_int_in != 0)
+ {
+ rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
+ rsp.statusByte = 0x00; // Use interrupt endpoint, instead.
+
+ usbtmc_read_stb_interrupt_488_t intMsg =
+ {
+ .bNotify1 = {
+ .one = 1,
+ .bTag = bTag & 0x7Fu,
+ },
+ .StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))
+ };
+ usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg));
+ }
+ else
+ {
+ rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
+ }
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
+ return true;
+ }
+ // USB488 optional requests
+ case USB488_bREQUEST_REN_CONTROL:
+ case USB488_bREQUEST_GO_TO_LOCAL:
+ case USB488_bREQUEST_LOCAL_LOCKOUT:
+ {
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(false);
+ return false;
+ }
+#endif
+
+ default:
+ TU_VERIFY(false);
+ return false;
+ }
+ TU_VERIFY(false);
+}
+
+#endif /* CFG_TUD_TSMC */
diff --git a/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.h b/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.h
new file mode 100755
index 0000000..6228003
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/usbtmc/usbtmc_device.h
@@ -0,0 +1,122 @@
+/*
+ * usbtmc_device.h
+ *
+ * Created on: Sep 10, 2019
+ * Author: nconrad
+ */
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 N Conrad
+ *
+ * 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 CLASS_USBTMC_USBTMC_DEVICE_H_
+#define CLASS_USBTMC_USBTMC_DEVICE_H_
+
+#include "usbtmc.h"
+
+// Enable 488 mode by default
+#if !defined(CFG_TUD_USBTMC_ENABLE_488)
+#define CFG_TUD_USBTMC_ENABLE_488 (1)
+#endif
+
+// USB spec says that full-speed must be 8,16,32, or 64.
+// However, this driver implementation requires it to be >=32
+#define USBTMCD_MAX_PACKET_SIZE (64u)
+
+/***********************************************
+ * Functions to be implemeted by the class implementation
+ */
+
+// In order to proceed, app must call call tud_usbtmc_start_bus_read(rhport) during or soon after:
+// * tud_usbtmc_open_cb
+// * tud_usbtmc_msg_data_cb
+// * tud_usbtmc_msgBulkIn_complete_cb
+// * tud_usbtmc_msg_trigger_cb
+// * (successful) tud_usbtmc_check_abort_bulk_out_cb
+// * (successful) tud_usbtmc_check_abort_bulk_in_cb
+// * (successful) tud_usmtmc_bulkOut_clearFeature_cb
+
+#if (CFG_TUD_USBTMC_ENABLE_488)
+usbtmc_response_capabilities_488_t const * tud_usbtmc_get_capabilities_cb(void);
+#else
+usbtmc_response_capabilities_t const * tud_usbtmc_get_capabilities_cb(void);
+#endif
+
+void tud_usbtmc_open_cb(uint8_t interface_id);
+
+bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader);
+// transfer_complete does not imply that a message is complete.
+bool tud_usbtmc_msg_data_cb( void *data, size_t len, bool transfer_complete);
+void tud_usbtmc_bulkOut_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
+
+bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request);
+bool tud_usbtmc_msgBulkIn_complete_cb(void);
+void tud_usbtmc_bulkIn_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
+
+bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult);
+bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult);
+bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult);
+
+bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
+bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
+bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp);
+
+// Indicator pulse should be 0.5 to 1.0 seconds long
+TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
+
+#if (CFG_TUD_USBTMC_ENABLE_488)
+uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult);
+TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
+//TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb();
+#endif
+
+/*******************************************
+ * Called from app
+ *
+ * We keep a reference to the buffer, so it MUST not change until the app is
+ * notified that the transfer is complete.
+ ******************************************/
+
+bool tud_usbtmc_transmit_dev_msg_data(
+ const void * data, size_t len,
+ bool endOfMessage, bool usingTermChar);
+
+bool tud_usbtmc_start_bus_read(void);
+
+
+/* "callbacks" from USB device core */
+
+uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+void usbtmcd_reset_cb(uint8_t rhport);
+bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+void usbtmcd_init_cb(void);
+
+/************************************************************
+ * USBTMC Descriptor Templates
+ *************************************************************/
+
+
+#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/vendor/vendor_device.c b/sw/Core/Src/tinyusb/src/class/vendor/vendor_device.c
new file mode 100755
index 0000000..3fcea89
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/vendor/vendor_device.c
@@ -0,0 +1,237 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_VENDOR)
+
+#include "vendor_device.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+ tu_fifo_t rx_ff;
+ tu_fifo_t tx_ff;
+
+ uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
+
+#if CFG_FIFO_MUTEX
+ osal_mutex_def_t rx_ff_mutex;
+ osal_mutex_def_t tx_ff_mutex;
+#endif
+
+ // Endpoint Transfer buffer
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_VENDOR_EPSIZE];
+} vendord_interface_t;
+
+CFG_TUSB_MEM_SECTION static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
+
+#define ITF_MEM_RESET_SIZE offsetof(vendord_interface_t, rx_ff)
+
+
+bool tud_vendor_n_mounted (uint8_t itf)
+{
+ return _vendord_itf[itf].ep_in && _vendord_itf[itf].ep_out;
+}
+
+uint32_t tud_vendor_n_available (uint8_t itf)
+{
+ return tu_fifo_count(&_vendord_itf[itf].rx_ff);
+}
+
+bool tud_vendor_n_peek(uint8_t itf, int pos, uint8_t* u8)
+{
+ return tu_fifo_peek_at(&_vendord_itf[itf].rx_ff, pos, u8);
+}
+
+//--------------------------------------------------------------------+
+// Read API
+//--------------------------------------------------------------------+
+static void _prep_out_transaction (vendord_interface_t* p_itf)
+{
+ // skip if previous transfer not complete
+ if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_itf->ep_out) ) return;
+
+ // Prepare for incoming data but only allow what we can store in the ring buffer.
+ uint16_t max_read = tu_fifo_remaining(&p_itf->rx_ff);
+ if ( max_read >= CFG_TUD_VENDOR_EPSIZE )
+ {
+ usbd_edpt_xfer(TUD_OPT_RHPORT, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE);
+ }
+}
+
+uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize)
+{
+ vendord_interface_t* p_itf = &_vendord_itf[itf];
+ uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, bufsize);
+ _prep_out_transaction(p_itf);
+ return num_read;
+}
+
+//--------------------------------------------------------------------+
+// Write API
+//--------------------------------------------------------------------+
+static bool maybe_transmit(vendord_interface_t* p_itf)
+{
+ // skip if previous transfer not complete
+ TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_itf->ep_in) );
+
+ uint16_t count = tu_fifo_read_n(&p_itf->tx_ff, p_itf->epin_buf, CFG_TUD_VENDOR_EPSIZE);
+ if (count > 0)
+ {
+ TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_itf->ep_in, p_itf->epin_buf, count) );
+ }
+ return true;
+}
+
+uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize)
+{
+ vendord_interface_t* p_itf = &_vendord_itf[itf];
+ uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, bufsize);
+ maybe_transmit(p_itf);
+ return ret;
+}
+
+uint32_t tud_vendor_n_write_available (uint8_t itf)
+{
+ return tu_fifo_remaining(&_vendord_itf[itf].tx_ff);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void vendord_init(void)
+{
+ tu_memclr(_vendord_itf, sizeof(_vendord_itf));
+
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
+ {
+ vendord_interface_t* p_itf = &_vendord_itf[i];
+
+ // config fifo
+ tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
+ tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
+
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&p_itf->rx_ff, osal_mutex_create(&p_itf->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex));
+#endif
+ }
+}
+
+void vendord_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
+ {
+ vendord_interface_t* p_itf = &_vendord_itf[i];
+
+ tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
+ tu_fifo_clear(&p_itf->rx_ff);
+ tu_fifo_clear(&p_itf->tx_ff);
+ }
+}
+
+uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass, 0);
+
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ TU_VERIFY(max_len >= drv_len, 0);
+
+ // Find available interface
+ vendord_interface_t* p_vendor = NULL;
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
+ {
+ if ( _vendord_itf[i].ep_in == 0 && _vendord_itf[i].ep_out == 0 )
+ {
+ p_vendor = &_vendord_itf[i];
+ break;
+ }
+ }
+ TU_VERIFY(p_vendor, 0);
+
+ // Open endpoint pair with usbd helper
+ TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
+
+ p_vendor->itf_num = itf_desc->bInterfaceNumber;
+
+ // Prepare for incoming data
+ if ( !usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)) )
+ {
+ TU_LOG1_FAILED();
+ TU_BREAKPOINT();
+ }
+
+ return drv_len;
+}
+
+bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) rhport;
+ (void) result;
+
+ uint8_t itf = 0;
+ vendord_interface_t* p_itf = _vendord_itf;
+
+ for ( ; ; itf++, p_itf++)
+ {
+ if (itf >= TU_ARRAY_SIZE(_vendord_itf)) return false;
+
+ if ( ( ep_addr == p_itf->ep_out ) || ( ep_addr == p_itf->ep_in ) ) break;
+ }
+
+ if ( ep_addr == p_itf->ep_out )
+ {
+ // Receive new data
+ tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, xferred_bytes);
+
+ // Invoked callback if any
+ if (tud_vendor_rx_cb) tud_vendor_rx_cb(itf);
+
+ _prep_out_transaction(p_itf);
+ }
+ else if ( ep_addr == p_itf->ep_in )
+ {
+ // Send complete, try to send more if possible
+ maybe_transmit(p_itf);
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/vendor/vendor_device.h b/sw/Core/Src/tinyusb/src/class/vendor/vendor_device.h
new file mode 100755
index 0000000..a4235bf
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/vendor/vendor_device.h
@@ -0,0 +1,130 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_VENDOR_DEVICE_H_
+#define _TUSB_VENDOR_DEVICE_H_
+
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+
+#ifndef CFG_TUD_VENDOR_EPSIZE
+#define CFG_TUD_VENDOR_EPSIZE 64
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+//--------------------------------------------------------------------+
+bool tud_vendor_n_mounted (uint8_t itf);
+
+uint32_t tud_vendor_n_available (uint8_t itf);
+uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
+bool tud_vendor_n_peek (uint8_t itf, int pos, uint8_t* u8);
+
+uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
+uint32_t tud_vendor_n_write_available (uint8_t itf);
+
+static inline
+uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+static inline bool tud_vendor_mounted (void);
+static inline uint32_t tud_vendor_available (void);
+static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
+static inline bool tud_vendor_peek (int pos, uint8_t* u8);
+static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
+static inline uint32_t tud_vendor_write_str (char const* str);
+static inline uint32_t tud_vendor_write_available (void);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str)
+{
+ return tud_vendor_n_write(itf, str, strlen(str));
+}
+
+static inline bool tud_vendor_mounted (void)
+{
+ return tud_vendor_n_mounted(0);
+}
+
+static inline uint32_t tud_vendor_available (void)
+{
+ return tud_vendor_n_available(0);
+}
+
+static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
+{
+ return tud_vendor_n_read(0, buffer, bufsize);
+}
+
+static inline bool tud_vendor_peek (int pos, uint8_t* u8)
+{
+ return tud_vendor_n_peek(0, pos, u8);
+}
+
+static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
+{
+ return tud_vendor_n_write(0, buffer, bufsize);
+}
+
+static inline uint32_t tud_vendor_write_str (char const* str)
+{
+ return tud_vendor_n_write_str(0, str);
+}
+
+static inline uint32_t tud_vendor_write_available (void)
+{
+ return tud_vendor_n_write_available(0);
+}
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void vendord_init(void);
+void vendord_reset(uint8_t rhport);
+uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_VENDOR_DEVICE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/class/vendor/vendor_host.c b/sw/Core/Src/tinyusb/src/class/vendor/vendor_host.c
new file mode 100755
index 0000000..0524cb9
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/vendor/vendor_host.c
@@ -0,0 +1,146 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_VENDOR)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "common/tusb_common.h"
+#include "vendor_host.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+custom_interface_info_t custom_interface[CFG_TUSB_HOST_DEVICE_MAX];
+
+static tusb_error_t cush_validate_paras(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
+{
+ if ( !tusbh_custom_is_mounted(dev_addr, vendor_id, product_id) )
+ {
+ return TUSB_ERROR_DEVICE_NOT_READY;
+ }
+
+ TU_ASSERT( p_buffer != NULL && length != 0, TUSB_ERROR_INVALID_PARA);
+
+ return TUSB_ERROR_NONE;
+}
+//--------------------------------------------------------------------+
+// APPLICATION API (need to check parameters)
+//--------------------------------------------------------------------+
+tusb_error_t tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
+{
+ TU_ASSERT_ERR( cush_validate_paras(dev_addr, vendor_id, product_id, p_buffer, length) );
+
+ if ( !hcd_pipe_is_idle(custom_interface[dev_addr-1].pipe_in) )
+ {
+ return TUSB_ERROR_INTERFACE_IS_BUSY;
+ }
+
+ (void) hcd_pipe_xfer( custom_interface[dev_addr-1].pipe_in, p_buffer, length, true);
+
+ return TUSB_ERROR_NONE;
+}
+
+tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length)
+{
+ TU_ASSERT_ERR( cush_validate_paras(dev_addr, vendor_id, product_id, p_data, length) );
+
+ if ( !hcd_pipe_is_idle(custom_interface[dev_addr-1].pipe_out) )
+ {
+ return TUSB_ERROR_INTERFACE_IS_BUSY;
+ }
+
+ (void) hcd_pipe_xfer( custom_interface[dev_addr-1].pipe_out, p_data, length, true);
+
+ return TUSB_ERROR_NONE;
+}
+
+//--------------------------------------------------------------------+
+// USBH-CLASS API
+//--------------------------------------------------------------------+
+void cush_init(void)
+{
+ tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUSB_HOST_DEVICE_MAX);
+}
+
+tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
+{
+ // FIXME quick hack to test lpc1k custom class with 2 bulk endpoints
+ uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
+ p_desc = tu_desc_next(p_desc);
+
+ //------------- Bulk Endpoints Descriptor -------------//
+ for(uint32_t i=0; i<2; i++)
+ {
+ tusb_desc_endpoint_t const *p_endpoint = (tusb_desc_endpoint_t const *) p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType, TUSB_ERROR_INVALID_PARA);
+
+ pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
+ &custom_interface[dev_addr-1].pipe_in : &custom_interface[dev_addr-1].pipe_out;
+ *p_pipe_hdl = usbh_edpt_open(dev_addr, p_endpoint, TUSB_CLASS_VENDOR_SPECIFIC);
+ TU_ASSERT ( pipehandle_is_valid(*p_pipe_hdl), TUSB_ERROR_HCD_OPEN_PIPE_FAILED );
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ (*p_length) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+ return TUSB_ERROR_NONE;
+}
+
+void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event)
+{
+
+}
+
+void cush_close(uint8_t dev_addr)
+{
+ tusb_error_t err1, err2;
+ custom_interface_info_t * p_interface = &custom_interface[dev_addr-1];
+
+ // TODO re-consider to check pipe valid before calling pipe_close
+ if( pipehandle_is_valid( p_interface->pipe_in ) )
+ {
+ err1 = hcd_pipe_close( p_interface->pipe_in );
+ }
+
+ if ( pipehandle_is_valid( p_interface->pipe_out ) )
+ {
+ err2 = hcd_pipe_close( p_interface->pipe_out );
+ }
+
+ tu_memclr(p_interface, sizeof(custom_interface_info_t));
+
+ TU_ASSERT(err1 == TUSB_ERROR_NONE && err2 == TUSB_ERROR_NONE, (void) 0 );
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/class/vendor/vendor_host.h b/sw/Core/Src/tinyusb/src/class/vendor/vendor_host.h
new file mode 100755
index 0000000..fa18793
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/class/vendor/vendor_host.h
@@ -0,0 +1,74 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup Group_Custom Custom Class (not supported yet)
+ * @{ */
+
+#ifndef _TUSB_VENDOR_HOST_H_
+#define _TUSB_VENDOR_HOST_H_
+
+#include "common/tusb_common.h"
+#include "host/usbh.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct {
+ pipe_handle_t pipe_in;
+ pipe_handle_t pipe_out;
+}custom_interface_info_t;
+
+//--------------------------------------------------------------------+
+// USBH-CLASS DRIVER API
+//--------------------------------------------------------------------+
+static inline bool tusbh_custom_is_mounted(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id)
+{
+ (void) vendor_id; // TODO check this later
+ (void) product_id;
+// return (tusbh_device_get_mounted_class_flag(dev_addr) & TU_BIT(TUSB_CLASS_MAPPED_INDEX_END-1) ) != 0;
+ return false;
+}
+
+tusb_error_t tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length);
+tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void cush_init(void);
+tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
+void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event);
+void cush_close(uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_VENDOR_HOST_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_common.h b/sw/Core/Src/tinyusb/src/common/tusb_common.h
new file mode 100755
index 0000000..f10be88
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_common.h
@@ -0,0 +1,300 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_Common
+ * \defgroup Group_CommonH common.h
+ * @{ */
+
+#ifndef _TUSB_COMMON_H_
+#define _TUSB_COMMON_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Macros Helper
+//--------------------------------------------------------------------+
+#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
+#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
+#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) )
+
+#define TU_U16_HIGH(u16) ((uint8_t) (((u16) >> 8) & 0x00ff))
+#define TU_U16_LOW(u16) ((uint8_t) ((u16) & 0x00ff))
+#define U16_TO_U8S_BE(u16) TU_U16_HIGH(u16), TU_U16_LOW(u16)
+#define U16_TO_U8S_LE(u16) TU_U16_LOW(u16), TU_U16_HIGH(u16)
+
+#define U32_B1_U8(u32) ((uint8_t) ((((uint32_t) u32) >> 24) & 0x000000ff)) // MSB
+#define U32_B2_U8(u32) ((uint8_t) ((((uint32_t) u32) >> 16) & 0x000000ff))
+#define U32_B3_U8(u32) ((uint8_t) ((((uint32_t) u32) >> 8) & 0x000000ff))
+#define U32_B4_U8(u32) ((uint8_t) (((uint32_t) u32) & 0x000000ff)) // LSB
+
+#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32)
+#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32)
+
+#define TU_BIT(n) (1U << (n))
+
+//--------------------------------------------------------------------+
+// Includes
+//--------------------------------------------------------------------+
+
+// Standard Headers
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+
+// Tinyusb Common Headers
+#include "tusb_option.h"
+#include "tusb_compiler.h"
+#include "tusb_verify.h"
+#include "tusb_error.h" // TODO remove
+#include "tusb_timeout.h"
+#include "tusb_types.h"
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+#define tu_memclr(buffer, size) memset((buffer), 0, (size))
+#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var)))
+
+static inline uint32_t tu_u32(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4)
+{
+ return ( ((uint32_t) b1) << 24) + ( ((uint32_t) b2) << 16) + ( ((uint32_t) b3) << 8) + b4;
+}
+
+static inline uint16_t tu_u16(uint8_t high, uint8_t low)
+{
+ return (uint16_t)((((uint16_t) high) << 8) + low);
+}
+
+static inline uint8_t tu_u16_high(uint16_t u16) { return (uint8_t) (((uint16_t) (u16 >> 8)) & 0x00ff); }
+static inline uint8_t tu_u16_low (uint16_t u16) { return (uint8_t) (u16 & 0x00ff); }
+
+// Min
+static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; }
+static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; }
+static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; }
+
+// Max
+static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; }
+static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; }
+static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
+
+// Align
+static inline uint32_t tu_align(uint32_t value, uint32_t alignment)
+{
+ return value & ((uint32_t) ~(alignment-1));
+}
+
+static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
+static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
+static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
+static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
+
+//------------- Mathematics -------------//
+static inline uint32_t tu_abs(int32_t value) { return (uint32_t)((value < 0) ? (-value) : value); }
+
+/// inclusive range checking
+static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
+{
+ return (lower <= value) && (value <= upper);
+}
+
+// log2 of a value is its MSB's position
+// TODO use clz TODO remove
+static inline uint8_t tu_log2(uint32_t value)
+{
+ uint8_t result = 0;
+
+ while (value >>= 1)
+ {
+ result++;
+ }
+ return result;
+}
+
+// Bit
+static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); }
+static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); }
+static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; }
+
+/*------------------------------------------------------------------*/
+/* Count number of arguments of __VA_ARGS__
+ * - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s
+ * - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th)
+ * - _RSEQ_N() is reverse sequential to N to add padding to have
+ * Nth position is the same as the number of arguments
+ * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma)
+ *------------------------------------------------------------------*/
+#ifndef TU_ARGS_NUM
+
+#define TU_ARGS_NUM(...) NARG_(_0, ##__VA_ARGS__,_RSEQ_N())
+
+#define NARG_(...) _GET_NTH_ARG(__VA_ARGS__)
+#define _GET_NTH_ARG( \
+ _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
+ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
+ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
+ _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
+ _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
+ _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
+ _61,_62,_63,N,...) N
+#define _RSEQ_N() \
+ 62,61,60, \
+ 59,58,57,56,55,54,53,52,51,50, \
+ 49,48,47,46,45,44,43,42,41,40, \
+ 39,38,37,36,35,34,33,32,31,30, \
+ 29,28,27,26,25,24,23,22,21,20, \
+ 19,18,17,16,15,14,13,12,11,10, \
+ 9,8,7,6,5,4,3,2,1,0
+#endif
+
+// To be removed
+//------------- Binary constant -------------//
+#if defined(__GNUC__) && !defined(__CC_ARM)
+
+#define TU_BIN8(x) ((uint8_t) (0b##x))
+#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2))
+#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4))
+
+#else
+
+// internal macro of B8, B16, B32
+#define _B8__(x) (((x&0x0000000FUL)?1:0) \
+ +((x&0x000000F0UL)?2:0) \
+ +((x&0x00000F00UL)?4:0) \
+ +((x&0x0000F000UL)?8:0) \
+ +((x&0x000F0000UL)?16:0) \
+ +((x&0x00F00000UL)?32:0) \
+ +((x&0x0F000000UL)?64:0) \
+ +((x&0xF0000000UL)?128:0))
+
+#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL))
+#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb))
+#define TU_BIN32(dmsb,db2,db3,dlsb) \
+ (((uint32_t)TU_BIN8(dmsb)<<24) \
+ + ((uint32_t)TU_BIN8(db2)<<16) \
+ + ((uint32_t)TU_BIN8(db3)<<8) \
+ + TU_BIN8(dlsb))
+#endif
+
+//--------------------------------------------------------------------+
+// Debug Function
+//--------------------------------------------------------------------+
+
+// CFG_TUSB_DEBUG for debugging
+// 0 : no debug
+// 1 : print when there is error
+// 2 : print out log
+#if CFG_TUSB_DEBUG
+
+void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
+
+#ifdef CFG_TUSB_DEBUG_PRINTF
+ extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...);
+ #define tu_printf CFG_TUSB_DEBUG_PRINTF
+#else
+ #define tu_printf printf
+#endif
+
+static inline
+void tu_print_var(uint8_t const* buf, uint32_t bufsize)
+{
+ for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
+}
+
+// Log with debug level 1
+#define TU_LOG1 tu_printf
+#define TU_LOG1_MEM tu_print_mem
+#define TU_LOG1_VAR(_x) tu_print_var((uint8_t const*)(_x), sizeof(*(_x)))
+#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\n", (uint32_t) (_x) )
+#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\n", (uint32_t) (_x) )
+#define TU_LOG1_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
+#define TU_LOG1_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__)
+
+// Log with debug level 2
+#if CFG_TUSB_DEBUG > 1
+ #define TU_LOG2 TU_LOG1
+ #define TU_LOG2_MEM TU_LOG1_MEM
+ #define TU_LOG2_VAR TU_LOG1_VAR
+ #define TU_LOG2_INT TU_LOG1_INT
+ #define TU_LOG2_HEX TU_LOG1_HEX
+ #define TU_LOG2_LOCATION() TU_LOG1_LOCATION()
+#endif
+
+
+typedef struct
+{
+ uint32_t key;
+ const char* data;
+} tu_lookup_entry_t;
+
+typedef struct
+{
+ uint16_t count;
+ tu_lookup_entry_t const* items;
+} tu_lookup_table_t;
+
+static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key)
+{
+ for(uint16_t i=0; i<p_table->count; i++)
+ {
+ if (p_table->items[i].key == key) return p_table->items[i].data;
+ }
+
+ return NULL;
+}
+
+#endif // CFG_TUSB_DEBUG
+
+#ifndef TU_LOG1
+ #define TU_LOG1(...)
+ #define TU_LOG1_MEM(...)
+ #define TU_LOG1_VAR(...)
+ #define TU_LOG1_INT(...)
+ #define TU_LOG1_HEX(...)
+ #define TU_LOG1_LOCATION()
+ #define TU_LOG1_FAILED()
+#endif
+
+#ifndef TU_LOG2
+ #define TU_LOG2(...)
+ #define TU_LOG2_MEM(...)
+ #define TU_LOG2_VAR(...)
+ #define TU_LOG2_INT(...)
+ #define TU_LOG2_HEX(...)
+ #define TU_LOG2_LOCATION()
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_COMMON_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_compiler.h b/sw/Core/Src/tinyusb/src/common/tusb_compiler.h
new file mode 100755
index 0000000..22c8366
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_compiler.h
@@ -0,0 +1,162 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_Common
+ * \defgroup Group_Compiler Compiler
+ * \brief Group_Compiler brief
+ * @{ */
+
+#ifndef _TUSB_COMPILER_H_
+#define _TUSB_COMPILER_H_
+
+#define TU_STRING(x) #x ///< stringify without expand
+#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
+#define TU_STRCAT(a, b) a##b ///< concat without expand
+#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
+
+#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
+ #define _TU_COUNTER_ __COUNTER__
+#else
+ #define _TU_COUNTER_ __LINE__
+#endif
+
+// Compile-time Assert
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+ #define TU_VERIFY_STATIC _Static_assert
+#elif defined (__cplusplus) && __cplusplus >= 201103L
+ #define TU_VERIFY_STATIC static_assert
+#else
+ #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
+#endif
+
+// for declaration of reserved field, make use of _TU_COUNTER_
+#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_)
+
+#define TU_LITTLE_ENDIAN (0x12u)
+#define TU_BIG_ENDIAN (0x21u)
+
+//--------------------------------------------------------------------+
+// Compiler porting with Attribute and Endian
+//--------------------------------------------------------------------+
+#if defined(__GNUC__)
+ #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
+ #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
+ #define TU_ATTR_PACKED __attribute__ ((packed))
+ #define TU_ATTR_PREPACKED
+ #define TU_ATTR_WEAK __attribute__ ((weak))
+ #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
+ #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
+ #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
+
+ // Endian conversion use well-known host to network (big endian) naming
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #else
+ #define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #endif
+
+ #define TU_BSWAP16(u16) (__builtin_bswap16(u16))
+ #define TU_BSWAP32(u32) (__builtin_bswap32(u32))
+
+#elif defined(__TI_COMPILER_VERSION__)
+ #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
+ #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
+ #define TU_ATTR_PACKED __attribute__ ((packed))
+ #define TU_ATTR_PREPACKED
+ #define TU_ATTR_WEAK __attribute__ ((weak))
+ #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
+ #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
+ #define TU_ATTR_USED __attribute__ ((used))
+
+ // __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian)
+ #if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__)
+ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #else
+ #define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #endif
+
+ #define TU_BSWAP16(u16) (__builtin_bswap16(u16))
+ #define TU_BSWAP32(u32) (__builtin_bswap32(u32))
+
+#elif defined(__ICCARM__)
+ #include <intrinsics.h>
+ #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
+ #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
+ #define TU_ATTR_PACKED __attribute__ ((packed))
+ #define TU_ATTR_PREPACKED
+ #define TU_ATTR_WEAK __attribute__ ((weak))
+ #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
+ #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
+ #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
+
+ // Endian conversion use well-known host to network (big endian) naming
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #else
+ #define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #endif
+
+ #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16))
+ #define TU_BSWAP32(u32) (__iar_builtin_REV(u32))
+#else
+ #error "Compiler attribute porting is required"
+#endif
+
+#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN)
+
+ #define tu_htons(u16) (TU_BSWAP16(u16))
+ #define tu_ntohs(u16) (TU_BSWAP16(u16))
+
+ #define tu_htonl(u32) (TU_BSWAP32(u32))
+ #define tu_ntohl(u32) (TU_BSWAP32(u32))
+
+ #define tu_htole16(u16) (u16)
+ #define tu_le16toh(u16) (u16)
+
+ #define tu_htole32(u32) (u32)
+ #define tu_le32toh(u32) (u32)
+
+#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN)
+
+ #define tu_htons(u16) (u16)
+ #define tu_ntohs(u16) (u16)
+
+ #define tu_htonl(u32) (u32)
+ #define tu_ntohl(u32) (u32)
+
+ #define tu_htole16(u16) (tu_bswap16(u16))
+ #define tu_le16toh(u16) (tu_bswap16(u16))
+
+ #define tu_htole32(u32) (tu_bswap32(u32))
+ #define tu_le32toh(u32) (tu_bswap32(u32))
+
+#else
+ #error Byte order is undefined
+#endif
+
+#endif /* _TUSB_COMPILER_H_ */
+
+/// @}
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_error.h b/sw/Core/Src/tinyusb/src/common/tusb_error.h
new file mode 100755
index 0000000..d7ad8c3
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_error.h
@@ -0,0 +1,75 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_Common
+ * \defgroup Group_Error Error Codes
+ * @{ */
+
+#ifndef _TUSB_ERRORS_H_
+#define _TUSB_ERRORS_H_
+
+#include "tusb_option.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define ERROR_ENUM(x) x,
+#define ERROR_STRING(x) #x,
+
+#define ERROR_TABLE(ENTRY) \
+ ENTRY(TUSB_ERROR_NONE )\
+ ENTRY(TUSB_ERROR_INVALID_PARA )\
+ ENTRY(TUSB_ERROR_DEVICE_NOT_READY )\
+ ENTRY(TUSB_ERROR_INTERFACE_IS_BUSY )\
+ ENTRY(TUSB_ERROR_HCD_OPEN_PIPE_FAILED )\
+ ENTRY(TUSB_ERROR_OSAL_TIMEOUT )\
+ ENTRY(TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED )\
+ ENTRY(TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED )\
+ ENTRY(TUSB_ERROR_NOT_SUPPORTED )\
+ ENTRY(TUSB_ERROR_NOT_ENOUGH_MEMORY )\
+ ENTRY(TUSB_ERROR_FAILED )\
+
+/// \brief Error Code returned
+/// TODO obsolete and to be remove
+typedef enum
+{
+ ERROR_TABLE(ERROR_ENUM)
+ TUSB_ERROR_COUNT
+}tusb_error_t;
+
+#if CFG_TUSB_DEBUG
+/// Enum to String for debugging purposes. Only available if \ref CFG_TUSB_DEBUG > 0
+extern char const* const tusb_strerr[TUSB_ERROR_COUNT];
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_ERRORS_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_fifo.c b/sw/Core/Src/tinyusb/src/common/tusb_fifo.c
new file mode 100755
index 0000000..30629af
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_fifo.c
@@ -0,0 +1,669 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include <string.h>
+
+#include "osal/osal.h"
+#include "tusb_fifo.h"
+
+// Supress IAR warning
+// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
+#if defined(__ICCARM__)
+#pragma diag_suppress = Pa082
+#endif
+
+// implement mutex lock and unlock
+#if CFG_FIFO_MUTEX
+
+static void tu_fifo_lock(tu_fifo_t *f)
+{
+ if (f->mutex)
+ {
+ osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ }
+}
+
+static void tu_fifo_unlock(tu_fifo_t *f)
+{
+ if (f->mutex)
+ {
+ osal_mutex_unlock(f->mutex);
+ }
+}
+
+#else
+
+#define tu_fifo_lock(_ff)
+#define tu_fifo_unlock(_ff)
+
+#endif
+
+bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
+{
+ if (depth > 0x8000) return false; // Maximum depth is 2^15 items
+
+ tu_fifo_lock(f);
+
+ f->buffer = (uint8_t*) buffer;
+ f->depth = depth;
+ f->item_size = item_size;
+ f->overwritable = overwritable;
+
+ f->max_pointer_idx = 2*depth - 1; // Limit index space to 2*depth - this allows for a fast "modulo" calculation but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable only if overflow happens once (important for unsupervised DMA applications)
+ f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
+
+ f->rd_idx = f->wr_idx = 0;
+
+ tu_fifo_unlock(f);
+
+ return true;
+}
+
+// Static functions are intended to work on local variables
+
+static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
+{
+ while ( idx >= depth) idx -= depth;
+ return idx;
+}
+
+// send one item to FIFO WITHOUT updating write pointer
+static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t wRel)
+{
+ memcpy(f->buffer + (wRel * f->item_size), data, f->item_size);
+}
+
+// send n items to FIFO WITHOUT updating write pointer
+static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRel)
+{
+ if(wRel + n <= f->depth) // Linear mode only
+ {
+ memcpy(f->buffer + (wRel * f->item_size), data, n*f->item_size);
+ }
+ else // Wrap around
+ {
+ uint16_t nLin = f->depth - wRel;
+
+ // Write data to linear part of buffer
+ memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
+
+ // Write data wrapped around
+ memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
+ }
+}
+
+// get one item from FIFO WITHOUT updating read pointer
+static inline void _ff_pull(tu_fifo_t* f, void * p_buffer, uint16_t rRel)
+{
+ memcpy(p_buffer, f->buffer + (rRel * f->item_size), f->item_size);
+}
+
+// get n items from FIFO WITHOUT updating read pointer
+static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
+{
+ if(rRel + n <= f->depth) // Linear mode only
+ {
+ memcpy(p_buffer, f->buffer + (rRel * f->item_size), n*f->item_size);
+ }
+ else // Wrap around
+ {
+ uint16_t nLin = f->depth - rRel;
+
+ // Read data from linear part of buffer
+ memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
+
+ // Read data wrapped part
+ memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
+ }
+}
+
+// Advance an absolute pointer
+static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index
+ if ((p > p + offset) || (p + offset > f->max_pointer_idx))
+ {
+ p = (p + offset) + f->non_used_index_space;
+ }
+ else
+ {
+ p += offset;
+ }
+ return p;
+}
+
+// Backward an absolute pointer
+static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index
+ if ((p < p - offset) || (p - offset > f->max_pointer_idx))
+ {
+ p = (p - offset) - f->non_used_index_space;
+ }
+ else
+ {
+ p -= offset;
+ }
+ return p;
+}
+
+// get relative from absolute pointer
+static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ return _ff_mod(advance_pointer(f, p, offset), f->depth);
+}
+
+// Works on local copies of w and r
+static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = wAbs-rAbs;
+
+ // In case we have non-power of two depth we need a further modification
+ if (rAbs > wAbs) cnt -= f->non_used_index_space;
+
+ return cnt;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs)
+{
+ return wAbs == rAbs;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return (_tu_fifo_count(f, wAbs, rAbs) == f->depth);
+}
+
+// Works on local copies of w and r
+// BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
+// Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
+// write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
+// write pointer wraps and you pointer states are messed up. This can only happen if you
+// use DMAs, write functions do not allow such an error.
+static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return (_tu_fifo_count(f, wAbs, rAbs) > f->depth);
+}
+
+// Works on local copies of w
+// For more details see _tu_fifo_overflow()!
+static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
+{
+ f->rd_idx = backward_pointer(f, wAbs, f->depth);
+}
+
+// Works on local copies of w and r
+// Must be protected by mutexes since in case of an overflow read pointer gets modified
+static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _tu_fifo_correct_read_pointer(f, wAbs);
+ cnt = f->depth;
+ }
+
+ // Skip beginning of buffer
+ if (cnt == 0 || offset >= cnt) return false;
+
+ uint16_t rRel = get_relative_pointer(f, rAbs, offset);
+
+ // Peek data
+ _ff_pull(f, p_buffer, rRel);
+
+ return true;
+}
+
+// Works on local copies of w and r
+// Must be protected by mutexes since in case of an overflow read pointer gets modified
+static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _tu_fifo_correct_read_pointer(f, wAbs);
+ rAbs = f->rd_idx;
+ cnt = f->depth;
+ }
+
+ // Skip beginning of buffer
+ if (cnt == 0 || offset >= cnt) return 0;
+
+ // Check if we can read something at and after offset - if too less is available we read what remains
+ cnt -= offset;
+ if (cnt < n) {
+ if (cnt == 0) return 0;
+ n = cnt;
+ }
+
+ uint16_t rRel = get_relative_pointer(f, rAbs, offset);
+
+ // Peek data
+ _ff_pull_n(f, p_buffer, n, rRel);
+
+ return n;
+}
+
+// Works on local copies of w and r
+static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return f->depth - _tu_fifo_count(f, wAbs, rAbs);
+}
+
+/******************************************************************************/
+/*!
+ @brief Get number of items in FIFO.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_count(tu_fifo_t* f)
+{
+ return _tu_fifo_count(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if FIFO is empty.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+bool tu_fifo_empty(tu_fifo_t* f)
+{
+ return _tu_fifo_empty(f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if FIFO is full.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+bool tu_fifo_full(tu_fifo_t* f)
+{
+ return _tu_fifo_full(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Get remaining space in FIFO.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_remaining(tu_fifo_t* f)
+{
+ return _tu_fifo_remaining(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if overflow happened.
+
+ BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
+ Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
+ write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
+ write pointer wraps and you pointer states are messed up. This can only happen if you
+ use DMAs, write functions do not allow such an error. Avoid such nasty things!
+
+ All reading functions (read, peek) check for overflows and correct read pointer on their own such
+ that latest items are read.
+ If required (e.g. for DMA use) you can also correct the read pointer by
+ tu_fifo_correct_read_pointer().
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns True if overflow happened
+ */
+/******************************************************************************/
+bool tu_fifo_overflowed(tu_fifo_t* f)
+{
+ return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx);
+}
+
+// Only use in case tu_fifo_overflow() returned true!
+void tu_fifo_correct_read_pointer(tu_fifo_t* f)
+{
+ tu_fifo_lock(f);
+ _tu_fifo_correct_read_pointer(f, f->wr_idx);
+ tu_fifo_unlock(f);
+}
+
+/******************************************************************************/
+/*!
+ @brief Read one element out of the buffer.
+
+ This function will return the element located at the array index of the
+ read pointer, and then increment the read pointer index.
+ This function checks for an overflow and corrects read pointer if required.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] buffer
+ Pointer to the place holder for data read from the buffer
+
+ @returns TRUE if the queue is not empty
+ */
+/******************************************************************************/
+bool tu_fifo_read(tu_fifo_t* f, void * buffer)
+{
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+
+ // Peek the data
+ bool ret = _tu_fifo_peek_at(f, 0, buffer, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
+
+ // Advance pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, ret);
+
+ tu_fifo_unlock(f);
+ return ret;
+}
+
+/******************************************************************************/
+/*!
+ @brief This function will read n elements from the array index specified by
+ the read pointer and increment the read index.
+ This function checks for an overflow and corrects read pointer if required.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] buffer
+ The pointer to data location
+ @param[in] count
+ Number of element that buffer can afford
+
+ @returns number of items read from the FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
+{
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+
+ // Peek the data
+ count = _tu_fifo_peek_at_n(f, 0, buffer, count, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
+
+ // Advance read pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, count);
+
+ tu_fifo_unlock(f);
+ return count;
+}
+
+/******************************************************************************/
+/*!
+ @brief Read one item without removing it from the FIFO.
+ This function checks for an overflow and corrects read pointer if required.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] offset
+ Position to read from in the FIFO buffer with respect to read pointer
+ @param[in] p_buffer
+ Pointer to the place holder for data read from the buffer
+
+ @returns TRUE if the queue is not empty
+ */
+/******************************************************************************/
+bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
+{
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+ bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
+ tu_fifo_unlock(f);
+ return ret;
+}
+
+/******************************************************************************/
+/*!
+ @brief Read n items without removing it from the FIFO
+ This function checks for an overflow and corrects read pointer if required.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] offset
+ Position to read from in the FIFO buffer with respect to read pointer
+ @param[in] p_buffer
+ Pointer to the place holder for data read from the buffer
+ @param[in] n
+ Number of items to peek
+
+ @returns Number of bytes written to p_buffer
+ */
+/******************************************************************************/
+uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n)
+{
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+ bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx);
+ tu_fifo_unlock(f);
+ return ret;
+}
+
+/******************************************************************************/
+/*!
+ @brief Write one element into the buffer.
+
+ This function will write one element into the array index specified by
+ the write pointer and increment the write index.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] data
+ The byte to add to the FIFO
+
+ @returns TRUE if the data was written to the FIFO (overwrittable
+ FIFO will always return TRUE)
+ */
+/******************************************************************************/
+bool tu_fifo_write(tu_fifo_t* f, const void * data)
+{
+ tu_fifo_lock(f);
+
+ uint16_t w = f->wr_idx;
+
+ if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false;
+
+ uint16_t wRel = get_relative_pointer(f, w, 0);
+
+ // Write data
+ _ff_push(f, data, wRel);
+
+ // Advance pointer
+ f->wr_idx = advance_pointer(f, w, 1);
+
+ tu_fifo_unlock(f);
+
+ return true;
+}
+
+/******************************************************************************/
+/*!
+ @brief This function will write n elements into the array index specified by
+ the write pointer and increment the write index.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] data
+ The pointer to data to add to the FIFO
+ @param[in] count
+ Number of element
+ @return Number of written elements
+ */
+/******************************************************************************/
+uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
+{
+ if ( count == 0 ) return 0;
+
+ tu_fifo_lock(f);
+
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint8_t const* buf8 = (uint8_t const*) data;
+
+ if (!f->overwritable)
+ {
+ // Not overwritable limit up to full
+ count = tu_min16(count, _tu_fifo_remaining(f, w, r));
+ }
+ else if (count > f->depth)
+ {
+ // Only copy last part
+ buf8 = buf8 + (count - f->depth) * f->item_size;
+ count = f->depth;
+
+ // We start writing at the read pointer's position since we fill the complete
+ // buffer and we do not want to modify the read pointer within a write function!
+ // This would end up in a race condition with read functions!
+ f->wr_idx = r;
+ }
+
+ uint16_t wRel = get_relative_pointer(f, w, 0);
+
+ // Write data
+ _ff_push_n(f, buf8, count, wRel);
+
+ // Advance pointer
+ f->wr_idx = advance_pointer(f, w, count);
+
+ tu_fifo_unlock(f);
+
+ return count;
+}
+
+/******************************************************************************/
+/*!
+ @brief Clear the fifo read and write pointers
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ */
+/******************************************************************************/
+bool tu_fifo_clear(tu_fifo_t *f)
+{
+ tu_fifo_lock(f);
+ f->rd_idx = f->wr_idx = 0;
+ f->max_pointer_idx = 2*f->depth-1;
+ f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
+ tu_fifo_unlock(f);
+
+ return true;
+}
+
+/******************************************************************************/
+/*!
+ @brief Change the fifo mode to overwritable or not overwritable
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] overwritable
+ Overwritable mode the fifo is set to
+*/
+/******************************************************************************/
+bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
+{
+ tu_fifo_lock(f);
+
+ f->overwritable = overwritable;
+
+ tu_fifo_unlock(f);
+
+ return true;
+}
+
+/******************************************************************************/
+/*!
+ @brief Advance write pointer - intended to be used in combination with DMA.
+ It is possible to fill the FIFO by use of a DMA in circular mode. Within
+ DMA ISRs you may update the write pointer to be able to read from the FIFO.
+ As long as the DMA is the only process writing into the FIFO this is safe
+ to use.
+
+ USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] n
+ Number of items the write pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
+{
+ f->wr_idx = advance_pointer(f, f->wr_idx, n);
+}
+
+/******************************************************************************/
+/*!
+ @brief Advance read pointer - intended to be used in combination with DMA.
+ It is possible to read from the FIFO by use of a DMA in linear mode. Within
+ DMA ISRs you may update the read pointer to be able to again write into the
+ FIFO. As long as the DMA is the only process reading from the FIFO this is
+ safe to use.
+
+ USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] n
+ Number of items the read pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
+{
+ f->rd_idx = advance_pointer(f, f->rd_idx, n);
+}
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_fifo.h b/sw/Core/Src/tinyusb/src/common/tusb_fifo.h
new file mode 100755
index 0000000..2943784
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_fifo.h
@@ -0,0 +1,141 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_Common
+ * \defgroup group_fifo fifo
+ * @{ */
+
+#ifndef _TUSB_FIFO_H_
+#define _TUSB_FIFO_H_
+
+// Due to the use of unmasked pointers, this FIFO does not suffer from loosing
+// one item slice. Furthermore, write and read operations are completely
+// decoupled as write and read functions do not modify a common state. Henceforth,
+// writing or reading from the FIFO within an ISR is safe as long as no other
+// process (thread or ISR) interferes.
+// Also, this FIFO is ready to be used in combination with a DMA as the write and
+// read pointers can be updated from within a DMA ISR. Overflows are detectable
+// within a certain number (see tu_fifo_overflow()).
+
+// mutex is only needed for RTOS
+// for OS None, we don't get preempted
+#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE)
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if CFG_FIFO_MUTEX
+#define tu_fifo_mutex_t osal_mutex_t
+#endif
+
+
+/** \struct tu_fifo_t
+ * \brief Simple Circular FIFO
+ */
+typedef struct
+{
+ uint8_t* buffer ; ///< buffer pointer
+ uint16_t depth ; ///< max items
+ uint16_t item_size ; ///< size of each item
+ bool overwritable ;
+
+ uint16_t max_pointer_idx ; ///< maximum absolute pointer index
+ uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
+
+ volatile uint16_t wr_idx ; ///< write pointer
+ volatile uint16_t rd_idx ; ///< read pointer
+
+#if CFG_FIFO_MUTEX
+ tu_fifo_mutex_t mutex;
+#endif
+
+} tu_fifo_t;
+
+#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \
+{ \
+ .buffer = _buffer, \
+ .depth = _depth, \
+ .item_size = sizeof(_type), \
+ .overwritable = _overwritable, \
+ .max_pointer_idx = 2*(_depth)-1, \
+ .non_used_index_space = UINT16_MAX - (2*(_depth)-1) \
+}
+
+#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
+ uint8_t _name##_buf[_depth*sizeof(_type)]; \
+ tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
+
+bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
+bool tu_fifo_clear(tu_fifo_t *f);
+bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
+
+#if CFG_FIFO_MUTEX
+static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl)
+{
+ f->mutex = mutex_hdl;
+}
+#endif
+
+bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
+uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count);
+
+bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
+uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count);
+
+bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
+uint16_t tu_fifo_peek_at_n (tu_fifo_t* f, uint16_t pos, void * p_buffer, uint16_t n);
+
+uint16_t tu_fifo_count (tu_fifo_t* f);
+bool tu_fifo_empty (tu_fifo_t* f);
+bool tu_fifo_full (tu_fifo_t* f);
+uint16_t tu_fifo_remaining (tu_fifo_t* f);
+bool tu_fifo_overflowed (tu_fifo_t* f);
+void tu_fifo_correct_read_pointer (tu_fifo_t* f);
+
+// Pointer modifications intended to be used in combinations with DMAs.
+// USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
+void tu_fifo_advance_write_pointer (tu_fifo_t *f, uint16_t n);
+void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
+
+static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
+{
+ return tu_fifo_peek_at(f, 0, p_buffer);
+}
+
+static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
+{
+ return f->depth;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_FIFO_H_ */
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_timeout.h b/sw/Core/Src/tinyusb/src/common/tusb_timeout.h
new file mode 100755
index 0000000..ce53955
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_timeout.h
@@ -0,0 +1,80 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_Common Common Files
+ * \defgroup Group_TimeoutTimer timeout timer
+ * @{ */
+
+#ifndef _TUSB_TIMEOUT_H_
+#define _TUSB_TIMEOUT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ uint32_t start;
+ uint32_t interval;
+}tu_timeout_t;
+
+#if 0
+
+extern uint32_t tusb_hal_millis(void);
+
+static inline void tu_timeout_set(tu_timeout_t* tt, uint32_t msec)
+{
+ tt->interval = msec;
+ tt->start = tusb_hal_millis();
+}
+
+static inline bool tu_timeout_expired(tu_timeout_t* tt)
+{
+ return ( tusb_hal_millis() - tt->start ) >= tt->interval;
+}
+
+// For used with periodic event to prevent drift
+static inline void tu_timeout_reset(tu_timeout_t* tt)
+{
+ tt->start += tt->interval;
+}
+
+static inline void tu_timeout_restart(tu_timeout_t* tt)
+{
+ tt->start = tusb_hal_millis();
+}
+
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_TIMEOUT_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_types.h b/sw/Core/Src/tinyusb/src/common/tusb_types.h
new file mode 100755
index 0000000..ba3fe2c
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_types.h
@@ -0,0 +1,498 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_usb_definitions
+ * \defgroup USBDef_Type USB Types
+ * @{ */
+
+#ifndef _TUSB_TYPES_H_
+#define _TUSB_TYPES_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "tusb_compiler.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*------------------------------------------------------------------*/
+/* CONSTANTS
+ *------------------------------------------------------------------*/
+
+/// defined base on EHCI specs value for Endpoint Speed
+typedef enum
+{
+ TUSB_SPEED_FULL = 0,
+ TUSB_SPEED_LOW ,
+ TUSB_SPEED_HIGH,
+ TUSB_SPEED_INVALID = 0xff,
+}tusb_speed_t;
+
+/// defined base on USB Specs Endpoint's bmAttributes
+typedef enum
+{
+ TUSB_XFER_CONTROL = 0 ,
+ TUSB_XFER_ISOCHRONOUS ,
+ TUSB_XFER_BULK ,
+ TUSB_XFER_INTERRUPT
+}tusb_xfer_type_t;
+
+typedef enum
+{
+ TUSB_DIR_OUT = 0,
+ TUSB_DIR_IN = 1,
+
+ TUSB_DIR_IN_MASK = 0x80
+}tusb_dir_t;
+
+/// USB Descriptor Types
+typedef enum
+{
+ TUSB_DESC_DEVICE = 0x01,
+ TUSB_DESC_CONFIGURATION = 0x02,
+ TUSB_DESC_STRING = 0x03,
+ TUSB_DESC_INTERFACE = 0x04,
+ TUSB_DESC_ENDPOINT = 0x05,
+ TUSB_DESC_DEVICE_QUALIFIER = 0x06,
+ TUSB_DESC_OTHER_SPEED_CONFIG = 0x07,
+ TUSB_DESC_INTERFACE_POWER = 0x08,
+ TUSB_DESC_OTG = 0x09,
+ TUSB_DESC_DEBUG = 0x0A,
+ TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B,
+
+ TUSB_DESC_BOS = 0x0F,
+ TUSB_DESC_DEVICE_CAPABILITY = 0x10,
+
+ TUSB_DESC_FUNCTIONAL = 0x21,
+
+ // Class Specific Descriptor
+ TUSB_DESC_CS_DEVICE = 0x21,
+ TUSB_DESC_CS_CONFIGURATION = 0x22,
+ TUSB_DESC_CS_STRING = 0x23,
+ TUSB_DESC_CS_INTERFACE = 0x24,
+ TUSB_DESC_CS_ENDPOINT = 0x25,
+
+ TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30,
+ TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
+}tusb_desc_type_t;
+
+typedef enum
+{
+ TUSB_REQ_GET_STATUS = 0 ,
+ TUSB_REQ_CLEAR_FEATURE = 1 ,
+ TUSB_REQ_RESERVED = 2 ,
+ TUSB_REQ_SET_FEATURE = 3 ,
+ TUSB_REQ_RESERVED2 = 4 ,
+ TUSB_REQ_SET_ADDRESS = 5 ,
+ TUSB_REQ_GET_DESCRIPTOR = 6 ,
+ TUSB_REQ_SET_DESCRIPTOR = 7 ,
+ TUSB_REQ_GET_CONFIGURATION = 8 ,
+ TUSB_REQ_SET_CONFIGURATION = 9 ,
+ TUSB_REQ_GET_INTERFACE = 10 ,
+ TUSB_REQ_SET_INTERFACE = 11 ,
+ TUSB_REQ_SYNCH_FRAME = 12
+}tusb_request_code_t;
+
+typedef enum
+{
+ TUSB_REQ_FEATURE_EDPT_HALT = 0,
+ TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
+ TUSB_REQ_FEATURE_TEST_MODE = 2
+}tusb_request_feature_selector_t;
+
+typedef enum
+{
+ TUSB_REQ_TYPE_STANDARD = 0,
+ TUSB_REQ_TYPE_CLASS,
+ TUSB_REQ_TYPE_VENDOR,
+ TUSB_REQ_TYPE_INVALID
+} tusb_request_type_t;
+
+typedef enum
+{
+ TUSB_REQ_RCPT_DEVICE =0,
+ TUSB_REQ_RCPT_INTERFACE,
+ TUSB_REQ_RCPT_ENDPOINT,
+ TUSB_REQ_RCPT_OTHER
+} tusb_request_recipient_t;
+
+// https://www.usb.org/defined-class-codes
+typedef enum
+{
+ TUSB_CLASS_UNSPECIFIED = 0 ,
+ TUSB_CLASS_AUDIO = 1 ,
+ TUSB_CLASS_CDC = 2 ,
+ TUSB_CLASS_HID = 3 ,
+ TUSB_CLASS_RESERVED_4 = 4 ,
+ TUSB_CLASS_PHYSICAL = 5 ,
+ TUSB_CLASS_IMAGE = 6 ,
+ TUSB_CLASS_PRINTER = 7 ,
+ TUSB_CLASS_MSC = 8 ,
+ TUSB_CLASS_HUB = 9 ,
+ TUSB_CLASS_CDC_DATA = 10 ,
+ TUSB_CLASS_SMART_CARD = 11 ,
+ TUSB_CLASS_RESERVED_12 = 12 ,
+ TUSB_CLASS_CONTENT_SECURITY = 13 ,
+ TUSB_CLASS_VIDEO = 14 ,
+ TUSB_CLASS_PERSONAL_HEALTHCARE = 15 ,
+ TUSB_CLASS_AUDIO_VIDEO = 16 ,
+
+ TUSB_CLASS_DIAGNOSTIC = 0xDC ,
+ TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 ,
+ TUSB_CLASS_MISC = 0xEF ,
+ TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
+ TUSB_CLASS_VENDOR_SPECIFIC = 0xFF
+}tusb_class_code_t;
+
+typedef enum
+{
+ MISC_SUBCLASS_COMMON = 2
+}misc_subclass_type_t;
+
+typedef enum
+{
+ MISC_PROTOCOL_IAD = 1
+}misc_protocol_type_t;
+
+typedef enum
+{
+ APP_SUBCLASS_USBTMC = 0x03,
+ APP_SUBCLASS_DFU_RUNTIME = 0x01
+} app_subclass_type_t;
+
+typedef enum
+{
+ DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
+ DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
+ DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
+ DEVICE_CAPABILITY_CONTAINER_id = 0x04,
+ DEVICE_CAPABILITY_PLATFORM = 0x05,
+ DEVICE_CAPABILITY_POWER_DELIVERY = 0x06,
+ DEVICE_CAPABILITY_BATTERY_INFO = 0x07,
+ DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08,
+ DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09,
+ DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A,
+ DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B,
+ DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C,
+ DEVICE_CAPABILITY_BILLBOARD = 0x0D,
+ DEVICE_CAPABILITY_AUTHENTICATION = 0x0E,
+ DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F,
+ DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10
+}device_capability_type_t;
+
+enum {
+ TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
+ TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6),
+};
+
+#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
+
+/// Device State TODO remove
+typedef enum
+{
+ TUSB_DEVICE_STATE_UNPLUG = 0 ,
+ TUSB_DEVICE_STATE_CONFIGURED ,
+ TUSB_DEVICE_STATE_SUSPENDED ,
+}tusb_device_state_t;
+
+typedef enum
+{
+ XFER_RESULT_SUCCESS,
+ XFER_RESULT_FAILED,
+ XFER_RESULT_STALLED,
+}xfer_result_t;
+
+enum // TODO remove
+{
+ DESC_OFFSET_LEN = 0,
+ DESC_OFFSET_TYPE = 1
+};
+
+enum
+{
+ INTERFACE_INVALID_NUMBER = 0xff
+};
+
+
+typedef enum
+{
+ MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
+ MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
+ MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
+ MS_OS_20_FEATURE_COMPATBLE_ID = 0x03,
+ MS_OS_20_FEATURE_REG_PROPERTY = 0x04,
+ MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05,
+ MS_OS_20_FEATURE_MODEL_ID = 0x06,
+ MS_OS_20_FEATURE_CCGP_DEVICE = 0x07,
+ MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
+} microsoft_os_20_type_t;
+
+enum
+{
+ CONTROL_STAGE_SETUP,
+ CONTROL_STAGE_DATA,
+ CONTROL_STAGE_ACK
+};
+
+//--------------------------------------------------------------------+
+// USB Descriptors
+//--------------------------------------------------------------------+
+
+/// USB Device Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes.
+ uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
+ uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
+
+ uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
+ uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
+ uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
+ uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
+
+ uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
+ uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
+ uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
+ uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
+ uint8_t iProduct ; ///< Index of string descriptor describing product.
+ uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
+
+ uint8_t bNumConfigurations ; ///< Number of possible configurations.
+} tusb_desc_device_t;
+
+TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
+
+// USB Binary Device Object Store (BOS) Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes
+ uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
+ uint16_t wTotalLength ; ///< Total length of data returned for this descriptor
+ uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS
+} tusb_desc_bos_t;
+
+/// USB Configuration Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes
+ uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
+ uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
+
+ uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration
+ uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration.
+ uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration
+ uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one.
+ uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA).
+} tusb_desc_configuration_t;
+
+/// USB Interface Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes
+ uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
+
+ uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration.
+ uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field
+ uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe.
+ uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF.
+ uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
+ uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface.
+ uint8_t iInterface ; ///< Index of string descriptor describing this interface
+} tusb_desc_interface_t;
+
+/// USB Endpoint Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes
+ uint8_t bDescriptorType ; ///< ENDPOINT Descriptor Type
+
+ uint8_t bEndpointAddress ; ///< The address of the endpoint on the USB device described by this descriptor. The address is encoded as follows: \n Bit 3...0: The endpoint number \n Bit 6...4: Reserved, reset to zero \n Bit 7: Direction, ignored for control endpoints 0 = OUT endpoint 1 = IN endpoint.
+
+ struct TU_ATTR_PACKED {
+ uint8_t xfer : 2;
+ uint8_t sync : 2;
+ uint8_t usage : 2;
+ uint8_t : 2;
+ } bmAttributes ; ///< This field describes the endpoint's attributes when it is configured using the bConfigurationValue. \n Bits 1..0: Transfer Type \n- 00 = Control \n- 01 = Isochronous \n- 10 = Bulk \n- 11 = Interrupt \n If not an isochronous endpoint, bits 5..2 are reserved and must be set to zero. If isochronous, they are defined as follows: \n Bits 3..2: Synchronization Type \n- 00 = No Synchronization \n- 01 = Asynchronous \n- 10 = Adaptive \n- 11 = Synchronous \n Bits 5..4: Usage Type \n- 00 = Data endpoint \n- 01 = Feedback endpoint \n- 10 = Implicit feedback Data endpoint \n- 11 = Reserved \n Refer to Chapter 5 of USB 2.0 specification for more information. \n All other bits are reserved and must be reset to zero. Reserved bits must be ignored by the host.
+
+ struct TU_ATTR_PACKED {
+ uint16_t size : 11; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. \n For isochronous endpoints, this value is used to reserve the bus time in the schedule, required for the per-(micro)frame data payloads. The pipe may, on an ongoing basis, actually use less bandwidth than that reserved. The device reports, if necessary, the actual bandwidth used via its normal, non-USB defined mechanisms. \n For all endpoints, bits 10..0 specify the maximum packet size (in bytes). \n For high-speed isochronous and interrupt endpoints: \n Bits 12..11 specify the number of additional transaction opportunities per microframe: \n- 00 = None (1 transaction per microframe) \n- 01 = 1 additional (2 per microframe) \n- 10 = 2 additional (3 per microframe) \n- 11 = Reserved \n Bits 15..13 are reserved and must be set to zero.
+ uint16_t hs_period_mult : 2;
+ uint16_t TU_RESERVED : 3;
+ }wMaxPacketSize;
+
+ uint8_t bInterval ; ///< Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units). \n- For full-/high-speed isochronous endpoints, this value must be in the range from 1 to 16. The bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$). \n- For full-/low-speed interrupt endpoints, the value of this field may be from 1 to 255. \n- For high-speed interrupt endpoints, the bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$) . This value must be from 1 to 16. \n- For high-speed bulk/control OUT endpoints, the bInterval must specify the maximum NAK rate of the endpoint. A value of 0 indicates the endpoint never NAKs. Other values indicate at most 1 NAK each bInterval number of microframes. This value must be in the range from 0 to 255. \n Refer to Chapter 5 of USB 2.0 specification for more information.
+} tusb_desc_endpoint_t;
+
+/// USB Other Speed Configuration Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of descriptor
+ uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
+ uint16_t wTotalLength ; ///< Total length of data returned
+
+ uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration
+ uint8_t bConfigurationValue ; ///< Value to use to select configuration
+ uint8_t IConfiguration ; ///< Index of string descriptor
+ uint8_t bmAttributes ; ///< Same as Configuration descriptor
+ uint8_t bMaxPower ; ///< Same as Configuration descriptor
+} tusb_desc_other_speed_t;
+
+/// USB Device Qualifier Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of descriptor
+ uint8_t bDescriptorType ; ///< Device Qualifier Type
+ uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
+
+ uint8_t bDeviceClass ; ///< Class Code
+ uint8_t bDeviceSubClass ; ///< SubClass Code
+ uint8_t bDeviceProtocol ; ///< Protocol Code
+ uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed
+ uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations
+ uint8_t bReserved ; ///< Reserved for future use, must be zero
+} tusb_desc_device_qualifier_t;
+
+/// USB Interface Association Descriptor (IAD ECN)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of descriptor
+ uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
+
+ uint8_t bFirstInterface ; ///< Index of the first associated interface.
+ uint8_t bInterfaceCount ; ///< Total number of associated interfaces.
+
+ uint8_t bFunctionClass ; ///< Interface class ID.
+ uint8_t bFunctionSubClass ; ///< Interface subclass ID.
+ uint8_t bFunctionProtocol ; ///< Interface protocol ID.
+
+ uint8_t iFunction ; ///< Index of the string descriptor describing the interface association.
+} tusb_desc_interface_assoc_t;
+
+// USB String Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes
+ uint8_t bDescriptorType ; ///< Descriptor Type
+ uint16_t unicode_string[];
+} tusb_desc_string_t;
+
+// USB Binary Device Object Store (BOS)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength;
+ uint8_t bDescriptorType ;
+ uint8_t bDevCapabilityType;
+ uint8_t bReserved;
+ uint8_t PlatformCapabilityUUID[16];
+ uint8_t CapabilityData[];
+} tusb_desc_bos_platform_t;
+
+// USB WebuSB URL Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bScheme;
+ char url[];
+} tusb_desc_webusb_url_t;
+
+/*------------------------------------------------------------------*/
+/* Types
+ *------------------------------------------------------------------*/
+typedef struct TU_ATTR_PACKED{
+ union {
+ struct TU_ATTR_PACKED {
+ uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
+ uint8_t type : 2; ///< Request type tusb_request_type_t.
+ uint8_t direction : 1; ///< Direction type. tusb_dir_t
+ } bmRequestType_bit;
+
+ uint8_t bmRequestType;
+ };
+
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} tusb_control_request_t;
+
+TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
+
+// TODO move to somewhere suitable
+static inline uint8_t bm_request_type(uint8_t direction, uint8_t type, uint8_t recipient)
+{
+ return ((uint8_t) (direction << 7)) | ((uint8_t) (type << 5)) | (recipient);
+}
+
+//--------------------------------------------------------------------+
+// Endpoint helper
+//--------------------------------------------------------------------+
+
+// Get direction from Endpoint address
+static inline tusb_dir_t tu_edpt_dir(uint8_t addr)
+{
+ return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+}
+
+// Get Endpoint number from address
+static inline uint8_t tu_edpt_number(uint8_t addr)
+{
+ return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
+}
+
+static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
+{
+ return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
+}
+
+//--------------------------------------------------------------------+
+// Descriptor helper
+//--------------------------------------------------------------------+
+static inline uint8_t const * tu_desc_next(void const* desc)
+{
+ uint8_t const* desc8 = (uint8_t const*) desc;
+ return desc8 + desc8[DESC_OFFSET_LEN];
+}
+
+static inline uint8_t tu_desc_type(void const* desc)
+{
+ return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
+}
+
+static inline uint8_t tu_desc_len(void const* desc)
+{
+ return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_TYPES_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/common/tusb_verify.h b/sw/Core/Src/tinyusb/src/common/tusb_verify.h
new file mode 100755
index 0000000..8b724ae
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/common/tusb_verify.h
@@ -0,0 +1,183 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+#ifndef TUSB_VERIFY_H_
+#define TUSB_VERIFY_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "tusb_option.h"
+#include "tusb_compiler.h"
+
+/*------------------------------------------------------------------*/
+/* This file use an advanced macro technique to mimic the default parameter
+ * as C++ for the sake of code simplicity. Beware of a headache macro
+ * manipulation that you are told to stay away.
+ *
+ *
+ * This contains macros for both VERIFY and ASSERT:
+ *
+ * VERIFY: Used when there is an error condition which is not the
+ * fault of the MCU. For example, bounds checking on data
+ * sent to the micro over USB should use this function.
+ * Another example is checking for buffer overflows, where
+ * returning from the active function causes a NAK.
+ *
+ * ASSERT: Used for error conditions that are caused by MCU firmware
+ * bugs. This is used to discover bugs in the code more
+ * quickly. One example would be adding assertions in library
+ * function calls to confirm a function's (untainted)
+ * parameters are valid.
+ *
+ *
+ * The difference in behaviour is that ASSERT triggers a breakpoint while
+ * verify does not.
+ *
+ * #define TU_VERIFY(cond) if(cond) return false;
+ * #define TU_VERIFY(cond,ret) if(cond) return ret;
+ *
+ * #define TU_VERIFY_HDLR(cond,handler) if(cond) {handler; return false;}
+ * #define TU_VERIFY_HDLR(cond,ret,handler) if(cond) {handler; return ret;}
+ *
+ * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;}
+ * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
+ *
+ *------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TU_VERIFY Helper
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_DEBUG
+ #include <stdio.h>
+ #define _MESS_ERR(_err) tu_printf("%s %d: failed, error = %s\r\n", __func__, __LINE__, tusb_strerr[_err])
+ #define _MESS_FAILED() tu_printf("%s %d: assert failed\r\n", __func__, __LINE__)
+#else
+ #define _MESS_ERR(_err) do {} while (0)
+ #define _MESS_FAILED() do {} while (0)
+#endif
+
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+ #define TU_BREAKPOINT() do \
+ { \
+ volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+ if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
+ } while(0)
+
+#elif defined(__riscv)
+ #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
+
+#else
+ #define TU_BREAKPOINT() do {} while (0)
+#endif
+
+/*------------------------------------------------------------------*/
+/* Macro Generator
+ *------------------------------------------------------------------*/
+
+// Helper to implement optional parameter for TU_VERIFY Macro family
+#define GET_3RD_ARG(arg1, arg2, arg3, ...) arg3
+#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
+
+/*------------- Generator for TU_VERIFY and TU_VERIFY_HDLR -------------*/
+#define TU_VERIFY_DEFINE(_cond, _handler, _ret) do \
+{ \
+ if ( !(_cond) ) { _handler; return _ret; } \
+} while(0)
+
+/*------------------------------------------------------------------*/
+/* TU_VERIFY
+ * - TU_VERIFY_1ARGS : return false if failed
+ * - TU_VERIFY_2ARGS : return provided value if failed
+ *------------------------------------------------------------------*/
+#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, , false)
+#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, , _ret)
+
+#define TU_VERIFY(...) GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, UNUSED)(__VA_ARGS__)
+
+
+/*------------------------------------------------------------------*/
+/* TU_VERIFY WITH HANDLER
+ * - TU_VERIFY_HDLR_2ARGS : execute handler, return false if failed
+ * - TU_VERIFY_HDLR_3ARGS : execute handler, return provided error if failed
+ *------------------------------------------------------------------*/
+#define TU_VERIFY_HDLR_2ARGS(_cond, _handler) TU_VERIFY_DEFINE(_cond, _handler, false)
+#define TU_VERIFY_HDLR_3ARGS(_cond, _handler, _ret) TU_VERIFY_DEFINE(_cond, _handler, _ret)
+
+#define TU_VERIFY_HDLR(...) GET_4TH_ARG(__VA_ARGS__, TU_VERIFY_HDLR_3ARGS, TU_VERIFY_HDLR_2ARGS,UNUSED)(__VA_ARGS__)
+
+/*------------------------------------------------------------------*/
+/* ASSERT
+ * basically TU_VERIFY with TU_BREAKPOINT() as handler
+ * - 1 arg : return false if failed
+ * - 2 arg : return error if failed
+ *------------------------------------------------------------------*/
+#define ASSERT_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), false)
+#define ASSERT_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), _ret)
+
+#ifndef TU_ASSERT
+#define TU_ASSERT(...) GET_3RD_ARG(__VA_ARGS__, ASSERT_2ARGS, ASSERT_1ARGS,UNUSED)(__VA_ARGS__)
+#endif
+
+// TODO remove TU_ASSERT_ERR() later
+
+/*------------- Generator for TU_VERIFY_ERR and TU_VERIFY_ERR_HDLR -------------*/
+#define TU_VERIFY_ERR_DEF2(_error, _handler) do \
+{ \
+ uint32_t _err = (uint32_t)(_error); \
+ if ( 0 != _err ) { _MESS_ERR(_err); _handler; return _err; } \
+} while(0)
+
+#define TU_VERIFY_ERR_DEF3(_error, _handler, _ret) do \
+{ \
+ uint32_t _err = (uint32_t)(_error); \
+ if ( 0 != _err ) { _MESS_ERR(_err); _handler; return _ret; } \
+} while(0)
+
+/*------------------------------------------------------------------*/
+/* ASSERT Error
+ * basically TU_VERIFY Error with TU_BREAKPOINT() as handler
+ *------------------------------------------------------------------*/
+#define ASSERT_ERR_1ARGS(_error) TU_VERIFY_ERR_DEF2(_error, TU_BREAKPOINT())
+#define ASSERT_ERR_2ARGS(_error, _ret) TU_VERIFY_ERR_DEF3(_error, TU_BREAKPOINT(), _ret)
+
+#ifndef TU_ASSERT_ERR
+#define TU_ASSERT_ERR(...) GET_3RD_ARG(__VA_ARGS__, ASSERT_ERR_2ARGS, ASSERT_ERR_1ARGS,UNUSED)(__VA_ARGS__)
+#endif
+
+/*------------------------------------------------------------------*/
+/* ASSERT HDLR
+ *------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* TUSB_VERIFY_H_ */
diff --git a/sw/Core/Src/tinyusb/src/device/dcd.h b/sw/Core/Src/tinyusb/src/device/dcd.h
new file mode 100755
index 0000000..b7e5a8d
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/device/dcd.h
@@ -0,0 +1,167 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_usbd
+ * \defgroup group_dcd Device Controller Driver (DCD)
+ * @{ */
+
+#ifndef _TUSB_DCD_H_
+#define _TUSB_DCD_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef enum
+{
+ DCD_EVENT_INVALID = 0,
+ DCD_EVENT_BUS_RESET,
+ DCD_EVENT_UNPLUGGED,
+ DCD_EVENT_SOF,
+ DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support
+ DCD_EVENT_RESUME,
+
+ DCD_EVENT_SETUP_RECEIVED,
+ DCD_EVENT_XFER_COMPLETE,
+
+ // Not an DCD event, just a convenient way to defer ISR function
+ USBD_EVENT_FUNC_CALL,
+
+ DCD_EVENT_COUNT
+} dcd_eventid_t;
+
+typedef struct TU_ATTR_ALIGNED(4)
+{
+ uint8_t rhport;
+ uint8_t event_id;
+
+ union
+ {
+ // BUS RESET
+ struct {
+ tusb_speed_t speed;
+ } bus_reset;
+
+ // SETUP_RECEIVED
+ tusb_control_request_t setup_received;
+
+ // XFER_COMPLETE
+ struct {
+ uint8_t ep_addr;
+ uint8_t result;
+ uint32_t len;
+ }xfer_complete;
+
+ // FUNC_CALL
+ struct {
+ void (*func) (void*);
+ void* param;
+ }func_call;
+ };
+} dcd_event_t;
+
+//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// Initialize controller to device mode
+void dcd_init (uint8_t rhport);
+
+// Interrupt Handler
+void dcd_int_handler(uint8_t rhport);
+
+// Enable device interrupt
+void dcd_int_enable (uint8_t rhport);
+
+// Disable device interrupt
+void dcd_int_disable(uint8_t rhport);
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
+
+// Wake up host
+void dcd_remote_wakeup(uint8_t rhport);
+
+// Connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
+
+// Disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Invoked when a control transfer's status stage is complete.
+// May help DCD to prepare for next control transfer, this API is optional.
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
+
+// Configure endpoint's registers according to descriptor
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
+
+// Close an endpoint.
+// Since it is weak, caller must TU_ASSERT this function's existence before calling it.
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
+
+// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
+
+// Stall endpoint
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
+
+// clear stall, data toggle is also reset to DATA0
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
+
+//--------------------------------------------------------------------+
+// Event API (implemented by stack)
+//--------------------------------------------------------------------+
+
+// Called by DCD to notify device stack
+extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
+
+// helper to send bus signal event
+extern void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
+
+// helper to send bus reset event
+extern void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr);
+
+// helper to send setup received
+extern void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
+
+// helper to send transfer complete event
+extern void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_DCD_H_ */
+
+/// @}
diff --git a/sw/Core/Src/tinyusb/src/device/usbd.c b/sw/Core/Src/tinyusb/src/device/usbd.c
new file mode 100755
index 0000000..ca5a482
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/device/usbd.c
@@ -0,0 +1,1300 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED
+
+#include "tusb.h"
+#include "usbd.h"
+#include "device/usbd_pvt.h"
+#include "dcd.h"
+
+#ifndef CFG_TUD_TASK_QUEUE_SZ
+#define CFG_TUD_TASK_QUEUE_SZ 16
+#endif
+
+#ifndef CFG_TUD_EP_MAX
+#define CFG_TUD_EP_MAX 9
+#endif
+
+//--------------------------------------------------------------------+
+// Device Data
+//--------------------------------------------------------------------+
+typedef struct
+{
+ struct TU_ATTR_PACKED
+ {
+ volatile uint8_t connected : 1;
+ volatile uint8_t addressed : 1;
+ volatile uint8_t suspended : 1;
+
+ uint8_t remote_wakeup_en : 1; // enable/disable by host
+ uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute
+ uint8_t self_powered : 1; // configuration descriptor's attribute
+ };
+
+ volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
+ uint8_t speed;
+
+ uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
+ uint8_t ep2drv[CFG_TUD_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid )
+
+ struct TU_ATTR_PACKED
+ {
+ volatile bool busy : 1;
+ volatile bool stalled : 1;
+ volatile bool claimed : 1;
+
+ // TODO merge ep2drv here, 4-bit should be sufficient
+ }ep_status[CFG_TUD_EP_MAX][2];
+
+}usbd_device_t;
+
+static usbd_device_t _usbd_dev;
+
+// Invalid driver ID in itf2drv[] ep2drv[][] mapping
+enum { DRVID_INVALID = 0xFFu };
+
+//--------------------------------------------------------------------+
+// Class Driver
+//--------------------------------------------------------------------+
+#if CFG_TUSB_DEBUG >= 2
+ #define DRIVER_NAME(_name) .name = _name,
+#else
+ #define DRIVER_NAME(_name)
+#endif
+
+// Built-in class drivers
+static usbd_class_driver_t const _usbd_driver[] =
+{
+ #if CFG_TUD_CDC
+ {
+ DRIVER_NAME("CDC")
+ .init = cdcd_init,
+ .reset = cdcd_reset,
+ .open = cdcd_open,
+ .control_xfer_cb = cdcd_control_xfer_cb,
+ .xfer_cb = cdcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_MSC
+ {
+ DRIVER_NAME("MSC")
+ .init = mscd_init,
+ .reset = mscd_reset,
+ .open = mscd_open,
+ .control_xfer_cb = mscd_control_xfer_cb,
+ .xfer_cb = mscd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_HID
+ {
+ DRIVER_NAME("HID")
+ .init = hidd_init,
+ .reset = hidd_reset,
+ .open = hidd_open,
+ .control_xfer_cb = hidd_control_xfer_cb,
+ .xfer_cb = hidd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_AUDIO
+ {
+ DRIVER_NAME("AUDIO")
+ .init = audiod_init,
+ .reset = audiod_reset,
+ .open = audiod_open,
+ .control_xfer_cb = audiod_control_xfer_cb,
+ .xfer_cb = audiod_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_MIDI
+ {
+ DRIVER_NAME("MIDI")
+ .init = midid_init,
+ .open = midid_open,
+ .reset = midid_reset,
+ .control_xfer_cb = midid_control_xfer_cb,
+ .xfer_cb = midid_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_VENDOR
+ {
+ DRIVER_NAME("VENDOR")
+ .init = vendord_init,
+ .reset = vendord_reset,
+ .open = vendord_open,
+ .control_xfer_cb = tud_vendor_control_xfer_cb,
+ .xfer_cb = vendord_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_USBTMC
+ {
+ DRIVER_NAME("TMC")
+ .init = usbtmcd_init_cb,
+ .reset = usbtmcd_reset_cb,
+ .open = usbtmcd_open_cb,
+ .control_xfer_cb = usbtmcd_control_xfer_cb,
+ .xfer_cb = usbtmcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_DFU_RUNTIME
+ {
+ DRIVER_NAME("DFU-RUNTIME")
+ .init = dfu_rtd_init,
+ .reset = dfu_rtd_reset,
+ .open = dfu_rtd_open,
+ .control_xfer_cb = dfu_rtd_control_xfer_cb,
+ .xfer_cb = dfu_rtd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_NET
+ {
+ DRIVER_NAME("NET")
+ .init = netd_init,
+ .reset = netd_reset,
+ .open = netd_open,
+ .control_xfer_cb = netd_control_xfer_cb,
+ .xfer_cb = netd_xfer_cb,
+ .sof = NULL,
+ },
+ #endif
+
+ #if CFG_TUD_BTH
+ {
+ DRIVER_NAME("BTH")
+ .init = btd_init,
+ .reset = btd_reset,
+ .open = btd_open,
+ .control_xfer_cb = btd_control_xfer_cb,
+ .xfer_cb = btd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
+};
+
+enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
+
+// Additional class drivers implemented by application
+static usbd_class_driver_t const * _app_driver = NULL;
+static uint8_t _app_driver_count = 0;
+
+// virtually joins built-in and application drivers together.
+// Application is positioned first to allow overwriting built-in ones.
+static inline usbd_class_driver_t const * get_driver(uint8_t drvid)
+{
+ // Application drivers
+ if ( usbd_app_driver_get_cb )
+ {
+ if ( drvid < _app_driver_count ) return &_app_driver[drvid];
+ drvid -= _app_driver_count;
+ }
+
+ // Built-in drivers
+ if (drvid < BUILTIN_DRIVER_COUNT) return &_usbd_driver[drvid];
+
+ return NULL;
+}
+
+#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
+
+//--------------------------------------------------------------------+
+// DCD Event
+//--------------------------------------------------------------------+
+
+// Event queue
+// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr)
+OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
+static osal_queue_t _usbd_q;
+
+// Mutex for claiming endpoint, only needed when using with preempted RTOS
+#if CFG_TUSB_OS != OPT_OS_NONE
+static osal_mutex_def_t _ubsd_mutexdef;
+static osal_mutex_t _usbd_mutex;
+#endif
+
+
+//--------------------------------------------------------------------+
+// Prototypes
+//--------------------------------------------------------------------+
+static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
+static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
+static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
+static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// from usbd_control.c
+void usbd_control_reset(void);
+void usbd_control_set_request(tusb_control_request_t const *request);
+void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
+bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+
+//--------------------------------------------------------------------+
+// Debug
+//--------------------------------------------------------------------+
+#if CFG_TUSB_DEBUG >= 2
+static char const* const _usbd_event_str[DCD_EVENT_COUNT] =
+{
+ "Invalid" ,
+ "Bus Reset" ,
+ "Unplugged" ,
+ "SOF" ,
+ "Suspend" ,
+ "Resume" ,
+ "Setup Received" ,
+ "Xfer Complete" ,
+ "Func Call"
+};
+
+static char const* const _tusb_std_request_str[] =
+{
+ "Get Status" ,
+ "Clear Feature" ,
+ "Reserved" ,
+ "Set Feature" ,
+ "Reserved" ,
+ "Set Address" ,
+ "Get Descriptor" ,
+ "Set Descriptor" ,
+ "Get Configuration" ,
+ "Set Configuration" ,
+ "Get Interface" ,
+ "Set Interface" ,
+ "Synch Frame"
+};
+
+// for usbd_control to print the name of control complete driver
+void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
+{
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
+ {
+ usbd_class_driver_t const * driver = get_driver(i);
+ if ( driver->control_xfer_cb == callback )
+ {
+ TU_LOG2(" %s control complete\r\n", driver->name);
+ return;
+ }
+ }
+}
+
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+tusb_speed_t tud_speed_get(void)
+{
+ return (tusb_speed_t) _usbd_dev.speed;
+}
+
+bool tud_connected(void)
+{
+ return _usbd_dev.connected;
+}
+
+bool tud_mounted(void)
+{
+ return _usbd_dev.cfg_num ? true : false;
+}
+
+bool tud_suspended(void)
+{
+ return _usbd_dev.suspended;
+}
+
+bool tud_remote_wakeup(void)
+{
+ // only wake up host if this feature is supported and enabled and we are suspended
+ TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en );
+ dcd_remote_wakeup(TUD_OPT_RHPORT);
+ return true;
+}
+
+bool tud_disconnect(void)
+{
+ TU_VERIFY(dcd_disconnect);
+ dcd_disconnect(TUD_OPT_RHPORT);
+ return true;
+}
+
+bool tud_connect(void)
+{
+ TU_VERIFY(dcd_connect);
+ dcd_connect(TUD_OPT_RHPORT);
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBD Task
+//--------------------------------------------------------------------+
+bool tud_init (void)
+{
+ TU_LOG2("USBD init\r\n");
+
+ tu_varclr(&_usbd_dev);
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ // Init device mutex
+ _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
+ TU_ASSERT(_usbd_mutex);
+#endif
+
+ // Init device queue & task
+ _usbd_q = osal_queue_create(&_usbd_qdef);
+ TU_ASSERT(_usbd_q);
+
+ // Get application driver if available
+ if ( usbd_app_driver_get_cb )
+ {
+ _app_driver = usbd_app_driver_get_cb(&_app_driver_count);
+ }
+
+ // Init class drivers
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
+ {
+ usbd_class_driver_t const * driver = get_driver(i);
+ TU_LOG2("%s init\r\n", driver->name);
+ driver->init();
+ }
+
+ // Init device controller driver
+ dcd_init(TUD_OPT_RHPORT);
+ dcd_int_enable(TUD_OPT_RHPORT);
+
+ return true;
+}
+
+static void usbd_reset(uint8_t rhport)
+{
+ tu_varclr(&_usbd_dev);
+
+ memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping
+ memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping
+
+ usbd_control_reset();
+
+ for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
+ {
+ get_driver(i)->reset(rhport);
+ }
+}
+
+bool tud_task_event_ready(void)
+{
+ // Skip if stack is not initialized
+ if ( !tusb_inited() ) return false;
+
+ return !osal_queue_empty(_usbd_q);
+}
+
+/* USB Device Driver task
+ * This top level thread manages all device controller event and delegates events to class-specific drivers.
+ * This should be called periodically within the mainloop or rtos thread.
+ *
+ @code
+ int main(void)
+ {
+ application_init();
+ tusb_init();
+
+ while(1) // the mainloop
+ {
+ application_code();
+ tud_task(); // tinyusb device task
+ }
+ }
+ @endcode
+ */
+void tud_task (void)
+{
+ // Skip if stack is not initialized
+ if ( !tusb_inited() ) return;
+
+ // Loop until there is no more events in the queue
+ while (1)
+ {
+ dcd_event_t event;
+
+ if ( !osal_queue_receive(_usbd_q, &event) ) return;
+
+#if CFG_TUSB_DEBUG >= 2
+ if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG2("\r\n"); // extra line for setup
+ TU_LOG2("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
+#endif
+
+ switch ( event.event_id )
+ {
+ case DCD_EVENT_BUS_RESET:
+ TU_LOG2("\r\n");
+ usbd_reset(event.rhport);
+ _usbd_dev.speed = event.bus_reset.speed;
+ break;
+
+ case DCD_EVENT_UNPLUGGED:
+ TU_LOG2("\r\n");
+ usbd_reset(event.rhport);
+
+ // invoke callback
+ if (tud_umount_cb) tud_umount_cb();
+ break;
+
+ case DCD_EVENT_SETUP_RECEIVED:
+ TU_LOG2_VAR(&event.setup_received);
+ TU_LOG2("\r\n");
+
+ // Mark as connected after receiving 1st setup packet.
+ // But it is easier to set it every time instead of wasting time to check then set
+ _usbd_dev.connected = 1;
+
+ // mark both in & out control as free
+ _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = false;
+ _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0;
+ _usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = false;
+ _usbd_dev.ep_status[0][TUSB_DIR_IN ].claimed = 0;
+
+ // Process control request
+ if ( !process_control_request(event.rhport, &event.setup_received) )
+ {
+ TU_LOG2(" Stall EP0\r\n");
+ // Failed -> stall both control endpoint IN and OUT
+ dcd_edpt_stall(event.rhport, 0);
+ dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK);
+ }
+ break;
+
+ case DCD_EVENT_XFER_COMPLETE:
+ {
+ // Invoke the class callback associated with the endpoint address
+ uint8_t const ep_addr = event.xfer_complete.ep_addr;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+
+ TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
+
+ _usbd_dev.ep_status[epnum][ep_dir].busy = false;
+ _usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
+
+ if ( 0 == epnum )
+ {
+ usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
+ }
+ else
+ {
+ usbd_class_driver_t const * driver = get_driver( _usbd_dev.ep2drv[epnum][ep_dir] );
+ TU_ASSERT(driver, );
+
+ TU_LOG2(" %s xfer callback\r\n", driver->name);
+ driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
+ }
+ }
+ break;
+
+ case DCD_EVENT_SUSPEND:
+ TU_LOG2("\r\n");
+ if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
+ break;
+
+ case DCD_EVENT_RESUME:
+ TU_LOG2("\r\n");
+ if (tud_resume_cb) tud_resume_cb();
+ break;
+
+ case DCD_EVENT_SOF:
+ TU_LOG2("\r\n");
+ for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
+ {
+ usbd_class_driver_t const * driver = get_driver(i);
+ if ( driver->sof ) driver->sof(event.rhport);
+ }
+ break;
+
+ case USBD_EVENT_FUNC_CALL:
+ TU_LOG2("\r\n");
+ if ( event.func_call.func ) event.func_call.func(event.func_call.param);
+ break;
+
+ default:
+ TU_BREAKPOINT();
+ break;
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Control Request Parser & Handling
+//--------------------------------------------------------------------+
+
+// Helper to invoke class driver control request handler
+static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request)
+{
+ usbd_control_set_complete_callback(driver->control_xfer_cb);
+ TU_LOG2(" %s control request\r\n", driver->name);
+ return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request);
+}
+
+// This handles the actual request and its response.
+// return false will cause its caller to stall control endpoint
+static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ usbd_control_set_complete_callback(NULL);
+
+ TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID);
+
+ // Vendor request
+ if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR )
+ {
+ TU_VERIFY(tud_vendor_control_xfer_cb);
+
+ usbd_control_set_complete_callback(tud_vendor_control_xfer_cb);
+ return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request);
+ }
+
+#if CFG_TUSB_DEBUG >= 2
+ if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME)
+ {
+ TU_LOG2(" %s", _tusb_std_request_str[p_request->bRequest]);
+ if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG2("\r\n");
+ }
+#endif
+
+ switch ( p_request->bmRequestType_bit.recipient )
+ {
+ //------------- Device Requests e.g in enumeration -------------//
+ case TUSB_REQ_RCPT_DEVICE:
+ if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type )
+ {
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv));
+
+ usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]);
+ TU_VERIFY(driver);
+
+ // forward to class driver: "non-STD request to Interface"
+ return invoke_class_control(rhport, driver, p_request);
+ }
+
+ if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type )
+ {
+ // Non standard request is not supported
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ switch ( p_request->bRequest )
+ {
+ case TUSB_REQ_SET_ADDRESS:
+ // Depending on mcu, status phase could be sent either before or after changing device address,
+ // or even require stack to not response with status at all
+ // Therefore DCD must take full responsibility to response and include zlp status packet if needed.
+ usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API
+ dcd_set_address(rhport, (uint8_t) p_request->wValue);
+ // skip tud_control_status()
+ _usbd_dev.addressed = 1;
+ break;
+
+ case TUSB_REQ_GET_CONFIGURATION:
+ {
+ uint8_t cfg_num = _usbd_dev.cfg_num;
+ tud_control_xfer(rhport, p_request, &cfg_num, 1);
+ }
+ break;
+
+ case TUSB_REQ_SET_CONFIGURATION:
+ {
+ uint8_t const cfg_num = (uint8_t) p_request->wValue;
+
+ if ( !_usbd_dev.cfg_num && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) );
+ _usbd_dev.cfg_num = cfg_num;
+
+ tud_control_status(rhport, p_request);
+ }
+ break;
+
+ case TUSB_REQ_GET_DESCRIPTOR:
+ TU_VERIFY( process_get_descriptor(rhport, p_request) );
+ break;
+
+ case TUSB_REQ_SET_FEATURE:
+ // Only support remote wakeup for device feature
+ TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+
+ // Host may enable remote wake up before suspending especially HID device
+ _usbd_dev.remote_wakeup_en = true;
+ tud_control_status(rhport, p_request);
+ break;
+
+ case TUSB_REQ_CLEAR_FEATURE:
+ // Only support remote wakeup for device feature
+ TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+
+ // Host may disable remote wake up after resuming
+ _usbd_dev.remote_wakeup_en = false;
+ tud_control_status(rhport, p_request);
+ break;
+
+ case TUSB_REQ_GET_STATUS:
+ {
+ // Device status bit mask
+ // - Bit 0: Self Powered
+ // - Bit 1: Remote Wakeup enabled
+ uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0);
+ tud_control_xfer(rhport, p_request, &status, 2);
+ }
+ break;
+
+ // Unknown/Unsupported request
+ default: TU_BREAKPOINT(); return false;
+ }
+ break;
+
+ //------------- Class/Interface Specific Request -------------//
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv));
+
+ usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]);
+ TU_VERIFY(driver);
+
+ // all requests to Interface (STD or Class) is forwarded to class driver.
+ // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE
+ if ( !invoke_class_control(rhport, driver, p_request) )
+ {
+ // For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class
+ // driver doesn't use alternate settings or implement this
+ TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type);
+
+ if (TUSB_REQ_GET_INTERFACE == p_request->bRequest)
+ {
+ uint8_t alternate = 0;
+ tud_control_xfer(rhport, p_request, &alternate, 1);
+ }else if (TUSB_REQ_SET_INTERFACE == p_request->bRequest)
+ {
+ tud_control_status(rhport, p_request);
+ } else
+ {
+ return false;
+ }
+ }
+ }
+ break;
+
+ //------------- Endpoint Request -------------//
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t const ep_addr = tu_u16_low(p_request->wIndex);
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+
+ TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) );
+
+ usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]);
+
+ if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type )
+ {
+ // Forward class request to its driver
+ TU_VERIFY(driver);
+ return invoke_class_control(rhport, driver, p_request);
+ }
+ else
+ {
+ // Handle STD request to endpoint
+ switch ( p_request->bRequest )
+ {
+ case TUSB_REQ_GET_STATUS:
+ {
+ uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000;
+ tud_control_xfer(rhport, p_request, &status, 2);
+ }
+ break;
+
+ case TUSB_REQ_CLEAR_FEATURE:
+ case TUSB_REQ_SET_FEATURE:
+ {
+ if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue )
+ {
+ if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest )
+ {
+ usbd_edpt_clear_stall(rhport, ep_addr);
+ }else
+ {
+ usbd_edpt_stall(rhport, ep_addr);
+ }
+ }
+
+ if (driver)
+ {
+ // Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request
+ // We will also forward std request targeted endpoint to class drivers as well
+
+ // STD request must always be ACKed regardless of driver returned value
+ // Also clear complete callback if driver set since it can also stall the request.
+ (void) invoke_class_control(rhport, driver, p_request);
+ usbd_control_set_complete_callback(NULL);
+
+ // skip ZLP status if driver already did that
+ if ( !_usbd_dev.ep_status[0][TUSB_DIR_IN].busy ) tud_control_status(rhport, p_request);
+ }
+ }
+ break;
+
+ // Unknown/Unsupported request
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+ }
+ break;
+
+ // Unknown recipient
+ default: TU_BREAKPOINT(); return false;
+ }
+
+ return true;
+}
+
+// Process Set Configure Request
+// This function parse configuration descriptor & open drivers accordingly
+static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
+{
+ tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); // index is cfg_num-1
+ TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION);
+
+ // Parse configuration descriptor
+ _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0;
+ _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0;
+
+ // Parse interface descriptor
+ uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t);
+ uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + desc_cfg->wTotalLength;
+
+ while( p_desc < desc_end )
+ {
+ tusb_desc_interface_assoc_t const * desc_itf_assoc = NULL;
+
+ // Class will always starts with Interface Association (if any) and then Interface descriptor
+ if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
+ {
+ desc_itf_assoc = (tusb_desc_interface_assoc_t const *) p_desc;
+ p_desc = tu_desc_next(p_desc); // next to Interface
+ }
+
+ TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
+
+ tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc;
+ uint16_t const remaining_len = desc_end-p_desc;
+
+ uint8_t drv_id;
+ for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++)
+ {
+ usbd_class_driver_t const *driver = get_driver(drv_id);
+ uint16_t const drv_len = driver->open(rhport, desc_itf, remaining_len);
+
+ if ( drv_len > 0 )
+ {
+ // Open successfully, check if length is correct
+ TU_ASSERT( sizeof(tusb_desc_interface_t) <= drv_len && drv_len <= remaining_len);
+
+ // Interface number must not be used already
+ TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]);
+
+ TU_LOG2(" %s opened\r\n", driver->name);
+ _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id;
+
+ // If IAD exist, assign all interfaces to the same driver
+ if (desc_itf_assoc)
+ {
+ // IAD's first interface number and class should match with opened interface
+ TU_ASSERT(desc_itf_assoc->bFirstInterface == desc_itf->bInterfaceNumber &&
+ desc_itf_assoc->bFunctionClass == desc_itf->bInterfaceClass);
+
+ for(uint8_t i=1; i<desc_itf_assoc->bInterfaceCount; i++)
+ {
+ _usbd_dev.itf2drv[desc_itf->bInterfaceNumber+i] = drv_id;
+ }
+ }
+
+ mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, drv_len, drv_id); // TODO refactor
+
+ p_desc += drv_len; // next interface
+
+ break;
+ }
+ }
+
+ // Failed if cannot find supported driver
+ TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT);
+ }
+
+ // invoke callback
+ if (tud_mount_cb) tud_mount_cb();
+
+ return true;
+}
+
+// Helper marking endpoint of interface belongs to class driver
+static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
+{
+ uint16_t len = 0;
+
+ while( len < desc_len )
+ {
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+ {
+ uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress;
+
+ ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
+ }
+
+ len = (uint16_t)(len + tu_desc_len(p_desc));
+ p_desc = tu_desc_next(p_desc);
+ }
+}
+
+// return descriptor's buffer and update desc_len
+static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
+ uint8_t const desc_index = tu_u16_low( p_request->wValue );
+
+ switch(desc_type)
+ {
+ case TUSB_DESC_DEVICE:
+ {
+ TU_LOG2(" Device\r\n");
+
+ uint16_t len = sizeof(tusb_desc_device_t);
+
+ // Only send up to EP0 Packet Size if not addressed
+ // This only happens with the very first get device descriptor and EP0 size = 8 or 16.
+ if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed)
+ {
+ len = CFG_TUD_ENDPOINT0_SIZE;
+
+ // Hack here: we modify the request length to prevent usbd_control response with zlp
+ ((tusb_control_request_t*) p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE;
+ }
+
+ return tud_control_xfer(rhport, p_request, (void*) tud_descriptor_device_cb(), len);
+ }
+ break;
+
+ case TUSB_DESC_BOS:
+ {
+ TU_LOG2(" BOS\r\n");
+
+ // requested by host if USB > 2.0 ( i.e 2.1 or 3.x )
+ if (!tud_descriptor_bos_cb) return false;
+
+ tusb_desc_bos_t const* desc_bos = (tusb_desc_bos_t const*) tud_descriptor_bos_cb();
+
+ uint16_t total_len;
+ // Use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&total_len, (uint8_t*) desc_bos + offsetof(tusb_desc_bos_t, wTotalLength), 2);
+
+ return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len);
+ }
+ break;
+
+ case TUSB_DESC_CONFIGURATION:
+ {
+ TU_LOG2(" Configuration[%u]\r\n", desc_index);
+
+ tusb_desc_configuration_t const* desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index);
+ TU_ASSERT(desc_config);
+
+ uint16_t total_len;
+ // Use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&total_len, (uint8_t*) desc_config + offsetof(tusb_desc_configuration_t, wTotalLength), 2);
+
+ return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len);
+ }
+ break;
+
+ case TUSB_DESC_STRING:
+ {
+ TU_LOG2(" String[%u]\r\n", desc_index);
+
+ // String Descriptor always uses the desc set from user
+ uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex);
+ TU_VERIFY(desc_str);
+
+ // first byte of descriptor is its size
+ return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]);
+ }
+ break;
+
+ case TUSB_DESC_DEVICE_QUALIFIER:
+ TU_LOG2(" Device Qualifier\r\n");
+
+ // Host sends this request to ask why our device with USB BCD from 2.0
+ // but is running at Full/Low Speed. If not highspeed capable stall this request,
+ // otherwise return the descriptor that could work in highspeed mode
+ if ( tud_descriptor_device_qualifier_cb )
+ {
+ uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb();
+ TU_ASSERT(desc_qualifier);
+
+ // first byte of descriptor is its size
+ return tud_control_xfer(rhport, p_request, (void*) desc_qualifier, desc_qualifier[0]);
+ }else
+ {
+ return false;
+ }
+ break;
+
+ case TUSB_DESC_OTHER_SPEED_CONFIG:
+ TU_LOG2(" Other Speed Configuration\r\n");
+
+ // After Device Qualifier descriptor is received host will ask for this descriptor
+ return false; // not supported
+ break;
+
+ default: return false;
+ }
+}
+
+//--------------------------------------------------------------------+
+// DCD Event Handler
+//--------------------------------------------------------------------+
+void dcd_event_handler(dcd_event_t const * event, bool in_isr)
+{
+ switch (event->event_id)
+ {
+ case DCD_EVENT_UNPLUGGED:
+ // UNPLUGGED event can be bouncing, only processing if we are currently connected
+ if ( _usbd_dev.connected )
+ {
+ _usbd_dev.connected = 0;
+ _usbd_dev.addressed = 0;
+ _usbd_dev.cfg_num = 0;
+ _usbd_dev.suspended = 0;
+ osal_queue_send(_usbd_q, event, in_isr);
+ }
+ break;
+
+ case DCD_EVENT_SOF:
+ return; // skip SOF event for now
+ break;
+
+ case DCD_EVENT_SUSPEND:
+ // NOTE: When plugging/unplugging device, the D+/D- state are unstable and
+ // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ).
+ // In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish
+ // suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected
+ if ( _usbd_dev.connected )
+ {
+ _usbd_dev.suspended = 1;
+ osal_queue_send(_usbd_q, event, in_isr);
+ }
+ break;
+
+ case DCD_EVENT_RESUME:
+ // skip event if not connected (especially required for SAMD)
+ if ( _usbd_dev.connected )
+ {
+ _usbd_dev.suspended = 0;
+ osal_queue_send(_usbd_q, event, in_isr);
+ }
+ break;
+
+ default:
+ osal_queue_send(_usbd_q, event, in_isr);
+ break;
+ }
+}
+
+void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
+{
+ dcd_event_t event = { .rhport = rhport, .event_id = eid };
+ dcd_event_handler(&event, in_isr);
+}
+
+void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
+{
+ dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
+ event.bus_reset.speed = speed;
+ dcd_event_handler(&event, in_isr);
+}
+
+void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
+{
+ dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
+ memcpy(&event.setup_received, setup, 8);
+
+ dcd_event_handler(&event, in_isr);
+}
+
+void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
+{
+ dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
+
+ event.xfer_complete.ep_addr = ep_addr;
+ event.xfer_complete.len = xferred_bytes;
+ event.xfer_complete.result = result;
+
+ dcd_event_handler(&event, in_isr);
+}
+
+//--------------------------------------------------------------------+
+// Helper
+//--------------------------------------------------------------------+
+
+// Parse consecutive endpoint descriptors (IN & OUT)
+bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
+{
+ for(int i=0; i<ep_count; i++)
+ {
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && xfer_type == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
+
+ if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
+ {
+ (*ep_in) = desc_ep->bEndpointAddress;
+ }else
+ {
+ (*ep_out) = desc_ep->bEndpointAddress;
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ return true;
+}
+
+// Helper to defer an isr function
+void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr)
+{
+ dcd_event_t event =
+ {
+ .rhport = 0,
+ .event_id = USBD_EVENT_FUNC_CALL,
+ };
+
+ event.func_call.func = func;
+ event.func_call.param = param;
+
+ dcd_event_handler(&event, in_isr);
+}
+
+//--------------------------------------------------------------------+
+// USBD Endpoint API
+//--------------------------------------------------------------------+
+
+bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
+{
+ TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, desc_ep->wMaxPacketSize.size);
+
+ switch (desc_ep->bmAttributes.xfer)
+ {
+ case TUSB_XFER_ISOCHRONOUS:
+ {
+ uint16_t const max_epsize = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 1023);
+ TU_ASSERT(desc_ep->wMaxPacketSize.size <= max_epsize);
+ }
+ break;
+
+ case TUSB_XFER_BULK:
+ if (_usbd_dev.speed == TUSB_SPEED_HIGH)
+ {
+ // Bulk highspeed must be EXACTLY 512
+ TU_ASSERT(desc_ep->wMaxPacketSize.size == 512);
+ }else
+ {
+ // TODO Bulk fullspeed can only be 8, 16, 32, 64
+ TU_ASSERT(desc_ep->wMaxPacketSize.size <= 64);
+ }
+ break;
+
+ case TUSB_XFER_INTERRUPT:
+ {
+ uint16_t const max_epsize = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 64);
+ TU_ASSERT(desc_ep->wMaxPacketSize.size <= max_epsize);
+ }
+ break;
+
+ default: return false;
+ }
+
+ return dcd_edpt_open(rhport, desc_ep);
+}
+
+bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ // pre-check to help reducing mutex lock
+ TU_VERIFY((_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0));
+
+ osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+#endif
+
+ // can only claim the endpoint if it is not busy and not claimed yet.
+ bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0);
+ if (ret)
+ {
+ _usbd_dev.ep_status[epnum][dir].claimed = 1;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_unlock(_usbd_mutex);
+#endif
+
+ return ret;
+}
+
+bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+#endif
+
+ // can only release the endpoint if it is claimed and not busy
+ bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 1);
+ if (ret)
+ {
+ _usbd_dev.ep_status[epnum][dir].claimed = 0;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_unlock(_usbd_mutex);
+#endif
+
+ return ret;
+}
+
+bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
+
+ // Attempt to transfer on a busy endpoint, sound like an race condition !
+ TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
+
+ // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
+ // and usbd task can preempt and clear the busy
+ _usbd_dev.ep_status[epnum][dir].busy = true;
+
+ if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) )
+ {
+ TU_LOG2("OK\r\n");
+ return true;
+ }else
+ {
+ // DCD error, mark endpoint as ready to allow next transfer
+ _usbd_dev.ep_status[epnum][dir].busy = false;
+ _usbd_dev.ep_status[epnum][dir].claimed = 0;
+ TU_LOG2("failed\r\n");
+ TU_BREAKPOINT();
+ return false;
+ }
+}
+
+bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ return _usbd_dev.ep_status[epnum][dir].busy;
+}
+
+void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ dcd_edpt_stall(rhport, ep_addr);
+ _usbd_dev.ep_status[epnum][dir].stalled = true;
+ _usbd_dev.ep_status[epnum][dir].busy = true;
+}
+
+void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ dcd_edpt_clear_stall(rhport, ep_addr);
+ _usbd_dev.ep_status[epnum][dir].stalled = false;
+ _usbd_dev.ep_status[epnum][dir].busy = false;
+}
+
+bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ return _usbd_dev.ep_status[epnum][dir].stalled;
+}
+
+/**
+ * usbd_edpt_close will disable an endpoint.
+ *
+ * In progress transfers on this EP may be delivered after this call.
+ *
+ */
+void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+ TU_ASSERT(dcd_edpt_close, /**/);
+ TU_LOG2(" CLOSING Endpoint: 0x%02X\r\n", ep_addr);
+
+ dcd_edpt_close(rhport, ep_addr);
+
+ return;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/device/usbd.h b/sw/Core/Src/tinyusb/src/device/usbd.h
new file mode 100755
index 0000000..56615b0
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/device/usbd.h
@@ -0,0 +1,679 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_usbd
+ * @{ */
+
+#ifndef _TUSB_USBD_H_
+#define _TUSB_USBD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "common/tusb_common.h"
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Init device stack
+bool tud_init (void);
+
+// Task function should be called in main/rtos loop
+void tud_task (void);
+
+// Check if there is pending events need proccessing by tud_task()
+bool tud_task_event_ready(void);
+
+// Interrupt handler, name alias to DCD
+extern void dcd_int_handler(uint8_t rhport);
+#define tud_int_handler dcd_int_handler
+
+// Get current bus speed
+tusb_speed_t tud_speed_get(void);
+
+// Check if device is connected (may not mounted/configured yet)
+// True if just got out of Bus Reset and received the very first data from host
+bool tud_connected(void);
+
+// Check if device is connected and configured
+bool tud_mounted(void);
+
+// Check if device is suspended
+bool tud_suspended(void);
+
+// Check if device is ready to transfer
+static inline bool tud_ready(void)
+{
+ return tud_mounted() && !tud_suspended();
+}
+
+// Remote wake up host, only if suspended and enabled by host
+bool tud_remote_wakeup(void);
+
+// Enable pull-up resistor on D+ D-
+// Return false on unsupported MCUs
+bool tud_disconnect(void);
+
+// Disable pull-up resistor on D+ D-
+// Return false on unsupported MCUs
+bool tud_connect(void);
+
+// Carry out Data and Status stage of control transfer
+// - If len = 0, it is equivalent to sending status only
+// - If len > wLength : it will be truncated
+bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len);
+
+// Send STATUS (zero length) packet
+bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request);
+
+//--------------------------------------------------------------------+
+// Application Callbacks (WEAK is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received GET DEVICE DESCRIPTOR request
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void);
+
+// Invoked when received GET BOS DESCRIPTOR request
+// Application return pointer to descriptor
+TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void);
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index);
+
+// 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);
+
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void);
+
+// Invoked when device is mounted (configured)
+TU_ATTR_WEAK void tud_mount_cb(void);
+
+// Invoked when device is unmounted
+TU_ATTR_WEAK void tud_umount_cb(void);
+
+// Invoked when usb bus is suspended
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
+
+// Invoked when usb bus is resumed
+TU_ATTR_WEAK void tud_resume_cb(void);
+
+// Invoked when received control request with VENDOR TYPE
+TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+
+//--------------------------------------------------------------------+
+// Binary Device Object Store (BOS) Descriptor Templates
+//--------------------------------------------------------------------+
+
+#define TUD_BOS_DESC_LEN 5
+
+// total length, number of device caps
+#define TUD_BOS_DESCRIPTOR(_total_len, _caps_num) \
+ 5, TUSB_DESC_BOS, U16_TO_U8S_LE(_total_len), _caps_num
+
+// Device Capability Platform 128-bit UUID + Data
+#define TUD_BOS_PLATFORM_DESCRIPTOR(...) \
+ 4+TU_ARGS_NUM(__VA_ARGS__), TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_PLATFORM, 0x00, __VA_ARGS__
+
+//------------- WebUSB BOS Platform -------------//
+
+// Descriptor Length
+#define TUD_BOS_WEBUSB_DESC_LEN 24
+
+// Vendor Code, iLandingPage
+#define TUD_BOS_WEBUSB_DESCRIPTOR(_vendor_code, _ipage) \
+ TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_WEBUSB_UUID, U16_TO_U8S_LE(0x0100), _vendor_code, _ipage)
+
+#define TUD_BOS_WEBUSB_UUID \
+ 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \
+ 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65
+
+//------------- Microsoft OS 2.0 Platform -------------//
+#define TUD_BOS_MICROSOFT_OS_DESC_LEN 28
+
+// Total Length of descriptor set, vendor code
+#define TUD_BOS_MS_OS_20_DESCRIPTOR(_desc_set_len, _vendor_code) \
+ TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0)
+
+#define TUD_BOS_MS_OS_20_UUID \
+ 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \
+ 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F
+
+//--------------------------------------------------------------------+
+// Configuration & Interface Descriptor Templates
+//--------------------------------------------------------------------+
+
+//------------- Configuration -------------//
+#define TUD_CONFIG_DESC_LEN (9)
+
+// Config number, interface count, string index, total length, attribute, power in mA
+#define TUD_CONFIG_DESCRIPTOR(config_num, _itfcount, _stridx, _total_len, _attribute, _power_ma) \
+ 9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(_total_len), _itfcount, config_num, _stridx, TU_BIT(7) | _attribute, (_power_ma)/2
+
+//------------- CDC -------------//
+
+// Length of template descriptor: 66 bytes
+#define TUD_CDC_DESC_LEN (8+9+5+5+4+5+7+9+7+7)
+
+// CDC Descriptor Template
+// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+#define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \
+ /* Interface Associate */\
+ 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, 0,\
+ /* CDC Control Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, _stridx,\
+ /* CDC Header */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
+ /* CDC Call */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
+ /* CDC ACM: support line request */\
+ 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\
+ /* CDC Union */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
+ /* Endpoint Notification */\
+ 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 16,\
+ /* CDC Data Interface */\
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
+ /* Endpoint Out */\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
+
+//------------- MSC -------------//
+
+// Length of template descriptor: 23 bytes
+#define TUD_MSC_DESC_LEN (9 + 7 + 7)
+
+// Interface number, string index, EP Out & EP In address, EP size
+#define TUD_MSC_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
+ /* Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_MSC, MSC_SUBCLASS_SCSI, MSC_PROTOCOL_BOT, _stridx,\
+ /* Endpoint Out */\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
+
+//------------- HID -------------//
+
+// Length of template descriptor: 25 bytes
+#define TUD_HID_DESC_LEN (9 + 9 + 7)
+
+// HID Input only descriptor
+// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+#define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \
+ /* Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\
+ /* HID descriptor */\
+ 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
+
+// Length of template descriptor: 32 bytes
+#define TUD_HID_INOUT_DESC_LEN (9 + 9 + 7 + 7)
+
+// HID Input & Output descriptor
+// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
+#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epout, _epin, _epsize, _ep_interval) \
+ /* Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\
+ /* HID descriptor */\
+ 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\
+ /* Endpoint Out */\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval, \
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
+
+//------------- MIDI -------------//
+// MIDI v1.0 is based on Audio v1.0
+
+#define TUD_MIDI_DESC_HEAD_LEN (9 + 9 + 9 + 7)
+#define TUD_MIDI_DESC_HEAD(_itfnum, _stridx, _numcables) \
+ /* Audio Control (AC) Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\
+ /* AC Header */\
+ 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum) + 1),\
+ /* MIDI Streaming (MS) Interface */\
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\
+ /* MS Header */\
+ 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN)
+
+#define TUD_MIDI_JACKID_IN_EMB(_cablenum) \
+ (uint8_t)(((_cablenum) - 1) * 4 + 1)
+
+#define TUD_MIDI_JACKID_IN_EXT(_cablenum) \
+ (uint8_t)(((_cablenum) - 1) * 4 + 2)
+
+#define TUD_MIDI_JACKID_OUT_EMB(_cablenum) \
+ (uint8_t)(((_cablenum) - 1) * 4 + 3)
+
+#define TUD_MIDI_JACKID_OUT_EXT(_cablenum) \
+ (uint8_t)(((_cablenum) - 1) * 4 + 4)
+
+#define TUD_MIDI_DESC_JACK_LEN (6 + 6 + 9 + 9)
+#define TUD_MIDI_DESC_JACK(_cablenum) \
+ /* MS In Jack (Embedded) */\
+ 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_IN_EMB(_cablenum), 0,\
+ /* MS In Jack (External) */\
+ 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_IN_EXT(_cablenum), 0,\
+ /* MS Out Jack (Embedded), connected to In Jack External */\
+ 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_OUT_EMB(_cablenum), 1, TUD_MIDI_JACKID_IN_EXT(_cablenum), 1, 0,\
+ /* MS Out Jack (External), connected to In Jack Embedded */\
+ 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, 0
+
+#define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables))
+#define TUD_MIDI_DESC_EP(_epout, _epsize, _numcables) \
+ /* Endpoint: Note Audio v1.0's endpoint has 9 bytes instead of 7 */\
+ 9, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, 0, 0, \
+ /* MS Endpoint (connected to embedded jack) */\
+ (uint8_t)(4 + (_numcables)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, _numcables
+
+// Length of template descriptor (88 bytes)
+#define TUD_MIDI_DESC_LEN (TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2)
+
+// MIDI simple descriptor
+// - 1 Embedded Jack In connected to 1 External Jack Out
+// - 1 Embedded Jack out connected to 1 External Jack In
+#define TUD_MIDI_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
+ TUD_MIDI_DESC_HEAD(_itfnum, _stridx, 1),\
+ TUD_MIDI_DESC_JACK(1),\
+ TUD_MIDI_DESC_EP(_epout, _epsize, 1),\
+ TUD_MIDI_JACKID_IN_EMB(1),\
+ TUD_MIDI_DESC_EP(_epin, _epsize, 1),\
+ TUD_MIDI_JACKID_OUT_EMB(1)
+
+//------------- AUDIO -------------//
+
+/* Standard Interface Association Descriptor (IAD) */
+#define TUD_AUDIO_DESC_IAD_LEN 8
+#define TUD_AUDIO_DESC_IAD(_firstitfs, _nitfs, _stridx) \
+ TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitfs, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx
+
+/* Standard AC Interface Descriptor(4.7.1) */
+#define TUD_AUDIO_DESC_STD_AC_LEN 9
+#define TUD_AUDIO_DESC_STD_AC(_itfnum, _nEPs, _stridx) /* _nEPs is 0 or 1 */\
+ TUD_AUDIO_DESC_STD_AC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_INT_PROTOCOL_CODE_V2, _stridx
+
+/* Class-Specific AC Interface Header Descriptor(4.7.2) */
+#define TUD_AUDIO_DESC_CS_AC_LEN 9
+#define TUD_AUDIO_DESC_CS_AC(_bcdADC, _category, _totallen, _ctrl) /* _bcdADC : Audio Device Class Specification Release Number in Binary-Coded Decimal, _category : see audio_function_t, _totallen : Total number of bytes returned for the class-specific AudioControl interface i.e. Clock Source, Unit and Terminal descriptors - Do not include TUD_AUDIO_DESC_CS_AC_LEN, we already do this here*/ \
+ TUD_AUDIO_DESC_CS_AC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), _category, U16_TO_U8S_LE(_totallen + TUD_AUDIO_DESC_CS_AC_LEN), _ctrl
+
+/* Clock Source Descriptor(4.7.2.1) */
+#define TUD_AUDIO_DESC_CLK_SRC_LEN 8
+#define TUD_AUDIO_DESC_CLK_SRC(_clkid, _attr, _ctrl, _assocTerm, _stridx) \
+ TUD_AUDIO_DESC_CLK_SRC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE, _clkid, _attr, _ctrl, _assocTerm, _stridx
+
+/* Input Terminal Descriptor(4.7.2.4) */
+#define TUD_AUDIO_DESC_INPUT_TERM_LEN 17
+#define TUD_AUDIO_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _clkid, _nchannelslogical, _channelcfg, _idxchannelnames, _ctrl, _stridx) \
+ TUD_AUDIO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _clkid, _nchannelslogical, U32_TO_U8S_LE(_channelcfg), _idxchannelnames, U16_TO_U8S_LE(_ctrl), _stridx
+
+/* Output Terminal Descriptor(4.7.2.5) */
+#define TUD_AUDIO_DESC_OUTPUT_TERM_LEN 12
+#define TUD_AUDIO_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _clkid, _ctrl, _stridx) \
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _srcid, _clkid, U16_TO_U8S_LE(_ctrl), _stridx
+
+/* Feature Unit Descriptor(4.7.2.8) */
+// 1 - Channel
+#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN 6+(1+1)*4
+#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _stridx) \
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), _stridx
+
+// 2 - Channels
+#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN (6+(2+1)*4)
+#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) \
+ TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), _stridx
+
+// For more channels, add definitions here
+
+/* Standard AS Interface Descriptor(4.9.1) */
+#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9
+#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
+ TUD_AUDIO_DESC_STD_AS_INT_LEN, TUSB_DESC_INTERFACE, _itfnum, _altset, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_INT_PROTOCOL_CODE_V2, _stridx
+
+/* Class-Specific AS Interface Descriptor(4.9.2) */
+#define TUD_AUDIO_DESC_CS_AS_INT_LEN 16
+#define TUD_AUDIO_DESC_CS_AS_INT(_termid, _ctrl, _formattype, _formats, _nchannelsphysical, _channelcfg, _stridx) \
+ TUD_AUDIO_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_AS_GENERAL, _termid, _ctrl, _formattype, U32_TO_U8S_LE(_formats), _nchannelsphysical, U32_TO_U8S_LE(_channelcfg), _stridx
+
+/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */
+#define TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN 6
+#define TUD_AUDIO_DESC_TYPE_I_FORMAT(_subslotsize, _bitresolution) /* _subslotsize is number of bytes per sample (i.e. subslot) and can be 1,2,3, or 4 */\
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO_FORMAT_TYPE_I, _subslotsize, _bitresolution
+
+/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */
+#define TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN 7
+#define TUD_AUDIO_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval) \
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN, TUSB_DESC_ENDPOINT, _ep, _attr, U16_TO_U8S_LE(_maxEPsize), _interval
+
+/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */
+#define TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN 8
+#define TUD_AUDIO_DESC_CS_AS_ISO_EP(_attr, _ctrl, _lockdelayunit, _lockdelay) \
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO_CS_EP_SUBTYPE_GENERAL, _attr, _ctrl, _lockdelayunit, U16_TO_U8S_LE(_lockdelay)
+
+/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */
+#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7
+#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_NO_SYNC | TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval
+
+// AUDIO simple descriptor (UAC2) for 1 microphone input
+// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
+
+#define TUD_AUDIO_MIC_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\
+ + 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)
+
+#define TUD_AUDIO_MIC_DESC_N_AS_INT 1 // Number of AS interfaces
+
+#define TUD_AUDIO_MIC_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
+ /* Standard Interface Association Descriptor (IAD) */\
+ TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ /* Standard AC Interface Descriptor(4.7.1) */\
+ TUD_AUDIO_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),\
+ /* Clock Source Descriptor(4.7.2.1) */\
+ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_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_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_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_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ /* Feature Unit Descriptor(4.7.2.8) */\
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_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, /*_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)((_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),\
+ /* Class-Specific AS Interface Descriptor(4.9.2) */\
+ TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_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),\
+ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
+ TUD_AUDIO_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*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 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)
+
+// AUDIO simple descriptor (UAC2) for mono speaker
+// - 1 Input Terminal, 2 Feature Unit (Mute and Volume Control), 3 Output Terminal, 4 Clock Source
+
+#define TUD_AUDIO_SPEAKER_MONO_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_ONE_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_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize, _epfb) \
+ /* Standard Interface Association Descriptor (IAD) */\
+ TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ /* Standard AC Interface Descriptor(4.7.1) */\
+ TUD_AUDIO_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_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_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_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_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*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_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),\
+ /* Feature Unit Descriptor(4.7.2.8) */\
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_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)((_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),\
+ /* 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*/ 0x01, /*_channelcfg*/ AUDIO_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),\
+ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 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),\
+ /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\
+
+//------------- TUD_USBTMC/USB488 -------------//
+#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
+#define TUD_USBTMC_APP_SUBCLASS 0x03u
+
+#define TUD_USBTMC_PROTOCOL_STD 0x00u
+#define TUD_USBTMC_PROTOCOL_USB488 0x01u
+
+// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID,
+// bulk-in endpoint ID
+#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \
+ /* Interface */ \
+ 0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx
+
+#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u
+
+#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \
+ /* Endpoint Out */ \
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \
+ /* Endpoint In */ \
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u
+
+#define TUD_USBTMC_BULK_DESCRIPTORS_LEN (7u+7u)
+
+/* optional interrupt endpoint */ \
+// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number?
+#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \
+ 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), 0x16
+
+#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u)
+
+
+//------------- Vendor -------------//
+#define TUD_VENDOR_DESC_LEN (9+7+7)
+
+// Interface number, string index, EP Out & IN address, EP size
+#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
+ /* Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\
+ /* Endpoint Out */\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
+
+//------------- DFU Runtime -------------//
+#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
+#define TUD_DFU_APP_SUBCLASS 0x01u
+
+// Length of template descriptr: 18 bytes
+#define TUD_DFU_RT_DESC_LEN (9 + 9)
+
+// DFU runtime descriptor
+// Interface number, string index, attributes, detach timeout, transfer size
+#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \
+ /* Interface */ \
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \
+ /* Function */ \
+ 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101)
+
+
+//------------- CDC-ECM -------------//
+
+// Length of template descriptor: 71 bytes
+#define TUD_CDC_ECM_DESC_LEN (8+9+5+5+13+7+9+9+7+7)
+
+// CDC-ECM Descriptor Template
+// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
+#define TUD_CDC_ECM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \
+ /* Interface Association */\
+ 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, 0,\
+ /* CDC Control Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, _desc_stridx,\
+ /* CDC-ECM Header */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
+ /* CDC-ECM Union */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
+ /* CDC-ECM Functional Descriptor */\
+ 13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0,\
+ /* Endpoint Notification */\
+ 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\
+ /* CDC Data Interface (default inactive) */\
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
+ /* CDC Data Interface (alternative active) */\
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
+ /* Endpoint Out */\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
+
+
+//------------- RNDIS -------------//
+
+#if 0
+/* Windows XP */
+#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC
+#define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
+#define TUD_RNDIS_ITF_PROTOCOL 0xFF /* CDC_COMM_PROTOCOL_MICROSOFT_RNDIS */
+#else
+/* Windows 7+ */
+#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_WIRELESS_CONTROLLER
+#define TUD_RNDIS_ITF_SUBCLASS 0x01
+#define TUD_RNDIS_ITF_PROTOCOL 0x03
+#endif
+
+// Length of template descriptor: 66 bytes
+#define TUD_RNDIS_DESC_LEN (8+9+5+5+4+5+7+9+7+7)
+
+// RNDIS Descriptor Template
+// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+#define TUD_RNDIS_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \
+ /* Interface Association */\
+ 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, 0,\
+ /* CDC Control Interface */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, _stridx,\
+ /* CDC-ACM Header */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\
+ /* CDC Call Management */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
+ /* ACM */\
+ 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 0,\
+ /* CDC Union */\
+ 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
+ /* Endpoint Notification */\
+ 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\
+ /* CDC Data Interface */\
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
+ /* Endpoint In */\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
+ /* Endpoint Out */\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
+
+//------------- BT Radio -------------//
+#define TUD_BT_APP_CLASS (TUSB_CLASS_WIRELESS_CONTROLLER)
+#define TUD_BT_APP_SUBCLASS 0x01
+#define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER 0x01
+#define TUD_BT_PROTOCOL_AMP_CONTROLLER 0x02
+
+#ifndef CFG_TUD_BTH_ISO_ALT_COUNT
+#define CFG_TUD_BTH_ISO_ALT_COUNT 0
+#endif
+
+// Length of template descriptor: 30 bytes + number of ISO alternatives * 23
+#define TUD_BTH_DESC_LEN (9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7))
+
+/* Primary Interface */
+#define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
+ 9, TUSB_DESC_INTERFACE, _itfnum, _stridx, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \
+ /* Endpoint In for events */ \
+ 7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \
+ /* Endpoint In for ACL data */ \
+ 7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1, \
+ /* Endpoint Out for ACL data */ \
+ 7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1
+
+#define TUD_BTH_ISO_ITF(_itfnum, _alt, _ep_in, _ep_out, _n) ,\
+ /* Interface with 2 endpoints */ \
+ 9, TUSB_DESC_INTERFACE, _itfnum, _alt, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \
+ /* Isochronous endpoints */ \
+ 7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1, \
+ 7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1
+
+#define _FIRST(a, ...) a
+#define _REST(a, ...) __VA_ARGS__
+
+#define TUD_BTH_ISO_ITF_0(_itfnum, ...)
+#define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__))
+#define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
+ TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+#define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
+ TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+#define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
+ TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+#define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
+ TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+#define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
+ TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+
+#define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \
+ TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__)
+
+// BT Primary controller descriptor
+// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes
+#define TUD_BTH_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size,...) \
+ TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
+ TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_USBD_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/device/usbd_control.c b/sw/Core/Src/tinyusb/src/device/usbd_control.c
new file mode 100755
index 0000000..d153801
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/device/usbd_control.c
@@ -0,0 +1,232 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED
+
+#include "tusb.h"
+#include "device/usbd_pvt.h"
+#include "dcd.h"
+
+#if CFG_TUSB_DEBUG >= 2
+extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
+#endif
+
+enum
+{
+ EDPT_CTRL_OUT = 0x00,
+ EDPT_CTRL_IN = 0x80
+};
+
+typedef struct
+{
+ tusb_control_request_t request;
+
+ uint8_t* buffer;
+ uint16_t data_len;
+ uint16_t total_xferred;
+
+ usbd_control_xfer_cb_t complete_cb;
+} usbd_control_xfer_t;
+
+static usbd_control_xfer_t _ctrl_xfer;
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
+static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Queue ZLP status transaction
+static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request)
+{
+ // Opposite to endpoint in Data Phase
+ uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
+ return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
+}
+
+// Status phase
+bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
+{
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
+ _ctrl_xfer.total_xferred = 0;
+ _ctrl_xfer.data_len = 0;
+
+ return _status_stage_xact(rhport, request);
+}
+
+// Queue a transaction in Data Stage
+// Each transaction has up to Endpoint0's max packet size.
+// This function can also transfer an zero-length packet
+static bool _data_stage_xact(uint8_t rhport)
+{
+ uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
+
+ uint8_t ep_addr = EDPT_CTRL_OUT;
+
+ if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
+ {
+ ep_addr = EDPT_CTRL_IN;
+ if ( xact_len ) memcpy(_usbd_ctrl_buf, _ctrl_xfer.buffer, xact_len);
+ }
+
+ return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len);
+}
+
+// Transmit data to/from the control endpoint.
+// If the request's wLength is zero, a status packet is sent instead.
+bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
+{
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = (uint8_t*) buffer;
+ _ctrl_xfer.total_xferred = 0U;
+ _ctrl_xfer.data_len = tu_min16(len, request->wLength);
+
+ if (request->wLength > 0U)
+ {
+ if(_ctrl_xfer.data_len > 0U)
+ {
+ TU_ASSERT(buffer);
+ }
+
+// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len);
+
+ // Data stage
+ TU_ASSERT( _data_stage_xact(rhport) );
+ }
+ else
+ {
+ // Status stage
+ TU_ASSERT( _status_stage_xact(rhport, request) );
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBD API
+//--------------------------------------------------------------------+
+
+void usbd_control_reset(void);
+void usbd_control_set_request(tusb_control_request_t const *request);
+void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
+bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+void usbd_control_reset(void)
+{
+ tu_varclr(&_ctrl_xfer);
+}
+
+// Set complete callback
+void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp )
+{
+ _ctrl_xfer.complete_cb = fp;
+}
+
+// for dcd_set_address where DCD is responsible for status response
+void usbd_control_set_request(tusb_control_request_t const *request)
+{
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
+ _ctrl_xfer.total_xferred = 0;
+ _ctrl_xfer.data_len = 0;
+}
+
+// callback when a transaction complete on
+// - DATA stage of control endpoint or
+// - Status stage
+bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+
+ // Endpoint Address is opposite to direction bit, this is Status Stage complete event
+ if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction )
+ {
+ TU_ASSERT(0 == xferred_bytes);
+
+ // invoke optional dcd hook if available
+ if (dcd_edpt0_status_complete) dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
+
+ if (_ctrl_xfer.complete_cb)
+ {
+ // TODO refactor with usbd_driver_print_control_complete_name
+ _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
+ }
+
+ return true;
+ }
+
+ if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
+ {
+ TU_VERIFY(_ctrl_xfer.buffer);
+ memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
+ }
+
+ _ctrl_xfer.total_xferred += xferred_bytes;
+ _ctrl_xfer.buffer += xferred_bytes;
+
+ // Data Stage is complete when all request's length are transferred or
+ // a short packet is sent including zero-length packet.
+ if ( (_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE) )
+ {
+ // DATA stage is complete
+ bool is_ok = true;
+
+ // invoke complete callback if set
+ // callback can still stall control in status phase e.g out data does not make sense
+ if ( _ctrl_xfer.complete_cb )
+ {
+ #if CFG_TUSB_DEBUG >= 2
+ usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
+ #endif
+
+ is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request);
+ }
+
+ if ( is_ok )
+ {
+ // Send status
+ TU_ASSERT( _status_stage_xact(rhport, &_ctrl_xfer.request) );
+ }else
+ {
+ // Stall both IN and OUT control endpoint
+ dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
+ dcd_edpt_stall(rhport, EDPT_CTRL_IN);
+ }
+ }
+ else
+ {
+ // More data to transfer
+ TU_ASSERT( _data_stage_xact(rhport) );
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/device/usbd_pvt.h b/sw/Core/Src/tinyusb/src/device/usbd_pvt.h
new file mode 100755
index 0000000..212c3a2
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/device/usbd_pvt.h
@@ -0,0 +1,112 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+#ifndef USBD_PVT_H_
+#define USBD_PVT_H_
+
+#include "osal/osal.h"
+#include "common/tusb_fifo.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Drivers
+//--------------------------------------------------------------------+
+
+typedef struct
+{
+ #if CFG_TUSB_DEBUG >= 2
+ char const* name;
+ #endif
+
+ void (* init ) (void);
+ void (* reset ) (uint8_t rhport);
+ uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
+ bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+ bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+ void (* sof ) (uint8_t rhport); /* optional */
+} usbd_class_driver_t;
+
+// Invoked when initializing device stack to get additional class drivers.
+// Can optionally implemented by application to extend/overwrite class driver support.
+// Note: The drivers array must be accessible at all time when stack is active
+usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) TU_ATTR_WEAK;
+
+
+typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+
+//--------------------------------------------------------------------+
+// USBD Endpoint API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
+
+// Close an endpoint
+void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr);
+
+// Submit a usb transfer
+bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
+
+// Claim an endpoint before submitting a transfer.
+// If caller does not make any transfer, it must release endpoint for others.
+bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);
+
+// Release an endpoint without submitting a transfer
+bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr);
+
+// Check if endpoint transferring is complete
+bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr);
+
+// Stall endpoint
+void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr);
+
+// Clear stalled endpoint
+void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr);
+
+// Check if endpoint is stalled
+bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr);
+
+static inline
+bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr)
+{
+ return !usbd_edpt_busy(rhport, ep_addr) && !usbd_edpt_stalled(rhport, ep_addr);
+}
+
+/*------------------------------------------------------------------*/
+/* Helper
+ *------------------------------------------------------------------*/
+
+bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
+void usbd_defer_func( osal_task_func_t func, void* param, bool in_isr );
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* USBD_PVT_H_ */
diff --git a/sw/Core/Src/tinyusb/src/host/hcd.h b/sw/Core/Src/tinyusb/src/host/hcd.h
new file mode 100755
index 0000000..4810ef8
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/hcd.h
@@ -0,0 +1,168 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_usbh
+ * \defgroup Group_HCD Host Controller Driver (HCD)
+ * @{ */
+
+#ifndef _TUSB_HCD_H_
+#define _TUSB_HCD_H_
+
+#include <common/tusb_common.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+ //--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef enum
+{
+ HCD_EVENT_DEVICE_ATTACH,
+ HCD_EVENT_DEVICE_REMOVE,
+ HCD_EVENT_XFER_COMPLETE,
+
+ // Not an HCD event, just a convenient way to defer ISR function
+ USBH_EVENT_FUNC_CALL,
+
+ HCD_EVENT_COUNT
+} hcd_eventid_t;
+
+typedef struct
+{
+ uint8_t rhport;
+ uint8_t event_id;
+ uint8_t dev_addr;
+
+ union
+ {
+ // Attach, Remove
+ struct {
+ uint8_t hub_addr;
+ uint8_t hub_port;
+ } connection;
+
+ // XFER_COMPLETE
+ struct {
+ uint8_t ep_addr;
+ uint8_t result;
+ uint32_t len;
+ } xfer_complete;
+
+ // FUNC_CALL
+ struct {
+ void (*func) (void*);
+ void* param;
+ }func_call;
+ };
+
+} hcd_event_t;
+
+#if TUSB_OPT_HOST_ENABLED
+// Max number of endpoints per device
+enum {
+ HCD_MAX_ENDPOINT = CFG_TUSB_HOST_DEVICE_MAX*(CFG_TUH_HUB + CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC +
+ CFG_TUH_MSC*2 + CFG_TUH_CDC*3),
+
+ HCD_MAX_XFER = HCD_MAX_ENDPOINT*2,
+};
+
+//#define HCD_MAX_ENDPOINT 16
+//#define HCD_MAX_XFER 16
+#endif
+
+//--------------------------------------------------------------------+
+// Controller & Port API
+//--------------------------------------------------------------------+
+bool hcd_init(uint8_t rhport);
+void hcd_int_handler(uint8_t rhport);
+void hcd_int_enable (uint8_t rhport);
+void hcd_int_disable(uint8_t rhport);
+
+// Get micro frame number (125 us)
+uint32_t hcd_uframe_number(uint8_t rhport);
+
+// Get frame number (1ms)
+static inline uint32_t hcd_frame_number(uint8_t rhport)
+{
+ return hcd_uframe_number(rhport) >> 3;
+}
+
+/// return the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t hostid);
+void hcd_port_reset(uint8_t hostid);
+void hcd_port_reset_end(uint8_t rhport);
+tusb_speed_t hcd_port_speed_get(uint8_t hostid);
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+
+bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr);
+bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr);
+bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
+
+// TODO merge with pipe_xfer
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
+
+//--------------------------------------------------------------------+
+// PIPE API
+//--------------------------------------------------------------------+
+// TODO control xfer should be used via usbh layer
+bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes); // only queue, not transferring yet
+bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete);
+
+// tusb_error_t hcd_pipe_cancel();
+
+//--------------------------------------------------------------------+
+// Event API (implemented by stack)
+//--------------------------------------------------------------------+
+
+// Called by HCD to notify stack
+extern void hcd_event_handler(hcd_event_t const* event, bool in_isr);
+
+// Helper to send device attach event
+extern void hcd_event_device_attach(uint8_t rhport, bool in_isr);
+
+// Helper to send device removal event
+extern void hcd_event_device_remove(uint8_t rhport, bool in_isr);
+
+// Helper to send USB transfer event
+extern void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_HCD_H_ */
+
+/// @}
diff --git a/sw/Core/Src/tinyusb/src/host/hub.c b/sw/Core/Src/tinyusb/src/host/hub.c
new file mode 100755
index 0000000..2191c45
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/hub.c
@@ -0,0 +1,361 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_HUB)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "hub.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t port_count;
+ uint8_t status_change; // data from status change interrupt endpoint
+
+ hub_port_status_response_t port_status;
+}usbh_hub_t;
+
+CFG_TUSB_MEM_SECTION static usbh_hub_t hub_data[CFG_TUSB_HOST_DEVICE_MAX];
+TU_ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)];
+
+//OSAL_SEM_DEF(hub_enum_semaphore);
+//static osal_semaphore_handle_t hub_enum_sem_hdl;
+
+//--------------------------------------------------------------------+
+// HUB
+//--------------------------------------------------------------------+
+bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, tuh_control_complete_cb_t complete_cb)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HUB_REQUEST_CLEAR_FEATURE,
+ .wValue = feature,
+ .wIndex = hub_port,
+ .wLength = 0
+ };
+
+ TU_LOG2("HUB Clear Port Feature: addr = %u port = %u, feature = %u\r\n", hub_addr, hub_port, feature);
+ TU_ASSERT( tuh_control_xfer(hub_addr, &request, NULL, complete_cb) );
+ return true;
+}
+
+bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_control_complete_cb_t complete_cb)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = HUB_REQUEST_GET_STATUS,
+ .wValue = 0,
+ .wIndex = hub_port,
+ .wLength = 4
+ };
+
+ TU_LOG2("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
+ TU_ASSERT( tuh_control_xfer( hub_addr, &request, resp, complete_cb) );
+ return true;
+}
+
+bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_control_complete_cb_t complete_cb)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HUB_REQUEST_SET_FEATURE,
+ .wValue = HUB_FEATURE_PORT_RESET,
+ .wIndex = hub_port,
+ .wLength = 0
+ };
+
+ TU_LOG2("HUB Reset Port: addr = %u port = %u\r\n", hub_addr, hub_port);
+ TU_ASSERT( tuh_control_xfer(hub_addr, &request, NULL, complete_cb) );
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API (don't require to verify parameters)
+//--------------------------------------------------------------------+
+void hub_init(void)
+{
+ tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof(usbh_hub_t));
+// hub_enum_sem_hdl = osal_semaphore_create( OSAL_SEM_REF(hub_enum_semaphore) );
+}
+
+bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+{
+ // not support multiple TT yet
+ if ( itf_desc->bInterfaceProtocol > 1 ) return false;
+
+ //------------- Open Interrupt Status Pipe -------------//
+ tusb_desc_endpoint_t const *ep_desc;
+ ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
+ TU_ASSERT(TUSB_XFER_INTERRUPT == ep_desc->bmAttributes.xfer);
+
+ TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
+
+ hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber;
+ hub_data[dev_addr-1].ep_in = ep_desc->bEndpointAddress;
+
+ (*p_length) = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
+
+ return true;
+}
+
+static bool config_get_hub_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_port_power_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+
+static bool config_get_hub_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ usbh_hub_t* p_hub = &hub_data[dev_addr-1];
+
+ // only use number of ports in hub descriptor
+ descriptor_hub_desc_t const* desc_hub = (descriptor_hub_desc_t const*) _hub_buffer;
+ p_hub->port_count = desc_hub->bNbrPorts;
+
+ // May need to GET_STATUS
+
+ // Ports must be powered on to be able to detect connection
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HUB_REQUEST_SET_FEATURE,
+ .wValue = HUB_FEATURE_PORT_POWER,
+ .wIndex = 1, // starting with port 1
+ .wLength = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, config_port_power_complete) );
+
+ return true;
+}
+
+static bool config_port_power_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+ usbh_hub_t* p_hub = &hub_data[dev_addr-1];
+
+ if (request->wIndex == p_hub->port_count)
+ {
+ // All ports are power -> queue notification status endpoint and
+ // complete the SET CONFIGURATION
+ TU_ASSERT( usbh_edpt_xfer(dev_addr, p_hub->ep_in, &p_hub->status_change, 1) );
+
+ usbh_driver_set_config_complete(dev_addr, p_hub->itf_num);
+ }else
+ {
+ tusb_control_request_t new_request = *request;
+ new_request.wIndex++; // power next port
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, config_port_power_complete) );
+ }
+
+ return true;
+}
+
+bool hub_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ usbh_hub_t* p_hub = &hub_data[dev_addr-1];
+ TU_ASSERT(itf_num == p_hub->itf_num);
+
+ //------------- Get Hub Descriptor -------------//
+ tusb_control_request_t request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = HUB_REQUEST_GET_DESCRIPTOR,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = sizeof(descriptor_hub_desc_t)
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &request, _hub_buffer, config_get_hub_desc_complete) );
+
+ return true;
+}
+
+static bool connection_clear_conn_change_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool connection_get_status_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool connection_port_reset_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+
+static bool connection_port_reset_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ TU_ASSERT(result == XFER_RESULT_SUCCESS);
+
+ // usbh_hub_t * p_hub = &hub_data[dev_addr-1];
+ uint8_t const port_num = (uint8_t) request->wIndex;
+
+ // submit attach event
+ hcd_event_t event =
+ {
+ .rhport = usbh_get_rhport(dev_addr),
+ .event_id = HCD_EVENT_DEVICE_ATTACH,
+ .connection =
+ {
+ .hub_addr = dev_addr,
+ .hub_port = port_num
+ }
+ };
+
+ hcd_event_handler(&event, false);
+
+ return true;
+}
+
+static bool connection_clear_conn_change_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ TU_ASSERT(result == XFER_RESULT_SUCCESS);
+
+ usbh_hub_t * p_hub = &hub_data[dev_addr-1];
+ uint8_t const port_num = (uint8_t) request->wIndex;
+
+ if ( p_hub->port_status.status.connection )
+ {
+ // Reset port if attach event
+ hub_port_reset(dev_addr, port_num, connection_port_reset_complete);
+ }else
+ {
+ // submit detach event
+ hcd_event_t event =
+ {
+ .rhport = usbh_get_rhport(dev_addr),
+ .event_id = HCD_EVENT_DEVICE_REMOVE,
+ .connection =
+ {
+ .hub_addr = dev_addr,
+ .hub_port = port_num
+ }
+ };
+
+ hcd_event_handler(&event, false);
+ }
+
+ return true;
+}
+
+static bool connection_get_status_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ TU_ASSERT(result == XFER_RESULT_SUCCESS);
+ usbh_hub_t * p_hub = &hub_data[dev_addr-1];
+ uint8_t const port_num = (uint8_t) request->wIndex;
+
+ // Connection change
+ if (p_hub->port_status.change.connection)
+ {
+ // Port is powered and enabled
+ //TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
+
+ // Acknowledge Port Connection Change
+ hub_port_clear_feature(dev_addr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete);
+ }else
+ {
+ // Other changes are: Enable, Suspend, Over Current, Reset, L1 state
+ // TODO clear change
+
+ // prepare for next hub status
+ // TODO continue with status_change, or maybe we can do it again with status
+ hub_status_pipe_queue(dev_addr);
+ }
+
+ return true;
+}
+
+// is the response of interrupt endpoint polling
+#include "usbh_hcd.h" // FIXME remove
+bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports
+ (void) ep_addr;
+ TU_ASSERT( result == XFER_RESULT_SUCCESS);
+
+ usbh_hub_t * p_hub = &hub_data[dev_addr-1];
+
+ TU_LOG2("Port Status Change = 0x%02X\r\n", p_hub->status_change);
+ for (uint8_t port=1; port <= p_hub->port_count; port++)
+ {
+ // TODO HUB ignore bit0 hub_status_change
+ if ( tu_bit_test(p_hub->status_change, port) )
+ {
+ hub_port_get_status(dev_addr, port, &p_hub->port_status, connection_get_status_complete);
+ break;
+ }
+ }
+
+ // NOTE: next status transfer is queued by usbh.c after handling this request
+
+ return true;
+}
+
+void hub_close(uint8_t dev_addr)
+{
+ tu_memclr(&hub_data[dev_addr-1], sizeof(usbh_hub_t));
+// osal_semaphore_reset(hub_enum_sem_hdl);
+}
+
+bool hub_status_pipe_queue(uint8_t dev_addr)
+{
+ usbh_hub_t * p_hub = &hub_data[dev_addr-1];
+ return hcd_pipe_xfer(dev_addr, p_hub->ep_in, &p_hub->status_change, 1, true);
+}
+
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/host/hub.h b/sw/Core/Src/tinyusb/src/host/hub.h
new file mode 100755
index 0000000..851bb8e
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/hub.h
@@ -0,0 +1,195 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_Hub Hub (Host only)
+ * \details Like most PC's OS, Hub support is completely hidden from Application. In fact, application cannot determine whether
+ * a device is mounted directly via roothub or via a hub's port. All Hub-related procedures are performed and managed
+ * by tinyusb stack. Unless you are trying to develop the stack itself, there are nothing else can be used by Application.
+ * \note Due to my laziness, only 1-level of Hub is supported. In other way, the stack cannot mount a hub via another hub.
+ * @{
+ */
+
+#ifndef _TUSB_HUB_H_
+#define _TUSB_HUB_H_
+
+#include "common/tusb_common.h"
+#include "usbh.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//D1...D0: Logical Power Switching Mode
+//00: Ganged power switching (all ports’power at
+//once)
+//01: Individual port power switching
+//1X: Reserved. Used only on 1.0 compliant hubs
+//that implement no power switching
+//D2: Identifies a Compound Device
+//0: Hub is not part of a compound device.
+//1: Hub is part of a compound device.
+//D4...D3: Over-current Protection Mode
+//00: Global Over-current Protection. The hub
+//reports over-current as a summation of all
+//ports’current draw, without a breakdown of
+//individual port over-current status.
+//01: Individual Port Over-current Protection. The
+//hub reports over-current on a per-port basis.
+//Each port has an over-current status.
+//1X: No Over-current Protection. This option is
+//allowed only for bus-powered hubs that do not
+//implement over-current protection.
+//
+//D6...D5: TT Think TIme
+//00: TT requires at most 8 FS bit times of inter
+//transaction gap on a full-/low-speed
+//downstream bus.
+//01: TT requires at most 16 FS bit times.
+//10: TT requires at most 24 FS bit times.
+//11: TT requires at most 32 FS bit times.
+//D7: Port Indicators Supported
+//0: Port Indicators are not supported on its
+//downstream facing ports and the
+//PORT_INDICATOR request has no effect.
+//1: Port Indicators are supported on its
+//downstream facing ports and the
+//PORT_INDICATOR request controls the
+//indicators. See Section 11.5.3.
+//D15...D8: Reserved
+
+typedef struct TU_ATTR_PACKED{
+ uint8_t bLength ; ///< Size of descriptor
+ uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
+ uint8_t bNbrPorts;
+ uint16_t wHubCharacteristics;
+ uint8_t bPwrOn2PwrGood;
+ uint8_t bHubContrCurrent;
+ uint8_t DeviceRemovable; // bitmap each bit for a port (from bit1)
+ uint8_t PortPwrCtrlMask; // just for compatibility, should be 0xff
+} descriptor_hub_desc_t;
+
+TU_VERIFY_STATIC( sizeof(descriptor_hub_desc_t) == 9, "size is not correct");
+
+enum {
+ HUB_REQUEST_GET_STATUS = 0 ,
+ HUB_REQUEST_CLEAR_FEATURE = 1 ,
+
+ HUB_REQUEST_SET_FEATURE = 3 ,
+
+ HUB_REQUEST_GET_DESCRIPTOR = 6 ,
+ HUB_REQUEST_SET_DESCRIPTOR = 7 ,
+ HUB_REQUEST_CLEAR_TT_BUFFER = 8 ,
+ HUB_REQUEST_RESET_TT = 9 ,
+ HUB_REQUEST_GET_TT_STATE = 10 ,
+ HUB_REQUEST_STOP_TT = 11
+};
+
+enum {
+ HUB_FEATURE_HUB_LOCAL_POWER_CHANGE = 0,
+ HUB_FEATURE_HUB_OVER_CURRENT_CHANGE
+};
+
+enum{
+ HUB_FEATURE_PORT_CONNECTION = 0,
+ HUB_FEATURE_PORT_ENABLE = 1,
+ HUB_FEATURE_PORT_SUSPEND = 2,
+ HUB_FEATURE_PORT_OVER_CURRENT = 3,
+ HUB_FEATURE_PORT_RESET = 4,
+
+ HUB_FEATURE_PORT_POWER = 8,
+ HUB_FEATURE_PORT_LOW_SPEED = 9,
+
+ HUB_FEATURE_PORT_CONNECTION_CHANGE = 16,
+ HUB_FEATURE_PORT_ENABLE_CHANGE = 17,
+ HUB_FEATURE_PORT_SUSPEND_CHANGE = 18,
+ HUB_FEATURE_PORT_OVER_CURRENT_CHANGE = 19,
+ HUB_FEATURE_PORT_RESET_CHANGE = 20,
+ HUB_FEATURE_PORT_TEST = 21,
+ HUB_FEATURE_PORT_INDICATOR = 22
+};
+
+// data in response of HUB_REQUEST_GET_STATUS, wIndex = 0 (hub)
+typedef struct {
+ union{
+ struct TU_ATTR_PACKED {
+ uint16_t local_power_source : 1;
+ uint16_t over_current : 1;
+ uint16_t : 14;
+ };
+
+ uint16_t value;
+ } status, change;
+} hub_status_response_t;
+
+TU_VERIFY_STATIC( sizeof(hub_status_response_t) == 4, "size is not correct");
+
+// data in response of HUB_REQUEST_GET_STATUS, wIndex = Port num
+typedef struct {
+ union {
+ struct TU_ATTR_PACKED {
+ uint16_t connection : 1;
+ uint16_t port_enable : 1;
+ uint16_t suspend : 1;
+ uint16_t over_current : 1;
+ uint16_t reset : 1;
+
+ uint16_t : 3;
+ uint16_t port_power : 1;
+ uint16_t low_speed : 1;
+ uint16_t high_speed : 1;
+ uint16_t port_test_mode : 1;
+ uint16_t port_indicator_control : 1;
+ uint16_t TU_RESERVED : 3;
+ };
+
+ uint16_t value;
+ } status, change;
+} hub_port_status_response_t;
+
+TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct");
+
+bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_control_complete_cb_t complete_cb);
+bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_control_complete_cb_t complete_cb);
+bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature, tuh_control_complete_cb_t complete_cb);
+bool hub_status_pipe_queue(uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void hub_init(void);
+bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
+bool hub_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void hub_close(uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_HUB_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/host/usbh.c b/sw/Core/Src/tinyusb/src/host/usbh.c
new file mode 100755
index 0000000..a4ee4cb
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/usbh.c
@@ -0,0 +1,965 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "common/tusb_common.h"
+
+#if TUSB_OPT_HOST_ENABLED
+
+#ifndef CFG_TUH_TASK_QUEUE_SZ
+#define CFG_TUH_TASK_QUEUE_SZ 16
+#endif
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "tusb.h"
+#include "hub.h"
+#include "usbh_hcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+#if CFG_TUSB_DEBUG >= 2
+ #define DRIVER_NAME(_name) .name = _name,
+#else
+ #define DRIVER_NAME(_name)
+#endif
+
+static usbh_class_driver_t const usbh_class_drivers[] =
+{
+ #if CFG_TUH_CDC
+ {
+ DRIVER_NAME("CDC")
+ .class_code = TUSB_CLASS_CDC,
+ .init = cdch_init,
+ .open = cdch_open,
+ .set_config = cdch_set_config,
+ .xfer_cb = cdch_xfer_cb,
+ .close = cdch_close
+ },
+ #endif
+
+ #if CFG_TUH_MSC
+ {
+ DRIVER_NAME("MSC")
+ .class_code = TUSB_CLASS_MSC,
+ .init = msch_init,
+ .open = msch_open,
+ .set_config = msch_set_config,
+ .xfer_cb = msch_xfer_cb,
+ .close = msch_close
+ },
+ #endif
+
+ #if HOST_CLASS_HID
+ {
+ DRIVER_NAME("HID")
+ .class_code = TUSB_CLASS_HID,
+ .init = hidh_init,
+ .open = hidh_open_subtask,
+ .set_config = hidh_set_config,
+ .xfer_cb = hidh_xfer_cb,
+ .close = hidh_close
+ },
+ #endif
+
+ #if CFG_TUH_HUB
+ {
+ DRIVER_NAME("HUB")
+ .class_code = TUSB_CLASS_HUB,
+ .init = hub_init,
+ .open = hub_open,
+ .set_config = hub_set_config,
+ .xfer_cb = hub_xfer_cb,
+ .close = hub_close
+ },
+ #endif
+
+ #if CFG_TUH_VENDOR
+ {
+ DRIVER_NAME("VENDOR")
+ .class_code = TUSB_CLASS_VENDOR_SPECIFIC,
+ .init = cush_init,
+ .open = cush_open_subtask,
+ .xfer_cb = cush_isr,
+ .close = cush_close
+ }
+ #endif
+};
+
+enum { USBH_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
+
+enum { RESET_DELAY = 500 }; // 200 USB specs say only 50ms but many devices require much longer
+
+enum { CONFIG_NUM = 1 }; // default to use configuration 1
+
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+// including zero-address
+CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1];
+
+// Event queue
+// role device/host is used by OS NONE for mutex (disable usb isr)
+OSAL_QUEUE_DEF(OPT_MODE_HOST, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
+static osal_queue_t _usbh_q;
+
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _usbh_ctrl_buf[CFG_TUSB_HOST_ENUM_BUFFER_SIZE];
+
+//------------- Helper Function Prototypes -------------//
+static bool enum_new_device(hcd_event_t* event);
+
+// from usbh_control.c
+extern bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+uint8_t usbh_get_rhport(uint8_t dev_addr)
+{
+ return _usbh_devices[dev_addr].rhport;
+}
+
+//--------------------------------------------------------------------+
+// PUBLIC API (Parameter Verification is required)
+//--------------------------------------------------------------------+
+tusb_device_state_t tuh_device_get_state (uint8_t const dev_addr)
+{
+ TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_DEVICE_STATE_UNPLUG);
+ return (tusb_device_state_t) _usbh_devices[dev_addr].state;
+}
+
+tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr)
+{
+ TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_SPEED_INVALID);
+ return (tusb_speed_t) _usbh_devices[dev_addr].speed;
+}
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+void osal_task_delay(uint32_t msec)
+{
+ (void) msec;
+
+ const uint32_t start = hcd_frame_number(TUH_OPT_RHPORT);
+ while ( ( hcd_frame_number(TUH_OPT_RHPORT) - start ) < msec ) {}
+}
+#endif
+
+//--------------------------------------------------------------------+
+// CLASS-USBD API (don't require to verify parameters)
+//--------------------------------------------------------------------+
+bool tuh_init(void)
+{
+ tu_memclr(_usbh_devices, sizeof(usbh_device_t)*(CFG_TUSB_HOST_DEVICE_MAX+1));
+
+ //------------- Enumeration & Reporter Task init -------------//
+ _usbh_q = osal_queue_create( &_usbh_qdef );
+ TU_ASSERT(_usbh_q != NULL);
+
+ //------------- Semaphore, Mutex for Control Pipe -------------//
+ for(uint8_t i=0; i<CFG_TUSB_HOST_DEVICE_MAX+1; i++) // including address zero
+ {
+ usbh_device_t * const dev = &_usbh_devices[i];
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ dev->mutex = osal_mutex_create(&dev->mutexdef);
+ TU_ASSERT(dev->mutex);
+#endif
+
+ memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping
+ memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping
+ }
+
+ // Class drivers init
+ for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
+ {
+ TU_LOG2("%s init\r\n", usbh_class_drivers[drv_id].name);
+ usbh_class_drivers[drv_id].init();
+ }
+
+ TU_ASSERT(hcd_init(TUH_OPT_RHPORT));
+ hcd_int_enable(TUH_OPT_RHPORT);
+
+ return true;
+}
+
+bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ // pre-check to help reducing mutex lock
+ TU_VERIFY((dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0));
+ osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+#endif
+
+ // can only claim the endpoint if it is not busy and not claimed yet.
+ bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0);
+ if (ret)
+ {
+ dev->ep_status[epnum][dir].claimed = 1;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_unlock(dev->mutex);
+#endif
+
+ return ret;
+}
+
+bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+#endif
+
+ // can only release the endpoint if it is claimed and not busy
+ bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 1);
+ if (ret)
+ {
+ dev->ep_status[epnum][dir].claimed = 0;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_unlock(dev->mutex);
+#endif
+
+ return ret;
+}
+
+bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+ return hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes);
+}
+
+bool usbh_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size)
+{
+ tusb_desc_endpoint_t ep0_desc =
+ {
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
+ .wMaxPacketSize = { .size = max_packet_size },
+ .bInterval = 0
+ };
+
+ return hcd_edpt_open(_usbh_devices[dev_addr].rhport, dev_addr, &ep0_desc);
+}
+
+bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+ bool ret = hcd_edpt_open(rhport, dev_addr, ep_desc);
+
+ if (ret)
+ {
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+
+ // new endpoints belongs to latest interface (last valid value)
+ // TODO FIXME not true with ISO
+ uint8_t drvid = 0xff;
+ for(uint8_t i=0; i < sizeof(dev->itf2drv); i++)
+ {
+ if ( dev->itf2drv[i] == 0xff ) break;
+ drvid = dev->itf2drv[i];
+ }
+ TU_ASSERT(drvid < USBH_CLASS_DRIVER_COUNT);
+
+ uint8_t const ep_addr = ep_desc->bEndpointAddress;
+ dev->ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = drvid;
+ }
+
+ return ret;
+}
+
+//--------------------------------------------------------------------+
+// HCD Event Handler
+//--------------------------------------------------------------------+
+
+void hcd_event_handler(hcd_event_t const* event, bool in_isr)
+{
+ switch (event->event_id)
+ {
+ default:
+ osal_queue_send(_usbh_q, event, in_isr);
+ break;
+ }
+}
+
+// interrupt caused by a TD (with IOC=1) in pipe of class class_code
+void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
+{
+ hcd_event_t event =
+ {
+ .rhport = 0, // TODO correct rhport
+ .event_id = HCD_EVENT_XFER_COMPLETE,
+ .dev_addr = dev_addr,
+ .xfer_complete =
+ {
+ .ep_addr = ep_addr,
+ .result = result,
+ .len = xferred_bytes
+ }
+ };
+
+ hcd_event_handler(&event, in_isr);
+}
+
+void hcd_event_device_attach(uint8_t rhport, bool in_isr)
+{
+ hcd_event_t event =
+ {
+ .rhport = rhport,
+ .event_id = HCD_EVENT_DEVICE_ATTACH
+ };
+
+ event.connection.hub_addr = 0;
+ event.connection.hub_port = 0;
+
+ hcd_event_handler(&event, in_isr);
+}
+
+void hcd_event_device_remove(uint8_t hostid, bool in_isr)
+{
+ hcd_event_t event =
+ {
+ .rhport = hostid,
+ .event_id = HCD_EVENT_DEVICE_REMOVE
+ };
+
+ event.connection.hub_addr = 0;
+ event.connection.hub_port = 0;
+
+ hcd_event_handler(&event, in_isr);
+}
+
+
+// a device unplugged on hostid, hub_addr, hub_port
+// return true if found and unmounted device, false if cannot find
+static void usbh_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
+{
+ //------------- find the all devices (star-network) under port that is unplugged -------------//
+ for (uint8_t dev_addr = 0; dev_addr <= CFG_TUSB_HOST_DEVICE_MAX; dev_addr ++)
+ {
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+
+ // TODO Hub multiple level
+ if (dev->rhport == rhport &&
+ (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr == 0 & hub_port == 0 means roothub
+ (hub_port == 0 || dev->hub_port == hub_port) &&
+ dev->state != TUSB_DEVICE_STATE_UNPLUG)
+ {
+ // Invoke callback before close driver
+ if (tuh_umount_cb) tuh_umount_cb(dev_addr);
+
+ // Close class driver
+ for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
+ {
+ TU_LOG2("%s close\r\n", usbh_class_drivers[drv_id].name);
+ usbh_class_drivers[drv_id].close(dev_addr);
+ }
+
+ memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping
+ memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping
+
+ hcd_device_close(rhport, dev_addr);
+
+ dev->state = TUSB_DEVICE_STATE_UNPLUG;
+ }
+ }
+}
+
+/* USB Host Driver task
+ * This top level thread manages all host controller event and delegates events to class-specific drivers.
+ * This should be called periodically within the mainloop or rtos thread.
+ *
+ @code
+ int main(void)
+ {
+ application_init();
+ tusb_init();
+
+ while(1) // the mainloop
+ {
+ application_code();
+ tuh_task(); // tinyusb host task
+ }
+ }
+ @endcode
+ */
+void tuh_task(void)
+{
+ // Skip if stack is not initialized
+ if ( !tusb_inited() ) return;
+
+ // Loop until there is no more events in the queue
+ while (1)
+ {
+ hcd_event_t event;
+ if ( !osal_queue_receive(_usbh_q, &event) ) return;
+
+ switch (event.event_id)
+ {
+ case HCD_EVENT_DEVICE_ATTACH:
+ // TODO due to the shared _usbh_ctrl_buf, we must complete enumerating
+ // one device before enumerating another one.
+ TU_LOG2("USBH DEVICE ATTACH\r\n");
+ enum_new_device(&event);
+ break;
+
+ case HCD_EVENT_DEVICE_REMOVE:
+ TU_LOG2("USBH DEVICE REMOVED\r\n");
+ usbh_device_unplugged(event.rhport, event.connection.hub_addr, event.connection.hub_port);
+
+ #if CFG_TUH_HUB
+ // TODO remove
+ if ( event.connection.hub_addr != 0)
+ {
+ // done with hub, waiting for next data on status pipe
+ (void) hub_status_pipe_queue( event.connection.hub_addr );
+ }
+ #endif
+ break;
+
+ case HCD_EVENT_XFER_COMPLETE:
+ {
+ usbh_device_t* dev = &_usbh_devices[event.dev_addr];
+ uint8_t const ep_addr = event.xfer_complete.ep_addr;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+
+ TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
+
+ if ( 0 == epnum )
+ {
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+ }else
+ {
+ uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
+ TU_ASSERT(drv_id < USBH_CLASS_DRIVER_COUNT, );
+
+ TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name);
+ usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+ }
+ }
+ break;
+
+ case USBH_EVENT_FUNC_CALL:
+ if ( event.func_call.func ) event.func_call.func(event.func_call.param);
+ break;
+
+ default: break;
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// INTERNAL HELPER
+//--------------------------------------------------------------------+
+static uint8_t get_new_address(void)
+{
+ for (uint8_t addr=1; addr <= CFG_TUSB_HOST_DEVICE_MAX; addr++)
+ {
+ if (_usbh_devices[addr].state == TUSB_DEVICE_STATE_UNPLUG) return addr;
+ }
+ return CFG_TUSB_HOST_DEVICE_MAX+1;
+}
+
+void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
+{
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+
+ for(itf_num++; itf_num < sizeof(dev->itf2drv); itf_num++)
+ {
+ // continue with next valid interface
+ uint8_t const drv_id = dev->itf2drv[itf_num];
+ if (drv_id != 0xff)
+ {
+ usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
+ TU_LOG2("%s set config itf = %u\r\n", driver->name, itf_num);
+ driver->set_config(dev_addr, itf_num);
+ break;
+ }
+ }
+
+ // all interface are configured
+ if (itf_num == sizeof(dev->itf2drv))
+ {
+ // Invoke callback if available
+ if (tuh_mount_cb) tuh_mount_cb(dev_addr);
+ }
+}
+
+//--------------------------------------------------------------------+
+// Enumeration Process
+// is a lengthy process with a seires of control transfer to configure
+// newly attached device. Each step is handled by a function in this
+// section
+// TODO due to the shared _usbh_ctrl_buf, we must complete enumerating
+// one device before enumerating another one.
+//--------------------------------------------------------------------+
+
+static bool enum_request_addr0_device_desc(void);
+static bool enum_request_set_addr(void);
+
+static bool enum_get_addr0_device_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool enum_set_address_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool enum_get_device_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool enum_get_9byte_config_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool enum_get_config_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool enum_set_config_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
+
+static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) dev_addr; (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+ enum_request_addr0_device_desc();
+ return true;
+}
+
+static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) dev_addr; (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+ usbh_device_t* dev0 = &_usbh_devices[0];
+
+ enum_request_set_addr();
+
+ // done with hub, waiting for next data on status pipe
+ (void) hub_status_pipe_queue( dev0->hub_addr );
+
+ return true;
+}
+
+static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) dev_addr; (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+ usbh_device_t* dev0 = &_usbh_devices[0];
+
+ hub_port_status_response_t port_status;
+ memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+
+ // Acknowledge Port Reset Change if Reset Successful
+ if (port_status.change.reset)
+ {
+ TU_ASSERT( hub_port_clear_feature(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) );
+ }
+
+ return true;
+}
+
+static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) dev_addr; (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+ usbh_device_t* dev0 = &_usbh_devices[0];
+
+ hub_port_status_response_t port_status;
+ memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+
+ if ( !port_status.status.connection )
+ {
+ // device unplugged while delaying, nothing else to do, queue hub status
+ return hub_status_pipe_queue(dev_addr);
+ }
+
+ dev0->speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
+ (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
+
+ // Acknowledge Port Reset Change
+ if (port_status.change.reset)
+ {
+ hub_port_clear_feature(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset0_complete);
+ }
+
+ return true;
+}
+
+
+static bool enum_request_set_addr(void)
+{
+ // Set Address
+ TU_LOG2("Set Address \r\n");
+ uint8_t const new_addr = get_new_address();
+ TU_ASSERT(new_addr <= CFG_TUSB_HOST_DEVICE_MAX); // TODO notify application we reach max devices
+
+ usbh_device_t* dev0 = &_usbh_devices[0];
+ usbh_device_t* new_dev = &_usbh_devices[new_addr];
+
+ new_dev->rhport = dev0->rhport;
+ new_dev->hub_addr = dev0->hub_addr;
+ new_dev->hub_port = dev0->hub_port;
+ new_dev->speed = dev0->speed;
+ new_dev->connected = 1;
+ new_dev->ep0_packet_size = ((tusb_desc_device_t*) _usbh_ctrl_buf)->bMaxPacketSize0;
+
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_ADDRESS,
+ .wValue = new_addr,
+ .wIndex = 0,
+ .wLength = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(0, &new_request, NULL, enum_set_address_complete) );
+
+ return true;
+}
+
+static bool enum_new_device(hcd_event_t* event)
+{
+ usbh_device_t* dev0 = &_usbh_devices[0];
+ dev0->rhport = event->rhport; // TODO refractor integrate to device_pool
+ dev0->hub_addr = event->connection.hub_addr;
+ dev0->hub_port = event->connection.hub_port;
+ dev0->state = TUSB_DEVICE_STATE_UNPLUG;
+
+ //------------- connected/disconnected directly with roothub -------------//
+ if (dev0->hub_addr == 0)
+ {
+ // wait until device is stable
+ osal_task_delay(RESET_DELAY);
+
+ // device unplugged while delaying
+ if ( !hcd_port_connect_status(dev0->rhport) ) return true;
+
+ dev0->speed = hcd_port_speed_get( dev0->rhport );
+
+ enum_request_addr0_device_desc();
+ }
+#if CFG_TUH_HUB
+ //------------- connected/disconnected via hub -------------//
+ else
+ {
+ // wait until device is stable
+ osal_task_delay(RESET_DELAY);
+ TU_ASSERT( hub_port_get_status(dev0->hub_addr, dev0->hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete) );
+ }
+#endif // CFG_TUH_HUB
+
+ return true;
+}
+
+static bool enum_request_addr0_device_desc(void)
+{
+ // TODO probably doesn't need to open/close each enumeration
+ TU_ASSERT( usbh_pipe_control_open(0, 8) );
+
+ //------------- Get first 8 bytes of device descriptor to get Control Endpoint Size -------------//
+ TU_LOG2("Get 8 byte of Device Descriptor\r\n");
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = TUSB_DESC_DEVICE << 8,
+ .wIndex = 0,
+ .wLength = 8
+ };
+ TU_ASSERT( tuh_control_xfer(0, &request, _usbh_ctrl_buf, enum_get_addr0_device_desc_complete) );
+
+ return true;
+}
+
+// After Get Device Descriptor of Address 0
+static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+ TU_ASSERT(0 == dev_addr);
+
+ usbh_device_t* dev0 = &_usbh_devices[0];
+
+ if (XFER_RESULT_SUCCESS != result)
+ {
+#if CFG_TUH_HUB
+ // TODO remove, waiting for next data on status pipe
+ if (dev0->hub_addr != 0) hub_status_pipe_queue(dev0->hub_addr);
+#endif
+
+ return false;
+ }
+
+ // Reset device again before Set Address
+ TU_LOG2("Port reset \r\n");
+
+ if (dev0->hub_addr == 0)
+ {
+ // connected directly to roothub
+ hcd_port_reset( dev0->rhport ); // reset port after 8 byte descriptor
+ osal_task_delay(RESET_DELAY);
+
+ enum_request_set_addr();
+ }
+#if CFG_TUH_HUB
+ else
+ {
+ // after RESET_DELAY the hub_port_reset() already complete
+ TU_ASSERT( hub_port_reset(dev0->hub_addr, dev0->hub_port, NULL) );
+ osal_task_delay(RESET_DELAY);
+
+ tuh_task(); // FIXME temporarily to clean up port_reset control transfer
+
+ TU_ASSERT( hub_port_get_status(dev0->hub_addr, dev0->hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) );
+ }
+#endif
+
+ return true;
+}
+
+// After SET_ADDRESS is complete
+static bool enum_set_address_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ TU_ASSERT(0 == dev_addr);
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ uint8_t const new_addr = (uint8_t const) request->wValue;
+
+ usbh_device_t* new_dev = &_usbh_devices[new_addr];
+ new_dev->addressed = 1;
+
+ // TODO close device 0, may not be needed
+ usbh_device_t* dev0 = &_usbh_devices[0];
+ hcd_device_close(dev0->rhport, 0);
+ dev0->state = TUSB_DEVICE_STATE_UNPLUG;
+
+ // open control pipe for new address
+ TU_ASSERT ( usbh_pipe_control_open(new_addr, new_dev->ep0_packet_size) );
+
+ // Get full device descriptor
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = TUSB_DESC_DEVICE << 8,
+ .wIndex = 0,
+ .wLength = sizeof(tusb_desc_device_t)
+ };
+
+ TU_ASSERT(tuh_control_xfer(new_addr, &new_request, _usbh_ctrl_buf, enum_get_device_desc_complete));
+
+ return true;
+}
+
+static bool enum_get_device_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+
+ dev->vendor_id = desc_device->idVendor;
+ dev->product_id = desc_device->idProduct;
+
+// if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf);
+
+ TU_LOG2("Get 9 bytes of Configuration Descriptor\r\n");
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = (TUSB_DESC_CONFIGURATION << 8) | (CONFIG_NUM - 1),
+ .wIndex = 0,
+ .wLength = 9
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, _usbh_ctrl_buf, enum_get_9byte_config_desc_complete) );
+
+ return true;
+}
+
+static bool enum_get_9byte_config_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ // TODO not enough buffer to hold configuration descriptor
+ tusb_desc_configuration_t const * desc_config = (tusb_desc_configuration_t const*) _usbh_ctrl_buf;
+ uint16_t total_len;
+
+ // Use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&total_len, (uint8_t*) desc_config + offsetof(tusb_desc_configuration_t, wTotalLength), 2);
+
+ TU_ASSERT(total_len <= CFG_TUSB_HOST_ENUM_BUFFER_SIZE);
+
+ //Get full configuration descriptor
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = (TUSB_DESC_CONFIGURATION << 8) | (CONFIG_NUM - 1),
+ .wIndex = 0,
+ .wLength = total_len
+
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, _usbh_ctrl_buf, enum_get_config_desc_complete) );
+
+ return true;
+}
+
+static bool enum_get_config_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ // Parse configuration & set up drivers
+ // Driver open aren't allowed to make any usb transfer yet
+ parse_configuration_descriptor(dev_addr, (tusb_desc_configuration_t*) _usbh_ctrl_buf);
+
+ TU_LOG2("Set Configuration Descriptor\r\n");
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_CONFIGURATION,
+ .wValue = CONFIG_NUM,
+ .wIndex = 0,
+ .wLength = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, enum_set_config_complete) );
+
+ return true;
+}
+
+static bool enum_set_config_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ TU_LOG2("Device configured\r\n");
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+ dev->configured = 1;
+ dev->state = TUSB_DEVICE_STATE_CONFIGURED;
+
+ // Start the Set Configuration process for interfaces (itf = 0xff)
+ // Since driver can perform control transfer within its set_config, this is done asynchronously.
+ // The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete()
+ usbh_driver_set_config_complete(dev_addr, 0xff);
+
+ return true;
+}
+
+static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg)
+{
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+ uint8_t const* p_desc = (uint8_t const*) desc_cfg;
+ p_desc = tu_desc_next(p_desc);
+
+ // parse each interfaces
+ while( p_desc < _usbh_ctrl_buf + desc_cfg->wTotalLength )
+ {
+ // skip until we see interface descriptor
+ if ( TUSB_DESC_INTERFACE != tu_desc_type(p_desc) )
+ {
+ p_desc = tu_desc_next(p_desc); // skip the descriptor, increase by the descriptor's length
+ }else
+ {
+ tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc;
+
+ // Check if class is supported
+ uint8_t drv_id;
+ for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
+ {
+ if ( usbh_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break;
+ }
+
+ if( drv_id >= USBH_CLASS_DRIVER_COUNT )
+ {
+ // skip unsupported class
+ p_desc = tu_desc_next(p_desc);
+ }
+ else
+ {
+ usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
+
+ // Interface number must not be used already TODO alternate interface
+ TU_ASSERT( dev->itf2drv[desc_itf->bInterfaceNumber] == 0xff );
+ dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id;
+
+ if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && dev->hub_addr != 0)
+ {
+ // TODO Attach hub to Hub is not currently supported
+ // skip this interface
+ p_desc = tu_desc_next(p_desc);
+ }
+ else
+ {
+ TU_LOG2("%s open\r\n", driver->name);
+
+ uint16_t itf_len = 0;
+ TU_ASSERT( driver->open(dev->rhport, dev_addr, desc_itf, &itf_len) );
+ TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) );
+ p_desc += itf_len;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/host/usbh.h b/sw/Core/Src/tinyusb/src/host/usbh.h
new file mode 100755
index 0000000..a05010b
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/usbh.h
@@ -0,0 +1,130 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_usbh USB Host Core (USBH)
+ * @{ */
+
+#ifndef _TUSB_USBH_H_
+#define _TUSB_USBH_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "osal/osal.h" // TODO refractor move to common.h ?
+#include "hcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef enum tusb_interface_status_{
+ TUSB_INTERFACE_STATUS_READY = 0,
+ TUSB_INTERFACE_STATUS_BUSY,
+ TUSB_INTERFACE_STATUS_COMPLETE,
+ TUSB_INTERFACE_STATUS_ERROR,
+ TUSB_INTERFACE_STATUS_INVALID_PARA
+} tusb_interface_status_t;
+
+typedef struct {
+ #if CFG_TUSB_DEBUG >= 2
+ char const* name;
+ #endif
+
+ uint8_t class_code;
+
+ void (* const init )(void);
+ bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t* outlen);
+ bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
+ bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+ void (* const close )(uint8_t dev_addr);
+} usbh_class_driver_t;
+
+typedef bool (*tuh_control_complete_cb_t)(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+
+// Init host stack
+bool tuh_init(void);
+
+// Task function should be called in main/rtos loop
+void tuh_task(void);
+
+// Interrupt handler, name alias to HCD
+extern void hcd_int_handler(uint8_t rhport);
+#define tuh_int_handler hcd_int_handler
+
+tusb_device_state_t tuh_device_get_state (uint8_t dev_addr);
+tusb_speed_t tuh_device_get_speed (uint8_t dev_addr);
+static inline bool tuh_device_is_configured(uint8_t dev_addr)
+{
+ return tuh_device_get_state(dev_addr) == TUSB_DEVICE_STATE_CONFIGURED;
+}
+
+bool tuh_control_xfer (uint8_t dev_addr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb);
+
+//--------------------------------------------------------------------+
+// APPLICATION CALLBACK
+//--------------------------------------------------------------------+
+//TU_ATTR_WEAK uint8_t tuh_attach_cb (tusb_desc_device_t const *desc_device);
+
+/** Callback invoked when device is mounted (configured) */
+TU_ATTR_WEAK void tuh_mount_cb (uint8_t dev_addr);
+
+/** Callback invoked when device is unmounted (bus reset/unplugged) */
+TU_ATTR_WEAK void tuh_umount_cb(uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// CLASS-USBH & INTERNAL API
+// TODO move to usbh_pvt.h
+//--------------------------------------------------------------------+
+
+bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
+
+// Claim an endpoint before submitting a transfer.
+// If caller does not make any transfer, it must release endpoint for others.
+bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr);
+
+void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num);
+
+uint8_t usbh_get_rhport(uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_USBH_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/host/usbh_control.c b/sw/Core/Src/tinyusb/src/host/usbh_control.c
new file mode 100755
index 0000000..de55bd5
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/usbh_control.c
@@ -0,0 +1,140 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_HOST_ENABLED
+
+#include "tusb.h"
+#include "usbh_hcd.h"
+
+enum
+{
+ STAGE_SETUP,
+ STAGE_DATA,
+ STAGE_ACK
+};
+
+typedef struct
+{
+ tusb_control_request_t request TU_ATTR_ALIGNED(4);
+
+ uint8_t stage;
+ uint8_t* buffer;
+ tuh_control_complete_cb_t complete_cb;
+} usbh_control_xfer_t;
+
+static usbh_control_xfer_t _ctrl_xfer;
+
+//CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
+//static uint8_t _tuh_ctrl_buf[CFG_TUSB_HOST_ENUM_BUFFER_SIZE];
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+bool tuh_control_xfer (uint8_t dev_addr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb)
+{
+ // TODO need to claim the endpoint first
+
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+ const uint8_t rhport = dev->rhport;
+
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = buffer;
+ _ctrl_xfer.stage = STAGE_SETUP;
+ _ctrl_xfer.complete_cb = complete_cb;
+
+ TU_LOG2("Control Setup: ");
+ TU_LOG2_VAR(request);
+ TU_LOG2("\r\n");
+
+ // Send setup packet
+ TU_ASSERT( hcd_setup_send(rhport, dev_addr, (uint8_t const*) &_ctrl_xfer.request) );
+
+ return true;
+}
+
+static void _xfer_complete(uint8_t dev_addr, xfer_result_t result)
+{
+ if (_ctrl_xfer.complete_cb) _ctrl_xfer.complete_cb(dev_addr, &_ctrl_xfer.request, result);
+}
+
+bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) ep_addr;
+ (void) xferred_bytes;
+
+ usbh_device_t* dev = &_usbh_devices[dev_addr];
+ const uint8_t rhport = dev->rhport;
+
+ tusb_control_request_t const * request = &_ctrl_xfer.request;
+
+ if (XFER_RESULT_SUCCESS != result)
+ {
+ TU_LOG2("Control failed: result = %d\r\n", result);
+
+ // terminate transfer if any stage failed
+ _xfer_complete(dev_addr, result);
+ }else
+ {
+ switch(_ctrl_xfer.stage)
+ {
+ case STAGE_SETUP:
+ _ctrl_xfer.stage = STAGE_DATA;
+ if (request->wLength)
+ {
+ // Note: initial data toggle is always 1
+ hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength);
+ return true;
+ }
+ __attribute__((fallthrough));
+
+ case STAGE_DATA:
+ _ctrl_xfer.stage = STAGE_ACK;
+
+ if (request->wLength)
+ {
+ TU_LOG2("Control data:\r\n");
+ TU_LOG2_MEM(_ctrl_xfer.buffer, request->wLength, 2);
+ }
+
+ // data toggle is always 1
+ hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0);
+ break;
+
+ case STAGE_ACK:
+ _xfer_complete(dev_addr, result);
+ break;
+
+ default: return false;
+ }
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/host/usbh_hcd.h b/sw/Core/Src/tinyusb/src/host/usbh_hcd.h
new file mode 100755
index 0000000..abc7fd2
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/host/usbh_hcd.h
@@ -0,0 +1,112 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_HCD
+ * @{ */
+
+#ifndef _TUSB_USBH_HCD_H_
+#define _TUSB_USBH_HCD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "common/tusb_common.h"
+#include "osal/osal.h"
+
+#ifndef CFG_TUH_EP_MAX
+#define CFG_TUH_EP_MAX 9
+#endif
+
+//--------------------------------------------------------------------+
+// USBH-HCD common data structure
+//--------------------------------------------------------------------+
+
+// TODO move to usbh.c
+typedef struct {
+ //------------- port -------------//
+ uint8_t rhport;
+ uint8_t hub_addr;
+ uint8_t hub_port;
+ uint8_t speed;
+
+ //------------- device descriptor -------------//
+ uint16_t vendor_id;
+ uint16_t product_id;
+ uint8_t ep0_packet_size;
+
+ //------------- configuration descriptor -------------//
+ // uint8_t interface_count; // bNumInterfaces alias
+
+ //------------- device -------------//
+ struct TU_ATTR_PACKED
+ {
+ uint8_t connected : 1;
+ uint8_t addressed : 1;
+ uint8_t configured : 1;
+ uint8_t suspended : 1;
+ };
+
+ volatile uint8_t state; // device state, value from enum tusbh_device_state_t
+
+ uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
+ uint8_t ep2drv[CFG_TUH_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid )
+
+ struct TU_ATTR_PACKED
+ {
+ volatile bool busy : 1;
+ volatile bool stalled : 1;
+ volatile bool claimed : 1;
+
+ // TODO merge ep2drv here, 4-bit should be sufficient
+ }ep_status[CFG_TUH_EP_MAX][2];
+
+ // Mutex for claiming endpoint, only needed when using with preempted RTOS
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_def_t mutexdef;
+ osal_mutex_t mutex;
+#endif
+
+} usbh_device_t;
+
+extern usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; // including zero-address
+
+//--------------------------------------------------------------------+
+// callback from HCD ISR
+//--------------------------------------------------------------------+
+
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_USBH_HCD_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/osal/osal.h b/sw/Core/Src/tinyusb/src/osal/osal.h
new file mode 100755
index 0000000..28bdf47
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/osal/osal.h
@@ -0,0 +1,104 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_OSAL_H_
+#define _TUSB_OSAL_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/** \addtogroup group_osal
+ * @{ */
+
+#include "common/tusb_common.h"
+
+// Return immediately
+#define OSAL_TIMEOUT_NOTIMEOUT (0)
+// Default timeout
+#define OSAL_TIMEOUT_NORMAL (10)
+// Wait forever
+#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX)
+
+#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER
+
+typedef void (*osal_task_func_t)( void * );
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+ #include "osal_none.h"
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+ #include "osal_freertos.h"
+#elif CFG_TUSB_OS == OPT_OS_MYNEWT
+ #include "osal_mynewt.h"
+#elif CFG_TUSB_OS == OPT_OS_PICO
+ #include "osal_pico.h"
+#elif CFG_TUSB_OS == OPT_OS_RTTHREAD
+ #include "osal_rtthread.h"
+#elif CFG_TUSB_OS == OPT_OS_CUSTOM
+ #include "tusb_os_custom.h" // implemented by application
+#else
+ #error OS is not supported yet
+#endif
+
+//--------------------------------------------------------------------+
+// OSAL Porting API
+//--------------------------------------------------------------------+
+
+//------------- Semaphore -------------//
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
+static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
+
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
+
+//------------- Mutex -------------//
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
+static inline bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
+
+//------------- Queue -------------//
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
+static inline bool osal_queue_receive(osal_queue_t qhdl, void* data);
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
+static inline bool osal_queue_empty(osal_queue_t qhdl);
+
+#if 0 // TODO remove subtask related macros later
+// Sub Task
+#define OSAL_SUBTASK_BEGIN
+#define OSAL_SUBTASK_END return TUSB_ERROR_NONE;
+
+#define STASK_RETURN(_error) return _error;
+#define STASK_INVOKE(_subtask, _status) (_status) = _subtask
+#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+/** @} */
+
+#endif /* _TUSB_OSAL_H_ */
diff --git a/sw/Core/Src/tinyusb/src/osal/osal_freertos.h b/sw/Core/Src/tinyusb/src/osal/osal_freertos.h
new file mode 100755
index 0000000..004bd1b
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/osal/osal_freertos.h
@@ -0,0 +1,172 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_OSAL_FREERTOS_H_
+#define _TUSB_OSAL_FREERTOS_H_
+
+// FreeRTOS Headers
+#include "FreeRTOS.h"
+#include "semphr.h"
+#include "queue.h"
+#include "task.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+static inline void osal_task_delay(uint32_t msec)
+{
+ vTaskDelay( pdMS_TO_TICKS(msec) );
+}
+
+//--------------------------------------------------------------------+
+// Semaphore API
+//--------------------------------------------------------------------+
+typedef StaticSemaphore_t osal_semaphore_def_t;
+typedef SemaphoreHandle_t osal_semaphore_t;
+
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
+{
+ return xSemaphoreCreateBinaryStatic(semdef);
+}
+
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
+{
+ if ( !in_isr )
+ {
+ return xSemaphoreGive(sem_hdl) != 0;
+ }
+ else
+ {
+ BaseType_t xHigherPriorityTaskWoken;
+ BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
+
+#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
+ if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
+#else
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+#endif
+
+ return res != 0;
+ }
+}
+
+static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
+{
+ uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? portMAX_DELAY : pdMS_TO_TICKS(msec);
+ return xSemaphoreTake(sem_hdl, ticks);
+}
+
+static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl)
+{
+ xQueueReset(sem_hdl);
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API (priority inheritance)
+//--------------------------------------------------------------------+
+typedef StaticSemaphore_t osal_mutex_def_t;
+typedef SemaphoreHandle_t osal_mutex_t;
+
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
+{
+ return xSemaphoreCreateMutexStatic(mdef);
+}
+
+static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
+{
+ return osal_semaphore_wait(mutex_hdl, msec);
+}
+
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
+{
+ return xSemaphoreGive(mutex_hdl);
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
+ static _type _name##_##buf[_depth];\
+ osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf };
+
+typedef struct
+{
+ uint16_t depth;
+ uint16_t item_sz;
+ void* buf;
+
+ StaticQueue_t sq;
+}osal_queue_def_t;
+
+typedef QueueHandle_t osal_queue_t;
+
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
+{
+ return xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq);
+}
+
+static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
+{
+ return xQueueReceive(qhdl, data, portMAX_DELAY);
+}
+
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
+{
+ if ( !in_isr )
+ {
+ return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
+ }
+ else
+ {
+ BaseType_t xHigherPriorityTaskWoken;
+ BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
+
+#if CFG_TUSB_MCU == OPT_MCU_ESP32S2
+ if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
+#else
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+#endif
+
+ return res != 0;
+ }
+}
+
+static inline bool osal_queue_empty(osal_queue_t qhdl)
+{
+ return uxQueueMessagesWaiting(qhdl) == 0;
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_OSAL_FREERTOS_H_ */
diff --git a/sw/Core/Src/tinyusb/src/osal/osal_mynewt.h b/sw/Core/Src/tinyusb/src/osal/osal_mynewt.h
new file mode 100755
index 0000000..6882329
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/osal/osal_mynewt.h
@@ -0,0 +1,174 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef OSAL_MYNEWT_H_
+#define OSAL_MYNEWT_H_
+
+#include "os/os.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+static inline void osal_task_delay(uint32_t msec)
+{
+ os_time_delay( os_time_ms_to_ticks32(msec) );
+}
+
+//--------------------------------------------------------------------+
+// Semaphore API
+//--------------------------------------------------------------------+
+typedef struct os_sem osal_semaphore_def_t;
+typedef struct os_sem* osal_semaphore_t;
+
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
+{
+ return (os_sem_init(semdef, 0) == OS_OK) ? (osal_semaphore_t) semdef : NULL;
+}
+
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
+{
+ (void) in_isr;
+ return os_sem_release(sem_hdl) == OS_OK;
+}
+
+static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec)
+{
+ uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
+ return os_sem_pend(sem_hdl, ticks) == OS_OK;
+}
+
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
+{
+ // TODO implement later
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API (priority inheritance)
+//--------------------------------------------------------------------+
+typedef struct os_mutex osal_mutex_def_t;
+typedef struct os_mutex* osal_mutex_t;
+
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
+{
+ return (os_mutex_init(mdef) == OS_OK) ? (osal_mutex_t) mdef : NULL;
+}
+
+static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec)
+{
+ uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
+ return os_mutex_pend(mutex_hdl, ticks) == OS_OK;
+}
+
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
+{
+ return os_mutex_release(mutex_hdl) == OS_OK;
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
+ static _type _name##_##buf[_depth];\
+ static struct os_event _name##_##evbuf[_depth];\
+ osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, .evbuf = _name##_##evbuf};\
+
+typedef struct
+{
+ uint16_t depth;
+ uint16_t item_sz;
+ void* buf;
+ void* evbuf;
+
+ struct os_mempool mpool;
+ struct os_mempool epool;
+
+ struct os_eventq evq;
+}osal_queue_def_t;
+
+typedef osal_queue_def_t* osal_queue_t;
+
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
+{
+ if ( OS_OK != os_mempool_init(&qdef->mpool, qdef->depth, qdef->item_sz, qdef->buf, "usbd queue") ) return NULL;
+ if ( OS_OK != os_mempool_init(&qdef->epool, qdef->depth, sizeof(struct os_event), qdef->evbuf, "usbd evqueue") ) return NULL;
+
+ os_eventq_init(&qdef->evq);
+ return (osal_queue_t) qdef;
+}
+
+static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
+{
+ struct os_event* ev;
+ ev = os_eventq_get(&qhdl->evq);
+
+ memcpy(data, ev->ev_arg, qhdl->item_sz); // copy message
+ os_memblock_put(&qhdl->mpool, ev->ev_arg); // put back mem block
+ os_memblock_put(&qhdl->epool, ev); // put back ev block
+
+ return true;
+}
+
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
+{
+ (void) in_isr;
+
+ // get a block from mem pool for data
+ void* ptr = os_memblock_get(&qhdl->mpool);
+ if (!ptr) return false;
+ memcpy(ptr, data, qhdl->item_sz);
+
+ // get a block from event pool to put into queue
+ struct os_event* ev = (struct os_event*) os_memblock_get(&qhdl->epool);
+ if (!ev)
+ {
+ os_memblock_put(&qhdl->mpool, ptr);
+ return false;
+ }
+ tu_memclr(ev, sizeof(struct os_event));
+ ev->ev_arg = ptr;
+
+ os_eventq_put(&qhdl->evq, ev);
+
+ return true;
+}
+
+static inline bool osal_queue_empty(osal_queue_t qhdl)
+{
+ return STAILQ_EMPTY(&qhdl->evq.evq_list);
+}
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* OSAL_MYNEWT_H_ */
diff --git a/sw/Core/Src/tinyusb/src/osal/osal_none.h b/sw/Core/Src/tinyusb/src/osal/osal_none.h
new file mode 100755
index 0000000..a1f997c
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/osal/osal_none.h
@@ -0,0 +1,204 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_OSAL_NONE_H_
+#define _TUSB_OSAL_NONE_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+
+
+//--------------------------------------------------------------------+
+// Binary Semaphore API
+//--------------------------------------------------------------------+
+typedef struct
+{
+ volatile uint16_t count;
+}osal_semaphore_def_t;
+
+typedef osal_semaphore_def_t* osal_semaphore_t;
+
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
+{
+ semdef->count = 0;
+ return semdef;
+}
+
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
+{
+ (void) in_isr;
+ sem_hdl->count++;
+ return true;
+}
+
+// TODO blocking for now
+static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
+{
+ (void) msec;
+
+ while (sem_hdl->count == 0) { }
+ sem_hdl->count--;
+
+ return true;
+}
+
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
+{
+ sem_hdl->count = 0;
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API
+// Within tinyusb, mutex is never used in ISR context
+//--------------------------------------------------------------------+
+typedef osal_semaphore_def_t osal_mutex_def_t;
+typedef osal_semaphore_t osal_mutex_t;
+
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
+{
+ mdef->count = 1;
+ return mdef;
+}
+
+static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
+{
+ return osal_semaphore_wait(mutex_hdl, msec);
+}
+
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
+{
+ return osal_semaphore_post(mutex_hdl, false);
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+#include "common/tusb_fifo.h"
+
+// extern to avoid including dcd.h and hcd.h
+#if TUSB_OPT_DEVICE_ENABLED
+extern void dcd_int_disable(uint8_t rhport);
+extern void dcd_int_enable(uint8_t rhport);
+#endif
+
+#if TUSB_OPT_HOST_ENABLED
+extern void hcd_int_disable(uint8_t rhport);
+extern void hcd_int_enable(uint8_t rhport);
+#endif
+
+typedef struct
+{
+ uint8_t role; // device or host
+ tu_fifo_t ff;
+}osal_queue_def_t;
+
+typedef osal_queue_def_t* osal_queue_t;
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
+ uint8_t _name##_buf[_depth*sizeof(_type)]; \
+ osal_queue_def_t _name = { \
+ .role = _role, \
+ .ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
+ }
+
+// lock queue by disable USB interrupt
+static inline void _osal_q_lock(osal_queue_t qhdl)
+{
+ (void) qhdl;
+
+#if TUSB_OPT_DEVICE_ENABLED
+ if (qhdl->role == OPT_MODE_DEVICE) dcd_int_disable(TUD_OPT_RHPORT);
+#endif
+
+#if TUSB_OPT_HOST_ENABLED
+ if (qhdl->role == OPT_MODE_HOST) hcd_int_disable(TUH_OPT_RHPORT);
+#endif
+}
+
+// unlock queue
+static inline void _osal_q_unlock(osal_queue_t qhdl)
+{
+ (void) qhdl;
+
+#if TUSB_OPT_DEVICE_ENABLED
+ if (qhdl->role == OPT_MODE_DEVICE) dcd_int_enable(TUD_OPT_RHPORT);
+#endif
+
+#if TUSB_OPT_HOST_ENABLED
+ if (qhdl->role == OPT_MODE_HOST) hcd_int_enable(TUH_OPT_RHPORT);
+#endif
+}
+
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
+{
+ tu_fifo_clear(&qdef->ff);
+ return (osal_queue_t) qdef;
+}
+
+static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
+{
+ _osal_q_lock(qhdl);
+ bool success = tu_fifo_read(&qhdl->ff, data);
+ _osal_q_unlock(qhdl);
+
+ return success;
+}
+
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
+{
+ if (!in_isr) {
+ _osal_q_lock(qhdl);
+ }
+
+ bool success = tu_fifo_write(&qhdl->ff, data);
+
+ if (!in_isr) {
+ _osal_q_unlock(qhdl);
+ }
+
+ TU_ASSERT(success);
+
+ return success;
+}
+
+static inline bool osal_queue_empty(osal_queue_t qhdl)
+{
+ // Skip queue lock/unlock since this function is primarily called
+ // with interrupt disabled before going into low power mode
+ return tu_fifo_empty(&qhdl->ff);
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_OSAL_NONE_H_ */
diff --git a/sw/Core/Src/tinyusb/src/osal/osal_pico.h b/sw/Core/Src/tinyusb/src/osal/osal_pico.h
new file mode 100755
index 0000000..c277af2
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/osal/osal_pico.h
@@ -0,0 +1,185 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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_OSAL_PICO_H_
+#define _TUSB_OSAL_PICO_H_
+
+#include "pico/time.h"
+#include "pico/sem.h"
+#include "pico/mutex.h"
+#include "pico/critical_section.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+static inline void osal_task_delay(uint32_t msec)
+{
+ sleep_ms(msec);
+}
+
+//--------------------------------------------------------------------+
+// Binary Semaphore API
+//--------------------------------------------------------------------+
+typedef struct semaphore osal_semaphore_def_t, *osal_semaphore_t;
+
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
+{
+ sem_init(semdef, 0, 255);
+ return semdef;
+}
+
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
+{
+ sem_release(sem_hdl);
+ return true;
+}
+
+static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
+{
+ return sem_acquire_timeout_ms(sem_hdl, msec);
+}
+
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
+{
+ sem_reset(sem_hdl, 0);
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API
+// Within tinyusb, mutex is never used in ISR context
+//--------------------------------------------------------------------+
+typedef struct mutex osal_mutex_def_t, *osal_mutex_t;
+
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
+{
+ mutex_init(mdef);
+ return mdef;
+}
+
+static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
+{
+ return mutex_enter_timeout_ms(mutex_hdl, msec);
+}
+
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
+{
+ mutex_exit(mutex_hdl);
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+#include "common/tusb_fifo.h"
+
+#if TUSB_OPT_HOST_ENABLED
+extern void hcd_int_disable(uint8_t rhport);
+extern void hcd_int_enable(uint8_t rhport);
+#endif
+
+typedef struct
+{
+ tu_fifo_t ff;
+ struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
+} osal_queue_def_t;
+
+typedef osal_queue_def_t* osal_queue_t;
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
+ uint8_t _name##_buf[_depth*sizeof(_type)]; \
+ osal_queue_def_t _name = { \
+ .ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
+ }
+
+// lock queue by disable USB interrupt
+static inline void _osal_q_lock(osal_queue_t qhdl)
+{
+ critical_section_enter_blocking(&qhdl->critsec);
+}
+
+// unlock queue
+static inline void _osal_q_unlock(osal_queue_t qhdl)
+{
+ critical_section_exit(&qhdl->critsec);
+}
+
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
+{
+ critical_section_init(&qdef->critsec);
+ tu_fifo_clear(&qdef->ff);
+ return (osal_queue_t) qdef;
+}
+
+static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
+{
+ // TODO: revisit... docs say that mutexes are never used from IRQ context,
+ // however osal_queue_recieve may be. therefore my assumption is that
+ // the fifo mutex is not populated for queues used from an IRQ context
+ assert(!qhdl->ff.mutex);
+
+ _osal_q_lock(qhdl);
+ bool success = tu_fifo_read(&qhdl->ff, data);
+ _osal_q_unlock(qhdl);
+
+ return success;
+}
+
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
+{
+ // TODO: revisit... docs say that mutexes are never used from IRQ context,
+ // however osal_queue_recieve may be. therefore my assumption is that
+ // the fifo mutex is not populated for queues used from an IRQ context
+ assert(!qhdl->ff.mutex);
+
+ _osal_q_lock(qhdl);
+ bool success = tu_fifo_write(&qhdl->ff, data);
+ _osal_q_unlock(qhdl);
+
+ TU_ASSERT(success);
+
+ return success;
+}
+
+static inline bool osal_queue_empty(osal_queue_t qhdl)
+{
+ // TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single
+ // volatile read.
+
+ // Skip queue lock/unlock since this function is primarily called
+ // with interrupt disabled before going into low power mode
+ return tu_fifo_empty(&qhdl->ff);
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_OSAL_PICO_H_ */
diff --git a/sw/Core/Src/tinyusb/src/osal/osal_rtthread.h b/sw/Core/Src/tinyusb/src/osal/osal_rtthread.h
new file mode 100755
index 0000000..d5c062a
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/osal/osal_rtthread.h
@@ -0,0 +1,130 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 tfx2001 (2479727366@qq.com)
+ *
+ * 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_OSAL_RTTHREAD_H_
+#define _TUSB_OSAL_RTTHREAD_H_
+
+// RT-Thread Headers
+#include "rtthread.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+static inline void osal_task_delay(uint32_t msec) {
+ rt_thread_mdelay(msec);
+}
+
+//--------------------------------------------------------------------+
+// Semaphore API
+//--------------------------------------------------------------------+
+typedef struct rt_semaphore osal_semaphore_def_t;
+typedef rt_sem_t osal_semaphore_t;
+
+static inline osal_semaphore_t
+osal_semaphore_create(osal_semaphore_def_t *semdef) {
+ rt_sem_init(semdef, "tusb", 0, RT_IPC_FLAG_FIFO);
+ return semdef;
+}
+
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
+ (void) in_isr;
+ return rt_sem_release(sem_hdl) == RT_EOK;
+}
+
+static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
+ return rt_sem_take(sem_hdl, rt_tick_from_millisecond(msec)) == RT_EOK;
+}
+
+static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
+ // TODO: implement
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API (priority inheritance)
+//--------------------------------------------------------------------+
+typedef struct rt_mutex osal_mutex_def_t;
+typedef rt_mutex_t osal_mutex_t;
+
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
+ rt_mutex_init(mdef, "tusb", RT_IPC_FLAG_FIFO);
+ return mdef;
+}
+
+static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
+ return rt_mutex_take(mutex_hdl, rt_tick_from_millisecond(msec)) == RT_EOK;
+}
+
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
+ return rt_mutex_release(mutex_hdl) == RT_EOK;
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
+ static _type _name##_##buf[_depth]; \
+ osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf };
+
+typedef struct {
+ uint16_t depth;
+ uint16_t item_sz;
+ void *buf;
+
+ struct rt_messagequeue sq;
+} osal_queue_def_t;
+
+typedef rt_mq_t osal_queue_t;
+
+static inline osal_queue_t osal_queue_create(osal_queue_def_t *qdef) {
+ rt_mq_init(&(qdef->sq), "tusb", qdef->buf, qdef->item_sz,
+ qdef->item_sz * qdef->depth, RT_IPC_FLAG_FIFO);
+ return &(qdef->sq);
+}
+
+static inline bool osal_queue_receive(osal_queue_t qhdl, void *data) {
+ return rt_mq_recv(qhdl, data, qhdl->msg_size, RT_WAITING_FOREVER) == RT_EOK;
+}
+
+static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
+ (void) in_isr;
+ return rt_mq_send(qhdl, (void *)data, qhdl->msg_size) == RT_EOK;
+}
+
+static inline bool osal_queue_empty(osal_queue_t qhdl) {
+ return (qhdl->entry) == 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_OSAL_RTTHREAD_H_ */
diff --git a/sw/Core/Src/tinyusb/src/portable/dialog/da146xx/dcd_da146xx.c b/sw/Core/Src/tinyusb/src/portable/dialog/da146xx/dcd_da146xx.c
new file mode 100755
index 0000000..59f728d
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -0,0 +1,1085 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_DA1469X
+
+#include "DA1469xAB.h"
+
+#include "device/dcd.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+
+// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
+// We disable SOF for now until needed later on
+#define USE_SOF 0
+
+// Size of RX or TX FIFO.
+#define FIFO_SIZE 64
+
+#ifndef TU_DA1469X_FIFO_READ_THRESHOLD
+// RX FIFO is 64 bytes. When endpoint size is greater then 64, FIFO warning interrupt
+// is enabled to allow read incoming data during frame reception.
+// It is possible to stay in interrupt reading whole packet at once, but it may be
+// more efficient for MCU to read as much data as possible and when FIFO is hardly
+// filled exit interrupt handler waiting for next FIFO warning level interrupt
+// or packet end.
+// When running at 96MHz code that reads FIFO based on number of bytes stored in
+// USB_RXSx_REG.USB_RXCOUNT takes enough time to fill FIFO with two additional bytes.
+// Settings this threshold above this allows to leave interrupt handler and wait
+// for more bytes to before next ISR. This allows reduce overall ISR time to 1/3
+// of time that would be needed if ISR read as fast as possible.
+#define TU_DA1469X_FIFO_READ_THRESHOLD 4
+#endif
+
+#define EP_MAX 4
+
+#define NFSR_NODE_RESET 0
+#define NFSR_NODE_RESUME 1
+#define NFSR_NODE_OPERATIONAL 2
+#define NFSR_NODE_SUSPEND 3
+
+static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
+
+typedef struct
+{
+ union
+ {
+ __IOM uint32_t epc_in;
+ __IOM uint32_t USB_EPC0_REG; /*!< (@ 0x00000080) Endpoint Control 0 Register */
+ __IOM uint32_t USB_EPC1_REG; /*!< (@ 0x000000A0) Endpoint Control Register 1 */
+ __IOM uint32_t USB_EPC3_REG; /*!< (@ 0x000000C0) Endpoint Control Register 3 */
+ __IOM uint32_t USB_EPC5_REG; /*!< (@ 0x000000E0) Endpoint Control Register 5 */
+ };
+ union
+ {
+ __IOM uint32_t txd;
+ __IOM uint32_t USB_TXD0_REG; /*!< (@ 0x00000084) Transmit Data 0 Register */
+ __IOM uint32_t USB_TXD1_REG; /*!< (@ 0x000000A4) Transmit Data Register 1 */
+ __IOM uint32_t USB_TXD2_REG; /*!< (@ 0x000000C4) Transmit Data Register 2 */
+ __IOM uint32_t USB_TXD3_REG; /*!< (@ 0x000000E4) Transmit Data Register 3 */
+ };
+ union
+ {
+ __IOM uint32_t txs;
+ __IOM uint32_t USB_TXS0_REG; /*!< (@ 0x00000088) Transmit Status 0 Register */
+ __IOM uint32_t USB_TXS1_REG; /*!< (@ 0x000000A8) Transmit Status Register 1 */
+ __IOM uint32_t USB_TXS2_REG; /*!< (@ 0x000000C8) Transmit Status Register 2 */
+ __IOM uint32_t USB_TXS3_REG; /*!< (@ 0x000000E8) Transmit Status Register 3 */
+ };
+ union
+ {
+ __IOM uint32_t txc;
+ __IOM uint32_t USB_TXC0_REG; /*!< (@ 0x0000008C) Transmit command 0 Register */
+ __IOM uint32_t USB_TXC1_REG; /*!< (@ 0x000000AC) Transmit Command Register 1 */
+ __IOM uint32_t USB_TXC2_REG; /*!< (@ 0x000000CC) Transmit Command Register 2 */
+ __IOM uint32_t USB_TXC3_REG; /*!< (@ 0x000000EC) Transmit Command Register 3 */
+ };
+ union
+ {
+ __IOM uint32_t epc_out;
+ __IOM uint32_t USB_EP0_NAK_REG; /*!< (@ 0x00000090) EP0 INNAK and OUTNAK Register */
+ __IOM uint32_t USB_EPC2_REG; /*!< (@ 0x000000B0) Endpoint Control Register 2 */
+ __IOM uint32_t USB_EPC4_REG; /*!< (@ 0x000000D0) Endpoint Control Register 4 */
+ __IOM uint32_t USB_EPC6_REG; /*!< (@ 0x000000F0) Endpoint Control Register 6 */
+ };
+ union
+ {
+ __IOM uint32_t rxd;
+ __IOM uint32_t USB_RXD0_REG; /*!< (@ 0x00000094) Receive Data 0 Register */
+ __IOM uint32_t USB_RXD1_REG; /*!< (@ 0x000000B4) Receive Data Register,1 */
+ __IOM uint32_t USB_RXD2_REG; /*!< (@ 0x000000D4) Receive Data Register 2 */
+ __IOM uint32_t USB_RXD3_REG; /*!< (@ 0x000000F4) Receive Data Register 3 */
+ };
+ union
+ {
+ __IOM uint32_t rxs;
+ __IOM uint32_t USB_RXS0_REG; /*!< (@ 0x00000098) Receive Status 0 Register */
+ __IOM uint32_t USB_RXS1_REG; /*!< (@ 0x000000B8) Receive Status Register 1 */
+ __IOM uint32_t USB_RXS2_REG; /*!< (@ 0x000000D8) Receive Status Register 2 */
+ __IOM uint32_t USB_RXS3_REG; /*!< (@ 0x000000F8) Receive Status Register 3 */
+ };
+ union
+ {
+ __IOM uint32_t rxc;
+ __IOM uint32_t USB_RXC0_REG; /*!< (@ 0x0000009C) Receive Command 0 Register */
+ __IOM uint32_t USB_RXC1_REG; /*!< (@ 0x000000BC) Receive Command Register 1 */
+ __IOM uint32_t USB_RXC2_REG; /*!< (@ 0x000000DC) Receive Command Register 2 */
+ __IOM uint32_t USB_RXC3_REG; /*!< (@ 0x000000FC) Receive Command Register 3 */
+ };
+} EPx_REGS;
+
+#define EP_REGS(first_ep_reg) (EPx_REGS*)(&USB->first_ep_reg)
+
+// DMA channel pair to use, channel 6 will be used for RX channel 7 for TX direction.
+#ifndef TU_DA146XX_DMA_RX_CHANNEL
+#define TU_DA146XX_DMA_RX_CHANNEL 6
+#endif
+#define DA146XX_DMA_USB_MUX (0x6 << (TU_DA146XX_DMA_RX_CHANNEL * 2))
+#define DA146XX_DMA_USB_MUX_MASK (0xF << (TU_DA146XX_DMA_RX_CHANNEL * 2))
+
+typedef struct
+{
+ __IOM uint32_t DMAx_A_START_REG;
+ __IOM uint32_t DMAx_B_START_REG;
+ __IOM uint32_t DMAx_INT_REG;
+ __IOM uint32_t DMAx_LEN_REG;
+ __IOM uint32_t DMAx_CTRL_REG;
+ __IOM uint32_t DMAx_IDX_REG;
+ __IM uint32_t RESERVED[2]; // Extend structure size for array like usage, registers for each channel are 0x20 bytes apart.
+} da146xx_dma_channel_t;
+
+#define DMA_CHANNEL_REGS(n) ((da146xx_dma_channel_t *)(DMA) + n)
+#define RX_DMA_REGS DMA_CHANNEL_REGS(TU_DA146XX_DMA_RX_CHANNEL)
+#define TX_DMA_REGS DMA_CHANNEL_REGS((TU_DA146XX_DMA_RX_CHANNEL) + 1)
+
+#define RX_DMA_START ((1 << DMA_DMA0_CTRL_REG_DMA_ON_Pos) |\
+ (0 << DMA_DMA0_CTRL_REG_BW_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_DREQ_MODE_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_BINC_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_AINC_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_CIRCULAR_Pos) | \
+ (2 << DMA_DMA0_CTRL_REG_DMA_PRIO_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_IDLE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_INIT_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_REQ_SENSE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BURST_MODE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BUS_ERROR_DETECT_Pos))
+
+#define TX_DMA_START ((1 << DMA_DMA0_CTRL_REG_DMA_ON_Pos) |\
+ (0 << DMA_DMA0_CTRL_REG_BW_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_DREQ_MODE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BINC_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_AINC_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_CIRCULAR_Pos) | \
+ (2 << DMA_DMA0_CTRL_REG_DMA_PRIO_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_IDLE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_INIT_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_REQ_SENSE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BURST_MODE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BUS_ERROR_DETECT_Pos))
+
+// Dialog register fields and bit mask are very long. Filed masks repeat register names.
+// Those convenience macros are a way to reduce complexity of register modification lines.
+#define GET_BIT(val, field) (val & field ## _Msk) >> field ## _Pos
+#define REG_GET_BIT(reg, field) (USB->reg & USB_ ## reg ## _ ## field ## _Msk)
+#define REG_SET_BIT(reg, field) USB->reg |= USB_ ## reg ## _ ## field ## _Msk
+#define REG_CLR_BIT(reg, field) USB->reg &= ~USB_ ## reg ## _ ## field ## _Msk
+#define REG_SET_VAL(reg, field, val) USB->reg = (USB->reg & ~USB_ ## reg ## _ ## field ## _Msk) | (val << USB_ ## reg ## _ ## field ## _Pos)
+
+typedef struct {
+ EPx_REGS * regs;
+ uint8_t * buffer;
+ // Total length of current transfer
+ uint16_t total_len;
+ // Bytes transferred so far
+ uint16_t transferred;
+ uint16_t max_packet_size;
+ // Packet size sent or received so far. It is used to modify transferred field
+ // after ACK is received or when filling ISO endpoint with size larger then
+ // FIFO size.
+ uint16_t last_packet_size;
+ uint8_t ep_addr;
+ // DATA0/1 toggle bit 1 DATA1 is expected or transmitted
+ uint8_t data1 : 1;
+ // Endpoint is stalled
+ uint8_t stall : 1;
+ // ISO endpoint
+ uint8_t iso : 1;
+} xfer_ctl_t;
+
+static struct
+{
+ bool vbus_present;
+ bool in_reset;
+ xfer_ctl_t xfer_status[EP_MAX][2];
+ // Endpoints that use DMA, one for each direction
+ uint8_t dma_ep[2];
+} _dcd =
+{
+ .vbus_present = false,
+ .xfer_status =
+ {
+ { { .regs = EP_REGS(USB_EPC0_REG) }, { .regs = EP_REGS(USB_EPC0_REG) } },
+ { { .regs = EP_REGS(USB_EPC1_REG) }, { .regs = EP_REGS(USB_EPC1_REG) } },
+ { { .regs = EP_REGS(USB_EPC3_REG) }, { .regs = EP_REGS(USB_EPC3_REG) } },
+ { { .regs = EP_REGS(USB_EPC5_REG) }, { .regs = EP_REGS(USB_EPC5_REG) } },
+ }
+};
+
+// Two endpoint 0 descriptor definition for unified dcd_edpt_open()
+static const tusb_desc_endpoint_t ep0OUT_desc =
+{
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+
+ .bEndpointAddress = 0x00,
+ .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
+ .wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
+ .bInterval = 0
+};
+
+static const tusb_desc_endpoint_t ep0IN_desc =
+{
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+
+ .bEndpointAddress = 0x80,
+ .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
+ .wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
+ .bInterval = 0
+};
+
+#define XFER_CTL_BASE(_ep, _dir) &_dcd.xfer_status[_ep][_dir]
+
+// Function could be called when VBUS change was detected.
+void tusb_vbus_changed(bool present)
+{
+ if (present != _dcd.vbus_present)
+ {
+ _dcd.vbus_present = present;
+ if (present)
+ {
+ USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
+ USB->USB_NFSR_REG = 0;
+ USB->USB_FAR_REG = 0x80;
+ USB->USB_NFSR_REG = NFSR_NODE_RESET;
+ USB->USB_TXMSK_REG = 0;
+ USB->USB_RXMSK_REG = 0;
+
+ USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
+ USB_USB_MAMSK_REG_USB_M_ALT_Msk |
+ USB_USB_MAMSK_REG_USB_M_WARN_Msk;
+ USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk;
+ }
+ else
+ {
+ USB->USB_MCTRL_REG = 0;
+ }
+ }
+}
+
+static void fill_tx_fifo(xfer_ctl_t * xfer)
+{
+ int left_to_send;
+ uint8_t const *src;
+ EPx_REGS *regs = xfer->regs;
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
+
+ src = &xfer->buffer[xfer->transferred];
+ left_to_send = xfer->total_len - xfer->transferred;
+ if (left_to_send > xfer->max_packet_size - xfer->last_packet_size)
+ {
+ left_to_send = xfer->max_packet_size - xfer->last_packet_size;
+ }
+
+ // Loop checks TCOUNT all the time since this value is saturated to 31
+ // and can't be read just once before.
+ while ((regs->txs & USB_USB_TXS1_REG_USB_TCOUNT_Msk) > 0 && left_to_send > 0)
+ {
+ regs->txd = *src++;
+ xfer->last_packet_size++;
+ left_to_send--;
+ }
+ if (epnum != 0)
+ {
+ if (left_to_send > 0)
+ {
+ // Max packet size is set to value greater then FIFO. Enable fifo level warning
+ // to handle larger packets.
+ regs->txc |= (3 << USB_USB_TXC1_REG_USB_TFWL_Pos);
+ USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos);
+ }
+ else
+ {
+ xfer->regs->txc &= ~USB_USB_TXC1_REG_USB_TFWL_Msk;
+ USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos));
+ // Whole packet already in fifo, no need to refill it later. Mark last.
+ regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
+ }
+ }
+}
+
+static bool try_allocate_dma(uint8_t epnum, uint8_t dir)
+{
+ // TODO: Disable interrupts while checking
+ if (_dcd.dma_ep[dir] == 0)
+ {
+ _dcd.dma_ep[dir] = epnum;
+ if (dir == TUSB_DIR_OUT)
+ USB->USB_DMA_CTRL_REG = (USB->USB_DMA_CTRL_REG & ~USB_USB_DMA_CTRL_REG_USB_DMA_RX_Msk) |
+ ((epnum - 1) << USB_USB_DMA_CTRL_REG_USB_DMA_RX_Pos);
+ else
+ USB->USB_DMA_CTRL_REG = (USB->USB_DMA_CTRL_REG & ~USB_USB_DMA_CTRL_REG_USB_DMA_TX_Msk) |
+ ((epnum - 1) << USB_USB_DMA_CTRL_REG_USB_DMA_TX_Pos);
+ USB->USB_DMA_CTRL_REG |= USB_USB_DMA_CTRL_REG_USB_DMA_EN_Msk;
+ }
+ return _dcd.dma_ep[dir] == epnum;
+}
+
+static void start_rx_dma(volatile void *src, void *dst, uint16_t size)
+{
+ // Setup SRC and DST registers
+ RX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src;
+ RX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst;
+ // Don't need DMA interrupt, read end is determined by RX_LAST or RX_ERR events.
+ RX_DMA_REGS->DMAx_INT_REG = size - 1;
+ RX_DMA_REGS->DMAx_LEN_REG = size - 1;
+ RX_DMA_REGS->DMAx_CTRL_REG = RX_DMA_START;
+}
+
+static void start_rx_packet(xfer_ctl_t *xfer)
+{
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
+ uint16_t remaining = xfer->total_len - xfer->transferred;
+ uint16_t size = tu_min16(remaining, xfer->max_packet_size);
+
+ xfer->last_packet_size = 0;
+ if (xfer->max_packet_size > FIFO_SIZE && remaining > FIFO_SIZE)
+ {
+ if (try_allocate_dma(epnum, TUSB_DIR_OUT))
+ {
+ start_rx_dma(&xfer->regs->rxd, xfer->buffer + xfer->transferred, size);
+ }
+ else
+ {
+ // Other endpoint is using DMA in that direction, fall back to interrupts.
+ // For endpoint size greater then FIFO size enable FIFO level warning interrupt
+ // when FIFO has less then 17 bytes free.
+ xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk;
+ USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos);
+ }
+ }
+ else if (epnum != 0)
+ {
+ // If max_packet_size would fit in FIFO no need for FIFO level warning interrupt.
+ xfer->regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk;
+ USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos));
+ }
+ xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
+}
+
+static void start_tx_dma(void *src, volatile void *dst, uint16_t size)
+{
+ // Setup SRC and DST registers
+ TX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src;
+ TX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst;
+ // Interrupt not needed
+ TX_DMA_REGS->DMAx_INT_REG = size;
+ TX_DMA_REGS->DMAx_LEN_REG = size - 1;
+ TX_DMA_REGS->DMAx_CTRL_REG = TX_DMA_START;
+}
+
+static void start_tx_packet(xfer_ctl_t *xfer)
+{
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
+ uint16_t remaining = xfer->total_len - xfer->transferred;
+ uint16_t size = tu_min16(remaining, xfer->max_packet_size);
+ EPx_REGS *regs = xfer->regs;
+
+ xfer->last_packet_size = 0;
+
+ regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk;
+ regs->txc = USB_USB_TXC1_REG_USB_IGN_ISOMSK_Msk;
+ if (xfer->data1) xfer->regs->txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk;
+
+ if (xfer->max_packet_size > FIFO_SIZE && remaining > FIFO_SIZE && try_allocate_dma(epnum, TUSB_DIR_IN))
+ {
+ // Whole packet will be put in FIFO by DMA. Set LAST bit before start.
+ start_tx_dma(xfer->buffer + xfer->transferred, &regs->txd, size);
+ regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
+ }
+ else
+ {
+ fill_tx_fifo(xfer);
+ }
+ regs->txc |= USB_USB_TXC1_REG_USB_TX_EN_Msk;
+}
+
+static void read_rx_fifo(xfer_ctl_t *xfer, uint16_t bytes_in_fifo)
+{
+ EPx_REGS *regs = xfer->regs;
+ uint16_t remaining = xfer->total_len - xfer->transferred - xfer->last_packet_size;
+ uint16_t receive_this_time = bytes_in_fifo;
+
+ if (remaining < bytes_in_fifo) receive_this_time = remaining;
+
+ uint8_t *buf = xfer->buffer + xfer->transferred + xfer->last_packet_size;
+
+ for (int i = 0; i < receive_this_time; ++i) buf[i] = regs->rxd;
+
+ xfer->last_packet_size += receive_this_time;
+}
+
+static void handle_ep0_rx(void)
+{
+ int fifo_bytes;
+ uint32_t rxs0 = USB->USB_RXS0_REG;
+
+ xfer_ctl_t *xfer = XFER_CTL_BASE(0, TUSB_DIR_OUT);
+
+ fifo_bytes = GET_BIT(rxs0, USB_USB_RXS0_REG_USB_RCOUNT);
+ if (rxs0 & USB_USB_RXS0_REG_USB_SETUP_Msk)
+ {
+ xfer_ctl_t *xfer_in = XFER_CTL_BASE(0, TUSB_DIR_IN);
+ // Setup packet is in
+ for (int i = 0; i < fifo_bytes; ++i) _setup_packet[i] = USB->USB_RXD0_REG;
+
+ xfer->stall = 0;
+ xfer->data1 = 1;
+ xfer_in->stall = 0;
+ xfer_in->data1 = 1;
+ REG_SET_BIT(USB_TXC0_REG, USB_TOGGLE_TX0);
+ REG_CLR_BIT(USB_EPC0_REG, USB_STALL);
+ dcd_event_setup_received(0, _setup_packet,true);
+ }
+ else
+ {
+ if (GET_BIT(rxs0, USB_USB_RXS0_REG_USB_TOGGLE_RX0) != xfer->data1)
+ {
+ // Toggle bit does not match discard packet
+ REG_SET_BIT(USB_RXC0_REG, USB_FLUSH);
+ xfer->last_packet_size = 0;
+ }
+ else
+ {
+ read_rx_fifo(xfer, fifo_bytes);
+ if (rxs0 & USB_USB_RXS0_REG_USB_RX_LAST_Msk)
+ {
+ xfer->transferred += xfer->last_packet_size;
+ xfer->data1 ^= 1;
+
+ if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size)
+ {
+ dcd_event_xfer_complete(0, 0, xfer->transferred, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Re-enable reception
+ REG_SET_BIT(USB_RXC0_REG, USB_RX_EN);
+ }
+ xfer->last_packet_size = 0;
+ }
+ }
+ }
+}
+
+static void handle_ep0_tx(void)
+{
+ uint32_t txs0;
+ xfer_ctl_t *xfer = XFER_CTL_BASE(0, TUSB_DIR_IN);
+ EPx_REGS *regs = xfer->regs;
+
+ txs0 = regs->USB_TXS0_REG;
+
+ if (GET_BIT(txs0, USB_USB_TXS0_REG_USB_TX_DONE))
+ {
+ // ACK received
+ if (GET_BIT(txs0, USB_USB_TXS0_REG_USB_ACK_STAT))
+ {
+ xfer->transferred += xfer->last_packet_size;
+ xfer->last_packet_size = 0;
+ xfer->data1 ^= 1;
+ REG_SET_VAL(USB_TXC0_REG, USB_TOGGLE_TX0, xfer->data1);
+ if (xfer->transferred == xfer->total_len)
+ {
+ dcd_event_xfer_complete(0, 0 | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ return;
+ }
+ }
+ else
+ {
+ // Start from the beginning
+ xfer->last_packet_size = 0;
+ }
+ fill_tx_fifo(xfer);
+ }
+}
+
+static void handle_epx_rx_ev(uint8_t ep)
+{
+ uint32_t rxs;
+ int fifo_bytes;
+ xfer_ctl_t *xfer = XFER_CTL_BASE(ep, TUSB_DIR_OUT);
+
+ EPx_REGS *regs = xfer->regs;
+
+ do
+ {
+ rxs = regs->rxs;
+
+ if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_ERR))
+ {
+ regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
+ xfer->last_packet_size = 0;
+ if (_dcd.dma_ep[TUSB_DIR_OUT] == ep)
+ {
+ // Stop DMA
+ RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ // Restart DMA since packet was dropped, all parameters should still work.
+ RX_DMA_REGS->DMAx_CTRL_REG |= DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ }
+ break;
+ }
+ else
+ {
+ if (_dcd.dma_ep[TUSB_DIR_OUT] == ep)
+ {
+ // Disable DMA and update last_packet_size with what DMA reported.
+ RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ xfer->last_packet_size = RX_DMA_REGS->DMAx_IDX_REG;
+ // When DMA did not finished (packet was smaller then MPS), DMAx_IDX_REG holds exact number of bytes transmitted.
+ // When DMA finished value in DMAx_IDX_REG is one less then actual number of transmitted bytes.
+ if (xfer->last_packet_size == RX_DMA_REGS->DMAx_LEN_REG) xfer->last_packet_size++;
+ // Release DMA to use by other endpoints.
+ _dcd.dma_ep[TUSB_DIR_OUT] = 0;
+ }
+ fifo_bytes = GET_BIT(rxs, USB_USB_RXS1_REG_USB_RXCOUNT);
+ // FIFO maybe empty if DMA read it before or it's final iteration and function already read all that was to read.
+ if (fifo_bytes > 0)
+ {
+ read_rx_fifo(xfer, fifo_bytes);
+ }
+ if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST))
+ {
+ if (!xfer->iso && GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1)
+ {
+ // Toggle bit does not match discard packet
+ regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
+ }
+ else
+ {
+ xfer->data1 ^= 1;
+ xfer->transferred += xfer->last_packet_size;
+ if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size || xfer->iso)
+ {
+ dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Re-enable reception
+ start_rx_packet(xfer);
+ }
+ }
+ xfer->last_packet_size = 0;
+ }
+ }
+ } while (fifo_bytes > TU_DA1469X_FIFO_READ_THRESHOLD);
+}
+
+static void handle_rx_ev(void)
+{
+ if (USB->USB_RXEV_REG & 1)
+ handle_epx_rx_ev(1);
+ if (USB->USB_RXEV_REG & 2)
+ handle_epx_rx_ev(2);
+ if (USB->USB_RXEV_REG & 4)
+ handle_epx_rx_ev(3);
+}
+
+static void handle_epx_tx_ev(xfer_ctl_t *xfer)
+{
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
+ uint32_t txs;
+ EPx_REGS *regs = xfer->regs;
+
+ txs = regs->txs;
+
+ if (GET_BIT(txs, USB_USB_TXS1_REG_USB_TX_DONE))
+ {
+ if (_dcd.dma_ep[TUSB_DIR_IN] == epnum)
+ {
+ // Disable DMA and update last_packet_size with what DMA reported.
+ TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk;
+ xfer->last_packet_size = TX_DMA_REGS->DMAx_IDX_REG + 1;
+ // Release DMA to used by other endpoints.
+ _dcd.dma_ep[TUSB_DIR_IN] = 0;
+ }
+ if (GET_BIT(txs, USB_USB_TXS1_REG_USB_ACK_STAT))
+ {
+ // ACK received, update transfer state and DATA0/1 bit
+ xfer->transferred += xfer->last_packet_size;
+ xfer->last_packet_size = 0;
+ xfer->data1 ^= 1;
+
+ if (xfer->transferred == xfer->total_len)
+ {
+ dcd_event_xfer_complete(0, xfer->ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ return;
+ }
+ }
+ }
+ if (txs & USB_USB_TXS1_REG_USB_TX_URUN_Msk)
+ {
+ TU_LOG1("EP %d FIFO underrun\n", epnum);
+ }
+ // Start next or repeated packet.
+ start_tx_packet(xfer);
+}
+
+static void handle_tx_ev(void)
+{
+ if (USB->USB_TXEV_REG & 1)
+ handle_epx_tx_ev(XFER_CTL_BASE(1, TUSB_DIR_IN));
+ if (USB->USB_TXEV_REG & 2)
+ handle_epx_tx_ev(XFER_CTL_BASE(2, TUSB_DIR_IN));
+ if (USB->USB_TXEV_REG & 4)
+ handle_epx_tx_ev(XFER_CTL_BASE(3, TUSB_DIR_IN));
+}
+
+static void handle_bus_reset(void)
+{
+ USB->USB_NFSR_REG = 0;
+ USB->USB_FAR_REG = 0x80;
+ USB->USB_ALTMSK_REG = 0;
+ USB->USB_NFSR_REG = NFSR_NODE_RESET;
+ USB->USB_TXMSK_REG = 0;
+ USB->USB_RXMSK_REG = 0;
+ (void)USB->USB_ALTEV_REG;
+ _dcd.in_reset = true;
+
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ USB->USB_DMA_CTRL_REG = 0;
+
+ USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
+#if USE_SOF
+ USB_USB_MAMSK_REG_USB_M_FRAME_Msk |
+#endif
+ USB_USB_MAMSK_REG_USB_M_WARN_Msk |
+ USB_USB_MAMSK_REG_USB_M_ALT_Msk;
+ USB->USB_NFSR_REG = NFSR_NODE_OPERATIONAL;
+ USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_SD3_Msk |
+ USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
+ // There is no information about end of reset state
+ // USB_FRAME event will be used to enable reset detection again
+ REG_SET_BIT(USB_MAEV_REG, USB_FRAME);
+ dcd_edpt_open (0, &ep0OUT_desc);
+ dcd_edpt_open (0, &ep0IN_desc);
+}
+
+static void handle_alt_ev(void)
+{
+ uint32_t alt_ev = USB->USB_ALTEV_REG;
+
+ if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESET))
+ {
+ handle_bus_reset();
+ }
+ else
+ {
+ if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESUME))
+ {
+ USB->USB_NFSR_REG = NFSR_NODE_OPERATIONAL;
+ USB->USB_ALTMSK_REG &= ~USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
+ USB->USB_ALTMSK_REG |= USB_USB_ALTMSK_REG_USB_M_SD3_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+ if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_SD3))
+ {
+ USB->USB_NFSR_REG = NFSR_NODE_SUSPEND;
+ USB->USB_ALTMSK_REG |= USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
+ USB->USB_ALTMSK_REG &= ~USB_USB_ALTMSK_REG_USB_M_SD3_Msk | USB_USB_ALTMSK_REG_USB_M_SD5_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+ }
+}
+
+static void handle_epx_tx_warn_ev(uint8_t ep)
+{
+ fill_tx_fifo(XFER_CTL_BASE(ep, TUSB_DIR_IN));
+}
+
+static void handle_fifo_warning(void)
+{
+ uint32_t fifo_warning = USB->USB_FWEV_REG;
+
+ if (fifo_warning & 0x01)
+ handle_epx_tx_warn_ev(1);
+ if (fifo_warning & 0x02)
+ handle_epx_tx_warn_ev(2);
+ if (fifo_warning & 0x04)
+ handle_epx_tx_warn_ev(3);
+ if (fifo_warning & 0x10)
+ handle_epx_rx_ev(1);
+ if (fifo_warning & 0x20)
+ handle_epx_rx_ev(2);
+ if (fifo_warning & 0x40)
+ handle_epx_rx_ev(3);
+}
+
+static void handle_ep0_nak(void)
+{
+ uint32_t ep0_nak = USB->USB_EP0_NAK_REG;
+
+ if (REG_GET_BIT(USB_EPC0_REG, USB_STALL))
+ {
+ if (GET_BIT(ep0_nak, USB_USB_EP0_NAK_REG_USB_EP0_INNAK))
+ {
+ // EP0 is stalled and NAK was sent, it means that RX is enabled
+ // Disable RX for now.
+ REG_CLR_BIT(USB_RXC0_REG, USB_RX_EN);
+ REG_SET_BIT(USB_TXC0_REG, USB_TX_EN);
+ }
+ if (GET_BIT(ep0_nak, USB_USB_EP0_NAK_REG_USB_EP0_OUTNAK))
+ {
+ REG_SET_BIT(USB_RXC0_REG, USB_RX_EN);
+ }
+ }
+ else
+ {
+ REG_CLR_BIT(USB_MAMSK_REG, USB_M_EP0_NAK);
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init(uint8_t rhport)
+{
+ USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
+ tusb_vbus_changed((CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_VBUS_AVAILABLE_Msk) != 0);
+
+ dcd_connect(rhport);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void)rhport;
+
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void)rhport;
+
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void)rhport;
+
+ // Set default address for one ZLP
+ USB->USB_EPC0_REG = USB_USB_EPC0_REG_USB_DEF_Msk;
+ USB->USB_FAR_REG = (dev_addr & USB_USB_FAR_REG_USB_AD_Msk) | USB_USB_FAR_REG_USB_AD_EN_Msk;
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void)rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void)rhport;
+
+ REG_SET_BIT(USB_MCTRL_REG, USB_NAT);
+
+ // Select chosen DMA to be triggered by USB.
+ DMA->DMA_REQ_MUX_REG = (DMA->DMA_REQ_MUX_REG & ~DA146XX_DMA_USB_MUX_MASK) | DA146XX_DMA_USB_MUX;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void)rhport;
+
+ REG_CLR_BIT(USB_MCTRL_REG, USB_NAT);
+}
+
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+ (void)rhport;
+
+ uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ uint8_t iso_mask = 0;
+
+ TU_ASSERT(epnum < EP_MAX);
+
+ xfer->max_packet_size = desc_edpt->wMaxPacketSize.size;
+ xfer->ep_addr = desc_edpt->bEndpointAddress;
+ xfer->data1 = 0;
+ xfer->iso = 0;
+
+ if (epnum != 0 && desc_edpt->bmAttributes.xfer == 1)
+ {
+ iso_mask = USB_USB_EPC1_REG_USB_ISO_Msk;
+ xfer->iso = 1;
+ }
+
+ if (epnum == 0)
+ {
+ USB->USB_MAMSK_REG |= USB_USB_MAMSK_REG_USB_M_EP0_RX_Msk |
+ USB_USB_MAMSK_REG_USB_M_EP0_TX_Msk;
+ }
+ else
+ {
+ if (dir == TUSB_DIR_OUT)
+ {
+ xfer->regs->epc_out = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask;
+ USB->USB_RXMSK_REG |= 0x101 << (epnum - 1);
+ REG_SET_BIT(USB_MAMSK_REG, USB_M_RX_EV);
+ }
+ else
+ {
+ xfer->regs->epc_in = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask;
+ USB->USB_TXMSK_REG |= 0x101 << (epnum - 1);
+ REG_SET_BIT(USB_MAMSK_REG, USB_M_TX_EV);
+ }
+ }
+
+ return true;
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+
+ (void)rhport;
+
+ TU_ASSERT(epnum < EP_MAX,);
+
+ if (epnum == 0)
+ {
+ USB->USB_MAMSK_REG &= ~(USB_USB_MAMSK_REG_USB_M_EP0_RX_Msk |
+ USB_USB_MAMSK_REG_USB_M_EP0_TX_Msk);
+ }
+ else
+ {
+ if (dir == TUSB_DIR_OUT)
+ {
+ xfer->regs->rxc = USB_USB_RXC1_REG_USB_FLUSH_Msk;
+ xfer->regs->epc_out = 0;
+ USB->USB_RXMSK_REG &= ~(0x101 << (epnum - 1));
+ // Release DMA if needed
+ if (_dcd.dma_ep[TUSB_DIR_OUT] == epnum)
+ {
+ RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ _dcd.dma_ep[TUSB_DIR_OUT] = 0;
+ }
+ }
+ else
+ {
+ xfer->regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk;
+ xfer->regs->epc_in = 0;
+ USB->USB_TXMSK_REG &= ~(0x101 << (epnum - 1));
+ // Release DMA if needed
+ if (_dcd.dma_ep[TUSB_DIR_IN] == epnum)
+ {
+ TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk;
+ _dcd.dma_ep[TUSB_DIR_IN] = 0;
+ }
+ }
+ }
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+
+ (void)rhport;
+
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->last_packet_size = 0;
+ xfer->transferred = 0;
+
+ if (dir == TUSB_DIR_OUT)
+ {
+ start_rx_packet(xfer);
+ }
+ else // IN
+ {
+ start_tx_packet(xfer);
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ (void)rhport;
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->stall = 1;
+
+ if (epnum == 0)
+ {
+ // EP0 has just one registers to control stall for IN and OUT
+ REG_SET_BIT(USB_EPC0_REG, USB_STALL);
+ if (dir == TUSB_DIR_OUT)
+ {
+ xfer->regs->USB_RXC0_REG = USB_USB_RXC0_REG_USB_RX_EN_Msk;
+ }
+ else
+ {
+ if (xfer->regs->USB_RXC0_REG & USB_USB_RXC0_REG_USB_RX_EN_Msk)
+ {
+ // If RX is also enabled TX will not be stalled since RX has
+ // higher priority. Enable NAK interrupt to handle stall.
+ REG_SET_BIT(USB_MAMSK_REG, USB_M_EP0_NAK);
+ }
+ else
+ {
+ xfer->regs->USB_TXC0_REG |= USB_USB_TXC0_REG_USB_TX_EN_Msk;
+ }
+ }
+ }
+ else
+ {
+ if (dir == TUSB_DIR_OUT)
+ {
+ xfer->regs->epc_out |= USB_USB_EPC1_REG_USB_STALL_Msk;
+ xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
+ }
+ else
+ {
+ xfer->regs->epc_in |= USB_USB_EPC1_REG_USB_STALL_Msk;
+ xfer->regs->txc |= USB_USB_TXC1_REG_USB_TX_EN_Msk | USB_USB_TXC1_REG_USB_LAST_Msk;
+ }
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ (void)rhport;
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+
+ // Clear stall is called in response to Clear Feature ENDPOINT_HALT, reset toggle
+ xfer->data1 = 0;
+ xfer->stall = 0;
+
+ if (dir == TUSB_DIR_OUT)
+ {
+ xfer->regs->epc_out &= ~USB_USB_EPC1_REG_USB_STALL_Msk;
+ }
+ else
+ {
+ xfer->regs->epc_in &= ~USB_USB_EPC1_REG_USB_STALL_Msk;
+ }
+ if (epnum == 0)
+ {
+ REG_CLR_BIT(USB_MAMSK_REG, USB_M_EP0_NAK);
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* Interrupt Handler
+ *------------------------------------------------------------------*/
+
+void dcd_int_handler(uint8_t rhport)
+{
+ uint32_t int_status = USB->USB_MAEV_REG;
+
+ (void)rhport;
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_WARN))
+ {
+ handle_fifo_warning();
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_CH_EV))
+ {
+ // TODO: for now just clear interrupt
+ (void)USB->USB_CHARGER_STAT_REG;
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_EP0_NAK))
+ {
+ handle_ep0_nak();
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_EP0_RX))
+ {
+ handle_ep0_rx();
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_EP0_TX))
+ {
+ handle_ep0_tx();
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_RX_EV))
+ {
+ handle_rx_ev();
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_NAK))
+ {
+ (void)USB->USB_NAKEV_REG;
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_FRAME))
+ {
+ if (_dcd.in_reset)
+ {
+ // Enable reset detection
+ _dcd.in_reset = false;
+ (void)USB->USB_ALTEV_REG;
+ }
+#if USE_SOF
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+#else
+ // SOF was used to re-enable reset detection
+ // No need to keep it enabled
+ USB->USB_MAMSK_REG &= ~USB_USB_MAMSK_REG_USB_M_FRAME_Msk;
+#endif
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_TX_EV))
+ {
+ handle_tx_ev();
+ }
+
+ if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_ALT))
+ {
+ handle_alt_ev();
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/ehci/ehci.c b/sw/Core/Src/tinyusb/src/portable/ehci/ehci.c
new file mode 100755
index 0000000..e2327fd
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/ehci/ehci.c
@@ -0,0 +1,901 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "common/tusb_common.h"
+
+#if TUSB_OPT_HOST_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
+ CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX )
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "osal/osal.h"
+
+#include "host/hcd.h"
+#include "host/usbh_hcd.h"
+#include "ehci.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// Periodic frame list must be 4K alignment
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
+
+// EHCI portable
+uint32_t hcd_ehci_register_addr(uint8_t rhport);
+bool hcd_ehci_init (uint8_t rhport); // TODO move later
+
+//--------------------------------------------------------------------+
+// PROTOTYPE
+//--------------------------------------------------------------------+
+static inline ehci_link_t* get_period_head(uint8_t rhport, uint8_t interval_ms)
+{
+ (void) rhport;
+ return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min8(EHCI_FRAMELIST_SIZE, interval_ms) ) ];
+}
+
+static inline ehci_qhd_t* qhd_control(uint8_t dev_addr)
+{
+ return &ehci_data.control[dev_addr].qhd;
+}
+
+static inline ehci_qhd_t* qhd_async_head(uint8_t rhport)
+{
+ (void) rhport;
+ return qhd_control(0); // control qhd of dev0 is used as async head
+}
+
+static inline ehci_qtd_t* qtd_control(uint8_t dev_addr)
+{
+ return &ehci_data.control[dev_addr].qtd;
+}
+
+
+static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
+static inline ehci_qhd_t* qhd_find_free (void);
+static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
+
+// determine if a queue head has bus-related error
+static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd)
+{
+ return (p_qhd->qtd_overlay.buffer_err || p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err);
+ //p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error
+}
+
+static void qhd_init (ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+
+static inline ehci_qtd_t* qtd_find_free (void);
+static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd);
+static inline void qtd_insert_to_qhd (ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new);
+static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd);
+static void qtd_init (ehci_qtd_t* p_qtd, void* buffer, uint16_t total_bytes);
+
+static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
+static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer);
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+
+uint32_t hcd_uframe_number(uint8_t rhport)
+{
+ (void) rhport;
+ return ehci_data.uframe_number + ehci_data.regs->frame_index;
+}
+
+void hcd_port_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ ehci_registers_t* regs = ehci_data.regs;
+
+// regs->portsc_bm.port_enabled = 0; // disable port before reset
+// regs->portsc_bm.port_reset = 1;
+
+ uint32_t portsc = regs->portsc;
+
+ portsc &= ~(EHCI_PORTSC_MASK_PORT_EANBLED);
+ portsc |= EHCI_PORTSC_MASK_PORT_RESET;
+
+ regs->portsc = portsc;
+}
+
+#if 0
+void hcd_port_reset_end(uint8_t rhport)
+{
+ (void) rhport;
+
+ ehci_registers_t* regs = ehci_data.regs;
+ regs->portsc_bm.port_reset = 0;
+}
+#endif
+
+bool hcd_port_connect_status(uint8_t rhport)
+{
+ (void) rhport;
+ return ehci_data.regs->portsc_bm.current_connect_status;
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+ (void) rhport;
+ return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed
+}
+
+static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr)
+{
+ for(ehci_link_t* prev = list_head;
+ !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head);
+ prev = list_next(prev) )
+ {
+ // TODO check type for ISO iTD and siTD
+ ehci_qhd_t* qhd = (ehci_qhd_t*) list_next(prev);
+ if ( qhd->dev_addr == dev_addr )
+ {
+ // TODO deactive all TD, wait for QHD to inactive before removal
+ prev->address = qhd->next.address;
+
+ // EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller)
+ qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
+
+ if ( qhd->int_smask )
+ {
+ // period list queue element is guarantee to be free in the next frame (1 ms)
+ qhd->used = 0;
+ }else
+ {
+ // async list use async advance handshake
+ // mark as removing, will completely re-usable when async advance isr occurs
+ qhd->removing = 1;
+ }
+ }
+ }
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+ // skip dev0
+ if (dev_addr == 0) return;
+
+ // Remove from async list
+ list_remove_qhd_by_addr( (ehci_link_t*) qhd_async_head(rhport), dev_addr );
+
+ // Remove from all interval period list
+ for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++)
+ {
+ list_remove_qhd_by_addr( (ehci_link_t*) &ehci_data.period_head_arr[i], dev_addr);
+ }
+
+ // Async doorbell (EHCI 4.8.2 for operational details)
+ ehci_data.regs->command_bm.async_adv_doorbell = 1;
+}
+
+// EHCI controller init
+bool hcd_ehci_init(uint8_t rhport)
+{
+ tu_memclr(&ehci_data, sizeof(ehci_data_t));
+
+ ehci_data.regs = (ehci_registers_t* ) hcd_ehci_register_addr(rhport);
+
+ ehci_registers_t* regs = ehci_data.regs;
+
+ //------------- CTRLDSSEGMENT Register (skip) -------------//
+ //------------- USB INT Register -------------//
+ regs->inten = 0; // 1. disable all the interrupt
+ regs->status = EHCI_INT_MASK_ALL; // 2. clear all status
+
+ regs->inten = EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | EHCI_INT_MASK_ASYNC_ADVANCE |
+ EHCI_INT_MASK_NXP_PERIODIC | EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_FRAMELIST_ROLLOVER;
+
+ //------------- Asynchronous List -------------//
+ ehci_qhd_t * const async_head = qhd_async_head(rhport);
+ tu_memclr(async_head, sizeof(ehci_qhd_t));
+
+ async_head->next.address = (uint32_t) async_head; // circular list, next is itself
+ async_head->next.type = EHCI_QTYPE_QHD;
+ async_head->head_list_flag = 1;
+ async_head->qtd_overlay.halted = 1; // inactive most of time
+ async_head->qtd_overlay.next.terminate = 1; // TODO removed if verified
+
+ regs->async_list_addr = (uint32_t) async_head;
+
+ //------------- Periodic List -------------//
+ // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
+ for(uint32_t i=0; i<4; i++)
+ {
+ ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero
+ ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive
+ }
+
+ ehci_link_t * const framelist = ehci_data.period_framelist;
+ ehci_link_t * const period_1ms = get_period_head(rhport, 1);
+ // all links --> period_head_arr[0] (1ms)
+ // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
+ // 1, 5 --> period_head_arr[2] (4ms)
+ // 3 --> period_head_arr[3] (8ms)
+
+ // TODO EHCI_FRAMELIST_SIZE with other size than 8
+ for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i++)
+ {
+ framelist[i].address = (uint32_t) period_1ms;
+ framelist[i].type = EHCI_QTYPE_QHD;
+ }
+
+ for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i+=2)
+ {
+ list_insert(framelist + i, get_period_head(rhport, 2), EHCI_QTYPE_QHD);
+ }
+
+ for(uint32_t i=1; i<EHCI_FRAMELIST_SIZE; i+=4)
+ {
+ list_insert(framelist + i, get_period_head(rhport, 4), EHCI_QTYPE_QHD);
+ }
+
+ list_insert(framelist+3, get_period_head(rhport, 8), EHCI_QTYPE_QHD);
+
+ period_1ms->terminate = 1;
+
+ regs->periodic_list_base = (uint32_t) framelist;
+
+ //------------- TT Control (NXP only) -------------//
+ regs->nxp_tt_control = 0;
+
+ //------------- USB CMD Register -------------//
+ regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE)
+ | TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) // TODO enable period list only there is int/iso endpoint
+ | ((EHCI_CFG_FRAMELIST_SIZE_BITS & TU_BIN8(011)) << EHCI_USBCMD_POS_FRAMELIST_SZIE)
+ | ((EHCI_CFG_FRAMELIST_SIZE_BITS >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB);
+
+ //------------- ConfigFlag Register (skip) -------------//
+ regs->portsc_bm.port_power = 1; // enable port power
+
+ return true;
+}
+
+#if 0
+static void ehci_stop(uint8_t rhport)
+{
+ (void) rhport;
+
+ ehci_registers_t* regs = ehci_data.regs;
+
+ regs->command_bm.run_stop = 0;
+
+ // USB Spec: controller has to stop within 16 uframe = 2 frames
+ while( regs->status_bm.hc_halted == 0 ) {}
+}
+#endif
+
+//--------------------------------------------------------------------+
+// CONTROL PIPE API
+//--------------------------------------------------------------------+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ // FIXME control only for now
+ if ( epnum == 0 )
+ {
+ ehci_qhd_t* qhd = qhd_control(dev_addr);
+ ehci_qtd_t* qtd = qtd_control(dev_addr);
+
+ qtd_init(qtd, buffer, buflen);
+
+ // first first data toggle is always 1 (data & setup stage)
+ qtd->data_toggle = 1;
+ qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT;
+ qtd->int_on_complete = 1;
+ qtd->next.terminate = 1;
+
+ // sw region
+ qhd->p_qtd_list_head = qtd;
+ qhd->p_qtd_list_tail = qtd;
+
+ // attach TD
+ qhd->qtd_overlay.next.address = (uint32_t) qtd;
+ }else
+ {
+ ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ ehci_qtd_t *p_qtd = qtd_find_free();
+ TU_ASSERT(p_qtd);
+
+ qtd_init(p_qtd, buffer, buflen);
+ p_qtd->pid = p_qhd->pid;
+
+ // Insert TD to QH
+ qtd_insert_to_qhd(p_qhd, p_qtd);
+
+ p_qhd->p_qtd_list_tail->int_on_complete = 1;
+
+ // attach head QTD to QHD start transferring
+ p_qhd->qtd_overlay.next.address = (uint32_t) p_qhd->p_qtd_list_head;
+ }
+
+ return true;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+ (void) rhport;
+
+ ehci_qhd_t* qhd = &ehci_data.control[dev_addr].qhd;
+ ehci_qtd_t* td = &ehci_data.control[dev_addr].qtd;
+
+ qtd_init(td, (void*) setup_packet, 8);
+ td->pid = EHCI_PID_SETUP;
+ td->int_on_complete = 1;
+ td->next.terminate = 1;
+
+ // sw region
+ qhd->p_qtd_list_head = td;
+ qhd->p_qtd_list_tail = td;
+
+ // attach TD
+ qhd->qtd_overlay.next.address = (uint32_t) td;
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// BULK/INT/ISO PIPE API
+//--------------------------------------------------------------------+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void) rhport;
+
+ // TODO not support ISO yet
+ TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
+
+ //------------- Prepare Queue Head -------------//
+ ehci_qhd_t * p_qhd;
+
+ if ( ep_desc->bEndpointAddress == 0 )
+ {
+ p_qhd = qhd_control(dev_addr);
+ }else
+ {
+ p_qhd = qhd_find_free();
+ }
+ TU_ASSERT(p_qhd);
+
+ qhd_init(p_qhd, dev_addr, ep_desc);
+
+ // control of dev0 is always present as async head
+ if ( dev_addr == 0 ) return true;
+
+ // Insert to list
+ ehci_link_t * list_head = NULL;
+
+ switch (ep_desc->bmAttributes.xfer)
+ {
+ case TUSB_XFER_CONTROL:
+ case TUSB_XFER_BULK:
+ list_head = (ehci_link_t*) qhd_async_head(rhport);
+ break;
+
+ case TUSB_XFER_INTERRUPT:
+ list_head = get_period_head(rhport, p_qhd->interval_ms);
+ break;
+
+ case TUSB_XFER_ISOCHRONOUS:
+ // TODO iso is not supported
+ break;
+
+ default: break;
+ }
+
+ TU_ASSERT(list_head);
+
+ // TODO might need to disable async/period list
+ list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD);
+
+ return true;
+}
+
+bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes)
+{
+ //------------- set up QTD -------------//
+ ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ ehci_qtd_t *p_qtd = qtd_find_free();
+
+ TU_ASSERT(p_qtd);
+
+ qtd_init(p_qtd, buffer, total_bytes);
+ p_qtd->pid = p_qhd->pid;
+
+ //------------- insert TD to TD list -------------//
+ qtd_insert_to_qhd(p_qhd, p_qtd);
+
+ return true;
+}
+
+bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
+{
+ TU_ASSERT ( hcd_pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes) );
+
+ ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
+
+ if ( int_on_complete )
+ { // the just added qtd is pointed by list_tail
+ p_qhd->p_qtd_list_tail->int_on_complete = 1;
+ }
+ p_qhd->qtd_overlay.next.address = (uint32_t) p_qhd->p_qtd_list_head; // attach head QTD to QHD start transferring
+
+ return true;
+}
+
+bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ return !p_qhd->qtd_overlay.halted && (p_qhd->p_qtd_list_head != NULL);
+}
+
+bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ return p_qhd->qtd_overlay.halted && !qhd_has_xact_error(p_qhd);
+}
+
+bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ p_qhd->qtd_overlay.halted = 0;
+ // TODO reset data toggle ?
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// EHCI Interrupt Handler
+//--------------------------------------------------------------------+
+
+// async_advance is handshake between usb stack & ehci controller.
+// This isr mean it is safe to modify previously removed queue head from async list.
+// In tinyusb, queue head is only removed when device is unplugged.
+static void async_advance_isr(uint8_t rhport)
+{
+ (void) rhport;
+
+ ehci_qhd_t* qhd_pool = ehci_data.qhd_pool;
+ for(uint32_t i = 0; i < HCD_MAX_ENDPOINT; i++)
+ {
+ if ( qhd_pool[i].removing )
+ {
+ qhd_pool[i].removing = 0;
+ qhd_pool[i].used = 0;
+ }
+ }
+}
+
+static void port_connect_status_change_isr(uint8_t hostid)
+{
+ // NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device
+ if (ehci_data.regs->portsc_bm.current_connect_status)
+ {
+ hcd_port_reset(hostid);
+ hcd_event_device_attach(hostid, true);
+ }else // device unplugged
+ {
+ hcd_event_device_remove(hostid, true);
+ }
+}
+
+static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd)
+{
+ // free all TDs from the head td to the first active TD
+ while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active)
+ {
+ // TD need to be freed and removed from qhd, before invoking callback
+ bool is_ioc = (p_qhd->p_qtd_list_head->int_on_complete != 0);
+ p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes;
+
+ p_qhd->p_qtd_list_head->used = 0; // free QTD
+ qtd_remove_1st_from_qhd(p_qhd);
+
+ if (is_ioc)
+ {
+ // end of request
+ // call USBH callback
+ hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), p_qhd->total_xferred_bytes, XFER_RESULT_SUCCESS, true);
+ p_qhd->total_xferred_bytes = 0;
+ }
+ }
+}
+
+static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
+{
+ ehci_qhd_t *p_qhd = async_head;
+ do
+ {
+ if ( !p_qhd->qtd_overlay.halted ) // halted or error is processed in error isr
+ {
+ qhd_xfer_complete_isr(p_qhd);
+ }
+ p_qhd = qhd_next(p_qhd);
+ }while(p_qhd != async_head); // async list traversal, stop if loop around
+}
+
+static void period_list_xfer_complete_isr(uint8_t hostid, uint8_t interval_ms)
+{
+ uint16_t max_loop = 0;
+ uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1);
+ ehci_link_t next_item = * get_period_head(hostid, interval_ms);
+
+ // TODO abstract max loop guard for period
+ while( !next_item.terminate &&
+ !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) &&
+ max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUSB_HOST_DEVICE_MAX)
+ {
+ switch ( next_item.type )
+ {
+ case EHCI_QTYPE_QHD:
+ {
+ ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address);
+ if ( !p_qhd_int->qtd_overlay.halted )
+ {
+ qhd_xfer_complete_isr(p_qhd_int);
+ }
+ }
+ break;
+
+ case EHCI_QTYPE_ITD: // TODO support hs/fs ISO
+ case EHCI_QTYPE_SITD:
+ case EHCI_QTYPE_FSTN:
+
+ default: break;
+ }
+
+ next_item = *list_next(&next_item);
+ max_loop++;
+ }
+}
+
+static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd)
+{
+ if ( (p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || // addr0 cannot be protocol STALL
+ qhd_has_xact_error(p_qhd) )
+ {
+ // current qhd has error in transaction
+ xfer_result_t error_event;
+
+ // no error bits are set, endpoint is halted due to STALL
+ error_event = qhd_has_xact_error(p_qhd) ? XFER_RESULT_FAILED : XFER_RESULT_STALLED;
+
+ p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes;
+
+// if ( XFER_RESULT_FAILED == error_event ) TU_BREAKPOINT(); // TODO skip unplugged device
+
+ p_qhd->p_qtd_list_head->used = 0; // free QTD
+ qtd_remove_1st_from_qhd(p_qhd);
+
+ if ( 0 == p_qhd->ep_number )
+ {
+ // control cannot be halted --> clear all qtd list
+ p_qhd->p_qtd_list_head = NULL;
+ p_qhd->p_qtd_list_tail = NULL;
+
+ p_qhd->qtd_overlay.next.terminate = 1;
+ p_qhd->qtd_overlay.alternate.terminate = 1;
+ p_qhd->qtd_overlay.halted = 0;
+
+ ehci_qtd_t *p_setup = qtd_control(p_qhd->dev_addr);
+ p_setup->used = 0;
+ }
+
+ // call USBH callback
+ hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), p_qhd->total_xferred_bytes, error_event, true);
+
+ p_qhd->total_xferred_bytes = 0;
+ }
+}
+
+static void xfer_error_isr(uint8_t hostid)
+{
+ //------------- async list -------------//
+ ehci_qhd_t * const async_head = qhd_async_head(hostid);
+ ehci_qhd_t *p_qhd = async_head;
+ do
+ {
+ qhd_xfer_error_isr( p_qhd );
+ p_qhd = qhd_next(p_qhd);
+ }while(p_qhd != async_head); // async list traversal, stop if loop around
+
+ //------------- TODO refractor period list -------------//
+ uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1);
+ for (uint8_t interval_ms=1; interval_ms <= EHCI_FRAMELIST_SIZE; interval_ms *= 2)
+ {
+ ehci_link_t next_item = * get_period_head(hostid, interval_ms);
+
+ // TODO abstract max loop guard for period
+ while( !next_item.terminate &&
+ !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) )
+ {
+ switch ( next_item.type )
+ {
+ case EHCI_QTYPE_QHD:
+ {
+ ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address);
+ qhd_xfer_error_isr(p_qhd_int);
+ }
+ break;
+
+ // TODO support hs/fs ISO
+ case EHCI_QTYPE_ITD:
+ case EHCI_QTYPE_SITD:
+ case EHCI_QTYPE_FSTN:
+ default: break;
+ }
+
+ next_item = *list_next(&next_item);
+ }
+ }
+}
+
+//------------- Host Controller Driver's Interrupt Handler -------------//
+void hcd_int_handler(uint8_t rhport)
+{
+ ehci_registers_t* regs = ehci_data.regs;
+
+ uint32_t int_status = regs->status;
+ int_status &= regs->inten;
+
+ regs->status |= int_status; // Acknowledge handled interrupt
+
+ if (int_status == 0) return;
+
+ if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER)
+ {
+ ehci_data.uframe_number += (EHCI_FRAMELIST_SIZE << 3);
+ }
+
+ if (int_status & EHCI_INT_MASK_PORT_CHANGE)
+ {
+ uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
+
+ if (regs->portsc_bm.connect_status_change)
+ {
+ port_connect_status_change_isr(rhport);
+ }
+
+ regs->portsc |= port_status; // Acknowledge change bits in portsc
+ }
+
+ if (int_status & EHCI_INT_MASK_ERROR)
+ {
+ xfer_error_isr(rhport);
+ }
+
+ //------------- some QTD/SITD/ITD with IOC set is completed -------------//
+ if (int_status & EHCI_INT_MASK_NXP_ASYNC)
+ {
+ async_list_xfer_complete_isr( qhd_async_head(rhport) );
+ }
+
+ if (int_status & EHCI_INT_MASK_NXP_PERIODIC)
+ {
+ for (uint8_t i=1; i <= EHCI_FRAMELIST_SIZE; i *= 2)
+ {
+ period_list_xfer_complete_isr( rhport, i );
+ }
+ }
+
+ //------------- There is some removed async previously -------------//
+ if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC
+ {
+ async_advance_isr(rhport);
+ }
+}
+
+//--------------------------------------------------------------------+
+// HELPER
+//--------------------------------------------------------------------+
+
+
+//------------- queue head helper -------------//
+static inline ehci_qhd_t* qhd_find_free (void)
+{
+ for (uint32_t i=0; i<HCD_MAX_ENDPOINT; i++)
+ {
+ if ( !ehci_data.qhd_pool[i].used ) return &ehci_data.qhd_pool[i];
+ }
+
+ return NULL;
+}
+
+static inline ehci_qhd_t* qhd_next(ehci_qhd_t const * p_qhd)
+{
+ return (ehci_qhd_t*) tu_align32(p_qhd->next.address);
+}
+
+static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ehci_qhd_t* qhd_pool = ehci_data.qhd_pool;
+
+ for(uint32_t i=0; i<HCD_MAX_ENDPOINT; i++)
+ {
+ if ( (qhd_pool[i].dev_addr == dev_addr) &&
+ ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) )
+ {
+ return &qhd_pool[i];
+ }
+ }
+
+ return NULL;
+}
+
+//------------- TD helper -------------//
+static inline ehci_qtd_t* qtd_find_free(void)
+{
+ for (uint32_t i=0; i<HCD_MAX_XFER; i++)
+ {
+ if ( !ehci_data.qtd_pool[i].used ) return &ehci_data.qtd_pool[i];
+ }
+
+ return NULL;
+}
+
+static inline ehci_qtd_t* qtd_next(ehci_qtd_t const * p_qtd )
+{
+ return (ehci_qtd_t*) tu_align32(p_qtd->next.address);
+}
+
+static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd)
+{
+ if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL
+ {
+ p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL;
+ }else
+ {
+ p_qhd->p_qtd_list_head = qtd_next( p_qhd->p_qtd_list_head );
+ }
+}
+
+static inline void qtd_insert_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new)
+{
+ if (p_qhd->p_qtd_list_head == NULL) // empty list
+ {
+ p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = p_qtd_new;
+ }else
+ {
+ p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new;
+ p_qhd->p_qtd_list_tail = p_qtd_new;
+ }
+}
+
+static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+ // address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise)
+ if (dev_addr != 0)
+ {
+ tu_memclr(p_qhd, sizeof(ehci_qhd_t));
+ }
+
+ uint8_t const xfer_type = ep_desc->bmAttributes.xfer;
+ uint8_t const interval = ep_desc->bInterval;
+
+ p_qhd->dev_addr = dev_addr;
+ p_qhd->fl_inactive_next_xact = 0;
+ p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress);
+ p_qhd->ep_speed = _usbh_devices[dev_addr].speed;
+ p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0;
+ p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head
+ p_qhd->max_packet_size = ep_desc->wMaxPacketSize.size;
+ p_qhd->fl_ctrl_ep_flag = ((xfer_type == TUSB_XFER_CONTROL) && (p_qhd->ep_speed != TUSB_SPEED_HIGH)) ? 1 : 0;
+ p_qhd->nak_reload = 0;
+
+ // Bulk/Control -> smask = cmask = 0
+ // TODO Isochronous
+ if (TUSB_XFER_INTERRUPT == xfer_type)
+ {
+ if (TUSB_SPEED_HIGH == p_qhd->ep_speed)
+ {
+ TU_ASSERT( interval <= 16, );
+ if ( interval < 4) // sub milisecond interval
+ {
+ p_qhd->interval_ms = 0;
+ p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
+ (interval == 2) ? TU_BIN8(10101010) : TU_BIN8(01000100);
+ }else
+ {
+ p_qhd->interval_ms = (uint8_t) tu_min16( 1 << (interval-4), 255 );
+ p_qhd->int_smask = TU_BIT(interval % 8);
+ }
+ }else
+ {
+ TU_ASSERT( 0 != interval, );
+ // Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes
+ p_qhd->int_smask = 0x01;
+ p_qhd->fl_int_cmask = TU_BIN8(11100);
+ p_qhd->interval_ms = interval;
+ }
+ }else
+ {
+ p_qhd->int_smask = p_qhd->fl_int_cmask = 0;
+ }
+
+ p_qhd->fl_hub_addr = _usbh_devices[dev_addr].hub_addr;
+ p_qhd->fl_hub_port = _usbh_devices[dev_addr].hub_port;
+ p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet
+
+ //------------- HCD Management Data -------------//
+ p_qhd->used = 1;
+ p_qhd->removing = 0;
+ p_qhd->p_qtd_list_head = NULL;
+ p_qhd->p_qtd_list_tail = NULL;
+ p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint
+
+ //------------- active, but no TD list -------------//
+ p_qhd->qtd_overlay.halted = 0;
+ p_qhd->qtd_overlay.next.terminate = 1;
+ p_qhd->qtd_overlay.alternate.terminate = 1;
+ if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT)
+ {
+ p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11
+ }
+}
+
+static void qtd_init(ehci_qtd_t* p_qtd, void* buffer, uint16_t total_bytes)
+{
+ tu_memclr(p_qtd, sizeof(ehci_qtd_t));
+
+ p_qtd->used = 1;
+
+ p_qtd->next.terminate = 1; // init to null
+ p_qtd->alternate.terminate = 1; // not used, always set to terminated
+ p_qtd->active = 1;
+ p_qtd->err_count = 3; // TODO 3 consecutive errors tolerance
+ p_qtd->data_toggle = 0;
+ p_qtd->total_bytes = total_bytes;
+ p_qtd->expected_bytes = total_bytes;
+
+ p_qtd->buffer[0] = (uint32_t) buffer;
+ for(uint8_t i=1; i<5; i++)
+ {
+ p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
+ }
+}
+
+//------------- List Managing Helper -------------//
+static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
+{
+ new->address = current->address;
+ current->address = ((uint32_t) new) | (new_type << 1);
+}
+
+static inline ehci_link_t* list_next(ehci_link_t *p_link_pointer)
+{
+ return (ehci_link_t*) tu_align32(p_link_pointer->address);
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/ehci/ehci.h b/sw/Core/Src/tinyusb/src/portable/ehci/ehci.h
new file mode 100755
index 0000000..212f605
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/ehci/ehci.h
@@ -0,0 +1,460 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_HCD
+ * @{
+ * \defgroup EHCI
+ * \brief EHCI driver. All documents sources mentioned here (eg section 3.5) is referring to EHCI Specs unless state otherwise
+ * @{ */
+
+#ifndef _TUSB_EHCI_H_
+#define _TUSB_EHCI_H_
+
+
+/* Abbreviation
+ * HC: Host Controller
+ * HCD: Host Controller Driver
+ * QHD: Queue Head for non-ISO transfer
+ * QTD: Queue Transfer Descriptor for non-ISO transfer
+ * ITD: Iso Transfer Descriptor for highspeed
+ * SITD: Split ISO Transfer Descriptor for full-speed
+ * SMASK: Start Split mask for Slipt Transaction
+ * CMASK: Complete Split mask for Slipt Transaction
+*/
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// EHCI CONFIGURATION & CONSTANTS
+//--------------------------------------------------------------------+
+#define EHCI_CFG_FRAMELIST_SIZE_BITS 7 /// Framelist Size (NXP specific) (0:1024) - (1:512) - (2:256) - (3:128) - (4:64) - (5:32) - (6:16) - (7:8)
+#define EHCI_FRAMELIST_SIZE (1024 >> EHCI_CFG_FRAMELIST_SIZE_BITS)
+
+// TODO merge OHCI with EHCI
+enum {
+ EHCI_MAX_ITD = 4,
+ EHCI_MAX_SITD = 16
+};
+
+//------------- Validation -------------//
+TU_VERIFY_STATIC(EHCI_CFG_FRAMELIST_SIZE_BITS <= 7, "incorrect value");
+
+//--------------------------------------------------------------------+
+// EHCI Data Structure
+//--------------------------------------------------------------------+
+enum
+{
+ EHCI_QTYPE_ITD = 0 ,
+ EHCI_QTYPE_QHD ,
+ EHCI_QTYPE_SITD ,
+ EHCI_QTYPE_FSTN
+};
+
+/// EHCI PID
+enum
+{
+ EHCI_PID_OUT = 0 ,
+ EHCI_PID_IN ,
+ EHCI_PID_SETUP
+};
+
+/// Link pointer
+typedef union {
+ uint32_t address;
+ struct {
+ uint32_t terminate : 1;
+ uint32_t type : 2;
+ };
+}ehci_link_t;
+
+/// Queue Element Transfer Descriptor
+/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32)
+typedef struct
+{
+ // Word 0: Next QTD Pointer
+ ehci_link_t next;
+
+ // Word 1: Alternate Next QTD Pointer (not used)
+ union{
+ ehci_link_t alternate;
+ struct {
+ uint32_t : 5;
+ uint32_t used : 1;
+ uint32_t : 10;
+ uint32_t expected_bytes : 16;
+ };
+ };
+
+ // Word 2: qTQ Token
+ volatile uint32_t ping_err : 1 ; ///< For Highspeed: 0 Out, 1 Ping. Full/Slow used as error indicator
+ volatile uint32_t non_hs_split_state : 1 ; ///< Used by HC to track the state of slipt transaction
+ volatile uint32_t non_hs_missed_uframe : 1 ; ///< HC misses a complete slip transaction
+ volatile uint32_t xact_err : 1 ; ///< Error (Timeout, CRC, Bad PID ... )
+ volatile uint32_t babble_err : 1 ; ///< Babble detected, also set Halted bit to 1
+ volatile uint32_t buffer_err : 1 ; ///< Data overrun/underrun error
+ volatile uint32_t halted : 1 ; ///< Serious error or STALL received
+ volatile uint32_t active : 1 ; ///< Start transfer, clear by HC when complete
+
+ uint32_t pid : 2 ; ///< 0: OUT, 1: IN, 2 Setup
+ volatile uint32_t err_count : 2 ; ///< Error Counter of consecutive errors
+ volatile uint32_t current_page : 3 ; ///< Index into the qTD buffer pointer list
+ uint32_t int_on_complete : 1 ; ///< Interrupt on complete
+ volatile uint32_t total_bytes : 15 ; ///< Transfer bytes, decreased during transaction
+ volatile uint32_t data_toggle : 1 ; ///< Data Toogle bit
+
+
+ /// Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
+ uint32_t buffer[5];
+} ehci_qtd_t;
+
+TU_VERIFY_STATIC( sizeof(ehci_qtd_t) == 32, "size is not correct" );
+
+/// Queue Head
+typedef struct TU_ATTR_ALIGNED(32)
+{
+ // Word 0: Next QHD
+ ehci_link_t next;
+
+ // Word 1: Endpoint Characteristics
+ uint32_t dev_addr : 7 ; ///< device address
+ uint32_t fl_inactive_next_xact : 1 ; ///< Only valid for Periodic with Full/Slow speed
+ uint32_t ep_number : 4 ; ///< EP number
+ uint32_t ep_speed : 2 ; ///< 0: Full, 1: Low, 2: High
+ uint32_t data_toggle_control : 1 ; ///< 0: use DT in qHD, 1: use DT in qTD
+ uint32_t head_list_flag : 1 ; ///< Head of the queue
+ uint32_t max_packet_size : 11 ; ///< Max packet size
+ uint32_t fl_ctrl_ep_flag : 1 ; ///< 1 if is Full/Low speed control endpoint
+ uint32_t nak_reload : 4 ; ///< Used by HC
+
+ // Word 2: Endpoint Capabilities
+ uint32_t int_smask : 8 ; ///< Interrupt Schedule Mask
+ uint32_t fl_int_cmask : 8 ; ///< Split Completion Mask for Full/Slow speed
+ uint32_t fl_hub_addr : 7 ; ///< Hub Address for Full/Slow speed
+ uint32_t fl_hub_port : 7 ; ///< Hub Port for Full/Slow speed
+ uint32_t mult : 2 ; ///< Transaction per micro frame
+
+ // Word 3: Current qTD Pointer
+ volatile uint32_t qtd_addr;
+
+ // Word 4-11: Transfer Overlay
+ volatile ehci_qtd_t qtd_overlay;
+
+ //--------------------------------------------------------------------+
+ /// Due to the fact QHD is 32 bytes aligned but occupies only 48 bytes
+ /// thus there are 16 bytes padding free that we can make use of.
+ //--------------------------------------------------------------------+
+ uint8_t used;
+ uint8_t removing; // removed from asyn list, waiting for async advance
+ uint8_t pid;
+ uint8_t interval_ms; // polling interval in frames (or milisecond)
+
+ uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set
+ uint8_t reserved2[2];
+
+ ehci_qtd_t * volatile p_qtd_list_head; // head of the scheduled TD list
+ ehci_qtd_t * volatile p_qtd_list_tail; // tail of the scheduled TD list
+} ehci_qhd_t;
+
+TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
+
+/// Highspeed Isochronous Transfer Descriptor (section 3.3)
+typedef struct TU_ATTR_ALIGNED(32) {
+ // Word 0: Next Link Pointer
+ ehci_link_t next;
+
+ // Word 1-8: iTD Transaction Status and Control List
+ struct {
+ // iTD Control
+ volatile uint32_t offset : 12 ; ///< This field is a value that is an offset, expressed in bytes, from the beginning of a buffer.
+ volatile uint32_t page_select : 3 ; ///< These bits are set by software to indicate which of the buffer page pointers the offset field in this slot should be concatenated to produce the starting memory address for this transaction. The valid range of values for this field is 0 to 6
+ uint32_t int_on_complete : 1 ; ///< If this bit is set to a one, it specifies that when this transaction completes, the Host Controller should issue an interrupt at the next interrupt threshold
+ volatile uint32_t length : 12 ; ///< For an OUT, this field is the number of data bytes the host controller will send during the transaction. The host controller is not required to update this field to reflect the actual number of bytes transferred during the transfer
+ ///< For an IN, the initial value of the field is the number of bytes the host expects the endpoint to deliver. During the status update, the host controller writes back the number of bytes successfully received. The value in this register is the actual byte count
+ // iTD Status
+ volatile uint32_t error : 1 ; ///< Set to a one by the Host Controller during status update in the case where the host did not receive a valid response from the device (Timeout, CRC, Bad PID, etc.). This bit may only be set for isochronous IN transactions.
+ volatile uint32_t babble_err : 1 ; ///< Set to a 1 by the Host Controller during status update when a babble is detected during the transaction
+ volatile uint32_t buffer_err : 1 ; ///< Set to a 1 by the Host Controller during status update to indicate that the Host Controller is unable to keep up with the reception of incoming data (overrun) or is unable to supply data fast enough during transmission (underrun).
+ volatile uint32_t active : 1 ; ///< Set to 1 by software to enable the execution of an isochronous transaction by the Host Controller
+ } xact[8];
+
+ // Word 9-15 Buffer Page Pointer List (Plus)
+ uint32_t BufferPointer[7];
+
+// // FIXME: Store meta data into buffer pointer reserved for saving memory
+// /*---------- HCD Area ----------*/
+// uint32_t used;
+// uint32_t IhdIdx;
+// uint32_t reserved[6];
+} ehci_itd_t;
+
+TU_VERIFY_STATIC( sizeof(ehci_itd_t) == 64, "size is not correct" );
+
+/// Split (Full-Speed) Isochronous Transfer Descriptor
+typedef struct TU_ATTR_ALIGNED(32)
+{
+ // Word 0: Next Link Pointer
+ ehci_link_t next;
+
+ // Word 1: siTD Endpoint Characteristics
+ uint32_t dev_addr : 7; ///< This field selects the specific device serving as the data source or sink.
+ uint32_t : 1; ///< reserved
+ uint32_t ep_number : 4; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
+ uint32_t : 4; ///< This field is reserved and should be set to zero.
+ uint32_t hub_addr : 7; ///< This field holds the device address of the transaction translators’ hub.
+ uint32_t : 1; ///< reserved
+ uint32_t port_number : 7; ///< This field is the port number of the recipient transaction translator.
+ uint32_t direction : 1; ///< 0 = OUT; 1 = IN. This field encodes whether the full-speed transaction should be an IN or OUT.
+
+ // Word 2: Micro-frame Schedule Control
+ uint8_t int_smask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute complete-split transactions
+ uint8_t fl_int_cmask; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute start-split transactions.
+ uint16_t reserved ; ///< reserved
+
+ // Word 3: siTD Transfer Status and Control
+ // Status [7:0] TODO indentical to qTD Token'status --> refractor later
+ volatile uint32_t : 1 ; // reserved
+ volatile uint32_t split_state : 1 ;
+ volatile uint32_t missed_uframe : 1 ;
+ volatile uint32_t xact_err : 1 ;
+ volatile uint32_t babble_err : 1 ;
+ volatile uint32_t buffer_err : 1 ;
+ volatile uint32_t error : 1 ;
+ volatile uint32_t active : 1 ;
+ // Micro-frame Schedule Control
+ volatile uint32_t cmask_progress : 8 ; ///< This field is used by the host controller to record which split-completes have been executed. See Section 4.12.3.3.2 for behavioral requirements.
+ volatile uint32_t total_bytes : 10 ; ///< This field is initialized by software to the total number of bytes expected in this transfer. Maximum value is 1023
+ volatile uint32_t : 4 ; ///< reserved
+ volatile uint32_t page_select : 1 ; ///< Used to indicate which data page pointer should be concatenated with the CurrentOffsetfield to construct a data buffer pointer
+ uint32_t int_on_complete : 1 ; ///< Do not interrupt when transaction is complete. 1 = Do interrupt when transaction is complete
+ uint32_t : 0 ; // padding to the end of current storage unit
+
+ /// Word 4-5: Buffer Pointer List
+ uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
+
+// union{
+// uint32_t BufferPointer1;
+// struct {
+// volatile uint32_t TCount : 3;
+// volatile uint32_t TPosition : 2;
+// };
+// };
+
+ /*---------- Word 6 ----------*/
+ ehci_link_t back;
+
+ /// SITD is 32-byte aligned but occupies only 28 --> 4 bytes for storing extra data
+ uint8_t used;
+ uint8_t ihd_idx;
+ uint8_t reserved2[2];
+} ehci_sitd_t;
+
+TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
+
+//--------------------------------------------------------------------+
+// EHCI Operational Register
+//--------------------------------------------------------------------+
+enum ehci_interrupt_mask_{
+ EHCI_INT_MASK_USB = TU_BIT(0),
+ EHCI_INT_MASK_ERROR = TU_BIT(1),
+ EHCI_INT_MASK_PORT_CHANGE = TU_BIT(2),
+
+ EHCI_INT_MASK_FRAMELIST_ROLLOVER = TU_BIT(3),
+ EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR = TU_BIT(4),
+ EHCI_INT_MASK_ASYNC_ADVANCE = TU_BIT(5),
+ EHCI_INT_MASK_NXP_SOF = TU_BIT(7),
+
+ EHCI_INT_MASK_NXP_ASYNC = TU_BIT(18),
+ EHCI_INT_MASK_NXP_PERIODIC = TU_BIT(19),
+
+ EHCI_INT_MASK_ALL =
+ EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE |
+ EHCI_INT_MASK_FRAMELIST_ROLLOVER | EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR |
+ EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_NXP_SOF |
+ EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC
+};
+
+enum ehci_usbcmd_pos_ {
+ EHCI_USBCMD_POS_RUN_STOP = 0,
+ EHCI_USBCMD_POS_FRAMELIST_SZIE = 2,
+ EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
+ EHCI_USBCMD_POS_ASYNC_ENABLE = 5,
+ EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB = 15,
+ EHCI_USBCMD_POS_INTERRUPT_THRESHOLD = 16
+};
+
+enum ehci_portsc_change_mask_{
+ EHCI_PORTSC_MASK_CURRENT_CONNECT_STATUS = TU_BIT(0),
+ EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = TU_BIT(1),
+ EHCI_PORTSC_MASK_PORT_EANBLED = TU_BIT(2),
+ EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE = TU_BIT(3),
+ EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = TU_BIT(5),
+
+ EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
+
+ EHCI_PORTSC_MASK_ALL =
+ EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
+ EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE |
+ EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
+};
+
+typedef volatile struct
+{
+ union {
+ uint32_t command;
+
+ struct {
+ uint32_t run_stop : 1 ; ///< 1=Run. 0=Stop
+ uint32_t reset : 1 ; ///< SW write 1 to reset HC, clear by HC when complete
+ uint32_t framelist_size : 2 ; ///< Frame List size 0: 1024, 1: 512, 2: 256
+ uint32_t periodic_enable : 1 ; ///< This bit controls whether the host controller skips processing the Periodic Schedule. Values mean: 0b Do not process the Periodic Schedule 1b Use the PERIODICLISTBASE register to access the Periodic Schedule.
+ uint32_t async_enable : 1 ; ///< This bit controls whether the host controller skips processing the Asynchronous Schedule. Values mean: 0b Do not process the Asynchronous Schedule 1b Use the ASYNCLISTADDR register to access the Asynchronous Schedule.
+ uint32_t async_adv_doorbell : 1 ; ///< Tell HC to interrupt next time it advances async list. Clear by HC
+ uint32_t light_reset : 1 ; ///< Reset HC without affecting ports state
+ uint32_t async_park_count : 2 ; ///< not used by tinyusb
+ uint32_t : 1 ;
+ uint32_t async_park_enable : 1 ; ///< Enable park mode, not used by tinyusb
+ uint32_t : 3 ;
+ uint32_t nxp_framelist_size_msb : 1 ; ///< NXP customized : Bit 2 of the Frame List Size bits \n 011b: 128 elements \n 100b: 64 elements \n 101b: 32 elements \n 110b: 16 elements \n 111b: 8 elements
+ uint32_t int_threshold : 8 ; ///< Default 08h. Interrupt rate in unit of micro frame
+ }command_bm;
+ };
+
+ union {
+ uint32_t status;
+
+ struct {
+ uint32_t usb : 1 ; ///< qTD with IOC is retired
+ uint32_t usb_error : 1 ; ///< qTD retired due to error
+ uint32_t port_change_detect : 1 ; ///< Set when PortOwner or ForcePortResume change from 0 -> 1
+ uint32_t framelist_rollover : 1 ; ///< R/WC The Host Controller sets this bit to a one when the Frame List Index(see Section 2.3.4) rolls over from its maximum value to zero. The exact value at which the rollover occurs depends on the frame list size. For example, if the frame list size (as programmed in the Frame List Sizefield of the USBCMD register) is 1024, the Frame Index Registerrolls over every time FRINDEX[13] toggles. Similarly, if the size is 512, the Host Controller sets this bit to a one every time FRINDEX[12] toggles.
+ uint32_t pci_host_system_error : 1 ; ///< R/WC (not used by NXP) The Host Controller sets this bit to 1 when a serious error occurs during a host system access involving the Host Controller module. In a PCI system, conditions that set this bit to 1 include PCI Parity error, PCI Master Abort, and PCI Target Abort. When this error occurs, the Host Controller clears the Run/Stop bit in the Command register to prevent further execution of the scheduled TDs.
+ uint32_t async_adv : 1 ; ///< Async Advance interrupt
+ uint32_t : 1 ;
+ uint32_t nxp_int_sof : 1 ; ///< NXP customized: this bit will be set every 125us and can be used by host controller driver as a time base.
+ uint32_t : 4 ;
+ uint32_t hc_halted : 1 ; ///< Opposite value to run_stop bit.
+ uint32_t reclamation : 1 ; ///< Used to detect empty async shecudle
+ uint32_t periodic_status : 1 ; ///< Periodic schedule status
+ uint32_t async_status : 1 ; ///< Async schedule status
+ uint32_t : 2 ;
+ uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected andthe packet is on the asynchronous schedule.
+ uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the periodic schedule.
+ uint32_t : 12 ;
+ }status_bm;
+ };
+
+ union{
+ uint32_t inten;
+
+ struct {
+ uint32_t usb : 1 ;
+ uint32_t usb_error : 1 ;
+ uint32_t port_change_detect : 1 ;
+ uint32_t framelist_rollover : 1 ;
+ uint32_t pci_host_system_error : 1 ;
+ uint32_t async_adv : 1 ;
+ uint32_t : 1 ;
+ uint32_t nxp_int_sof : 1 ;
+ uint32_t : 10 ;
+ uint32_t nxp_int_async : 1 ;
+ uint32_t nxp_int_period : 1 ;
+ uint32_t : 12 ;
+ }inten_bm;
+ };
+
+ uint32_t frame_index ; ///< Micro frame counter
+ uint32_t ctrl_ds_seg ; ///< Control Data Structure Segment
+ uint32_t periodic_list_base ; ///< Beginning address of perodic frame list
+ uint32_t async_list_addr ; ///< Address of next async QHD to be executed
+ uint32_t nxp_tt_control ; ///< nxp embedded transaction translator (reserved by EHCI specs)
+ uint32_t reserved[8] ;
+ uint32_t config_flag ; ///< not used by NXP
+
+ union {
+ uint32_t portsc ; ///< port status and control
+ struct {
+ uint32_t current_connect_status : 1; ///< 0: No device, 1: Device is present on port
+ uint32_t connect_status_change : 1; ///< Change in Current Connect Status
+ uint32_t port_enabled : 1; ///< Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
+ uint32_t port_enable_change : 1; ///< Port Enabled has changed
+ uint32_t over_current_active : 1; ///< Port has an over-current condition
+ uint32_t over_current_change : 1; ///< Change to Over-current Active
+ uint32_t force_port_resume : 1; ///< Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
+ uint32_t suspend : 1; ///< Port in suspend state
+ uint32_t port_reset : 1; ///< 1=Port is in Reset. 0=Port is not in Reset
+ uint32_t nxp_highspeed_status : 1; ///< NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
+ uint32_t line_status : 2; ///< D+/D- state: 00: SE0, 10: J-state, 01: K-state
+ uint32_t port_power : 1; ///< 0= power off, 1= power on
+ uint32_t port_owner : 1; ///< not used by NXP
+ uint32_t port_indicator_control : 2; ///< 00b: off, 01b: Amber, 10b: green, 11b: undefined
+ uint32_t port_test_control : 4; ///< Port test mode, not used by tinyusb
+ uint32_t wake_on_connect_enable : 1; ///< Enables device connects as wake-up events
+ uint32_t wake_on_disconnect_enable : 1; ///< Enables device disconnects as wake-up events
+ uint32_t wake_on_over_current_enable : 1; ///< Enables over-current conditions as wake-up events
+ uint32_t nxp_phy_clock_disable : 1; ///< NXP customized: the PHY can be put into Low Power Suspend – Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
+ uint32_t nxp_port_force_fullspeed : 1; ///< NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
+ uint32_t : 1;
+ uint32_t nxp_port_speed : 2; ///< NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
+ uint32_t TU_RESERVED : 4;
+ }portsc_bm;
+ };
+}ehci_registers_t;
+
+//--------------------------------------------------------------------+
+// EHCI Data Organization
+//--------------------------------------------------------------------+
+typedef struct
+{
+ ehci_link_t period_framelist[EHCI_FRAMELIST_SIZE];
+
+ // for NXP ECHI, only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
+ // [0] : 1ms, [1] : 2ms, [2] : 4ms, [3] : 8 ms
+ ehci_qhd_t period_head_arr[4];
+
+ // Note control qhd of dev0 is used as head of async list
+ struct {
+ ehci_qhd_t qhd;
+ ehci_qtd_t qtd;
+ }control[CFG_TUSB_HOST_DEVICE_MAX+1];
+
+ ehci_qhd_t qhd_pool[HCD_MAX_ENDPOINT];
+ ehci_qtd_t qtd_pool[HCD_MAX_XFER] TU_ATTR_ALIGNED(32);
+
+ ehci_registers_t* regs;
+
+ volatile uint32_t uframe_number;
+}ehci_data_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_EHCI_H_ */
+
+/** @} */
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/portable/espressif/esp32s2/dcd_esp32s2.c b/sw/Core/Src/tinyusb/src/portable/espressif/esp32s2/dcd_esp32s2.c
new file mode 100755
index 0000000..7038417
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/espressif/esp32s2/dcd_esp32s2.c
@@ -0,0 +1,793 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 && TUSB_OPT_DEVICE_ENABLED
+
+// Espressif
+#include "driver/periph_ctrl.h"
+#include "freertos/xtensa_api.h"
+#include "esp_intr_alloc.h"
+#include "esp_log.h"
+#include "esp32s2/rom/gpio.h"
+#include "soc/dport_reg.h"
+#include "soc/gpio_sig_map.h"
+#include "soc/usb_periph.h"
+
+#include "device/dcd.h"
+
+// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
+// We disable SOF for now until needed later on
+#define USE_SOF 0
+
+// Max number of bi-directional endpoints including EP0
+// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0
+// We should probably prohibit enabling Endpoint IN > 4 (not done yet)
+#define EP_MAX USB_OUT_EP_NUM
+
+// FIFO size in bytes
+#define EP_FIFO_SIZE 1024
+
+// Max number of IN EP FIFOs
+#define EP_FIFO_NUM 5
+
+typedef struct {
+ uint8_t *buffer;
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t max_size;
+ bool short_packet;
+} xfer_ctl_t;
+
+static const char *TAG = "TUSB:DCD";
+static intr_handle_t usb_ih;
+
+
+static uint32_t _setup_packet[2];
+
+#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
+static xfer_ctl_t xfer_status[EP_MAX][2];
+
+// Keep count of how many FIFOs are in use
+static uint8_t _allocated_fifos = 1; //FIFO0 is always in use
+
+// Will either return an unused FIFO number, or 0 if all are used.
+static uint8_t get_free_fifo(void)
+{
+ if (_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++;
+ return 0;
+}
+
+// Setup the control endpoint 0.
+static void bus_reset(void)
+{
+ for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) {
+ USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
+ }
+
+ USB0.dcfg &= ~USB_DEVADDR_M; // reset address
+
+ USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M;
+ USB0.doepmsk |= USB_SETUPMSK_M | USB_XFERCOMPLMSK;
+ USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/;
+
+ // "USB Data FIFOs" section in reference manual
+ // Peripheral FIFO architecture
+ //
+ // --------------- 320 or 1024 ( 1280 or 4096 bytes )
+ // | IN FIFO MAX |
+ // ---------------
+ // | ... |
+ // --------------- y + x + 16 + GRXFSIZ
+ // | IN FIFO 2 |
+ // --------------- x + 16 + GRXFSIZ
+ // | IN FIFO 1 |
+ // --------------- 16 + GRXFSIZ
+ // | IN FIFO 0 |
+ // --------------- GRXFSIZ
+ // | OUT FIFO |
+ // | ( Shared ) |
+ // --------------- 0
+ //
+ // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits):
+ // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN
+ //
+ // - All EP OUT shared a unique OUT FIFO which uses
+ // * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets).
+ // * 2 locations for OUT endpoint control words.
+ // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes)
+ // * 1 location for global NAK (not required/used here).
+ // * It is recommended to allocate 2 times the largest packet size, therefore
+ // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52
+ USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10,
+ USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo
+ USB0.grxfsiz = 52;
+
+ // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
+ USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL);
+
+ // Ready to receive SETUP packet
+ USB0.out_ep_reg[0].doeptsiz |= USB_SUPCNT0_M;
+
+ USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M;
+}
+
+static void enum_done_processing(void)
+{
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then");
+ // On current silicon on the Full Speed core, speed is fixed to Full Speed.
+ // However, keep for debugging and in case Low Speed is ever supported.
+ uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V);
+
+ // Maximum packet size for EP 0 is set for both directions by writing DIEPCTL
+ if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz)
+ USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes
+ USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
+ xfer_status[0][TUSB_DIR_OUT].max_size = 64;
+ xfer_status[0][TUSB_DIR_IN].max_size = 64;
+ } else {
+ USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes
+ USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
+ xfer_status[0][TUSB_DIR_OUT].max_size = 8;
+ xfer_status[0][TUSB_DIR_IN].max_size = 8;
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init(uint8_t rhport)
+{
+ ESP_LOGV(TAG, "DCD init - Start");
+
+ // A. Disconnect
+ ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up");
+ USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect
+
+ // B. Programming DCFG
+ /* If USB host misbehaves during status portion of control xfer
+ (non zero-length packet), send STALL back and discard. Full speed. */
+ USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL
+ (3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476)
+
+ USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON
+ USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode
+ USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides
+
+ // C. Setting SNAKs, then connect
+ for (int n = 0; n < USB_OUT_EP_NUM; n++) {
+ USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
+ }
+
+ // D. Interruption masking
+ USB0.gintmsk = 0; //mask all
+ USB0.gotgint = ~0U; //clear OTG ints
+ USB0.gintsts = ~0U; //clear pending ints
+ USB0.gintmsk = USB_OTGINTMSK_M |
+ USB_MODEMISMSK_M |
+ #if USE_SOF
+ USB_SOFMSK_M |
+ #endif
+ USB_RXFLVIMSK_M |
+ USB_ERLYSUSPMSK_M |
+ USB_USBSUSPMSK_M |
+ USB_USBRSTMSK_M |
+ USB_ENUMDONEMSK_M |
+ USB_RESETDETMSK_M |
+ USB_DISCONNINTMSK_M; // host most only
+
+ dcd_connect(rhport);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void)rhport;
+ ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr);
+ USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S);
+ // Response with status after changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void)rhport;
+
+ // TODO must manually clear this bit after 1-15 ms
+ // USB0.DCTL |= USB_RMTWKUPSIG_M;
+}
+
+// connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ USB0.dctl &= ~USB_SFTDISCON_M;
+}
+
+// disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ USB0.dctl |= USB_SFTDISCON_M;
+}
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
+{
+ ESP_LOGV(TAG, "DCD endpoint opened");
+ (void)rhport;
+
+ usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
+ usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
+
+ uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+
+ TU_ASSERT(epnum < EP_MAX);
+
+ xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->max_size = desc_edpt->wMaxPacketSize.size;
+
+ if (dir == TUSB_DIR_OUT) {
+ out_ep[epnum].doepctl |= USB_USBACTEP0_M |
+ desc_edpt->bmAttributes.xfer << USB_EPTYPE0_S |
+ desc_edpt->wMaxPacketSize.size << USB_MPS0_S;
+ USB0.daintmsk |= (1 << (16 + epnum));
+ } else {
+ // "USB Data FIFOs" section in reference manual
+ // Peripheral FIFO architecture
+ //
+ // --------------- 320 or 1024 ( 1280 or 4096 bytes )
+ // | IN FIFO MAX |
+ // ---------------
+ // | ... |
+ // --------------- y + x + 16 + GRXFSIZ
+ // | IN FIFO 2 |
+ // --------------- x + 16 + GRXFSIZ
+ // | IN FIFO 1 |
+ // --------------- 16 + GRXFSIZ
+ // | IN FIFO 0 |
+ // --------------- GRXFSIZ
+ // | OUT FIFO |
+ // | ( Shared ) |
+ // --------------- 0
+ //
+ // Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints
+ // - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1)
+ // - Offset: GRXFSIZ + 16 + Size*(epnum-1)
+ // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
+
+ uint8_t fifo_num = get_free_fifo();
+ TU_ASSERT(fifo_num != 0);
+
+ in_ep[epnum].diepctl &= ~(USB_D_TXFNUM1_M | USB_D_EPTYPE1_M | USB_DI_SETD0PID1 | USB_D_MPS1_M);
+ in_ep[epnum].diepctl |= USB_D_USBACTEP1_M |
+ fifo_num << USB_D_TXFNUM1_S |
+ desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S |
+ (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) |
+ desc_edpt->wMaxPacketSize.size << 0;
+
+ USB0.daintmsk |= (1 << (0 + epnum));
+
+ // Both TXFD and TXSA are in unit of 32-bit words.
+ // IN FIFO 0 was configured during enumeration, hence the "+ 16".
+ uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16;
+ uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1);
+ uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1);
+
+ // DIEPTXF starts at FIFO #1.
+ USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset;
+ }
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
+{
+ (void)rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->short_packet = false;
+
+ uint16_t num_packets = (total_bytes / xfer->max_size);
+ uint8_t short_packet_size = total_bytes % xfer->max_size;
+
+ // Zero-size packet is special case.
+ if (short_packet_size > 0 || (total_bytes == 0)) {
+ num_packets++;
+ }
+
+ ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
+ epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
+ num_packets, total_bytes);
+
+ // IN and OUT endpoint xfers are interrupt-driven, we just schedule them
+ // here.
+ if (dir == TUSB_DIR_IN) {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
+ USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
+
+ // Enable fifo empty interrupt only if there are something to put in the fifo.
+ if(total_bytes != 0) {
+ USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
+ }
+ } else {
+ // Each complete packet for OUT xfers triggers XFRC.
+ USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
+ USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
+ }
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void)rhport;
+
+ usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
+ usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if (dir == TUSB_DIR_IN) {
+ // Only disable currently enabled non-control endpoint
+ if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) {
+ in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M);
+ } else {
+ // Stop transmitting packets and NAK IN xfers.
+ in_ep[epnum].diepctl |= USB_DI_SNAK1_M;
+ while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) ;
+
+ // Disable the endpoint. Note that both SNAK and STALL are set here.
+ in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | USB_D_EPDIS1_M);
+ while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) ;
+ in_ep[epnum].diepint = USB_D_EPDISBLD0_M;
+ }
+
+ // Flush the FIFO, and wait until we have confirmed it cleared.
+ uint8_t const fifo_num = ((in_ep[epnum].diepctl >> USB_D_TXFNUM1_S) & USB_D_TXFNUM1_V);
+ USB0.grstctl |= (fifo_num << USB_TXFNUM_S);
+ USB0.grstctl |= USB_TXFFLSH_M;
+ while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ;
+ } else {
+ // Only disable currently enabled non-control endpoint
+ if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) {
+ out_ep[epnum].doepctl |= USB_STALL0_M;
+ } else {
+ // Asserting GONAK is required to STALL an OUT endpoint.
+ // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
+ // anyway, and it can't be cleared by user code. If this while loop never
+ // finishes, we have bigger problems than just the stack.
+ USB0.dctl |= USB_SGOUTNAK_M;
+ while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) ;
+
+ // Ditto here- disable the endpoint. Note that only STALL and not SNAK
+ // is set here.
+ out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M);
+ while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) ;
+ out_ep[epnum].doepint = USB_EPDISBLD0_M;
+
+ // Allow other OUT endpoints to keep receiving.
+ USB0.dctl |= USB_CGOUTNAK_M;
+ }
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void)rhport;
+
+ usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
+ usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if (dir == TUSB_DIR_IN) {
+ in_ep[epnum].diepctl &= ~USB_D_STALL1_M;
+
+ uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S;
+ // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
+ // and bulk endpoints.
+ if (eptype == 2 || eptype == 3) {
+ in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M;
+ }
+ } else {
+ out_ep[epnum].doepctl &= ~USB_STALL1_M;
+
+ uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S;
+ // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
+ // and bulk endpoints.
+ if (eptype == 2 || eptype == 3) {
+ out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M;
+ }
+ }
+}
+
+/*------------------------------------------------------------------*/
+
+static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size)
+{
+ ESP_EARLY_LOGV(TAG, "USB - receive_packet");
+ volatile uint32_t *rx_fifo = USB0.fifo[0];
+
+ // See above TODO
+ // uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos;
+ // xfer->queued_len = xfer->total_len - remaining;
+
+ uint16_t remaining = xfer->total_len - xfer->queued_len;
+ uint16_t to_recv_size;
+
+ if (remaining <= xfer->max_size) {
+ // Avoid buffer overflow.
+ to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
+ } else {
+ // Room for full packet, choose recv_size based on what the microcontroller
+ // claims.
+ to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
+ }
+
+ uint8_t to_recv_rem = to_recv_size % 4;
+ uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
+
+ // Do not assume xfer buffer is aligned.
+ uint8_t *base = (xfer->buffer + xfer->queued_len);
+
+ // This for loop always runs at least once- skip if less than 4 bytes
+ // to collect.
+ if (to_recv_size >= 4) {
+ for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
+ uint32_t tmp = (*rx_fifo);
+ base[i] = tmp & 0x000000FF;
+ base[i + 1] = (tmp & 0x0000FF00) >> 8;
+ base[i + 2] = (tmp & 0x00FF0000) >> 16;
+ base[i + 3] = (tmp & 0xFF000000) >> 24;
+ }
+ }
+
+ // Do not read invalid bytes from RX FIFO.
+ if (to_recv_rem != 0) {
+ uint32_t tmp = (*rx_fifo);
+ uint8_t *last_32b_bound = base + to_recv_size_aligned;
+
+ last_32b_bound[0] = tmp & 0x000000FF;
+ if (to_recv_rem > 1) {
+ last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
+ }
+ if (to_recv_rem > 2) {
+ last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
+ }
+ }
+
+ xfer->queued_len += xfer_size;
+
+ // Per USB spec, a short OUT packet (including length 0) is always
+ // indicative of the end of a transfer (at least for ctl, bulk, int).
+ xfer->short_packet = (xfer_size < xfer->max_size);
+}
+
+static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num)
+{
+ ESP_EARLY_LOGV(TAG, "USB - transmit_packet");
+ volatile uint32_t *tx_fifo = USB0.fifo[fifo_num];
+
+ uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S;
+ xfer->queued_len = xfer->total_len - remaining;
+
+ uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
+ uint8_t to_xfer_rem = to_xfer_size % 4;
+ uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
+
+ // Buffer might not be aligned to 32b, so we need to force alignment
+ // by copying to a temp var.
+ uint8_t *base = (xfer->buffer + xfer->queued_len);
+
+ // This for loop always runs at least once- skip if less than 4 bytes
+ // to send off.
+ if (to_xfer_size >= 4) {
+ for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
+ uint32_t tmp = base[i] | (base[i + 1] << 8) |
+ (base[i + 2] << 16) | (base[i + 3] << 24);
+ (*tx_fifo) = tmp;
+ }
+ }
+
+ // Do not read beyond end of buffer if not divisible by 4.
+ if (to_xfer_rem != 0) {
+ uint32_t tmp = 0;
+ uint8_t *last_32b_bound = base + to_xfer_size_aligned;
+
+ tmp |= last_32b_bound[0];
+ if (to_xfer_rem > 1) {
+ tmp |= (last_32b_bound[1] << 8);
+ }
+ if (to_xfer_rem > 2) {
+ tmp |= (last_32b_bound[2] << 16);
+ }
+
+ (*tx_fifo) = tmp;
+ }
+}
+
+static void read_rx_fifo(void)
+{
+ // Pop control word off FIFO (completed xfers will have 2 control words,
+ // we only pop one ctl word each interrupt).
+ uint32_t const ctl_word = USB0.grxstsp;
+ uint8_t const pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S;
+ uint8_t const epnum = (ctl_word & USB_CHNUM_M ) >> USB_CHNUM_S;
+ uint16_t const bcnt = (ctl_word & USB_BCNT_M ) >> USB_BCNT_S;
+
+ switch (pktsts) {
+ case 0x01: // Global OUT NAK (Interrupt)
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK");
+ break;
+
+ case 0x02: { // Out packet recvd
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet");
+ xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
+ receive_packet(xfer, bcnt);
+ }
+ break;
+
+ case 0x03: // Out packet done (Interrupt)
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done");
+ break;
+
+ case 0x04: // Step 2: Setup transaction completed (Interrupt)
+ // After this event, OEPINT interrupt will occur with SETUP bit set
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done");
+ USB0.out_ep_reg[epnum].doeptsiz |= USB_SUPCNT0_M;
+ break;
+
+ case 0x06: { // Step1: Setup data packet received
+ volatile uint32_t *rx_fifo = USB0.fifo[0];
+
+ // We can receive up to three setup packets in succession, but
+ // only the last one is valid. Therefore we just overwrite it
+ _setup_packet[0] = (*rx_fifo);
+ _setup_packet[1] = (*rx_fifo);
+
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", _setup_packet[0], _setup_packet[1]);
+ }
+ break;
+
+ default: // Invalid, do something here, like breakpoint?
+ TU_BREAKPOINT();
+ break;
+ }
+}
+
+static void handle_epout_ints(void)
+{
+ // GINTSTS will be cleared with DAINT == 0
+ // DAINT for a given EP clears when DOEPINTx is cleared.
+ // DOEPINT will be cleared when DAINT's out bits are cleared.
+ for (int n = 0; n < USB_OUT_EP_NUM; n++) {
+ xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
+
+ if (USB0.daint & (1 << (16 + n))) {
+ // SETUP packet Setup Phase done.
+ if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) {
+ USB0.out_ep_reg[n].doepint = USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear
+ dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
+ }
+
+ // OUT XFER complete (single packet).q
+ if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) {
+
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)");
+ USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M;
+
+ // Transfer complete if short packet or total len is transferred
+ if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
+ xfer->short_packet = false;
+ dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ } else {
+ // Schedule another packet to be received.
+ USB0.out_ep_reg[n].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
+ USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
+ }
+ }
+ }
+ }
+}
+
+static void handle_epin_ints(void)
+{
+ // GINTSTS will be cleared with DAINT == 0
+ // DAINT for a given EP clears when DIEPINTx is cleared.
+ // IEPINT will be cleared when DAINT's out bits are cleared.
+ for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) {
+ xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN];
+
+ if (USB0.daint & (1 << (0 + n))) {
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n);
+ // IN XFER complete (entire xfer).
+ if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) {
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!");
+ USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M;
+ dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+
+ // XFER FIFO empty
+ if (USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M) {
+ ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!");
+ USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M;
+ transmit_packet(xfer, &USB0.in_ep_reg[n], n);
+
+ // Turn off TXFE if all bytes are written.
+ if (xfer->queued_len == xfer->total_len)
+ {
+ USB0.dtknqr4_fifoemptymsk &= ~(1 << n);
+ }
+ }
+
+ // XFER Timeout
+ if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) {
+ // Clear interrupt or enpoint will hang.
+ USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M;
+ // Maybe retry?
+ }
+ }
+ }
+}
+
+
+static void _dcd_int_handler(void* arg)
+{
+ (void) arg;
+ uint8_t const rhport = 0;
+
+ const uint32_t int_status = USB0.gintsts;
+ //const uint32_t int_msk = USB0.gintmsk;
+
+ if (int_status & USB_USBRST_M) {
+ // start of reset
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset");
+ USB0.gintsts = USB_USBRST_M;
+ // FIFOs will be reassigned when the endpoints are reopen
+ _allocated_fifos = 1;
+ bus_reset();
+ }
+
+ if (int_status & USB_RESETDET_M) {
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend");
+ USB0.gintsts = USB_RESETDET_M;
+ bus_reset();
+ }
+
+ if (int_status & USB_ENUMDONE_M) {
+ // ENUMDNE detects speed of the link. For full-speed, we
+ // always expect the same value. This interrupt is considered
+ // the end of reset.
+ USB0.gintsts = USB_ENUMDONE_M;
+ enum_done_processing();
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+ }
+
+ if(int_status & USB_USBSUSP_M)
+ {
+ USB0.gintsts = USB_USBSUSP_M;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+ }
+
+ if(int_status & USB_WKUPINT_M)
+ {
+ USB0.gintsts = USB_WKUPINT_M;
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+ }
+
+ if (int_status & USB_OTGINT_M)
+ {
+ // OTG INT bit is read-only
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected");
+
+ uint32_t const otg_int = USB0.gotgint;
+
+ if (otg_int & USB_SESENDDET_M)
+ {
+ dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
+ }
+
+ USB0.gotgint = otg_int;
+ }
+
+#if USE_SOF
+ if (int_status & USB_SOF_M) {
+ USB0.gintsts = USB_SOF_M;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); // do nothing actually
+ }
+#endif
+
+ if (int_status & USB_RXFLVI_M) {
+ // RXFLVL bit is read-only
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!");
+
+ // Mask out RXFLVL while reading data from FIFO
+ USB0.gintmsk &= ~USB_RXFLVIMSK_M;
+ read_rx_fifo();
+ USB0.gintmsk |= USB_RXFLVIMSK_M;
+ }
+
+ // OUT endpoint interrupt handling.
+ if (int_status & USB_OEPINT_M) {
+ // OEPINT is read-only
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!");
+ handle_epout_ints();
+ }
+
+ // IN endpoint interrupt handling.
+ if (int_status & USB_IEPINT_M) {
+ // IEPINT bit read-only
+ ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!");
+ handle_epin_ints();
+ }
+
+ // Without handling
+ USB0.gintsts |= USB_CURMOD_INT_M |
+ USB_MODEMIS_M |
+ USB_OTGINT_M |
+ USB_NPTXFEMP_M |
+ USB_GINNAKEFF_M |
+ USB_GOUTNAKEFF |
+ USB_ERLYSUSP_M |
+ USB_USBSUSP_M |
+ USB_ISOOUTDROP_M |
+ USB_EOPF_M |
+ USB_EPMIS_M |
+ USB_INCOMPISOIN_M |
+ USB_INCOMPIP_M |
+ USB_FETSUSP_M |
+ USB_PTXFEMP_M;
+}
+
+void dcd_int_enable (uint8_t rhport)
+{
+ (void) rhport;
+ esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t) _dcd_int_handler, NULL, &usb_ih);
+}
+
+void dcd_int_disable (uint8_t rhport)
+{
+ (void) rhport;
+ esp_intr_free(usb_ih);
+}
+
+#endif // OPT_MCU_ESP32S2
+
diff --git a/sw/Core/Src/tinyusb/src/portable/microchip/samd/dcd_samd.c b/sw/Core/Src/tinyusb/src/portable/microchip/samd/dcd_samd.c
new file mode 100755
index 0000000..3cff92f
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/microchip/samd/dcd_samd.c
@@ -0,0 +1,409 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
+ CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X)
+
+#include "sam.h"
+#include "device/dcd.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
+
+// Setup packet is only 8 bytes in length. However under certain scenario,
+// USB DMA controller may decide to overwrite/overflow the buffer with
+// 2 extra bytes of CRC. From datasheet's "Management of SETUP Transactions" section
+// If the number of received data bytes is the maximum data payload specified by
+// PCKSIZE.SIZE minus one, only the first CRC data is written to the data buffer.
+// If the number of received data is equal or less than the data payload specified
+// by PCKSIZE.SIZE minus two, both CRC data bytes are written to the data buffer.
+// Therefore we will need to increase it to 10 bytes here.
+static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8+2];
+
+// ready for receiving SETUP packet
+static inline void prepare_setup(void)
+{
+ // Only make sure the EP0 OUT buffer is ready
+ sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet;
+ sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(tusb_control_request_t);
+ sram_registers[0][0].PCKSIZE.bit.BYTE_COUNT = 0;
+}
+
+// Setup the control endpoint 0.
+static void bus_reset(void)
+{
+ // Max size of packets is 64 bytes.
+ UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
+ bank_out->PCKSIZE.bit.SIZE = 0x3;
+ UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
+ bank_in->PCKSIZE.bit.SIZE = 0x3;
+
+ UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
+ ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
+ ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
+
+ // Prepare for setup packet
+ prepare_setup();
+}
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init (uint8_t rhport)
+{
+ (void) rhport;
+
+ // Reset to get in a clean state.
+ USB->DEVICE.CTRLA.bit.SWRST = true;
+ while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {}
+ while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {}
+
+ USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
+ USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
+ USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
+
+ USB->DEVICE.QOSCTRL.bit.CQOS = 3; // High Quality
+ USB->DEVICE.QOSCTRL.bit.DQOS = 3; // High Quality
+
+ // Configure registers
+ USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers;
+ USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
+ USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
+ while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {}
+
+ USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
+ USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST;
+}
+
+#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_0_IRQn);
+ NVIC_EnableIRQ(USB_1_IRQn);
+ NVIC_EnableIRQ(USB_2_IRQn);
+ NVIC_EnableIRQ(USB_3_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_3_IRQn);
+ NVIC_DisableIRQ(USB_2_IRQn);
+ NVIC_DisableIRQ(USB_1_IRQn);
+ NVIC_DisableIRQ(USB_0_IRQn);
+}
+
+#elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+#else
+
+#error "No implementation available for dcd_int_enable / dcd_int_disable"
+
+#endif
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ (void) dev_addr;
+
+ // Response with zlp status
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0);
+
+ // DCD can only set address after status for this request is complete
+ // do it at dcd_edpt0_status_complete()
+
+ // Enable SUSPEND interrupt since the bus signal D+/D- are stable now.
+ USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending
+ USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+ USB->DEVICE.CTRLB.bit.UPRSM = 1;
+}
+
+// disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ USB->DEVICE.CTRLB.reg |= USB_DEVICE_CTRLB_DETACH;
+}
+
+// connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ USB->DEVICE.CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH;
+}
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+// Invoked when a control transfer's status stage is complete.
+// May help DCD to prepare for next control transfer, this API is optional.
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
+{
+ (void) rhport;
+
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS )
+ {
+ uint8_t const dev_addr = (uint8_t) request->wValue;
+ USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+ }
+
+ // Just finished status stage, prepare for next setup packet
+ // Note: we may already prepare setup when queueing the control status.
+ // but it has no harm to do it again here
+ prepare_setup();
+}
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+
+ UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
+ uint32_t size_value = 0;
+ while (size_value < 7) {
+ if (1 << (size_value + 3) == desc_edpt->wMaxPacketSize.size) {
+ break;
+ }
+ size_value++;
+ }
+
+ // unsupported endpoint size
+ if ( size_value == 7 && desc_edpt->wMaxPacketSize.size != 1023 ) return false;
+
+ bank->PCKSIZE.bit.SIZE = size_value;
+
+ UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+ if ( dir == TUSB_DIR_OUT )
+ {
+ ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1;
+ ep->EPINTENSET.bit.TRCPT0 = true;
+ }else
+ {
+ ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1;
+ ep->EPINTENSET.bit.TRCPT1 = true;
+ }
+
+ return true;
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
+ UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+ bank->ADDR.reg = (uint32_t) buffer;
+
+ // A SETUP token can occur immediately after an ZLP Status.
+ // So make sure we have a valid buffer for setup packet.
+ // Status = ZLP EP0 with direction opposite to one in the dir bit of current setup
+ if ( (epnum == 0) && (buffer == NULL) && (total_bytes == 0) && (dir != tu_edpt_dir(_setup_packet[0])) ) {
+ prepare_setup();
+ }
+
+ if ( dir == TUSB_DIR_OUT )
+ {
+ bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
+ bank->PCKSIZE.bit.BYTE_COUNT = 0;
+ ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY;
+ ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0;
+ } else
+ {
+ bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ bank->PCKSIZE.bit.BYTE_COUNT = total_bytes;
+ ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY;
+ ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1;
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
+ } else {
+ ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
+ }
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN;
+ } else {
+ ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT;
+ }
+}
+
+//--------------------------------------------------------------------+
+// Interrupt Handler
+//--------------------------------------------------------------------+
+void maybe_transfer_complete(void) {
+ uint32_t epints = USB->DEVICE.EPINTSMRY.reg;
+
+ for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) {
+ if ((epints & (1 << epnum)) == 0) {
+ continue;
+ }
+
+ UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+ uint32_t epintflag = ep->EPINTFLAG.reg;
+
+ // Handle IN completions
+ if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) {
+ UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN];
+ uint16_t const total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
+
+ dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, total_transfer_size, XFER_RESULT_SUCCESS, true);
+
+ ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1;
+ }
+
+ // Handle OUT completions
+ if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) {
+ UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT];
+ uint16_t const total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
+
+ dcd_event_xfer_complete(0, epnum, total_transfer_size, XFER_RESULT_SUCCESS, true);
+
+ ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0;
+ }
+ }
+}
+
+
+void dcd_int_handler (uint8_t rhport)
+{
+ (void) rhport;
+
+ uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg;
+
+ // Start of Frame
+ if ( int_status & USB_DEVICE_INTFLAG_SOF )
+ {
+ USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+
+ // SAMD doesn't distinguish between Suspend and Disconnect state.
+ // Both condition will cause SUSPEND interrupt triggered.
+ // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only
+ // enabled when we received SET_ADDRESS request and cleared on Bus Reset
+ if ( int_status & USB_DEVICE_INTFLAG_SUSPEND )
+ {
+ USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SUSPEND;
+
+ // Enable wakeup interrupt
+ USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending
+ USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+
+ // Wakeup interrupt is only enabled when we got suspended.
+ // Wakeup interrupt will disable itself
+ if ( int_status & USB_DEVICE_INTFLAG_WAKEUP )
+ {
+ USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+ // disable wakeup interrupt itself
+ USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+
+ // Enable of Reset
+ if ( int_status & USB_DEVICE_INTFLAG_EORST )
+ {
+ USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST;
+
+ // Disable both suspend and wakeup interrupt
+ USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
+
+ bus_reset();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ }
+
+ // Handle SETUP packet
+ if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP)
+ {
+ // This copies the data elsewhere so we can reuse the buffer.
+ dcd_event_setup_received(0, _setup_packet, true);
+
+ // Although Setup packet only set RXSTP bit,
+ // TRCPT0 bit could already be set by previous ZLP OUT Status (not handled until now).
+ // Since control status complete event is optional, we can just clear TRCPT0 and skip the status event
+ USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP | USB_DEVICE_EPINTFLAG_TRCPT0;
+ }
+
+ // Handle complete transfer
+ maybe_transfer_complete();
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/microchip/samg/dcd_samg.c b/sw/Core/Src/tinyusb/src/portable/microchip/samg/dcd_samg.c
new file mode 100755
index 0000000..2f9f109
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/microchip/samg/dcd_samg.c
@@ -0,0 +1,456 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_SAMG
+
+#include "sam.h"
+#include "device/dcd.h"
+
+// TODO should support (SAM3S || SAM4S || SAM4E || SAMG55)
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+#define EP_COUNT 6
+
+// Transfer descriptor
+typedef struct
+{
+ uint8_t* buffer;
+ uint16_t total_len;
+ volatile uint16_t actual_len;
+ uint16_t epsize;
+} xfer_desc_t;
+
+// Endpoint 0-5, each can only be either OUT or In
+xfer_desc_t _dcd_xfer[EP_COUNT];
+
+void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
+{
+ xfer->epsize = epsize;
+}
+
+void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
+{
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->actual_len = 0;
+}
+
+void xfer_end(xfer_desc_t* xfer)
+{
+ xfer->buffer = NULL;
+ xfer->total_len = 0;
+ xfer->actual_len = 0;
+}
+
+uint16_t xfer_packet_len(xfer_desc_t* xfer)
+{
+ // also cover zero-length packet
+ return tu_min16(xfer->total_len - xfer->actual_len, xfer->epsize);
+}
+
+void xfer_packet_done(xfer_desc_t* xfer)
+{
+ uint16_t const xact_len = xfer_packet_len(xfer);
+
+ xfer->buffer += xact_len;
+ xfer->actual_len += xact_len;
+}
+
+//------------- Transaction helpers -------------//
+
+// Write data to EP FIFO, return number of written bytes
+static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
+{
+ for(uint16_t i=0; i<xact_len; i++)
+ {
+ UDP->UDP_FDR[epnum] = (uint32_t) buffer[i];
+ }
+}
+
+// Read data from EP FIFO
+static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
+{
+ for(uint16_t i=0; i<xact_len; i++)
+ {
+ buffer[i] = (uint8_t) UDP->UDP_FDR[epnum];
+ }
+}
+
+
+//! Bitmap for all status bits in CSR that are not affected by a value 1.
+#define CSR_NO_EFFECT_1_ALL (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT | UDP_CSR_RXSETUP | UDP_CSR_TXCOMP)
+
+// Per Specs: CSR need synchronization each write
+static inline void csr_write(uint8_t epnum, uint32_t value)
+{
+ uint32_t const csr = value;
+ UDP->UDP_CSR[epnum] = csr;
+
+ volatile uint32_t nop_count;
+ for (nop_count = 0; nop_count < 20; nop_count ++) __NOP();
+}
+
+// Per Specs: CSR need synchronization each write
+static inline void csr_set(uint8_t epnum, uint32_t mask)
+{
+ csr_write(epnum, UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL | mask);
+}
+
+// Per Specs: CSR need synchronization each write
+static inline void csr_clear(uint8_t epnum, uint32_t mask)
+{
+ csr_write(epnum, (UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL) & ~mask);
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+
+// Set up endpoint 0, clear all other endpoints
+static void bus_reset(void)
+{
+ tu_memclr(_dcd_xfer, sizeof(_dcd_xfer));
+
+ xfer_epsize_set(&_dcd_xfer[0], CFG_TUD_ENDPOINT0_SIZE);
+
+ // Enable EP0 control
+ csr_write(0, UDP_CSR_EPEDS_Msk);
+
+ // Enable interrupt : EP0, Suspend, Resume, Wakeup
+ UDP->UDP_IER = UDP_IER_EP0INT_Msk | UDP_IER_RXSUSP_Msk | UDP_IER_RXRSM_Msk | UDP_IER_WAKEUP_Msk;
+
+ // Enable transceiver
+ UDP->UDP_TXVC &= ~UDP_TXVC_TXVDIS_Msk;
+}
+
+// Initialize controller to device mode
+void dcd_init (uint8_t rhport)
+{
+ tu_memclr(_dcd_xfer, sizeof(_dcd_xfer));
+ dcd_connect(rhport);
+}
+
+// Enable device interrupt
+void dcd_int_enable (uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(UDP_IRQn);
+}
+
+// Disable device interrupt
+void dcd_int_disable (uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(UDP_IRQn);
+}
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ (void) dev_addr;
+
+ // Response with zlp status
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0);
+
+ // DCD can only set address after status for this request is complete.
+ // do it at dcd_edpt0_status_complete()
+}
+
+// Wake up host
+void dcd_remote_wakeup (uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+
+ // Enable pull-up, disable transceiver
+ UDP->UDP_TXVC = UDP_TXVC_PUON | UDP_TXVC_TXVDIS_Msk;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+
+ // disable both pullup and transceiver
+ UDP->UDP_TXVC = UDP_TXVC_TXVDIS_Msk;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Invoked when a control transfer's status stage is complete.
+// May help DCD to prepare for next control transfer, this API is optional.
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
+{
+ (void) rhport;
+
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
+ {
+ if (request->bRequest == TUSB_REQ_SET_ADDRESS)
+ {
+ uint8_t const dev_addr = (uint8_t) request->wValue;
+
+ // Enable addressed state
+ UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN_Msk;
+
+ // Set new address & Function enable bit
+ UDP->UDP_FADDR = UDP_FADDR_FEN_Msk | UDP_FADDR_FADD(dev_addr);
+ }
+ else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION)
+ {
+ // Configured State
+ UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG_Msk;
+ }
+ }
+}
+
+// Configure endpoint's registers according to descriptor
+// SAMG doesn't support a same endpoint number with IN and OUT
+// e.g EP1 OUT & EP1 IN cannot exist together
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_desc->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+
+ // TODO Isochronous is not supported yet
+ TU_VERIFY(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
+ TU_VERIFY(epnum < EP_COUNT);
+
+ // Must not already enabled
+ TU_ASSERT((UDP->UDP_CSR[epnum] & UDP_CSR_EPEDS_Msk) == 0);
+
+ xfer_epsize_set(&_dcd_xfer[epnum], ep_desc->wMaxPacketSize.size);
+
+ // Configure type and enable EP
+ csr_write(epnum, UDP_CSR_EPEDS_Msk | UDP_CSR_EPTYPE(ep_desc->bmAttributes.xfer + 4*dir));
+
+ // Enable EP Interrupt for IN
+ if (dir == TUSB_DIR_IN) UDP->UDP_IER |= (1 << epnum);
+
+ return true;
+}
+
+// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_desc_t* xfer = &_dcd_xfer[epnum];
+ xfer_begin(xfer, buffer, total_bytes);
+
+ if (dir == TUSB_DIR_OUT)
+ {
+ // Enable interrupt when starting OUT transfer
+ if (epnum != 0) UDP->UDP_IER |= (1 << epnum);
+ }
+ else
+ {
+ xact_ep_write(epnum, xfer->buffer, xfer_packet_len(xfer));
+
+ // TX ready for transfer
+ csr_set(epnum, UDP_CSR_TXPKTRDY_Msk);
+ }
+
+ return true;
+}
+
+// Stall endpoint
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ // For EP0 USBD will stall both EP0 Out and In with 0x00 and 0x80
+ // only handle one by skipping 0x80
+ if ( ep_addr == tu_edpt_addr(0, TUSB_DIR_IN_MASK) ) return;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+
+ // Set force stall bit
+ csr_set(epnum, UDP_CSR_FORCESTALL_Msk);
+}
+
+// clear stall, data toggle is also reset to DATA0
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+
+ // clear stall
+ csr_clear(epnum, UDP_CSR_FORCESTALL_Msk);
+
+ // must also reset EP to clear data toggle
+ UDP->UDP_RST_EP |= (1 << epnum);
+ UDP->UDP_RST_EP &= ~(1 << epnum);
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+void dcd_int_handler(uint8_t rhport)
+{
+ uint32_t const intr_mask = UDP->UDP_IMR;
+ uint32_t const intr_status = UDP->UDP_ISR & intr_mask;
+
+ // clear interrupt
+ UDP->UDP_ICR = intr_status;
+
+ // Bus reset
+ if (intr_status & UDP_ISR_ENDBUSRES_Msk)
+ {
+ bus_reset();
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+ }
+
+ // SOF
+// if (intr_status & UDP_ISR_SOFINT_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+
+ // Suspend
+ if (intr_status & UDP_ISR_RXSUSP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+
+ // Resume
+ if (intr_status & UDP_ISR_RXRSM_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+
+ // Wakeup
+ if (intr_status & UDP_ISR_WAKEUP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+
+ //------------- Endpoints -------------//
+
+ if ( intr_status & TU_BIT(0) )
+ {
+ // setup packet
+ if ( UDP->UDP_CSR[0] & UDP_CSR_RXSETUP )
+ {
+ // get setup from FIFO
+ uint8_t setup[8];
+ for(uint8_t i=0; i<sizeof(setup); i++)
+ {
+ setup[i] = (uint8_t) UDP->UDP_FDR[0];
+ }
+
+ // notify usbd
+ dcd_event_setup_received(rhport, setup, true);
+
+ // Set EP direction bit according to DATA stage
+ // MUST only be set before RXSETUP is clear per specs
+ if ( tu_edpt_dir(setup[0]) )
+ {
+ csr_set(0, UDP_CSR_DIR_Msk);
+ }
+ else
+ {
+ csr_clear(0, UDP_CSR_DIR_Msk);
+ }
+
+ // Clear Setup, stall and other on-going transfer bits
+ csr_clear(0, UDP_CSR_RXSETUP_Msk | UDP_CSR_TXPKTRDY_Msk | UDP_CSR_TXCOMP_Msk | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT_Msk | UDP_CSR_FORCESTALL_Msk);
+ }
+ }
+
+ for(uint8_t epnum = 0; epnum < EP_COUNT; epnum++)
+ {
+ if ( intr_status & TU_BIT(epnum) )
+ {
+ xfer_desc_t* xfer = &_dcd_xfer[epnum];
+
+ //------------- Endpoint IN -------------//
+ if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk)
+ {
+ xfer_packet_done(xfer);
+
+ uint16_t const xact_len = xfer_packet_len(xfer);
+
+ if (xact_len)
+ {
+ // write to EP fifo
+ xact_ep_write(epnum, xfer->buffer, xact_len);
+
+ // TX ready for transfer
+ csr_set(epnum, UDP_CSR_TXPKTRDY_Msk);
+ }else
+ {
+ // xfer is complete
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+
+ // Required since control OUT can happen right after before stack handle this event
+ xfer_end(xfer);
+ }
+
+ // Clear TX Complete bit
+ csr_clear(epnum, UDP_CSR_TXCOMP_Msk);
+ }
+
+ //------------- Endpoint OUT -------------//
+ // Ping-Pong is a MUST for Bulk/Iso
+ // NOTE: When both Bank0 and Bank1 are both set, there is no way to know which one comes first
+ uint32_t const banks_complete = UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk);
+ if (banks_complete)
+ {
+ uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos);
+
+ // Read from EP fifo
+ xact_ep_read(epnum, xfer->buffer, xact_len);
+ xfer_packet_done(xfer);
+
+ if ( 0 == xfer_packet_len(xfer) )
+ {
+ // Disable OUT EP interrupt when transfer is complete
+ if (epnum != 0) UDP->UDP_IDR |= (1 << epnum);
+
+ dcd_event_xfer_complete(rhport, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+ xfer_end(xfer);
+ }
+
+ // Clear DATA Bank0/1 bit
+ csr_clear(epnum, banks_complete);
+ }
+
+ // Stall sent to host
+ if (UDP->UDP_CSR[epnum] & UDP_CSR_STALLSENT_Msk)
+ {
+ csr_clear(epnum, UDP_CSR_STALLSENT_Msk);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c b/sw/Core/Src/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c
new file mode 100755
index 0000000..6b8095a
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -0,0 +1,981 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_NRF5X
+
+#include "nrf.h"
+#include "nrf_clock.h"
+#include "nrf_power.h"
+#include "nrfx_usbd_errata.h"
+#include "device/dcd.h"
+
+// TODO remove later
+#include "device/usbd.h"
+#include "device/usbd_pvt.h" // to use defer function helper
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+enum
+{
+ // Max allowed by USB specs
+ MAX_PACKET_SIZE = 64,
+
+ // Mask of all END event (IN & OUT) for all endpoints. ENDEPIN0-7, ENDEPOUT0-7, ENDISOIN, ENDISOOUT
+ EDPT_END_ALL_MASK = (0xff << USBD_INTEN_ENDEPIN0_Pos) | (0xff << USBD_INTEN_ENDEPOUT0_Pos) |
+ USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
+};
+
+enum
+{
+ EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
+ EP_CBI_COUNT = 8 // Control Bulk Interrupt endpoints count
+};
+
+// Transfer Descriptor
+typedef struct
+{
+ uint8_t* buffer;
+ uint16_t total_len;
+ volatile uint16_t actual_len;
+ uint16_t mps; // max packet size
+
+ // nRF will auto accept OUT packet after DMA is done
+ // indicate packet is already ACK
+ volatile bool data_received;
+
+ // Set to true when data was transferred from RAM to ISO IN output buffer.
+ // New data can be put in ISO IN output buffer after SOF.
+ bool iso_in_transfer_ready;
+
+} xfer_td_t;
+
+// Data for managing dcd
+static struct
+{
+ // All 8 endpoints including control IN & OUT (offset 1)
+ // +1 for ISO endpoints
+ xfer_td_t xfer[EP_CBI_COUNT + 1][2];
+
+ // Number of pending DMA that is started but not handled yet by dcd_int_handler().
+ // Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
+ // However, in critical section with interrupt disabled, the DMA can be finished and added up
+ // until handled by dcd_init_handler() when exiting critical section.
+ volatile uint8_t dma_pending;
+}_dcd;
+
+/*------------------------------------------------------------------*/
+/* Control / Bulk / Interrupt (CBI) Transfer
+ *------------------------------------------------------------------*/
+
+// NVIC_GetEnableIRQ is only available in CMSIS v5
+#ifndef NVIC_GetEnableIRQ
+static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
+{
+ if ((int32_t)(IRQn) >= 0)
+ {
+ return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
+ }
+ else
+ {
+ return(0U);
+ }
+}
+#endif
+
+// helper to start DMA
+static void edpt_dma_start(volatile uint32_t* reg_startep)
+{
+ // Only one dma can be active
+ if ( _dcd.dma_pending )
+ {
+ if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
+ {
+ // Called within ISR, use usbd task to defer later
+ usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
+ return;
+ }
+ else
+ {
+ if ( __get_PRIMASK() || !NVIC_GetEnableIRQ(USBD_IRQn) )
+ {
+ // Called in critical section with interrupt disabled. We have to manually check
+ // for the DMA complete by comparing current pending DMA with number of ENDED Events
+ uint32_t ended = 0;
+
+ while ( _dcd.dma_pending > ((uint8_t) ended) )
+ {
+ ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT;
+
+ for (uint8_t i=0; i<EP_CBI_COUNT; i++)
+ {
+ ended += NRF_USBD->EVENTS_ENDEPIN[i] + NRF_USBD->EVENTS_ENDEPOUT[i];
+ }
+ }
+ }else
+ {
+ // Called in non-critical thread-mode, should be 99% of the time.
+ // Should be safe to blocking wait until previous DMA transfer complete
+ while ( _dcd.dma_pending ) { }
+ }
+ }
+ }
+
+ _dcd.dma_pending++;
+
+ (*reg_startep) = 1;
+ __ISB(); __DSB();
+}
+
+// DMA is complete
+static void edpt_dma_end(void)
+{
+ TU_ASSERT(_dcd.dma_pending, );
+ _dcd.dma_pending = 0;
+}
+
+// helper getting td
+static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
+{
+ return &_dcd.xfer[epnum][dir];
+}
+
+// Start DMA to move data from Endpoint -> RAM
+static void xact_out_dma(uint8_t epnum)
+{
+ xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
+ uint32_t xact_len;
+
+ if (epnum == EP_ISO_NUM)
+ {
+ xact_len = NRF_USBD->SIZE.ISOOUT;
+ // If ZERO bit is set, ignore ISOOUT length
+ if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) xact_len = 0;
+ else
+ {
+ // Trigger DMA move data from Endpoint -> SRAM
+ NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->ISOOUT.MAXCNT = xact_len;
+
+ edpt_dma_start(&NRF_USBD->TASKS_STARTISOOUT);
+ }
+ }
+ else
+ {
+ xact_len = (uint8_t)NRF_USBD->SIZE.EPOUT[epnum];
+
+ // Trigger DMA move data from Endpoint -> SRAM
+ NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
+
+ edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
+ }
+
+ xfer->buffer += xact_len;
+ xfer->actual_len += xact_len;
+}
+
+// Prepare for a CBI transaction IN, call at the start
+// it start DMA to transfer data from RAM -> Endpoint
+static void xact_in_dma(uint8_t epnum)
+{
+ xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
+
+ // Each transaction is up to Max Packet Size
+ uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
+
+ NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
+
+ xfer->buffer += xact_len;
+
+ edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+void dcd_init (uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USBD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USBD_IRQn);
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ (void) dev_addr;
+ // Set Address is automatically update by hw controller, nothing to do
+
+ // Enable usbevent for suspend and resume detection
+ // Since the bus signal D+/D- are stable now.
+
+ // Clear current pending first
+ NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE;
+ NRF_USBD->EVENTS_USBEVENT = 0;
+
+ NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+
+ // Bring controller out of low power mode
+ NRF_USBD->LOWPOWER = 0;
+
+ // Initiate RESUME signal
+ NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
+ NRF_USBD->TASKS_DPDMDRIVE = 1;
+
+ // TODO There is no USBEVENT Resume interrupt
+ // We may manually raise DCD_EVENT_RESUME event here
+}
+
+// disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ NRF_USBD->USBPULLUP = 0;
+
+ // Disable Pull-up does not trigger Power USB Removed, in fact it have no
+ // impact on the USB Power status at all -> need to submit unplugged event to the stack.
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
+}
+
+// connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ NRF_USBD->USBPULLUP = 1;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+
+ _dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
+
+ if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS)
+ {
+ if (dir == TUSB_DIR_OUT)
+ {
+ NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
+ NRF_USBD->EPOUTEN |= TU_BIT(epnum);
+
+ // Write any value to SIZE register will allow nRF to ACK/accept data
+ NRF_USBD->SIZE.EPOUT[epnum] = 0;
+ }else
+ {
+ NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
+ NRF_USBD->EPINEN |= TU_BIT(epnum);
+ }
+ }
+ else
+ {
+ TU_ASSERT(epnum == EP_ISO_NUM);
+ if (dir == TUSB_DIR_OUT)
+ {
+ // SPLIT ISO buffer when ISO IN endpoint is already opened.
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+ // Clear old events
+ NRF_USBD->EVENTS_ENDISOOUT = 0;
+ // Clear SOF event in case interrupt was not enabled yet.
+ if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+ // Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
+ NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
+ NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
+ }
+ else
+ {
+ NRF_USBD->EVENTS_ENDISOIN = 0;
+ // SPLIT ISO buffer when ISO OUT endpoint is already opened.
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+ // Clear SOF event in case interrupt was not enabled yet.
+ if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+ // Enable SOF and ISOIN interrupts, and ISOIN endpoint.
+ NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
+ NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
+ }
+ }
+ __ISB(); __DSB();
+
+ return true;
+}
+
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if (epnum != EP_ISO_NUM)
+ {
+ // CBI
+ if (dir == TUSB_DIR_OUT)
+ {
+ NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
+ NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
+ }
+ else
+ {
+ NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
+ NRF_USBD->EPINEN &= ~TU_BIT(epnum);
+ }
+ }
+ else
+ {
+ _dcd.xfer[EP_ISO_NUM][dir].mps = 0;
+ // ISO
+ if (dir == TUSB_DIR_OUT)
+ {
+ NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk;
+ NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk;
+ NRF_USBD->EVENTS_ENDISOOUT = 0;
+ }
+ else
+ {
+ NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk;
+ NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk;
+ }
+ // One of the ISO endpoints closed, no need to split buffers any more.
+ NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
+ // When both ISO endpoint are close there is no need for SOF any more.
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+ }
+ __ISB(); __DSB();
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_td_t* xfer = get_td(epnum, dir);
+
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->actual_len = 0;
+
+ // Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
+ bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
+
+ if ( control_status )
+ {
+ // Status Phase also requires Easy DMA has to be available as well !!!!
+ // However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
+ // Therefore dma_running state will be corrected right away
+ edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
+ if (_dcd.dma_pending) _dcd.dma_pending--; // correct the dma_running++ in dma start
+
+ // The nRF doesn't interrupt on status transmit so we queue up a success response.
+ dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false);
+ }
+ else if ( dir == TUSB_DIR_OUT )
+ {
+ if ( epnum == 0 )
+ {
+ // Accept next Control Out packet
+ NRF_USBD->TASKS_EP0RCVOUT = 1;
+ }else
+ {
+ if ( xfer->data_received )
+ {
+ // Data may already be received previously
+ xfer->data_received = false;
+
+ // start DMA to copy to SRAM
+ xact_out_dma(epnum);
+ }
+ else
+ {
+ // nRF auto accept next Bulk/Interrupt OUT packet
+ // nothing to do
+ }
+ }
+ }
+ else
+ {
+ // Start DMA to copy data from RAM -> Endpoint
+ xact_in_dma(epnum);
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+
+ if ( epnum == 0 )
+ {
+ NRF_USBD->TASKS_EP0STALL = 1;
+ }else if (epnum != EP_ISO_NUM)
+ {
+ NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+ }
+
+ __ISB(); __DSB();
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if ( epnum != 0 && epnum != EP_ISO_NUM )
+ {
+ // clear stall
+ NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+
+ // reset data toggle to DATA0
+ NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
+
+ // Write any value to SIZE register will allow nRF to ACK/accept data
+ // Drop any pending data
+ if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
+
+ __ISB(); __DSB();
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* Interrupt Handler
+ *------------------------------------------------------------------*/
+void bus_reset(void)
+{
+ for(int i=0; i<8; i++)
+ {
+ NRF_USBD->TASKS_STARTEPIN[i] = 0;
+ NRF_USBD->TASKS_STARTEPOUT[i] = 0;
+ }
+
+ NRF_USBD->TASKS_STARTISOIN = 0;
+ NRF_USBD->TASKS_STARTISOOUT = 0;
+
+ tu_varclr(&_dcd);
+ _dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
+ _dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ uint32_t const inten = NRF_USBD->INTEN;
+ uint32_t int_status = 0;
+
+ volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET;
+
+ for(uint8_t i=0; i<USBD_INTEN_EPDATA_Pos+1; i++)
+ {
+ if ( tu_bit_test(inten, i) && regevt[i] )
+ {
+ int_status |= TU_BIT(i);
+
+ // event clear
+ regevt[i] = 0;
+ __ISB(); __DSB();
+ }
+ }
+
+ if ( int_status & USBD_INTEN_USBRESET_Msk )
+ {
+ bus_reset();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ }
+
+ // ISOIN: Data was moved to endpoint buffer, client will be notified in SOF
+ if ( int_status & USBD_INTEN_ENDISOIN_Msk )
+ {
+ xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
+
+ xfer->actual_len = NRF_USBD->ISOIN.AMOUNT;
+ // Data transferred from RAM to endpoint output buffer.
+ // Next transfer can be scheduled after SOF.
+ xfer->iso_in_transfer_ready = true;
+ }
+
+ if ( int_status & USBD_INTEN_SOF_Msk )
+ {
+ // ISOOUT: Transfer data gathered in previous frame from buffer to RAM
+ if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
+ {
+ xact_out_dma(EP_ISO_NUM);
+ }
+ // ISOIN: Notify client that data was transferred
+ xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
+ if ( xfer->iso_in_transfer_ready )
+ {
+ xfer->iso_in_transfer_ready = false;
+ dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+ }
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+
+ if ( int_status & USBD_INTEN_USBEVENT_Msk )
+ {
+ uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & (USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk);
+ NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
+
+ if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+
+ // Put controller into low power mode
+ NRF_USBD->LOWPOWER = 1;
+
+ // Leave HFXO disable to application, since it may be used by other
+ }
+
+ if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME , true);
+ }
+ }
+
+ if ( int_status & EDPT_END_ALL_MASK )
+ {
+ // DMA complete move data from SRAM -> Endpoint
+ edpt_dma_end();
+ }
+
+ // Setup tokens are specific to the Control endpoint.
+ if ( int_status & USBD_INTEN_EP0SETUP_Msk )
+ {
+ uint8_t const setup[8] =
+ {
+ NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH,
+ NRF_USBD->WINDEXL , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
+ };
+
+ // nrf5x hw auto handle set address, there is no need to inform usb stack
+ tusb_control_request_t const * request = (tusb_control_request_t const *) setup;
+
+ if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
+ TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
+ TUSB_REQ_SET_ADDRESS == request->bRequest) )
+ {
+ dcd_event_setup_received(0, setup, true);
+ }
+ }
+
+ //--------------------------------------------------------------------+
+ /* Control/Bulk/Interrupt (CBI) Transfer
+ *
+ * Data flow is:
+ * (bus) (dma)
+ * Host <-------> Endpoint <-------> RAM
+ *
+ * For CBI OUT:
+ * - Host -> Endpoint
+ * EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPOUT[i]
+ * to start DMA. For Bulk/Interrupt, this step can occur automatically (without sw),
+ * which means data may or may not be ready (data_received flag).
+ * - Endpoint -> RAM
+ * ENDEPOUT[i] interrupted, transaction complete, sw prepare next transaction
+ *
+ * For CBI IN:
+ * - RAM -> Endpoint
+ * ENDEPIN[i] interrupted indicate DMA is complete. HW will start
+ * to move data to host
+ * - Endpoint -> Host
+ * EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPIN[i].
+ * Transaction is complete, sw prepare next transaction
+ *
+ * Note: in both Control In and Out of Data stage from Host <-> Endpoint
+ * EP0DATADONE will be set as interrupt source
+ */
+ //--------------------------------------------------------------------+
+
+ /* CBI OUT: Endpoint -> SRAM (aka transaction complete)
+ * Note: Since nRF controller auto ACK next packet without SW awareness
+ * We must handle this stage before Host -> Endpoint just in case 2 event happens at once
+ *
+ * ISO OUT: Transaction must fit in single packet, it can be shorter then total
+ * len if Host decides to sent fewer bytes, it this case transaction is also
+ * complete and next transfer is not initiated here like for CBI.
+ */
+ for(uint8_t epnum=0; epnum<EP_CBI_COUNT+1; epnum++)
+ {
+ if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
+ {
+ xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
+ uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
+
+ // Transfer complete if transaction len < Max Packet Size or total len is transferred
+ if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
+ {
+ if ( epnum == 0 )
+ {
+ // Accept next Control Out packet
+ NRF_USBD->TASKS_EP0RCVOUT = 1;
+ }else
+ {
+ // nRF auto accept next Bulk/Interrupt OUT packet
+ // nothing to do
+ }
+ }else
+ {
+ xfer->total_len = xfer->actual_len;
+
+ // CBI OUT complete
+ dcd_event_xfer_complete(0, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+
+ // Ended event for CBI IN : nothing to do
+ }
+
+ // Endpoint <-> Host ( In & OUT )
+ if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) )
+ {
+ uint32_t data_status = NRF_USBD->EPDATASTATUS;
+ NRF_USBD->EPDATASTATUS = data_status;
+ __ISB(); __DSB();
+
+ // EP0DATADONE is set with either Control Out on IN Data
+ // Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN.
+ // We will use BMREQUESTTYPE in setup packet to determine the direction
+ bool const is_control_in = (int_status & USBD_INTEN_EP0DATADONE_Msk) && (NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
+ bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
+
+ // CBI In: Endpoint -> Host (transaction complete)
+ for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++)
+ {
+ if ( tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in) )
+ {
+ xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
+
+ xfer->actual_len += NRF_USBD->EPIN[epnum].MAXCNT;
+
+ if ( xfer->actual_len < xfer->total_len )
+ {
+ // Start DMA to copy next data packet
+ xact_in_dma(epnum);
+ } else
+ {
+ // CBI IN complete
+ dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+
+ // CBI OUT: Host -> Endpoint
+ for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++)
+ {
+ if ( tu_bit_test(data_status, 16+epnum) || (epnum == 0 && is_control_out) )
+ {
+ xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
+
+ if (xfer->actual_len < xfer->total_len)
+ {
+ xact_out_dma(epnum);
+ }else
+ {
+ // Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet
+ // Mark this endpoint with data received
+ xfer->data_received = true;
+ }
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// HFCLK helper
+//--------------------------------------------------------------------+
+#ifdef SOFTDEVICE_PRESENT
+
+// For enable/disable hfclk with SoftDevice
+#include "nrf_mbr.h"
+#include "nrf_sdm.h"
+#include "nrf_soc.h"
+
+#ifndef SD_MAGIC_NUMBER
+ #define SD_MAGIC_NUMBER 0x51B1E5DB
+#endif
+
+static inline bool is_sd_existed(void)
+{
+ return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER;
+}
+
+// check if SD is existed and enabled
+static inline bool is_sd_enabled(void)
+{
+ if ( !is_sd_existed() ) return false;
+
+ uint8_t sd_en = false;
+ (void) sd_softdevice_is_enabled(&sd_en);
+ return sd_en;
+}
+#endif
+
+static bool hfclk_running(void)
+{
+#ifdef SOFTDEVICE_PRESENT
+ if ( is_sd_enabled() )
+ {
+ uint32_t is_running;
+ (void) sd_clock_hfclk_is_running(&is_running);
+ return (is_running ? true : false);
+ }
+#endif
+
+ return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+}
+
+static void hfclk_enable(void)
+{
+ // already running, nothing to do
+ if ( hfclk_running() ) return;
+
+#ifdef SOFTDEVICE_PRESENT
+ if ( is_sd_enabled() )
+ {
+ (void)sd_clock_hfclk_request();
+ return;
+ }
+#endif
+
+ nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED);
+ nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART);
+}
+
+static void hfclk_disable(void)
+{
+#ifdef SOFTDEVICE_PRESENT
+ if ( is_sd_enabled() )
+ {
+ (void)sd_clock_hfclk_release();
+ return;
+ }
+#endif
+
+ nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
+}
+
+// Power & Clock Peripheral on nRF5x to manage USB
+//
+// USB Bus power is managed by Power module, there are 3 VBUS power events:
+// Detected, Ready, Removed. Upon these power events, This function will
+// enable ( or disable ) usb & hfclk peripheral, set the usb pin pull up
+// accordingly to the controller Startup/Standby Sequence in USBD 51.4 specs.
+//
+// Therefore this function must be called to handle USB power event by
+// - nrfx_power_usbevt_init() : if Softdevice is not used or enabled
+// - SoftDevice SOC event : if SD is used and enabled
+void tusb_hal_nrf_power_event (uint32_t event)
+{
+ // Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h
+ enum {
+ USB_EVT_DETECTED = 0,
+ USB_EVT_REMOVED = 1,
+ USB_EVT_READY = 2
+ };
+
+ switch ( event )
+ {
+ case USB_EVT_DETECTED:
+ TU_LOG2("Power USB Detect\r\n");
+
+ if ( !NRF_USBD->ENABLE )
+ {
+ /* Prepare for READY event receiving */
+ NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
+ __ISB(); __DSB(); // for sync
+
+ /* Enable the peripheral */
+ // ERRATA 171, 187, 166
+
+ if ( nrfx_usbd_errata_187() )
+ {
+ // CRITICAL_REGION_ENTER();
+ if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
+ {
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ }
+ else
+ {
+ *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
+ }
+ // CRITICAL_REGION_EXIT();
+ }
+
+ if ( nrfx_usbd_errata_171() )
+ {
+ // CRITICAL_REGION_ENTER();
+ if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
+ {
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ }
+ else
+ {
+ *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
+ }
+ // CRITICAL_REGION_EXIT();
+ }
+
+ NRF_USBD->ENABLE = 1;
+ __ISB(); __DSB(); // for sync
+
+ // Enable HFCLK
+ hfclk_enable();
+ }
+ break;
+
+ case USB_EVT_READY:
+ TU_LOG2("Power USB Ready\r\n");
+
+ // Skip if pull-up is enabled and HCLK is already running.
+ // Application probably call this more than necessary.
+ if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
+
+ /* Waiting for USBD peripheral enabled */
+ while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
+
+ NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
+ __ISB(); __DSB(); // for sync
+
+ if ( nrfx_usbd_errata_171() )
+ {
+ // CRITICAL_REGION_ENTER();
+ if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
+ {
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ }
+ else
+ {
+ *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
+ }
+
+ // CRITICAL_REGION_EXIT();
+ }
+
+ if ( nrfx_usbd_errata_187() )
+ {
+ // CRITICAL_REGION_ENTER();
+ if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
+ {
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
+ *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
+ }
+ else
+ {
+ *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
+ }
+ // CRITICAL_REGION_EXIT();
+ }
+
+ if ( nrfx_usbd_errata_166() )
+ {
+ *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3;
+ *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40;
+
+ __ISB(); __DSB();
+ }
+
+ // ISO buffer Lower half for IN, upper half for OUT
+ NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+
+ // Enable interrupt
+ NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk |
+ USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
+
+ // Enable interrupt, priorities should be set by application
+ NVIC_ClearPendingIRQ(USBD_IRQn);
+ NVIC_EnableIRQ(USBD_IRQn);
+
+ // Wait for HFCLK
+ while ( !hfclk_running() ) { }
+
+ // Enable pull up
+ NRF_USBD->USBPULLUP = 1;
+ __ISB(); __DSB(); // for sync
+ break;
+
+ case USB_EVT_REMOVED:
+ TU_LOG2("Power USB Removed\r\n");
+ if ( NRF_USBD->ENABLE )
+ {
+ // Abort all transfers
+
+ // Disable pull up
+ NRF_USBD->USBPULLUP = 0;
+ __ISB(); __DSB(); // for sync
+
+ // Disable Interrupt
+ NVIC_DisableIRQ(USBD_IRQn);
+
+ // disable all interrupt
+ NRF_USBD->INTENCLR = NRF_USBD->INTEN;
+
+ NRF_USBD->ENABLE = 0;
+ __ISB(); __DSB(); // for sync
+
+ hfclk_disable();
+
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false);
+ }
+ break;
+
+ default: break;
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc120/dcd_nuc120.c b/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc120/dcd_nuc120.c
new file mode 100755
index 0000000..dc48e54
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc120/dcd_nuc120.c
@@ -0,0 +1,434 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2020 Peter Lawrence
+ *
+ * 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.
+ */
+
+/*
+ Theory of operation:
+
+ The NUC100/NUC120 USBD peripheral has six "EP"s, but each is simplex,
+ so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to
+ implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for
+ EP0_IN and EP0_OUT respectively. This leaves up to four for user usage.
+*/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC120)
+
+#include "device/dcd.h"
+#include "NUC100Series.h"
+
+/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */
+#define PERIPH_SETUP_BUF_BASE 0
+#define PERIPH_SETUP_BUF_LEN 8
+#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN)
+#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE
+#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN)
+#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE
+#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN)
+
+/* rather important info unfortunately not provided by device include files: how much there is */
+#define USBD_BUF_SIZE 512
+
+enum ep_enum
+{
+ PERIPH_EP0 = 0,
+ PERIPH_EP1 = 1,
+ PERIPH_EP2 = 2,
+ PERIPH_EP3 = 3,
+ PERIPH_EP4 = 4,
+ PERIPH_EP5 = 5,
+ PERIPH_MAX_EP,
+};
+
+/* set by dcd_set_address() */
+static volatile uint8_t assigned_address;
+
+/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */
+static uint32_t bufseg_addr;
+
+/* used by dcd_edpt_xfer() and the ISR to reset the data sync (DATA0/DATA1) in an EP0_IN transfer */
+static bool active_ep0_xfer;
+
+/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */
+static struct xfer_ctl_t
+{
+ uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
+ union {
+ uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
+ uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
+ };
+ uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */
+ uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */
+} xfer_table[PERIPH_MAX_EP];
+
+/*
+ local helper functions
+*/
+
+static void usb_attach(void)
+{
+ USBD->DRVSE0 &= ~USBD_DRVSE0_DRVSE0_Msk;
+}
+
+static void usb_detach(void)
+{
+ USBD->DRVSE0 |= USBD_DRVSE0_DRVSE0_Msk;
+}
+
+static void usb_control_send_zlp(void)
+{
+ USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQ_SYNC_Msk;
+ USBD->EP[PERIPH_EP0].MXPLD = 0;
+}
+
+/* reconstruct ep_addr from particular USB Configuration Register */
+static uint8_t decode_ep_addr(USBD_EP_T *ep)
+{
+ uint8_t ep_addr = ep->CFG & USBD_CFG_EP_NUM_Msk;
+ if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) )
+ ep_addr |= TUSB_DIR_IN_MASK;
+ return ep_addr;
+}
+
+/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EP0...) */
+static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add)
+{
+ USBD_EP_T *ep;
+ enum ep_enum ep_index;
+
+ for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++)
+ {
+ if (add)
+ {
+ /* take first peripheral endpoint that is unused */
+ if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep;
+ }
+ else
+ {
+ /* find a peripheral endpoint that matches ep_addr */
+ uint8_t candidate_ep_addr = decode_ep_addr(ep);
+ if (candidate_ep_addr == ep_addr) return ep;
+ }
+ }
+
+ return NULL;
+}
+
+/* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */
+static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
+{
+ uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
+
+ memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
+ ep->MXPLD = bytes_now;
+}
+
+/* called by dcd_init() as well as by the ISR during a USB bus reset */
+static void bus_reset(void)
+{
+ USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE;
+
+ for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++)
+ {
+ USBD->EP[ep_index].CFG = 0;
+ USBD->EP[ep_index].CFGP = 0;
+ }
+
+ /* allocate the default EP0 endpoints */
+
+ USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN;
+ USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE;
+ xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN;
+
+ USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT;
+ USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE;
+ xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN;
+
+ /* USB RAM beyond what we've allocated above is available to the user */
+ bufseg_addr = PERIPH_EP2_BUF_BASE;
+
+ /* Reset USB device address */
+ USBD->FADDR = 0;
+
+ /* reset EP0_IN flag */
+ active_ep0_xfer = false;
+}
+
+/* centralized location for USBD interrupt enable bit mask */
+static const uint32_t enabled_irqs = USBD_INTSTS_FLDET_STS_Msk | USBD_INTSTS_BUS_STS_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USB_STS_Msk;
+
+/*
+ NUC100/NUC120 TinyUSB API driver implementation
+*/
+
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ USBD->ATTR = 0x7D0;
+
+ usb_detach();
+
+ bus_reset();
+
+ usb_attach();
+
+ USBD->INTSTS = enabled_irqs;
+ USBD->INTEN = enabled_irqs;
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USBD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USBD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */
+ assigned_address = dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+ USBD->ATTR = USBD_ATTR_RWAKEUP_Msk;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void) rhport;
+
+ USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true);
+ TU_ASSERT(ep);
+
+ /* mine the data for the information we need */
+ int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ int const size = p_endpoint_desc->wMaxPacketSize.size;
+ tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer;
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* allocate buffer from USB RAM */
+ ep->BUFSEG = bufseg_addr;
+ bufseg_addr += size;
+ TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE);
+
+ /* construct USB Configuration Register value and then write it */
+ uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT;
+ if (TUSB_XFER_ISOCHRONOUS == type)
+ cfg |= USBD_CFG_TYPE_ISO;
+ ep->CFG = cfg;
+
+ /* make a note of the endpoint size */
+ xfer->max_packet_size = size;
+
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ /* mine the data for the information we need */
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* store away the information we'll needing now and later */
+ xfer->data_ptr = buffer;
+ xfer->in_remaining_bytes = total_bytes;
+ xfer->total_bytes = total_bytes;
+
+ /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */
+ if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQ_SYNC_Msk;
+
+ if (TUSB_DIR_IN == dir)
+ {
+ dcd_in_xfer(xfer, ep);
+ }
+ else
+ {
+ xfer->out_bytes_so_far = 0;
+ ep->MXPLD = xfer->max_packet_size;
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ ep->CFGP |= USBD_CFGP_SSTALL_Msk;
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ ep->CFG |= USBD_CFG_CSTALL_Msk;
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ uint32_t status = USBD->INTSTS;
+ uint32_t state = USBD->ATTR & 0xf;
+
+ if(status & USBD_INTSTS_FLDET_STS_Msk)
+ {
+ if(USBD->FLDET & USBD_FLDET_FLDET_Msk)
+ {
+ /* USB connect */
+ USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
+ }
+ else
+ {
+ /* USB disconnect */
+ USBD->ATTR &= ~USBD_ATTR_USB_EN_Msk;
+ }
+ }
+
+ if(status & USBD_INTSTS_BUS_STS_Msk)
+ {
+ if(state & USBD_STATE_USBRST)
+ {
+ /* USB bus reset */
+ USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
+
+ bus_reset();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ }
+
+ if(state & USBD_STATE_SUSPEND)
+ {
+ /* Enable USB but disable PHY */
+ USBD->ATTR &= ~USBD_ATTR_PHY_EN_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+
+ if(state & USBD_STATE_RESUME)
+ {
+ /* Enable USB and enable PHY */
+ USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+ }
+
+ if(status & USBD_INTSTS_SETUP_Msk)
+ {
+ /* clear the data ready flag of control endpoints */
+ USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk;
+ USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk;
+
+ /* get SETUP packet from USB buffer */
+ dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true);
+ }
+
+ if(status & USBD_INTSTS_USB_STS_Msk)
+ {
+ if (status & (1UL << USBD_INTSTS_EPEVT_Pos)) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */
+ {
+ /* given ACK from host has happened, we can now set the address (if not already done) */
+ if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address;
+
+ uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD;
+
+ active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size);
+
+ dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true);
+ }
+
+ /* service PERIPH_EP1 through PERIPH_EP7 */
+ enum ep_enum ep_index;
+ uint32_t mask;
+ struct xfer_ctl_t *xfer;
+ USBD_EP_T *ep;
+ for (ep_index = PERIPH_EP1, mask = (2UL << USBD_INTSTS_EPEVT_Pos), xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index < PERIPH_MAX_EP; ep_index++, mask <<= 1, xfer++, ep++)
+ {
+ if(status & mask)
+ {
+ USBD->INTSTS = mask;
+
+ uint16_t const available_bytes = ep->MXPLD;
+ uint8_t const ep_addr = decode_ep_addr(ep);
+ bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK);
+
+ if (out_ep)
+ {
+ /* copy the data from the PC to the previously provided buffer */
+ memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+ xfer->out_bytes_so_far += available_bytes;
+ xfer->data_ptr += available_bytes;
+
+ /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */
+ if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
+ dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
+ else
+ ep->MXPLD = xfer->max_packet_size;
+ }
+ else
+ {
+ /* update the bookkeeping to reflect the data that has now been sent to the PC */
+ xfer->in_remaining_bytes -= available_bytes;
+ xfer->data_ptr += available_bytes;
+
+ /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */
+ if (xfer->in_remaining_bytes)
+ dcd_in_xfer(xfer, ep);
+ else
+ dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+ }
+
+ /* acknowledge all interrupts */
+ USBD->INTSTS = status & enabled_irqs;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_detach();
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_attach();
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc121/dcd_nuc121.c b/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc121/dcd_nuc121.c
new file mode 100755
index 0000000..c9ead6d
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc121/dcd_nuc121.c
@@ -0,0 +1,451 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Peter Lawrence
+ *
+ * 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.
+ */
+
+/*
+ Theory of operation:
+
+ The NUC121/NUC125/NUC126 USBD peripheral has eight "EP"s, but each is simplex,
+ so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to
+ implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for
+ EP0_IN and EP0_OUT respectively. This leaves up to six for user usage.
+*/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) )
+
+#include "device/dcd.h"
+#include "NuMicro.h"
+
+/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */
+#define PERIPH_SETUP_BUF_BASE 0
+#define PERIPH_SETUP_BUF_LEN 8
+#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN)
+#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE
+#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN)
+#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE
+#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN)
+
+/* rather important info unfortunately not provided by device include files: how much there is */
+#define USBD_BUF_SIZE ((CFG_TUSB_MCU == OPT_MCU_NUC121) ? 768 : 512)
+
+enum ep_enum
+{
+ PERIPH_EP0 = 0,
+ PERIPH_EP1 = 1,
+ PERIPH_EP2 = 2,
+ PERIPH_EP3 = 3,
+ PERIPH_EP4 = 4,
+ PERIPH_EP5 = 5,
+ PERIPH_EP6 = 6,
+ PERIPH_EP7 = 7,
+ PERIPH_MAX_EP,
+};
+
+/* set by dcd_set_address() */
+static volatile uint8_t assigned_address;
+
+/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */
+static uint32_t bufseg_addr;
+
+/* used by dcd_edpt_xfer() and the ISR to reset the data sync (DATA0/DATA1) in an EP0_IN transfer */
+static bool active_ep0_xfer;
+
+/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */
+static struct xfer_ctl_t
+{
+ uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
+ union {
+ uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
+ uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
+ };
+ uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */
+ uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */
+} xfer_table[PERIPH_MAX_EP];
+
+/*
+ local helper functions
+*/
+
+static void usb_attach(void)
+{
+ USBD->SE0 &= ~USBD_SE0_SE0_Msk;
+}
+
+static void usb_detach(void)
+{
+ USBD->SE0 |= USBD_SE0_SE0_Msk;
+}
+
+static void usb_control_send_zlp(void)
+{
+ USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQSYNC_Msk;
+ USBD->EP[PERIPH_EP0].MXPLD = 0;
+}
+
+/* reconstruct ep_addr from particular USB Configuration Register */
+static uint8_t decode_ep_addr(USBD_EP_T *ep)
+{
+ uint8_t ep_addr = ep->CFG & USBD_CFG_EPNUM_Msk;
+ if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) )
+ ep_addr |= TUSB_DIR_IN_MASK;
+ return ep_addr;
+}
+
+/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EP0...) */
+static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add)
+{
+ USBD_EP_T *ep;
+ enum ep_enum ep_index;
+
+ for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++)
+ {
+ if (add)
+ {
+ /* take first peripheral endpoint that is unused */
+ if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep;
+ }
+ else
+ {
+ /* find a peripheral endpoint that matches ep_addr */
+ uint8_t candidate_ep_addr = decode_ep_addr(ep);
+ if (candidate_ep_addr == ep_addr) return ep;
+ }
+ }
+
+ return NULL;
+}
+
+/* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */
+static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
+{
+ uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
+
+ memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
+ ep->MXPLD = bytes_now;
+}
+
+/* called by dcd_init() as well as by the ISR during a USB bus reset */
+static void bus_reset(void)
+{
+ USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE;
+
+ for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++)
+ {
+ USBD->EP[ep_index].CFG = 0;
+ USBD->EP[ep_index].CFGP = 0;
+ }
+
+ /* allocate the default EP0 endpoints */
+
+ USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN;
+ USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE;
+ xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN;
+
+ USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT;
+ USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE;
+ xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN;
+
+ /* USB RAM beyond what we've allocated above is available to the user */
+ bufseg_addr = PERIPH_EP2_BUF_BASE;
+
+ /* Reset USB device address */
+ USBD->FADDR = 0;
+
+ /* reset EP0_IN flag */
+ active_ep0_xfer = false;
+}
+
+/* centralized location for USBD interrupt enable bit mask */
+static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk;
+
+/*
+ NUC121/NUC125/NUC126 TinyUSB API driver implementation
+*/
+
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+#ifdef SUPPORT_LPM
+ USBD->ATTR = 0x7D0 | USBD_LPMACK;
+#else
+ USBD->ATTR = 0x7D0;
+#endif
+
+ usb_detach();
+
+ bus_reset();
+
+ usb_attach();
+
+ USBD->INTSTS = enabled_irqs;
+ USBD->INTEN = enabled_irqs;
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USBD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USBD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */
+ assigned_address = dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+ USBD->ATTR = USBD_ATTR_RWAKEUP_Msk;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void) rhport;
+
+ USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true);
+ TU_ASSERT(ep);
+
+ /* mine the data for the information we need */
+ int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ int const size = p_endpoint_desc->wMaxPacketSize.size;
+ tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer;
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* allocate buffer from USB RAM */
+ ep->BUFSEG = bufseg_addr;
+ bufseg_addr += size;
+ TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE);
+
+ /* construct USB Configuration Register value and then write it */
+ uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT;
+ if (TUSB_XFER_ISOCHRONOUS == type)
+ cfg |= USBD_CFG_TYPE_ISO;
+ ep->CFG = cfg;
+
+ /* make a note of the endpoint size */
+ xfer->max_packet_size = size;
+
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ /* mine the data for the information we need */
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* store away the information we'll needing now and later */
+ xfer->data_ptr = buffer;
+ xfer->in_remaining_bytes = total_bytes;
+ xfer->total_bytes = total_bytes;
+
+ /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */
+ if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQSYNC_Msk;
+
+ if (TUSB_DIR_IN == dir)
+ {
+ dcd_in_xfer(xfer, ep);
+ }
+ else
+ {
+ xfer->out_bytes_so_far = 0;
+ ep->MXPLD = xfer->max_packet_size;
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ ep->CFGP |= USBD_CFGP_SSTALL_Msk;
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ ep->CFG |= USBD_CFG_CSTALL_Msk;
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ uint32_t status = USBD->INTSTS;
+#ifdef SUPPORT_LPM
+ uint32_t state = USBD->ATTR & 0x300f;
+#else
+ uint32_t state = USBD->ATTR & 0xf;
+#endif
+
+ if(status & USBD_INTSTS_VBDETIF_Msk)
+ {
+ if(USBD->VBUSDET & USBD_VBUSDET_VBUSDET_Msk)
+ {
+ /* USB connect */
+ USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
+ }
+ else
+ {
+ /* USB disconnect */
+ USBD->ATTR &= ~USBD_ATTR_USBEN_Msk;
+ }
+ }
+
+ if(status & USBD_INTSTS_BUSIF_Msk)
+ {
+ if(state & USBD_ATTR_USBRST_Msk)
+ {
+ /* USB bus reset */
+ USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
+
+ bus_reset();
+
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ }
+
+ if(state & USBD_ATTR_SUSPEND_Msk)
+ {
+ /* Enable USB but disable PHY */
+ USBD->ATTR &= ~USBD_ATTR_PHYEN_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+
+ if(state & USBD_ATTR_RESUME_Msk)
+ {
+ /* Enable USB and enable PHY */
+ USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+ }
+
+ if(status & USBD_INTSTS_SETUP_Msk)
+ {
+ /* clear the data ready flag of control endpoints */
+ USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk;
+ USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk;
+
+ /* get SETUP packet from USB buffer */
+ dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true);
+ }
+
+ if(status & USBD_INTSTS_USBIF_Msk)
+ {
+ if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */
+ {
+ /* given ACK from host has happened, we can now set the address (if not already done) */
+ if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address;
+
+ uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD;
+
+ active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size);
+
+ dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true);
+ }
+
+ /* service PERIPH_EP1 through PERIPH_EP7 */
+ enum ep_enum ep_index;
+ uint32_t mask;
+ struct xfer_ctl_t *xfer;
+ USBD_EP_T *ep;
+ for (ep_index = PERIPH_EP1, mask = USBD_INTSTS_EPEVT1_Msk, xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index <= PERIPH_EP7; ep_index++, mask <<= 1, xfer++, ep++)
+ {
+ if(status & mask)
+ {
+ USBD->INTSTS = mask;
+
+ uint16_t const available_bytes = ep->MXPLD;
+ uint8_t const ep_addr = decode_ep_addr(ep);
+ bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK);
+
+ if (out_ep)
+ {
+ /* copy the data from the PC to the previously provided buffer */
+ memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+ xfer->out_bytes_so_far += available_bytes;
+ xfer->data_ptr += available_bytes;
+
+ /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */
+ if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
+ dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
+ else
+ ep->MXPLD = xfer->max_packet_size;
+ }
+ else
+ {
+ /* update the bookkeeping to reflect the data that has now been sent to the PC */
+ xfer->in_remaining_bytes -= available_bytes;
+ xfer->data_ptr += available_bytes;
+
+ /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */
+ if (xfer->in_remaining_bytes)
+ dcd_in_xfer(xfer, ep);
+ else
+ dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+ }
+
+ if(status & USBD_INTSTS_SOFIF_Msk)
+ {
+ /* Start-Of-Frame event */
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+
+ /* acknowledge all interrupts */
+ USBD->INTSTS = status & enabled_irqs;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_detach();
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_attach();
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc505/dcd_nuc505.c b/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc505/dcd_nuc505.c
new file mode 100755
index 0000000..a7961a6
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -0,0 +1,657 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ *
+ * 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.
+ */
+
+/*
+ Theory of operation:
+
+ The NUC505 USBD peripheral has twelve "EP"s, where each is simplex, in addition
+ to dedicated support for the control endpoint (EP0). The non-user endpoints
+ are referred to as "user" EPs in this code, and follow the datasheet
+ nomenclature of EPA through EPL.
+*/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC505)
+
+#include "device/dcd.h"
+#include "NUC505Series.h"
+
+/*
+ * The DMA functionality of the USBD peripheral does not appear to succeed with
+ * transfer lengths that are longer (> 64 bytes) and are not a multiple of 4.
+ * Keep disabled for now.
+ */
+#define USE_DMA 0
+
+/* rather important info unfortunately not provided by device include files */
+#define USBD_BUF_SIZE 2048 /* how much USB buffer space there is */
+#define USBD_MAX_DMA_LEN 0x1000 /* max bytes that can be DMAed at one time */
+
+enum ep_enum
+{
+ PERIPH_EPA = 0,
+ PERIPH_EPB = 1,
+ PERIPH_EPC = 2,
+ PERIPH_EPD = 3,
+ PERIPH_EPE = 4,
+ PERIPH_EPF = 5,
+ PERIPH_EPG = 6,
+ PERIPH_EPH = 7,
+ PERIPH_EPI = 8,
+ PERIPH_EPJ = 9,
+ PERIPH_EPK = 10,
+ PERIPH_EPL = 11,
+ PERIPH_MAX_EP,
+};
+
+static const uint8_t epcfg_eptype_table[] =
+{
+ [TUSB_XFER_CONTROL] = 0, /* won't happen, since control EPs have dedicated registers */
+ [TUSB_XFER_ISOCHRONOUS] = 3 << USBD_EPCFG_EPTYPE_Pos,
+ [TUSB_XFER_BULK] = 1 << USBD_EPCFG_EPTYPE_Pos,
+ [TUSB_XFER_INTERRUPT] = 2 << USBD_EPCFG_EPTYPE_Pos,
+};
+
+static const uint8_t eprspctl_eptype_table[] =
+{
+ [TUSB_XFER_CONTROL] = 0, /* won't happen, since control EPs have dedicated registers */
+ [TUSB_XFER_ISOCHRONOUS] = 2 << USBD_EPRSPCTL_MODE_Pos, /* Fly Mode */
+ [TUSB_XFER_BULK] = 0 << USBD_EPRSPCTL_MODE_Pos, /* Auto-Validate Mode */
+ [TUSB_XFER_INTERRUPT] = 1 << USBD_EPRSPCTL_MODE_Pos, /* Manual-Validate Mode */
+};
+
+/* set by dcd_set_address() */
+static volatile uint8_t assigned_address;
+
+/* reset by bus_reset(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */
+static uint32_t bufseg_addr;
+
+/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_userEP_in_xfer(), and the ISR */
+static struct xfer_ctl_t
+{
+ uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
+ union {
+ uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
+ uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
+ };
+ uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */
+ uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */
+ uint8_t ep_addr;
+ bool dma_requested;
+} xfer_table[PERIPH_MAX_EP];
+
+/* in addition to xfer_table, additional bespoke bookkeeping is maintained for control EP0 IN */
+static struct
+{
+ uint8_t *data_ptr;
+ uint16_t in_remaining_bytes;
+ uint16_t total_bytes;
+} ctrl_in_xfer;
+
+static volatile struct xfer_ctl_t *current_dma_xfer;
+
+
+/*
+ local helper functions
+*/
+
+static void usb_attach(void)
+{
+ USBD->PHYCTL |= USBD_PHYCTL_DPPUEN_Msk;
+}
+
+static void usb_detach(void)
+{
+ USBD->PHYCTL &= ~USBD_PHYCTL_DPPUEN_Msk;
+}
+
+static void usb_control_send_zlp(void)
+{
+ USBD->CEPINTSTS = USBD_CEPINTSTS_STSDONEIF_Msk;
+ USBD->CEPCTL = 0; /* clear NAKCLR bit */
+ USBD->CEPINTEN = USBD_CEPINTEN_STSDONEIEN_Msk;
+}
+
+/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EPA...) */
+static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add)
+{
+ USBD_EP_T *ep;
+ enum ep_enum ep_index;
+ struct xfer_ctl_t *xfer;
+
+ for (ep_index = PERIPH_EPA, xfer = &xfer_table[PERIPH_EPA], ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, xfer++, ep++)
+ {
+ if (add)
+ {
+ /* take first peripheral endpoint that is unused */
+ if (0 == (ep->EPCFG & USBD_EPCFG_EPEN_Msk)) return ep;
+ }
+ else
+ {
+ /* find a peripheral endpoint that matches ep_addr */
+ if (xfer->ep_addr == ep_addr) return ep;
+ }
+ }
+
+ return NULL;
+}
+
+/* perform a non-control IN endpoint transfer; this is called by the ISR */
+static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
+{
+ uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
+ uint16_t countdown = bytes_now;
+
+ /* precompute what amount of data will be left */
+ xfer->in_remaining_bytes -= bytes_now;
+
+ /*
+ if there will be no more data to send, we replace the BUFEMPTYIF EP interrupt with TXPKIF;
+ that way, we alert TinyUSB as soon as this last packet has been sent
+ */
+ if (0 == xfer->in_remaining_bytes)
+ {
+ ep->EPINTSTS = USBD_EPINTSTS_TXPKIF_Msk;
+ ep->EPINTEN = USBD_EPINTEN_TXPKIEN_Msk;
+ }
+
+ /* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */
+ while (countdown > 3)
+ {
+ uint32_t u32;
+ memcpy(&u32, xfer->data_ptr, 4);
+
+ ep->EPDAT = u32;
+ xfer->data_ptr += 4; countdown -= 4;
+ }
+ while (countdown--)
+ ep->EPDAT_BYTE = *xfer->data_ptr++;
+
+ /* for short packets, we must nudge the peripheral to say 'that's all folks' */
+ if (bytes_now != xfer->max_packet_size)
+ ep->EPRSPCTL = USBD_EPRSPCTL_SHORTTXEN_Msk;
+}
+
+/* called by dcd_init() as well as by the ISR during a USB bus reset */
+static void bus_reset(void)
+{
+ for (enum ep_enum ep_index = PERIPH_EPA; ep_index < PERIPH_MAX_EP; ep_index++)
+ {
+ USBD->EP[ep_index].EPCFG = 0;
+ xfer_table[ep_index].dma_requested = false;
+ }
+
+ USBD->DMACNT = 0;
+ USBD->DMACTL = USBD_DMACTL_DMARST_Msk;
+ USBD->DMACTL = 0;
+
+ /* allocate the default EP0 endpoints */
+
+ USBD->CEPBUFSTART = 0;
+ USBD->CEPBUFEND = 0 + CFG_TUD_ENDPOINT0_SIZE - 1;
+
+ /* USB RAM beyond what we've allocated above is available to the user */
+ bufseg_addr = CFG_TUD_ENDPOINT0_SIZE;
+
+ /* Reset USB device address */
+ USBD->FADDR = 0;
+
+ current_dma_xfer = NULL;
+}
+
+#if USE_DMA
+/* this must only be called by the ISR; it does its best to share the single DMA engine across all user EPs (IN and OUT) */
+static void service_dma(void)
+{
+ if (current_dma_xfer)
+ return;
+
+ enum ep_enum ep_index;
+ struct xfer_ctl_t *xfer;
+ USBD_EP_T *ep;
+
+ for (ep_index = PERIPH_EPA, xfer = &xfer_table[PERIPH_EPA], ep = &USBD->EP[PERIPH_EPA]; ep_index < PERIPH_MAX_EP; ep_index++, xfer++, ep++)
+ {
+ uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk;
+
+ if (!xfer->dma_requested || !available_bytes)
+ continue;
+
+ /*
+ instruct DMA to copy the data from the PC to the previously provided buffer
+ when the bus interrupt DMADONEIEN subsequently fires, the transfer will have finished
+ */
+ USBD->DMACTL = xfer->ep_addr & USBD_DMACTL_EPNUM_Msk;
+ USBD->DMAADDR = (uint32_t)xfer->data_ptr;
+ USBD->DMACNT = available_bytes;
+ USBD->BUSINTSTS = USBD_BUSINTSTS_DMADONEIF_Msk;
+ xfer->out_bytes_so_far += available_bytes;
+ current_dma_xfer = xfer;
+ USBD->DMACTL |= USBD_DMACTL_DMAEN_Msk;
+
+ return;
+ }
+}
+#endif
+
+/* centralized location for USBD interrupt enable bit masks */
+static const uint32_t enabled_irqs = USBD_GINTEN_USBIEN_Msk | \
+ USBD_GINTEN_EPAIEN_Msk | USBD_GINTEN_EPBIEN_Msk | USBD_GINTEN_EPCIEN_Msk | USBD_GINTEN_EPDIEN_Msk | USBD_GINTEN_EPEIEN_Msk | USBD_GINTEN_EPFIEN_Msk | \
+ USBD_GINTEN_EPGIEN_Msk | USBD_GINTEN_EPHIEN_Msk | USBD_GINTEN_EPIIEN_Msk | USBD_GINTEN_EPJIEN_Msk | USBD_GINTEN_EPKIEN_Msk | USBD_GINTEN_EPLIEN_Msk | \
+ USBD_GINTEN_CEPIEN_Msk;
+
+/*
+ NUC505 TinyUSB API driver implementation
+*/
+
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ /* configure interrupts in their initial state; BUSINTEN and CEPINTEN will be subsequently and dynamically re-written as needed */
+ USBD->GINTEN = enabled_irqs;
+ USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_VBUSDETIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk;
+ USBD->CEPINTEN = 0;
+
+ bus_reset();
+
+ usb_attach();
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USBD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USBD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */
+ assigned_address = dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+ USBD->OPER |= USBD_OPER_RESUMEEN_Msk;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void) rhport;
+
+ USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true);
+ TU_ASSERT(ep);
+
+ /* mine the data for the information we need */
+ int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ int const size = p_endpoint_desc->wMaxPacketSize.size;
+ tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer;
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* allocate buffer from USB RAM */
+ ep->EPBUFSTART = bufseg_addr;
+ bufseg_addr += size;
+ ep->EPBUFEND = bufseg_addr - 1;
+ TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE);
+
+ ep->EPMPS = size;
+
+ ep->EPRSPCTL = USB_EP_RSPCTL_FLUSH | eprspctl_eptype_table[type];
+
+ /* construct USB Configuration Register value and then write it */
+ uint32_t cfg = (uint32_t)tu_edpt_number(p_endpoint_desc->bEndpointAddress) << USBD_EPCFG_EPNUM_Pos;
+ if (TUSB_DIR_IN == dir)
+ cfg |= USBD_EPCFG_EPDIR_Msk;
+ cfg |= epcfg_eptype_table[type] | USBD_EPCFG_EPEN_Msk;
+ ep->EPCFG = cfg;
+
+ /* make a note of the endpoint particulars */
+ xfer->max_packet_size = size;
+ xfer->ep_addr = p_endpoint_desc->bEndpointAddress;
+
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ if (0x80 == ep_addr) /* control EP0 IN */
+ {
+ if (total_bytes)
+ {
+ USBD->CEPCTL = USBD_CEPCTL_FLUSH_Msk;
+ ctrl_in_xfer.data_ptr = buffer;
+ ctrl_in_xfer.in_remaining_bytes = total_bytes;
+ ctrl_in_xfer.total_bytes = total_bytes;
+ USBD->CEPINTSTS = USBD_CEPINTSTS_INTKIF_Msk;
+ USBD->CEPINTEN = USBD_CEPINTEN_INTKIEN_Msk;
+ }
+ else
+ {
+ usb_control_send_zlp();
+ }
+ }
+ else if (0x00 == ep_addr) /* control EP0 OUT */
+ {
+ if (total_bytes)
+ {
+ /* if TinyUSB is asking for EP0 OUT data, it is almost certainly already in the buffer */
+ while (total_bytes < USBD->CEPRXCNT);
+ for (int count = 0; count < total_bytes; count++)
+ *buffer++ = USBD->CEPDAT_BYTE;
+
+ dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ else
+ {
+ /* mine the data for the information we need */
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* store away the information we'll needing now and later */
+ xfer->data_ptr = buffer;
+ xfer->in_remaining_bytes = total_bytes;
+ xfer->total_bytes = total_bytes;
+
+ if (TUSB_DIR_IN == dir)
+ {
+ ep->EPINTEN = USBD_EPINTEN_BUFEMPTYIEN_Msk;
+ }
+ else
+ {
+ xfer->out_bytes_so_far = 0;
+ ep->EPINTEN = USBD_EPINTEN_RXPKIEN_Msk;
+ }
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ if (tu_edpt_number(ep_addr))
+ {
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ ep->EPRSPCTL = (ep->EPRSPCTL & 0xf7) | USBD_EPRSPCTL_HALT_Msk;
+ }
+ else
+ {
+ USBD->CEPCTL = USBD_CEPCTL_STALLEN_Msk;
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ if (tu_edpt_number(ep_addr))
+ {
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ ep->EPRSPCTL = USBD_EPRSPCTL_TOGGLE_Msk;
+ }
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ uint32_t status = USBD->GINTSTS;
+
+ /* USB interrupt */
+ if (status & USBD_GINTSTS_USBIF_Msk)
+ {
+ uint32_t bus_state = USBD->BUSINTSTS;
+
+ if (bus_state & USBD_BUSINTSTS_SOFIF_Msk)
+ {
+ /* Start-Of-Frame event */
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+
+ if (bus_state & USBD_BUSINTSTS_RSTIF_Msk)
+ {
+ bus_reset();
+
+ USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk;
+ USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_SUSPENDIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk;
+ USBD->CEPINTSTS = 0x1ffc;
+
+ tusb_speed_t speed = (USBD->OPER & USBD_OPER_CURSPD_Msk) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
+ dcd_event_bus_reset(0, speed, true);
+ }
+
+ if (bus_state & USBD_BUSINTSTS_RESUMEIF_Msk)
+ {
+ USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_SUSPENDIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+
+ if (bus_state & USBD_BUSINTSTS_SUSPENDIF_Msk)
+ {
+ USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk;
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+
+ if (bus_state & USBD_BUSINTSTS_HISPDIF_Msk)
+ {
+ USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk;
+ }
+
+ if (bus_state & USBD_BUSINTSTS_DMADONEIF_Msk)
+ {
+#if USE_DMA
+ if (current_dma_xfer)
+ {
+ current_dma_xfer->dma_requested = false;
+
+ uint16_t available_bytes = USBD->DMACNT & USBD_DMACNT_DMACNT_Msk;
+
+ /* if the most recent DMA finishes the transfer, alert TinyUSB; otherwise, the next RXPKIF/INTKIF endpoint interrupt will prompt the next DMA */
+ if ( (current_dma_xfer->total_bytes == current_dma_xfer->out_bytes_so_far) || (available_bytes < current_dma_xfer->max_packet_size) )
+ {
+ dcd_event_xfer_complete(0, current_dma_xfer->ep_addr, current_dma_xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
+ }
+
+ current_dma_xfer = NULL;
+ service_dma();
+ }
+#endif
+ }
+
+ if (bus_state & USBD_BUSINTSTS_VBUSDETIF_Msk)
+ {
+ if (USBD->PHYCTL & USBD_PHYCTL_VBUSDET_Msk)
+ {
+ /* USB connect */
+ USBD->PHYCTL |= USBD_PHYCTL_PHYEN_Msk | USBD_PHYCTL_DPPUEN_Msk;
+ }
+ else
+ {
+ /* USB disconnect */
+ USBD->PHYCTL &= ~USBD_PHYCTL_DPPUEN_Msk;
+ }
+ }
+
+ USBD->BUSINTSTS = bus_state & (USBD_BUSINTSTS_SOFIF_Msk | USBD_BUSINTSTS_RSTIF_Msk | USBD_BUSINTSTS_RESUMEIF_Msk | USBD_BUSINTSTS_SUSPENDIF_Msk | USBD_BUSINTSTS_HISPDIF_Msk | USBD_BUSINTSTS_DMADONEIF_Msk | USBD_BUSINTSTS_PHYCLKVLDIF_Msk | USBD_BUSINTSTS_VBUSDETIF_Msk);
+ }
+
+ if (status & USBD_GINTSTS_CEPIF_Msk)
+ {
+ uint32_t cep_state = USBD->CEPINTSTS & USBD->CEPINTEN;
+
+ if (cep_state & USBD_CEPINTSTS_SETUPPKIF_Msk)
+ {
+ /* get SETUP packet from USB buffer */
+ uint8_t setup_packet[8];
+ setup_packet[0] = (uint8_t)(USBD->SETUP1_0 >> 0);
+ setup_packet[1] = (uint8_t)(USBD->SETUP1_0 >> 8);
+ setup_packet[2] = (uint8_t)(USBD->SETUP3_2 >> 0);
+ setup_packet[3] = (uint8_t)(USBD->SETUP3_2 >> 8);
+ setup_packet[4] = (uint8_t)(USBD->SETUP5_4 >> 0);
+ setup_packet[5] = (uint8_t)(USBD->SETUP5_4 >> 8);
+ setup_packet[6] = (uint8_t)(USBD->SETUP7_6 >> 0);
+ setup_packet[7] = (uint8_t)(USBD->SETUP7_6 >> 8);
+ dcd_event_setup_received(0, setup_packet, true);
+ }
+ else if (cep_state & USBD_CEPINTSTS_INTKIF_Msk)
+ {
+ USBD->CEPINTSTS = USBD_CEPINTSTS_TXPKIF_Msk;
+
+ if (!(cep_state & USBD_CEPINTSTS_STSDONEIF_Msk))
+ {
+ USBD->CEPINTEN = USBD_CEPINTEN_TXPKIEN_Msk;
+ uint16_t bytes_now = tu_min16(ctrl_in_xfer.in_remaining_bytes, CFG_TUD_ENDPOINT0_SIZE);
+ for (int count = 0; count < bytes_now; count++)
+ USBD->CEPDAT_BYTE = *ctrl_in_xfer.data_ptr++;
+ ctrl_in_xfer.in_remaining_bytes -= bytes_now;
+ USBD_START_CEP_IN(bytes_now);
+ }
+ else
+ {
+ USBD->CEPINTEN = USBD_CEPINTEN_TXPKIEN_Msk | USBD_CEPINTEN_STSDONEIEN_Msk;
+ }
+ }
+ else if (cep_state & USBD_CEPINTSTS_TXPKIF_Msk)
+ {
+ USBD->CEPINTSTS = USBD_CEPINTSTS_STSDONEIF_Msk;
+ USBD_SET_CEP_STATE(USB_CEPCTL_NAKCLR);
+
+ /* alert TinyUSB that the EP0 IN transfer has finished */
+ if ( (0 == ctrl_in_xfer.in_remaining_bytes) || (0 == ctrl_in_xfer.total_bytes) )
+ dcd_event_xfer_complete(0, 0x80, ctrl_in_xfer.total_bytes, XFER_RESULT_SUCCESS, true);
+
+ if (ctrl_in_xfer.in_remaining_bytes)
+ {
+ USBD->CEPINTSTS = USBD_CEPINTSTS_INTKIF_Msk;
+ USBD->CEPINTEN = USBD_CEPINTEN_INTKIEN_Msk;
+ }
+ else
+ {
+ /* TinyUSB does its own fragmentation and ZLP for EP0; a transfer of zero means a ZLP */
+ if (0 == ctrl_in_xfer.total_bytes) USBD->CEPCTL = USBD_CEPCTL_ZEROLEN_Msk;
+
+ USBD->CEPINTSTS = USBD_CEPINTSTS_STSDONEIF_Msk;
+ USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk | USBD_CEPINTEN_STSDONEIEN_Msk;
+ }
+ }
+ else if (cep_state & USBD_CEPINTSTS_STSDONEIF_Msk)
+ {
+ /* given ACK from host has happened, we can now set the address (if not already done) */
+ if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0))
+ {
+ USBD->FADDR = assigned_address;
+
+ for (enum ep_enum ep_index = PERIPH_EPA; ep_index < PERIPH_MAX_EP; ep_index++)
+ {
+ if (USBD->EP[ep_index].EPCFG & USBD_EPCFG_EPEN_Msk) USBD->EP[ep_index].EPRSPCTL = USBD_EPRSPCTL_TOGGLE_Msk;
+ }
+ }
+
+ USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk;
+ }
+
+ USBD->CEPINTSTS = cep_state;
+
+ return;
+ }
+
+ if (status & (USBD_GINTSTS_EPAIF_Msk | USBD_GINTSTS_EPBIF_Msk | USBD_GINTSTS_EPCIF_Msk | USBD_GINTSTS_EPDIF_Msk | USBD_GINTSTS_EPEIF_Msk | USBD_GINTSTS_EPFIF_Msk | USBD_GINTSTS_EPGIF_Msk | USBD_GINTSTS_EPHIF_Msk | USBD_GINTSTS_EPIIF_Msk | USBD_GINTSTS_EPJIF_Msk | USBD_GINTSTS_EPKIF_Msk | USBD_GINTSTS_EPLIF_Msk))
+ {
+ /* service PERIPH_EPA through PERIPH_EPL */
+ enum ep_enum ep_index;
+ uint32_t mask;
+ struct xfer_ctl_t *xfer;
+ USBD_EP_T *ep;
+ for (ep_index = PERIPH_EPA, mask = USBD_GINTSTS_EPAIF_Msk, xfer = &xfer_table[PERIPH_EPA], ep = &USBD->EP[PERIPH_EPA]; ep_index < PERIPH_MAX_EP; ep_index++, mask <<= 1, xfer++, ep++)
+ {
+ if(status & mask)
+ {
+ uint8_t const ep_addr = xfer->ep_addr;
+ bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK);
+ uint32_t ep_state = ep->EPINTSTS & ep->EPINTEN;
+
+ if (out_ep)
+ {
+#if USE_DMA
+ xfer->dma_requested = true;
+ service_dma();
+#else
+ uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk;
+ /* copy the data from the PC to the previously provided buffer */
+ for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++)
+ *xfer->data_ptr++ = ep->EPDAT_BYTE;
+
+ /* when the transfer is finished, alert TinyUSB; otherwise, continue accepting more data */
+ if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
+ dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
+#endif
+
+ }
+ else if (ep_state & USBD_EPINTSTS_BUFEMPTYIF_Msk)
+ {
+ /* send any remaining data */
+ dcd_userEP_in_xfer(xfer, ep);
+ }
+ else if (ep_state & USBD_EPINTSTS_TXPKIF_Msk)
+ {
+ /* alert TinyUSB that we've finished */
+ dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true);
+ ep->EPINTEN = 0;
+ }
+
+ ep->EPINTSTS = ep_state;
+ }
+ }
+ }
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_detach();
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_attach();
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/khci/dcd_khci.c b/sw/Core/Src/tinyusb/src/portable/nxp/khci/dcd_khci.c
new file mode 100755
index 0000000..519b8fb
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/khci/dcd_khci.c
@@ -0,0 +1,477 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Koji Kitayama
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX )
+
+#include "fsl_device_registers.h"
+#define KHCI USB0
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+enum {
+ TOK_PID_OUT = 0x1u,
+ TOK_PID_IN = 0x9u,
+ TOK_PID_SETUP = 0xDu,
+};
+
+typedef struct TU_ATTR_PACKED
+{
+ union {
+ uint32_t head;
+ struct {
+ union {
+ struct {
+ uint16_t : 2;
+ uint16_t tok_pid : 4;
+ uint16_t data : 1;
+ uint16_t own : 1;
+ uint16_t : 8;
+ };
+ struct {
+ uint16_t : 2;
+ uint16_t bdt_stall: 1;
+ uint16_t dts : 1;
+ uint16_t ninc : 1;
+ uint16_t keep : 1;
+ uint16_t : 10;
+ };
+ };
+ uint16_t bc : 10;
+ uint16_t : 6;
+ };
+ };
+ uint8_t *addr;
+}buffer_descriptor_t;
+
+TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
+
+typedef struct TU_ATTR_PACKED
+{
+ union {
+ uint32_t state;
+ struct {
+ uint32_t max_packet_size :11;
+ uint32_t : 5;
+ uint32_t odd : 1;
+ uint32_t :15;
+ };
+ };
+ uint16_t length;
+ uint16_t remaining;
+}endpoint_state_t;
+
+TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
+
+typedef struct
+{
+ union {
+ /* [#EP][OUT,IN][EVEN,ODD] */
+ buffer_descriptor_t bdt[16][2][2];
+ uint16_t bda[512];
+ };
+ TU_ATTR_ALIGNED(4) union {
+ endpoint_state_t endpoint[16][2];
+ endpoint_state_t endpoint_unified[16 * 2];
+ };
+ uint8_t setup_packet[8];
+ uint8_t addr;
+}dcd_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// BDT(Buffer Descriptor Table) must be 256-byte aligned
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
+
+TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
+
+static void prepare_next_setup_packet(uint8_t rhport)
+{
+ const unsigned out_odd = _dcd.endpoint[0][0].odd;
+ const unsigned in_odd = _dcd.endpoint[0][1].odd;
+ if (_dcd.bdt[0][0][out_odd].own) {
+ TU_LOG1("DCD fail to prepare the next SETUP %d %d\r\n", out_odd, in_odd);
+ return;
+ }
+ _dcd.bdt[0][0][out_odd].data = 0;
+ _dcd.bdt[0][0][out_odd ^ 1].data = 1;
+ _dcd.bdt[0][1][in_odd].data = 1;
+ _dcd.bdt[0][1][in_odd ^ 1].data = 0;
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
+ _dcd.setup_packet, sizeof(_dcd.setup_packet));
+}
+
+static void process_stall(uint8_t rhport)
+{
+ if (KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK) {
+ /* clear stall condition of the control pipe */
+ prepare_next_setup_packet(rhport);
+ KHCI->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
+ }
+}
+
+static void process_tokdne(uint8_t rhport)
+{
+ const unsigned s = KHCI->STAT;
+ KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
+ buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
+ endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
+ unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
+
+ /* fetch pid before discarded by the next steps */
+ const unsigned pid = bd->tok_pid;
+ /* reset values for a next transfer */
+ bd->bdt_stall = 0;
+ bd->dts = 1;
+ bd->ninc = 0;
+ bd->keep = 0;
+ /* update the odd variable to prepare for the next transfer */
+ ep->odd = odd ^ 1;
+ if (pid == TOK_PID_SETUP) {
+ dcd_event_setup_received(rhport, bd->addr, true);
+ KHCI->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+ return;
+ }
+ if (s >> 4) {
+ TU_LOG1("TKDNE %x\r\n", s);
+ }
+
+ const unsigned bc = bd->bc;
+ const unsigned remaining = ep->remaining - bc;
+ if (remaining && bc == ep->max_packet_size) {
+ /* continue the transferring consecutive data */
+ ep->remaining = remaining;
+ const int next_remaining = remaining - ep->max_packet_size;
+ if (next_remaining > 0) {
+ /* prepare to the after next transfer */
+ bd->addr += ep->max_packet_size * 2;
+ bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining;
+ __DSB();
+ bd->own = 1; /* the own bit must set after addr */
+ }
+ return;
+ }
+ const unsigned length = ep->length;
+ dcd_event_xfer_complete(rhport,
+ ((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT),
+ length - remaining, XFER_RESULT_SUCCESS, true);
+ if (0 == (s & USB_STAT_ENDP_MASK) && 0 == length) {
+ /* After completion a ZLP of control transfer,
+ * it prepares for the next steup transfer. */
+ if (_dcd.addr) {
+ /* When the transfer was the SetAddress,
+ * the device address should be updated here. */
+ KHCI->ADDR = _dcd.addr;
+ _dcd.addr = 0;
+ }
+ prepare_next_setup_packet(rhport);
+ }
+}
+
+static void process_bus_reset(uint8_t rhport)
+{
+ KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+ KHCI->CTL |= USB_CTL_ODDRST_MASK;
+ KHCI->ADDR = 0;
+ KHCI->INTEN = (KHCI->INTEN & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+
+ KHCI->ENDPOINT[0].ENDPT = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
+ for (unsigned i = 1; i < 16; ++i) {
+ KHCI->ENDPOINT[i].ENDPT = 0;
+ }
+ buffer_descriptor_t *bd = _dcd.bdt[0][0];
+ for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
+ bd->head = 0;
+ }
+ const endpoint_state_t ep0 = {
+ .max_packet_size = CFG_TUD_ENDPOINT0_SIZE,
+ .odd = 0,
+ .length = 0,
+ .remaining = 0,
+ };
+ _dcd.endpoint[0][0] = ep0;
+ _dcd.endpoint[0][1] = ep0;
+ tu_memclr(_dcd.endpoint[1], sizeof(_dcd.endpoint) - sizeof(_dcd.endpoint[0]));
+ _dcd.addr = 0;
+ prepare_next_setup_packet(rhport);
+ KHCI->CTL &= ~USB_CTL_ODDRST_MASK;
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+}
+
+static void process_bus_inactive(uint8_t rhport)
+{
+ (void) rhport;
+ const unsigned inten = KHCI->INTEN;
+ KHCI->INTEN = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
+ KHCI->USBCTRL |= USB_USBCTRL_SUSP_MASK;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+}
+
+static void process_bus_active(uint8_t rhport)
+{
+ (void) rhport;
+ KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+ const unsigned inten = KHCI->INTEN;
+ KHCI->INTEN = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+ while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+ tu_memclr(&_dcd, sizeof(_dcd));
+ KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
+ KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
+ KHCI->BDTPAGE2 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
+ KHCI->BDTPAGE3 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
+
+ dcd_connect(rhport);
+ NVIC_ClearPendingIRQ(USB0_IRQn);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ KHCI->INTEN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK |
+ USB_INTEN_SLEEPEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
+ NVIC_EnableIRQ(USB0_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB0_IRQn);
+ KHCI->INTEN = 0;
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ _dcd.addr = dev_addr & 0x7F;
+ /* Response with status first before changing device address */
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+ unsigned cnt = SystemCoreClock / 100;
+ KHCI->CTL |= USB_CTL_RESUME_MASK;
+ while (cnt--) __NOP();
+ KHCI->CTL &= ~USB_CTL_RESUME_MASK;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ KHCI->USBCTRL = 0;
+ KHCI->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
+ KHCI->CTL |= USB_CTL_USBENSOFEN_MASK;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ KHCI->CTL = 0;
+ KHCI->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void) rhport;
+
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ const unsigned xfer = ep_desc->bmAttributes.xfer;
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ const unsigned odd = ep->odd;
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
+
+ /* No support for control transfer */
+ TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
+
+ ep->max_packet_size = ep_desc->wMaxPacketSize.size;
+ unsigned val = USB_ENDPT_EPCTLDIS_MASK;
+ val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0;
+ val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+ KHCI->ENDPOINT[epn].ENDPT |= val;
+
+ if (xfer != TUSB_XFER_ISOCHRONOUS) {
+ bd[odd].dts = 1;
+ bd[odd].data = 0;
+ bd[odd ^ 1].dts = 1;
+ bd[odd ^ 1].data = 1;
+ }
+
+ return true;
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
+ const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+ KHCI->ENDPOINT[epn].ENDPT &= ~msk;
+ ep->max_packet_size = 0;
+ ep->length = 0;
+ ep->remaining = 0;
+ bd->head = 0;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB0_IRQn);
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
+
+ if (bd->own) {
+ TU_LOG1("DCD XFER fail %x %d %lx %lx\r\n", ep_addr, total_bytes, ep->state, bd->head);
+ return false; /* The last transfer has not completed */
+ }
+ ep->length = total_bytes;
+ ep->remaining = total_bytes;
+
+ const unsigned mps = ep->max_packet_size;
+ if (total_bytes > mps) {
+ buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
+ /* When total_bytes is greater than the max packet size,
+ * it prepares to the next transfer to avoid NAK in advance. */
+ next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps;
+ next->addr = buffer + mps;
+ next->own = 1;
+ }
+ bd->bc = total_bytes >= mps ? mps: total_bytes;
+ bd->addr = buffer;
+ __DSB();
+ bd->own = 1; /* the own bit must set after addr */
+ NVIC_EnableIRQ(USB0_IRQn);
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ const unsigned epn = ep_addr & 0xFu;
+ if (0 == epn) {
+ KHCI->ENDPOINT[epn].ENDPT |= USB_ENDPT_EPSTALL_MASK;
+ } else {
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+ bd[0].bdt_stall = 1;
+ bd[1].bdt_stall = 1;
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ const unsigned odd = _dcd.endpoint[epn][dir].odd;
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+
+ bd[odd ^ 1].own = 0;
+ bd[odd ^ 1].data = 1;
+ bd[odd ^ 1].bdt_stall = 0;
+ bd[odd].own = 0;
+ bd[odd].data = 0;
+ bd[odd].bdt_stall = 0;
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ uint32_t is = KHCI->ISTAT;
+ uint32_t msk = KHCI->INTEN;
+ KHCI->ISTAT = is & ~msk;
+ is &= msk;
+ if (is & USB_ISTAT_ERROR_MASK) {
+ /* TODO: */
+ uint32_t es = KHCI->ERRSTAT;
+ KHCI->ERRSTAT = es;
+ KHCI->ISTAT = is; /* discard any pending events */
+ return;
+ }
+
+ if (is & USB_ISTAT_USBRST_MASK) {
+ KHCI->ISTAT = is; /* discard any pending events */
+ process_bus_reset(rhport);
+ return;
+ }
+ if (is & USB_ISTAT_SLEEP_MASK) {
+ KHCI->ISTAT = USB_ISTAT_SLEEP_MASK;
+ process_bus_inactive(rhport);
+ return;
+ }
+ if (is & USB_ISTAT_RESUME_MASK) {
+ KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
+ process_bus_active(rhport);
+ return;
+ }
+ if (is & USB_ISTAT_SOFTOK_MASK) {
+ KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ return;
+ }
+ if (is & USB_ISTAT_STALL_MASK) {
+ KHCI->ISTAT = USB_ISTAT_STALL_MASK;
+ process_stall(rhport);
+ return;
+ }
+ if (is & USB_ISTAT_TOKDNE_MASK) {
+ process_tokdne(rhport);
+ return;
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
new file mode 100755
index 0000000..519d091
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
@@ -0,0 +1,582 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
+
+#include "device/dcd.h"
+#include "dcd_lpc17_40.h"
+#include "chip.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+#define DCD_ENDPOINT_MAX 32
+
+typedef struct TU_ATTR_ALIGNED(4)
+{
+ //------------- Word 0 -------------//
+ uint32_t next;
+
+ //------------- Word 1 -------------//
+ uint16_t atle_mode : 2; // 00: normal, 01: ATLE (auto length extraction)
+ uint16_t next_valid : 1;
+ uint16_t : 1; ///< reserved
+ uint16_t isochronous : 1; // is an iso endpoint
+ uint16_t max_packet_size : 11;
+
+ volatile uint16_t buflen; // bytes for non-iso, number of packets for iso endpoint
+
+ //------------- Word 2 -------------//
+ volatile uint32_t buffer;
+
+ //------------- Word 3 -------------//
+ volatile uint16_t retired : 1; // initialized to zero
+ volatile uint16_t status : 4;
+ volatile uint16_t iso_last_packet_valid : 1;
+ volatile uint16_t atle_lsb_extracted : 1; // used in ATLE mode
+ volatile uint16_t atle_msb_extracted : 1; // used in ATLE mode
+ volatile uint16_t atle_mess_len_position : 6; // used in ATLE mode
+ uint16_t : 2;
+
+ volatile uint16_t present_count; // For non-iso : The number of bytes transferred by the DMA engine
+ // For iso : number of packets
+
+ //------------- Word 4 -------------//
+ // uint32_t iso_packet_size_addr; // iso only, can be omitted for non-iso
+}dma_desc_t;
+
+TU_VERIFY_STATIC( sizeof(dma_desc_t) == 16, "size is not correct"); // TODO not support ISO for now
+
+typedef struct
+{
+ // must be 128 byte aligned
+ volatile dma_desc_t* udca[DCD_ENDPOINT_MAX];
+
+ // TODO DMA does not support control transfer (0-1 are not used, offset to reduce memory)
+ dma_desc_t dd[DCD_ENDPOINT_MAX];
+
+ struct
+ {
+ uint8_t* out_buffer;
+ uint8_t out_bytes;
+ volatile bool out_received; // indicate if data is already received in endpoint
+
+ uint8_t in_bytes;
+ } control;
+
+} dcd_data_t;
+
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(128) static dcd_data_t _dcd;
+
+
+//--------------------------------------------------------------------+
+// SIE Command
+//--------------------------------------------------------------------+
+static void sie_cmd_code (sie_cmdphase_t phase, uint8_t code_data)
+{
+ LPC_USB->DevIntClr = (DEV_INT_COMMAND_CODE_EMPTY_MASK | DEV_INT_COMMAND_DATA_FULL_MASK);
+ LPC_USB->CmdCode = (phase << 8) | (code_data << 16);
+
+ uint32_t const wait_flag = (phase == SIE_CMDPHASE_READ) ? DEV_INT_COMMAND_DATA_FULL_MASK : DEV_INT_COMMAND_CODE_EMPTY_MASK;
+ while ((LPC_USB->DevIntSt & wait_flag) == 0) {}
+
+ LPC_USB->DevIntClr = wait_flag;
+}
+
+static void sie_write (uint8_t cmd_code, uint8_t data_len, uint8_t data)
+{
+ sie_cmd_code(SIE_CMDPHASE_COMMAND, cmd_code);
+
+ if (data_len)
+ {
+ sie_cmd_code(SIE_CMDPHASE_WRITE, data);
+ }
+}
+
+static uint8_t sie_read (uint8_t cmd_code)
+{
+ sie_cmd_code(SIE_CMDPHASE_COMMAND , cmd_code);
+ sie_cmd_code(SIE_CMDPHASE_READ , cmd_code);
+ return (uint8_t) LPC_USB->CmdData;
+}
+
+//--------------------------------------------------------------------+
+// PIPE HELPER
+//--------------------------------------------------------------------+
+static inline uint8_t ep_addr2idx(uint8_t ep_addr)
+{
+ return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
+}
+
+static void set_ep_size(uint8_t ep_id, uint16_t max_packet_size)
+{
+ // follows example in 11.10.4.2
+ LPC_USB->ReEp |= TU_BIT(ep_id);
+ LPC_USB->EpInd = ep_id; // select index before setting packet size
+ LPC_USB->MaxPSize = max_packet_size;
+
+ while ((LPC_USB->DevIntSt & DEV_INT_ENDPOINT_REALIZED_MASK) == 0) {}
+ LPC_USB->DevIntClr = DEV_INT_ENDPOINT_REALIZED_MASK;
+}
+
+
+//--------------------------------------------------------------------+
+// CONTROLLER API
+//--------------------------------------------------------------------+
+static void bus_reset(void)
+{
+ // step 7 : slave mode set up
+ LPC_USB->EpIntClr = 0xFFFFFFFF; // clear all pending interrupt
+ LPC_USB->DevIntClr = 0xFFFFFFFF; // clear all pending interrupt
+ LPC_USB->EpIntEn = 0x03UL; // control endpoint cannot use DMA, non-control all use DMA
+ LPC_USB->EpIntPri = 0x03UL; // fast for control endpoint
+
+ // step 8 : DMA set up
+ LPC_USB->EpDMADis = 0xFFFFFFFF; // firstly disable all dma
+ LPC_USB->DMARClr = 0xFFFFFFFF; // clear all pending interrupt
+ LPC_USB->EoTIntClr = 0xFFFFFFFF;
+ LPC_USB->NDDRIntClr = 0xFFFFFFFF;
+ LPC_USB->SysErrIntClr = 0xFFFFFFFF;
+
+ tu_memclr(&_dcd, sizeof(dcd_data_t));
+}
+
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ //------------- user manual 11.13 usb device controller initialization -------------//
+ // step 6 : set up control endpoint
+ set_ep_size(0, CFG_TUD_ENDPOINT0_SIZE);
+ set_ep_size(1, CFG_TUD_ENDPOINT0_SIZE);
+
+ bus_reset();
+
+ LPC_USB->DevIntEn = (DEV_INT_DEVICE_STATUS_MASK | DEV_INT_ENDPOINT_FAST_MASK | DEV_INT_ENDPOINT_SLOW_MASK | DEV_INT_ERROR_MASK);
+ LPC_USB->UDCAH = (uint32_t) _dcd.udca;
+ LPC_USB->DMAIntEn = (DMA_INT_END_OF_XFER_MASK /*| DMA_INT_NEW_DD_REQUEST_MASK*/ | DMA_INT_ERROR_MASK);
+
+ dcd_connect(rhport);
+
+ // Clear pending IRQ
+ NVIC_ClearPendingIRQ(USB_IRQn);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ // Response with status first before changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+ sie_write(SIE_CMDCODE_SET_ADDRESS, 1, 0x80 | dev_addr); // 7th bit is : device_enable
+
+ // Also Set Configure Device to enable non-control endpoint response
+ sie_write(SIE_CMDCODE_CONFIGURE_DEVICE, 1, 1);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, SIE_DEV_STATUS_CONNECT_STATUS_MASK);
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ sie_write(SIE_CMDCODE_DEVICE_STATUS, 1, 0);
+}
+
+//--------------------------------------------------------------------+
+// CONTROL HELPER
+//--------------------------------------------------------------------+
+static inline uint8_t byte2dword(uint8_t bytes)
+{
+ return (bytes + 3) / 4; // length in dwords
+}
+
+static void control_ep_write(void const * buffer, uint8_t len)
+{
+ uint32_t const * buf32 = (uint32_t const *) buffer;
+
+ LPC_USB->Ctrl = USBCTRL_WRITE_ENABLE_MASK; // logical endpoint = 0
+ LPC_USB->TxPLen = (uint32_t) len;
+
+ for (uint8_t count = 0; count < byte2dword(len); count++)
+ {
+ LPC_USB->TxData = *buf32; // NOTE: cortex M3 have no problem with alignment
+ buf32++;
+ }
+
+ LPC_USB->Ctrl = 0;
+
+ // select control IN & validate the endpoint
+ sie_write(SIE_CMDCODE_ENDPOINT_SELECT+1, 0, 0);
+ sie_write(SIE_CMDCODE_BUFFER_VALIDATE , 0, 0);
+}
+
+static uint8_t control_ep_read(void * buffer, uint8_t len)
+{
+ LPC_USB->Ctrl = USBCTRL_READ_ENABLE_MASK; // logical endpoint = 0
+ while ((LPC_USB->RxPLen & USBRXPLEN_PACKET_READY_MASK) == 0) {} // TODO blocking, should have timeout
+
+ len = tu_min8(len, (uint8_t) (LPC_USB->RxPLen & USBRXPLEN_PACKET_LENGTH_MASK) );
+ uint32_t *buf32 = (uint32_t*) buffer;
+
+ for (uint8_t count=0; count < byte2dword(len); count++)
+ {
+ *buf32 = LPC_USB->RxData;
+ buf32++;
+ }
+
+ LPC_USB->Ctrl = 0;
+
+ // select control OUT & clear the endpoint
+ sie_write(SIE_CMDCODE_ENDPOINT_SELECT+0, 0, 0);
+ sie_write(SIE_CMDCODE_BUFFER_CLEAR , 0, 0);
+
+ return len;
+}
+
+//--------------------------------------------------------------------+
+// DCD Endpoint Port
+//--------------------------------------------------------------------+
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ uint8_t const ep_id = ep_addr2idx(p_endpoint_desc->bEndpointAddress);
+
+ // Endpoint type is fixed to endpoint number
+ // 1: interrupt, 2: Bulk, 3: Iso and so on
+ switch ( p_endpoint_desc->bmAttributes.xfer )
+ {
+ case TUSB_XFER_INTERRUPT:
+ TU_ASSERT((epnum % 3) == 1);
+ break;
+
+ case TUSB_XFER_BULK:
+ TU_ASSERT((epnum % 3) == 2 || (epnum == 15));
+ break;
+
+ case TUSB_XFER_ISOCHRONOUS:
+ TU_ASSERT((epnum % 3) == 0 && (epnum != 0) && (epnum != 15));
+ break;
+
+ default:
+ break;
+ }
+
+ //------------- Realize Endpoint with Max Packet Size -------------//
+ set_ep_size(ep_id, p_endpoint_desc->wMaxPacketSize.size);
+
+ //------------- first DD prepare -------------//
+ dma_desc_t* const dd = &_dcd.dd[ep_id];
+ tu_memclr(dd, sizeof(dma_desc_t));
+
+ dd->isochronous = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
+ dd->max_packet_size = p_endpoint_desc->wMaxPacketSize.size;
+ dd->retired = 1; // invalid at first
+
+ sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS + ep_id, 1, 0); // clear all endpoint status
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ if ( tu_edpt_number(ep_addr) == 0 )
+ {
+ sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+0, 1, SIE_SET_ENDPOINT_STALLED_MASK | SIE_SET_ENDPOINT_CONDITION_STALLED_MASK);
+ }else
+ {
+ uint8_t ep_id = ep_addr2idx( ep_addr );
+ sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, SIE_SET_ENDPOINT_STALLED_MASK);
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ uint8_t ep_id = ep_addr2idx(ep_addr);
+
+ sie_write(SIE_CMDCODE_ENDPOINT_SET_STATUS+ep_id, 1, 0);
+}
+
+static bool control_xact(uint8_t rhport, uint8_t dir, uint8_t * buffer, uint8_t len)
+{
+ (void) rhport;
+
+ if ( dir )
+ {
+ _dcd.control.in_bytes = len;
+ control_ep_write(buffer, len);
+ }else
+ {
+ if ( _dcd.control.out_received )
+ {
+ // Already received the DATA OUT packet
+ _dcd.control.out_received = false;
+ _dcd.control.out_buffer = NULL;
+ _dcd.control.out_bytes = 0;
+
+ uint8_t received = control_ep_read(buffer, len);
+ dcd_event_xfer_complete(0, 0, received, XFER_RESULT_SUCCESS, true);
+ }else
+ {
+ _dcd.control.out_buffer = buffer;
+ _dcd.control.out_bytes = len;
+ }
+ }
+
+ return true;
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ // Control transfer is not DMA support, and must be done in slave mode
+ if ( tu_edpt_number(ep_addr) == 0 )
+ {
+ return control_xact(rhport, tu_edpt_dir(ep_addr), buffer, (uint8_t) total_bytes);
+ }
+ else
+ {
+ uint8_t ep_id = ep_addr2idx(ep_addr);
+ dma_desc_t* dd = &_dcd.dd[ep_id];
+
+ // Prepare DMA descriptor
+ // Isochronous & max packet size must be preserved, Other fields of dd should be clear
+ uint16_t const ep_size = dd->max_packet_size;
+ uint8_t is_iso = dd->isochronous;
+
+ tu_memclr(dd, sizeof(dma_desc_t));
+ dd->isochronous = is_iso;
+ dd->max_packet_size = ep_size;
+ dd->buffer = (uint32_t) buffer;
+ dd->buflen = total_bytes;
+
+ _dcd.udca[ep_id] = dd;
+
+ if ( ep_id % 2 )
+ {
+ // Clear EP interrupt before Enable DMA
+ LPC_USB->EpIntEn &= ~TU_BIT(ep_id);
+ LPC_USB->EpDMAEn = TU_BIT(ep_id);
+
+ // endpoint IN need to actively raise DMA request
+ LPC_USB->DMARSet = TU_BIT(ep_id);
+ }else
+ {
+ // Enable DMA
+ LPC_USB->EpDMAEn = TU_BIT(ep_id);
+ }
+
+ return true;
+ }
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+
+// handle control xfer (slave mode)
+static void control_xfer_isr(uint8_t rhport, uint32_t ep_int_status)
+{
+ // Control out complete
+ if ( ep_int_status & TU_BIT(0) )
+ {
+ bool is_setup = sie_read(SIE_CMDCODE_ENDPOINT_SELECT+0) & SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK;
+
+ LPC_USB->EpIntClr = TU_BIT(0);
+
+ if (is_setup)
+ {
+ uint8_t setup_packet[8];
+ control_ep_read(setup_packet, 8); // TODO read before clear setup above
+
+ dcd_event_setup_received(rhport, setup_packet, true);
+ }
+ else if ( _dcd.control.out_buffer )
+ {
+ // software queued transfer previously
+ uint8_t received = control_ep_read(_dcd.control.out_buffer, _dcd.control.out_bytes);
+
+ _dcd.control.out_buffer = NULL;
+ _dcd.control.out_bytes = 0;
+
+ dcd_event_xfer_complete(rhport, 0, received, XFER_RESULT_SUCCESS, true);
+ }else
+ {
+ // hardware auto ack packet -> mark as received
+ _dcd.control.out_received = true;
+ }
+ }
+
+ // Control In complete
+ if ( ep_int_status & TU_BIT(1) )
+ {
+ LPC_USB->EpIntClr = TU_BIT(1);
+ dcd_event_xfer_complete(rhport, TUSB_DIR_IN_MASK, _dcd.control.in_bytes, XFER_RESULT_SUCCESS, true);
+ }
+}
+
+// handle bus event signal
+static void bus_event_isr(uint8_t rhport)
+{
+ uint8_t const dev_status = sie_read(SIE_CMDCODE_DEVICE_STATUS);
+ if (dev_status & SIE_DEV_STATUS_RESET_MASK)
+ {
+ bus_reset();
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+ }
+
+ if (dev_status & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)
+ {
+ // device is disconnected, require using VBUS (P1_30)
+ dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
+ }
+
+ if (dev_status & SIE_DEV_STATUS_SUSPEND_CHANGE_MASK)
+ {
+ if (dev_status & SIE_DEV_STATUS_SUSPEND_MASK)
+ {
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+ }
+ else
+ {
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+ }
+ }
+}
+
+// Helper to complete a DMA descriptor for non-control transfer
+static void dd_complete_isr(uint8_t rhport, uint8_t ep_id)
+{
+ dma_desc_t* const dd = &_dcd.dd[ep_id];
+ uint8_t result = (dd->status == DD_STATUS_NORMAL || dd->status == DD_STATUS_DATA_UNDERUN) ? XFER_RESULT_SUCCESS : XFER_RESULT_FAILED;
+ uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0);
+
+ dcd_event_xfer_complete(rhport, ep_addr, dd->present_count, result, true);
+}
+
+// main USB IRQ handler
+void dcd_int_handler(uint8_t rhport)
+{
+ uint32_t const dev_int_status = LPC_USB->DevIntSt & LPC_USB->DevIntEn;
+ LPC_USB->DevIntClr = dev_int_status;// Acknowledge handled interrupt
+
+ // Bus event
+ if (dev_int_status & DEV_INT_DEVICE_STATUS_MASK)
+ {
+ bus_event_isr(rhport);
+ }
+
+ // Endpoint interrupt
+ uint32_t const ep_int_status = LPC_USB->EpIntSt & LPC_USB->EpIntEn;
+
+ // Control Endpoint are fast
+ if (dev_int_status & DEV_INT_ENDPOINT_FAST_MASK)
+ {
+ // Note clear USBEpIntClr will also clear the setup received bit --> clear after handle setup packet
+ // Only clear USBEpIntClr 1 endpoint each, and should wait for CDFULL bit set
+ control_xfer_isr(rhport, ep_int_status);
+ }
+
+ // non-control IN are slow
+ if (dev_int_status & DEV_INT_ENDPOINT_SLOW_MASK)
+ {
+ for ( uint8_t ep_id = 3; ep_id < DCD_ENDPOINT_MAX; ep_id += 2 )
+ {
+ if ( tu_bit_test(ep_int_status, ep_id) )
+ {
+ LPC_USB->EpIntClr = TU_BIT(ep_id);
+
+ // Clear Ep interrupt for next DMA
+ LPC_USB->EpIntEn &= ~TU_BIT(ep_id);
+
+ dd_complete_isr(rhport, ep_id);
+ }
+ }
+ }
+
+ // DMA transfer complete (RAM <-> EP) for Non-Control
+ // OUT: USB transfer is fully complete
+ // IN : UBS transfer is still on-going -> enable EpIntEn to know when it is complete
+ uint32_t const dma_int_status = LPC_USB->DMAIntSt & LPC_USB->DMAIntEn;
+ if (dma_int_status & DMA_INT_END_OF_XFER_MASK)
+ {
+ uint32_t const eot = LPC_USB->EoTIntSt;
+ LPC_USB->EoTIntClr = eot; // acknowledge interrupt source
+
+ for ( uint8_t ep_id = 2; ep_id < DCD_ENDPOINT_MAX; ep_id++ )
+ {
+ if ( tu_bit_test(eot, ep_id) )
+ {
+ if ( ep_id & 0x01 )
+ {
+ // IN enable EpInt for end of usb transfer
+ LPC_USB->EpIntEn |= TU_BIT(ep_id);
+ }else
+ {
+ // OUT
+ dd_complete_isr(rhport, ep_id);
+ }
+ }
+ }
+ }
+
+ // Errors
+ if ( (dev_int_status & DEV_INT_ERROR_MASK) || (dma_int_status & DMA_INT_ERROR_MASK) )
+ {
+ uint32_t error_status = sie_read(SIE_CMDCODE_READ_ERROR_STATUS);
+ (void) error_status;
+ TU_BREAKPOINT();
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.h b/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
new file mode 100755
index 0000000..07daa32
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
@@ -0,0 +1,152 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_DCD_LPC17_40_H_
+#define _TUSB_DCD_LPC17_40_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Register Interface
+//--------------------------------------------------------------------+
+
+//------------- USB Interrupt USBIntSt -------------//
+//enum {
+// DCD_USB_REQ_LOW_PRIO_MASK = TU_BIT(0),
+// DCD_USB_REQ_HIGH_PRIO_MASK = TU_BIT(1),
+// DCD_USB_REQ_DMA_MASK = TU_BIT(2),
+// DCD_USB_REQ_NEED_CLOCK_MASK = TU_BIT(8),
+// DCD_USB_REQ_ENABLE_MASK = TU_BIT(31)
+//};
+
+//------------- Device Interrupt USBDevInt -------------//
+enum {
+ DEV_INT_FRAME_MASK = TU_BIT(0),
+ DEV_INT_ENDPOINT_FAST_MASK = TU_BIT(1),
+ DEV_INT_ENDPOINT_SLOW_MASK = TU_BIT(2),
+ DEV_INT_DEVICE_STATUS_MASK = TU_BIT(3),
+ DEV_INT_COMMAND_CODE_EMPTY_MASK = TU_BIT(4),
+ DEV_INT_COMMAND_DATA_FULL_MASK = TU_BIT(5),
+ DEV_INT_RX_ENDPOINT_PACKET_MASK = TU_BIT(6),
+ DEV_INT_TX_ENDPOINT_PACKET_MASK = TU_BIT(7),
+ DEV_INT_ENDPOINT_REALIZED_MASK = TU_BIT(8),
+ DEV_INT_ERROR_MASK = TU_BIT(9)
+};
+
+//------------- DMA Interrupt USBDMAInt-------------//
+enum {
+ DMA_INT_END_OF_XFER_MASK = TU_BIT(0),
+ DMA_INT_NEW_DD_REQUEST_MASK = TU_BIT(1),
+ DMA_INT_ERROR_MASK = TU_BIT(2)
+};
+
+//------------- USBCtrl -------------//
+enum {
+ USBCTRL_READ_ENABLE_MASK = TU_BIT(0),
+ USBCTRL_WRITE_ENABLE_MASK = TU_BIT(1),
+};
+
+//------------- USBRxPLen -------------//
+enum {
+ USBRXPLEN_PACKET_LENGTH_MASK = (TU_BIT(10)-1),
+ USBRXPLEN_DATA_VALID_MASK = TU_BIT(10),
+ USBRXPLEN_PACKET_READY_MASK = TU_BIT(11),
+};
+
+//------------- SIE Command Code -------------//
+typedef enum
+{
+ SIE_CMDPHASE_WRITE = 1,
+ SIE_CMDPHASE_READ = 2,
+ SIE_CMDPHASE_COMMAND = 5
+} sie_cmdphase_t;
+
+enum {
+ // device commands
+ SIE_CMDCODE_SET_ADDRESS = 0xd0,
+ SIE_CMDCODE_CONFIGURE_DEVICE = 0xd8,
+ SIE_CMDCODE_SET_MODE = 0xf3,
+ SIE_CMDCODE_READ_FRAME_NUMBER = 0xf5,
+ SIE_CMDCODE_READ_TEST_REGISTER = 0xfd,
+ SIE_CMDCODE_DEVICE_STATUS = 0xfe,
+ SIE_CMDCODE_GET_ERROR = 0xff,
+ SIE_CMDCODE_READ_ERROR_STATUS = 0xfb,
+
+ // endpoint commands
+ SIE_CMDCODE_ENDPOINT_SELECT = 0x00, // + endpoint index
+ SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT = 0x40, // + endpoint index, should use USBEpIntClr instead
+ SIE_CMDCODE_ENDPOINT_SET_STATUS = 0x40, // + endpoint index
+ SIE_CMDCODE_BUFFER_CLEAR = 0xf2,
+ SIE_CMDCODE_BUFFER_VALIDATE = 0xfa
+};
+
+//------------- SIE Device Status (get/set from SIE_CMDCODE_DEVICE_STATUS) -------------//
+enum {
+ SIE_DEV_STATUS_CONNECT_STATUS_MASK = TU_BIT(0),
+ SIE_DEV_STATUS_CONNECT_CHANGE_MASK = TU_BIT(1),
+ SIE_DEV_STATUS_SUSPEND_MASK = TU_BIT(2),
+ SIE_DEV_STATUS_SUSPEND_CHANGE_MASK = TU_BIT(3),
+ SIE_DEV_STATUS_RESET_MASK = TU_BIT(4)
+};
+
+//------------- SIE Select Endpoint Command -------------//
+enum {
+ SIE_SELECT_ENDPOINT_FULL_EMPTY_MASK = TU_BIT(0), // 0: empty, 1 full. IN endpoint checks empty, OUT endpoint check full
+ SIE_SELECT_ENDPOINT_STALL_MASK = TU_BIT(1),
+ SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK = TU_BIT(2), // clear by SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT
+ SIE_SELECT_ENDPOINT_PACKET_OVERWRITTEN_MASK = TU_BIT(3), // previous packet is overwritten by a SETUP packet
+ SIE_SELECT_ENDPOINT_NAK_MASK = TU_BIT(4), // last packet response is NAK (auto clear by an ACK)
+ SIE_SELECT_ENDPOINT_BUFFER1_FULL_MASK = TU_BIT(5),
+ SIE_SELECT_ENDPOINT_BUFFER2_FULL_MASK = TU_BIT(6)
+};
+
+typedef enum
+{
+ SIE_SET_ENDPOINT_STALLED_MASK = TU_BIT(0),
+ SIE_SET_ENDPOINT_DISABLED_MASK = TU_BIT(5),
+ SIE_SET_ENDPOINT_RATE_FEEDBACK_MASK = TU_BIT(6),
+ SIE_SET_ENDPOINT_CONDITION_STALLED_MASK = TU_BIT(7),
+}sie_endpoint_set_status_mask_t;
+
+//------------- DMA Descriptor Status -------------//
+enum {
+ DD_STATUS_NOT_SERVICED = 0,
+ DD_STATUS_BEING_SERVICED,
+ DD_STATUS_NORMAL,
+ DD_STATUS_DATA_UNDERUN, // short packet
+ DD_STATUS_DATA_OVERRUN,
+ DD_STATUS_SYSTEM_ERROR
+};
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
new file mode 100755
index 0000000..1c1faed
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
@@ -0,0 +1,47 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_HOST_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
+
+#include "chip.h"
+
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+#endif
+
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/sw/Core/Src/tinyusb/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
new file mode 100755
index 0000000..e4d9245
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -0,0 +1,415 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+/* Since 2012 starting with LPC11uxx, NXP start to use common USB Device Controller with code name LPC IP3511
+ * for almost their new MCUs. Currently supported and tested families are
+ * - LPC11U68, LPC11U37
+ * - LPC1347
+ * - LPC51U68
+ * - LPC54114
+ * - LPC55s69
+ *
+ * For similar controller of other families, this file may require some minimal changes to work with.
+ * Previous MCUs such as LPC17xx, LPC40xx, LPC18xx, LPC43xx have their own driver implementation.
+ */
+
+#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_LPC11UXX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC13XX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC15XX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC51UXX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC55XX)
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX || CFG_TUSB_MCU == OPT_MCU_LPC15XX
+ // LPC 11Uxx, 13xx, 15xx use lpcopen
+ #include "chip.h"
+ #define DCD_REGS LPC_USB
+
+#elif CFG_TUSB_MCU == OPT_MCU_LPC51UXX || CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC55XX // TODO 55xx has dual usb controllers
+ #include "fsl_device_registers.h"
+ #define DCD_REGS USB0
+
+#endif
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// Number of endpoints
+// - 11 13 15 51 54 has 5x2 endpoints
+// - 18/43 usb0 & 55s usb1 (HS) has 6x2 endpoints
+// - 18/43 usb1 & 55s usb0 (FS) has 4x2 endpoints
+#define EP_COUNT 10
+
+// only SRAM1 & USB RAM can be used for transfer.
+// Used to set DATABUFSTART which is 22-bit aligned
+// 2000 0000 to 203F FFFF
+#define SRAM_REGION 0x20000000
+
+/* Although device controller are the same. Somehow only LPC134x can execute
+ * DMA with 1023 bytes for Bulk/Control. Others (11u, 51u, 54xxx) can only work
+ * with max 64 bytes
+ */
+enum {
+ #if CFG_TUSB_MCU == OPT_MCU_LPC13XX
+ DMA_NBYTES_MAX = 1023
+ #else
+ DMA_NBYTES_MAX = 64
+ #endif
+};
+
+enum {
+ INT_SOF_MASK = TU_BIT(30),
+ INT_DEVICE_STATUS_MASK = TU_BIT(31)
+};
+
+enum {
+ CMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1,
+ CMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ),
+ CMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ),
+ CMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), ///< reflect the soft-connect only, does not reflect the actual attached state
+ CMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17),
+ CMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24),
+ CMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25),
+ CMDSTAT_RESET_CHANGE_MASK = TU_BIT(26),
+ CMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
+};
+
+typedef struct TU_ATTR_PACKED
+{
+ // Bits 21:6 (aligned 64) used in conjunction with bit 31:22 of DATABUFSTART
+ volatile uint16_t buffer_offset;
+
+ volatile uint16_t nbytes : 10 ;
+ uint16_t is_iso : 1 ;
+ uint16_t toggle_mode : 1 ;
+ uint16_t toggle_reset : 1 ;
+ uint16_t stall : 1 ;
+ uint16_t disable : 1 ;
+ volatile uint16_t active : 1 ;
+}ep_cmd_sts_t;
+
+TU_VERIFY_STATIC( sizeof(ep_cmd_sts_t) == 4, "size is not correct" );
+
+typedef struct
+{
+ uint16_t total_bytes;
+ uint16_t xferred_bytes;
+
+ uint16_t nbytes;
+}xfer_dma_t;
+
+// NOTE data will be transferred as soon as dcd get request by dcd_pipe(_queue)_xfer using double buffering.
+// current_td is used to keep track of number of remaining & xferred bytes of the current request.
+typedef struct
+{
+ // 256 byte aligned, 2 for double buffer (not used)
+ // Each cmd_sts can only transfer up to DMA_NBYTES_MAX bytes each
+ ep_cmd_sts_t ep[EP_COUNT][2];
+
+ xfer_dma_t dma[EP_COUNT];
+
+ TU_ATTR_ALIGNED(64) uint8_t setup_packet[8];
+}dcd_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+// EP list must be 256-byte aligned
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
+
+static inline uint16_t get_buf_offset(void const * buffer)
+{
+ uint32_t addr = (uint32_t) buffer;
+ TU_ASSERT( (addr & 0x3f) == 0, 0 );
+ return ( (addr >> 6) & 0xFFFFUL ) ;
+}
+
+static inline uint8_t ep_addr2id(uint8_t endpoint_addr)
+{
+ return 2*(endpoint_addr & 0x0F) + ((endpoint_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
+}
+
+//--------------------------------------------------------------------+
+// CONTROLLER API
+//--------------------------------------------------------------------+
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ DCD_REGS->EPLISTSTART = (uint32_t) _dcd.ep;
+ DCD_REGS->DATABUFSTART = SRAM_REGION; // 22-bit alignment
+
+ DCD_REGS->INTSTAT = DCD_REGS->INTSTAT; // clear all pending interrupt
+ DCD_REGS->INTEN = INT_DEVICE_STATUS_MASK;
+ DCD_REGS->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
+ CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
+
+ NVIC_ClearPendingIRQ(USB0_IRQn);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB0_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB0_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ // Response with status first before changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+ DCD_REGS->DEVCMDSTAT &= ~CMDSTAT_DEVICE_ADDR_MASK;
+ DCD_REGS->DEVCMDSTAT |= dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ DCD_REGS->DEVCMDSTAT |= CMDSTAT_DEVICE_CONNECT_MASK;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ DCD_REGS->DEVCMDSTAT &= ~CMDSTAT_DEVICE_CONNECT_MASK;
+}
+
+//--------------------------------------------------------------------+
+// DCD Endpoint Port
+//--------------------------------------------------------------------+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ // TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around
+ uint8_t const ep_id = ep_addr2id(ep_addr);
+ _dcd.ep[ep_id][0].stall = 1;
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const ep_id = ep_addr2id(ep_addr);
+
+ _dcd.ep[ep_id][0].stall = 0;
+ _dcd.ep[ep_id][0].toggle_reset = 1;
+ _dcd.ep[ep_id][0].toggle_mode = 0;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void) rhport;
+
+ // TODO not support ISO yet
+ TU_VERIFY(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
+
+ //------------- Prepare Queue Head -------------//
+ uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
+
+ // Check if endpoint is available
+ TU_ASSERT( _dcd.ep[ep_id][0].disable && _dcd.ep[ep_id][1].disable );
+
+ tu_memclr(_dcd.ep[ep_id], 2*sizeof(ep_cmd_sts_t));
+ _dcd.ep[ep_id][0].is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
+
+ // Enable EP interrupt
+ DCD_REGS->INTEN |= TU_BIT(ep_id);
+
+ return true;
+}
+
+static void prepare_ep_xfer(uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes)
+{
+ uint16_t const nbytes = tu_min16(total_bytes, DMA_NBYTES_MAX);
+
+ _dcd.dma[ep_id].nbytes = nbytes;
+
+ _dcd.ep[ep_id][0].buffer_offset = buf_offset;
+ _dcd.ep[ep_id][0].nbytes = nbytes;
+ _dcd.ep[ep_id][0].active = 1;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const ep_id = ep_addr2id(ep_addr);
+
+ tu_varclr(&_dcd.dma[ep_id]);
+ _dcd.dma[ep_id].total_bytes = total_bytes;
+
+ prepare_ep_xfer(ep_id, get_buf_offset(buffer), total_bytes);
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// IRQ
+//--------------------------------------------------------------------+
+static void bus_reset(void)
+{
+ tu_memclr(&_dcd, sizeof(dcd_data_t));
+
+ // disable all non-control endpoints on bus reset
+ for(uint8_t ep_id = 2; ep_id < EP_COUNT; ep_id++)
+ {
+ _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
+ }
+
+ _dcd.ep[0][1].buffer_offset = get_buf_offset(_dcd.setup_packet);
+
+ DCD_REGS->EPINUSE = 0;
+ DCD_REGS->EPBUFCFG = 0;
+ DCD_REGS->EPSKIP = 0xFFFFFFFF;
+
+ DCD_REGS->INTSTAT = DCD_REGS->INTSTAT; // clear all pending interrupt
+ DCD_REGS->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
+ DCD_REGS->INTEN = INT_DEVICE_STATUS_MASK | TU_BIT(0) | TU_BIT(1); // enable device status & control endpoints
+}
+
+static void process_xfer_isr(uint32_t int_status)
+{
+ for(uint8_t ep_id = 0; ep_id < EP_COUNT; ep_id++ )
+ {
+ if ( tu_bit_test(int_status, ep_id) )
+ {
+ ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0];
+ xfer_dma_t* xfer_dma = &_dcd.dma[ep_id];
+
+ xfer_dma->xferred_bytes += xfer_dma->nbytes - ep_cs->nbytes;
+
+ if ( (ep_cs->nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) )
+ {
+ // There is more data to transfer
+ // buff_offset has been already increased by hw to correct value for next transfer
+ prepare_ep_xfer(ep_id, ep_cs->buffer_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes);
+ }
+ else
+ {
+ xfer_dma->total_bytes = xfer_dma->xferred_bytes;
+
+ uint8_t const ep_addr = (ep_id / 2) | ((ep_id & 0x01) ? TUSB_DIR_IN_MASK : 0);
+
+ // TODO no way determine if the transfer is failed or not
+ dcd_event_xfer_complete(0, ep_addr, xfer_dma->xferred_bytes, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport; // TODO support multiple USB on supported mcu such as LPC55s69
+
+ uint32_t const cmd_stat = DCD_REGS->DEVCMDSTAT;
+
+ uint32_t int_status = DCD_REGS->INTSTAT & DCD_REGS->INTEN;
+ DCD_REGS->INTSTAT = int_status; // Acknowledge handled interrupt
+
+ if (int_status == 0) return;
+
+ //------------- Device Status -------------//
+ if ( int_status & INT_DEVICE_STATUS_MASK )
+ {
+ DCD_REGS->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
+ if ( cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
+ {
+ bus_reset();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ }
+
+ if (cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)
+ {
+ // device disconnect
+ if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
+ {
+ // debouncing as this can be set when device is powering
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
+ }
+ }
+
+ // TODO support suspend & resume
+ if (cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK)
+ {
+ if (cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK)
+ { // suspend signal, bus idle for more than 3ms
+ // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
+ if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+ }
+ }
+// else
+// { // resume signal
+// dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+// }
+// }
+ }
+
+ // Setup Receive
+ if ( tu_bit_test(int_status, 0) && (cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) )
+ {
+ // Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints
+ _dcd.ep[0][0].active = _dcd.ep[1][0].active = 0;
+ _dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 0;
+
+ DCD_REGS->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK;
+
+ dcd_event_setup_received(0, _dcd.setup_packet, true);
+
+ // keep waiting for next setup
+ _dcd.ep[0][1].buffer_offset = get_buf_offset(_dcd.setup_packet);
+
+ // clear bit0
+ int_status = tu_bit_clear(int_status, 0);
+ }
+
+ // Endpoint transfer complete interrupt
+ process_xfer_isr(int_status);
+}
+
+#endif
+
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/common_transdimension.h b/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/common_transdimension.h
new file mode 100755
index 0000000..7b94dac
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/common_transdimension.h
@@ -0,0 +1,136 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, 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 COMMON_TRANSDIMENSION_H_
+#define COMMON_TRANSDIMENSION_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// USBCMD
+enum {
+ USBCMD_RUN_STOP = TU_BIT(0),
+ USBCMD_RESET = TU_BIT(1),
+ USBCMD_SETUP_TRIPWIRE = TU_BIT(13),
+ USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD
+// Interrupt Threshold bit 23:16
+};
+
+// PORTSC1
+#define PORTSC1_PORT_SPEED_POS 26
+
+enum {
+ PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0),
+ PORTSC1_FORCE_PORT_RESUME = TU_BIT(6),
+ PORTSC1_SUSPEND = TU_BIT(7),
+ PORTSC1_FORCE_FULL_SPEED = TU_BIT(24),
+ PORTSC1_PORT_SPEED = TU_BIT(26) | TU_BIT(27)
+};
+
+// OTGSC
+enum {
+ OTGSC_VBUS_DISCHARGE = TU_BIT(0),
+ OTGSC_VBUS_CHARGE = TU_BIT(1),
+// OTGSC_HWASSIST_AUTORESET = TU_BIT(2),
+ OTGSC_OTG_TERMINATION = TU_BIT(3), ///< Must set to 1 when OTG go to device mode
+ OTGSC_DATA_PULSING = TU_BIT(4),
+ OTGSC_ID_PULLUP = TU_BIT(5),
+// OTGSC_HWASSIT_DATA_PULSE = TU_BIT(6),
+// OTGSC_HWASSIT_BDIS_ACONN = TU_BIT(7),
+ OTGSC_ID = TU_BIT(8), ///< 0 = A device, 1 = B Device
+ OTGSC_A_VBUS_VALID = TU_BIT(9),
+ OTGSC_A_SESSION_VALID = TU_BIT(10),
+ OTGSC_B_SESSION_VALID = TU_BIT(11),
+ OTGSC_B_SESSION_END = TU_BIT(12),
+ OTGSC_1MS_TOGGLE = TU_BIT(13),
+ OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14),
+};
+
+// USBMode
+enum {
+ USBMODE_CM_DEVICE = 2,
+ USBMODE_CM_HOST = 3,
+
+ USBMODE_SLOM = TU_BIT(3),
+ USBMODE_SDIS = TU_BIT(4),
+
+ USBMODE_VBUS_POWER_SELECT = TU_BIT(5), // Need to be enabled for LPC18XX/43XX in host mode
+};
+
+// Device Registers
+typedef struct
+{
+ //------------- ID + HW Parameter Registers-------------//
+ __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX
+
+ //------------- Capability Registers-------------//
+ __I uint8_t CAPLENGTH; ///< Capability Registers Length
+ __I uint8_t TU_RESERVED[1];
+ __I uint16_t HCIVERSION; ///< Host Controller Interface Version
+
+ __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters
+ __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters
+ __I uint32_t TU_RESERVED[5];
+
+ __I uint16_t DCIVERSION; ///< Device Controller Interface Version
+ __I uint8_t TU_RESERVED[2];
+
+ __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters
+ __I uint32_t TU_RESERVED[6];
+
+ //------------- Operational Registers -------------//
+ __IO uint32_t USBCMD; ///< USB Command Register
+ __IO uint32_t USBSTS; ///< USB Status Register
+ __IO uint32_t USBINTR; ///< Interrupt Enable Register
+ __IO uint32_t FRINDEX; ///< USB Frame Index
+ __I uint32_t TU_RESERVED;
+ __IO uint32_t DEVICEADDR; ///< Device Address
+ __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address
+ __I uint32_t TU_RESERVED;
+ __IO uint32_t BURSTSIZE; ///< Programmable Burst Size
+ __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning
+ uint32_t TU_RESERVED[4];
+ __IO uint32_t ENDPTNAK; ///< Endpoint NAK
+ __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable
+ __I uint32_t TU_RESERVED;
+ __IO uint32_t PORTSC1; ///< Port Status & Control
+ __I uint32_t TU_RESERVED[7];
+ __IO uint32_t OTGSC; ///< On-The-Go Status & control
+ __IO uint32_t USBMODE; ///< USB Device Mode
+ __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status
+ __IO uint32_t ENDPTPRIME; ///< Endpoint Prime
+ __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush
+ __I uint32_t ENDPTSTAT; ///< Endpoint Status
+ __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete
+ __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7
+} dcd_registers_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* COMMON_TRANSDIMENSION_H_ */
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c b/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c
new file mode 100755
index 0000000..afc7184
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c
@@ -0,0 +1,491 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+ #include "fsl_device_registers.h"
+#else
+ // LPCOpen for 18xx & 43xx
+ #include "chip.h"
+#endif
+
+#include "common/tusb_common.h"
+#include "device/dcd.h"
+#include "common_transdimension.h"
+
+#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1
+ #define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr
+#else
+ #define CleanInvalidateDCache_by_Addr(_addr, _dsize)
+#endif
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// ENDPTCTRL
+enum {
+ ENDPTCTRL_STALL = TU_BIT(0),
+ ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only
+ ENDPTCTRL_TOGGLE_RESET = TU_BIT(6),
+ ENDPTCTRL_ENABLE = TU_BIT(7)
+};
+
+// USBSTS, USBINTR
+enum {
+ INTR_USB = TU_BIT(0),
+ INTR_ERROR = TU_BIT(1),
+ INTR_PORT_CHANGE = TU_BIT(2),
+ INTR_RESET = TU_BIT(6),
+ INTR_SOF = TU_BIT(7),
+ INTR_SUSPEND = TU_BIT(8),
+ INTR_NAK = TU_BIT(16)
+};
+
+// Queue Transfer Descriptor
+typedef struct
+{
+ // Word 0: Next QTD Pointer
+ uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
+
+ // Word 1: qTQ Token
+ uint32_t : 3 ;
+ volatile uint32_t xact_err : 1 ;
+ uint32_t : 1 ;
+ volatile uint32_t buffer_err : 1 ;
+ volatile uint32_t halted : 1 ;
+ volatile uint32_t active : 1 ;
+ uint32_t : 2 ;
+ uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
+ uint32_t : 3 ;
+ uint32_t int_on_complete : 1 ;
+ volatile uint32_t total_bytes : 15 ;
+ uint32_t : 0 ;
+
+ // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
+ uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
+
+ //------------- DCD Area -------------//
+ uint16_t expected_bytes;
+ uint8_t reserved[2];
+} dcd_qtd_t;
+
+TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
+
+// Queue Head
+typedef struct
+{
+ // Word 0: Capabilities and Characteristics
+ uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
+ uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
+ uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize)
+ uint32_t : 2 ;
+ uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
+ uint32_t iso_mult : 2 ; ///<
+ uint32_t : 0 ;
+
+ // Word 1: Current qTD Pointer
+ volatile uint32_t qtd_addr;
+
+ // Word 2-9: Transfer Overlay
+ volatile dcd_qtd_t qtd_overlay;
+
+ // Word 10-11: Setup request (control OUT only)
+ volatile tusb_control_request_t setup_request;
+
+ //--------------------------------------------------------------------+
+ /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes
+ /// thus there are 16 bytes padding free that we can make use of.
+ //--------------------------------------------------------------------+
+ uint8_t reserved[16];
+} dcd_qhd_t;
+
+TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Variables
+//--------------------------------------------------------------------+
+
+typedef struct
+{
+ dcd_registers_t* regs; // registers
+ const IRQn_Type irqnum; // IRQ number
+ const uint8_t ep_count; // Max bi-directional Endpoints
+}dcd_controller_t;
+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+ // Each endpoint with direction (IN/OUT) occupies a queue head
+ // Therefore QHD_MAX is 2 x max endpoint count
+ #define QHD_MAX (8*2)
+
+ static const dcd_controller_t _dcd_controller[] =
+ {
+ // RT1010 and RT1020 only has 1 USB controller
+ #if FSL_FEATURE_SOC_USBHS_COUNT == 1
+ { .regs = (dcd_registers_t*) USB_BASE , .irqnum = USB_OTG1_IRQn, .ep_count = 8 }
+ #else
+ { .regs = (dcd_registers_t*) USB1_BASE, .irqnum = USB_OTG1_IRQn, .ep_count = 8 },
+ { .regs = (dcd_registers_t*) USB2_BASE, .irqnum = USB_OTG2_IRQn, .ep_count = 8 }
+ #endif
+ };
+
+#else
+ #define QHD_MAX (6*2)
+
+ static const dcd_controller_t _dcd_controller[] =
+ {
+ { .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
+ { .regs = (dcd_registers_t*) LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 }
+ };
+#endif
+
+#define QTD_NEXT_INVALID 0x01
+
+typedef struct {
+ // Must be at 2K alignment
+ dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64);
+ dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); // for portability, TinyUSB only queue 1 TD for each Qhd
+}dcd_data_t;
+
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
+static dcd_data_t _dcd_data;
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+/// follows LPC43xx User Manual 23.10.3
+static void bus_reset(uint8_t rhport)
+{
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+ // The reset value for all endpoint types is the control endpoint. If one endpoint
+ // direction is enabled and the paired endpoint of opposite direction is disabled, then the
+ // endpoint type of the unused direction must be changed from the control type to any other
+ // type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
+ // for the data PID tracking on the active endpoint.
+ for( int i=1; i < _dcd_controller[rhport].ep_count; i++)
+ {
+ dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18);
+ }
+
+ //------------- Clear All Registers -------------//
+ dcd_reg->ENDPTNAK = dcd_reg->ENDPTNAK;
+ dcd_reg->ENDPTNAKEN = 0;
+ dcd_reg->USBSTS = dcd_reg->USBSTS;
+ dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
+ dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE;
+
+ while (dcd_reg->ENDPTPRIME) {}
+ dcd_reg->ENDPTFLUSH = 0xFFFFFFFF;
+ while (dcd_reg->ENDPTFLUSH) {}
+
+ // read reset bit in portsc
+
+ //------------- Queue Head & Queue TD -------------//
+ tu_memclr(&_dcd_data, sizeof(dcd_data_t));
+
+ //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
+ _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1;
+ _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE;
+ _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
+
+ _dcd_data.qhd[0].int_on_setup = 1; // OUT only
+}
+
+void dcd_init(uint8_t rhport)
+{
+ tu_memclr(&_dcd_data, sizeof(dcd_data_t));
+
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+
+ // Reset controller
+ dcd_reg->USBCMD |= USBCMD_RESET;
+ while( dcd_reg->USBCMD & USBCMD_RESET ) {}
+
+ // Set mode to device, must be set immediately after reset
+ dcd_reg->USBMODE = USBMODE_CM_DEVICE;
+ dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
+
+ // TODO Force fullspeed on non-highspeed port
+ // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
+
+ CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+ dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
+ dcd_reg->USBSTS = dcd_reg->USBSTS;
+ dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND /*| INTR_SOF*/;
+
+ dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
+ dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ NVIC_EnableIRQ(_dcd_controller[rhport].irqnum);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ NVIC_DisableIRQ(_dcd_controller[rhport].irqnum);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ // Response with status first before changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ dcd_reg->USBCMD |= USBCMD_RUN_STOP;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
+}
+
+//--------------------------------------------------------------------+
+// HELPER
+//--------------------------------------------------------------------+
+// index to bit position in register
+static inline uint8_t ep_idx2bit(uint8_t ep_idx)
+{
+ return ep_idx/2 + ( (ep_idx%2) ? 16 : 0);
+}
+
+static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
+{
+ tu_memclr(p_qtd, sizeof(dcd_qtd_t));
+
+ p_qtd->next = QTD_NEXT_INVALID;
+ p_qtd->active = 1;
+ p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
+
+ if (data_ptr != NULL)
+ {
+ p_qtd->buffer[0] = (uint32_t) data_ptr;
+ for(uint8_t i=1; i<5; i++)
+ {
+ p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// DCD Endpoint Port
+//--------------------------------------------------------------------+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ // data toggle also need to be reset
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 );
+ dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0));
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ // TODO not support ISO yet
+ TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
+
+ uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ uint8_t const ep_idx = 2*epnum + dir;
+
+ // Must not exceed max endpoint number
+ TU_ASSERT( epnum < _dcd_controller[rhport].ep_count );
+
+ //------------- Prepare Queue Head -------------//
+ dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx];
+ tu_memclr(p_qhd, sizeof(dcd_qhd_t));
+
+ p_qhd->zero_length_termination = 1;
+ p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size;
+ p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
+
+ CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+ // Enable EP Control
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ dcd_reg->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0);
+
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const ep_idx = 2*epnum + dir;
+
+ if ( epnum == 0 )
+ {
+ // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
+ // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
+ while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
+ }
+
+ dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx];
+ dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx];
+
+ // Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the
+ // address to 32-byte boundaries.
+ // void* cast to suppress cast-align warning, buffer must be
+ CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) buffer, 4), total_bytes + 31);
+
+ //------------- Prepare qtd -------------//
+ qtd_init(p_qtd, buffer, total_bytes);
+ p_qtd->int_on_complete = true;
+ p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
+
+ CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+ // start transfer
+ dcd_reg->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ;
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+void dcd_int_handler(uint8_t rhport)
+{
+ dcd_registers_t* const dcd_reg = _dcd_controller[rhport].regs;
+
+ uint32_t const int_enable = dcd_reg->USBINTR;
+ uint32_t const int_status = dcd_reg->USBSTS & int_enable;
+ dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt
+
+ // disabled interrupt sources
+ if (int_status == 0) return;
+
+ if (int_status & INTR_RESET)
+ {
+ bus_reset(rhport);
+ uint32_t speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
+ dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
+ }
+
+ if (int_status & INTR_SUSPEND)
+ {
+ if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND)
+ {
+ // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
+ if ((dcd_reg->DEVICEADDR >> 25) & 0x0f)
+ {
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+ }
+ }
+ }
+
+ // Make sure we read the latest version of _dcd_data.
+ CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+
+ // TODO disconnection does not generate interrupt !!!!!!
+// if (int_status & INTR_PORT_CHANGE)
+// {
+// if ( !(dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) )
+// {
+// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
+// dcd_event_handler(&event, true);
+// }
+// }
+
+ if (int_status & INTR_USB)
+ {
+ uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
+ dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
+
+ if (dcd_reg->ENDPTSETUPSTAT)
+ {
+ //------------- Set up Received -------------//
+ // 23.10.10.2 Operational model for setup transfers
+ dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;// acknowledge
+
+ dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true);
+ }
+
+ if ( edpt_complete )
+ {
+ for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++)
+ {
+ if ( tu_bit_test(edpt_complete, ep_idx2bit(ep_idx)) )
+ {
+ // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
+ dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx];
+
+ uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
+ ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
+
+ uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 );
+ dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
+ }
+ }
+ }
+ }
+
+ if (int_status & INTR_SOF)
+ {
+ dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ }
+
+ if (int_status & INTR_NAK) {}
+ if (int_status & INTR_ERROR) TU_ASSERT(false, );
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/hcd_transdimension.c b/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/hcd_transdimension.c
new file mode 100755
index 0000000..e6b7afe
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/nxp/transdimension/hcd_transdimension.c
@@ -0,0 +1,127 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+// NXP Trans-Dimension USB IP implement EHCI for host functionality
+
+#if TUSB_OPT_HOST_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+ #include "fsl_device_registers.h"
+#else
+ // LPCOpen for 18xx & 43xx
+ #include "chip.h"
+#endif
+
+#include "common/tusb_common.h"
+#include "common_transdimension.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// TODO can be merged with dcd_controller_t
+typedef struct
+{
+ uint32_t regs_base; // registers base
+ const IRQn_Type irqnum; // IRQ number
+}hcd_controller_t;
+
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
+ static const hcd_controller_t _hcd_controller[] =
+ {
+ // RT1010 and RT1020 only has 1 USB controller
+ #if FSL_FEATURE_SOC_USBHS_COUNT == 1
+ { .regs_base = USB_BASE , .irqnum = USB_OTG1_IRQn }
+ #else
+ { .regs_base = USB1_BASE, .irqnum = USB_OTG1_IRQn },
+ { .regs_base = USB2_BASE, .irqnum = USB_OTG2_IRQn }
+ #endif
+ };
+
+#else
+ static const hcd_controller_t _hcd_controller[] =
+ {
+ { .regs_base = LPC_USB0_BASE, .irqnum = USB0_IRQn },
+ { .regs_base = LPC_USB1_BASE, .irqnum = USB1_IRQn }
+ };
+#endif
+
+// TODO better prototype later
+extern bool hcd_ehci_init (uint8_t rhport); // from ehci.c
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+bool hcd_init(uint8_t rhport)
+{
+ dcd_registers_t* dcd_reg = (dcd_registers_t*) _hcd_controller[rhport].regs_base;
+
+ // Reset controller
+ dcd_reg->USBCMD |= USBCMD_RESET;
+ while( dcd_reg->USBCMD & USBCMD_RESET ) {}
+
+ // Set mode to device, must be set immediately after reset
+#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
+ // LPC18XX/43XX need to set VBUS Power Select to HIGH
+ // RHPORT1 is fullspeed only (need external PHY for Highspeed)
+ dcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
+ if (rhport == 1) dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+#else
+ dcd_reg->USBMODE = USBMODE_CM_HOST;
+#endif
+
+ // FIXME force full speed, still have issue with Highspeed enumeration
+ dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+
+ return hcd_ehci_init(rhport);
+}
+
+void hcd_int_enable(uint8_t rhport)
+{
+ NVIC_EnableIRQ(_hcd_controller[rhport].irqnum);
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+ NVIC_DisableIRQ(_hcd_controller[rhport].irqnum);
+}
+
+uint32_t hcd_ehci_register_addr(uint8_t rhport)
+{
+ dcd_registers_t* hcd_reg = (dcd_registers_t*) _hcd_controller[rhport].regs_base;
+
+ // EHCI USBCMD has same address within dcd_register_t
+ return (uint32_t) &hcd_reg->USBCMD;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/ohci/ohci.c b/sw/Core/Src/tinyusb/src/portable/ohci/ohci.c
new file mode 100755
index 0000000..ee29485
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/ohci/ohci.c
@@ -0,0 +1,688 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include <common/tusb_common.h>
+
+#if TUSB_OPT_HOST_ENABLED && \
+ (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "osal/osal.h"
+
+#include "host/hcd.h"
+#include "host/usbh_hcd.h"
+#include "ohci.h"
+
+// TODO remove
+#include "chip.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+#define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE)
+
+enum {
+ OHCI_CONTROL_FUNCSTATE_RESET = 0,
+ OHCI_CONTROL_FUNCSTATE_RESUME,
+ OHCI_CONTROL_FUNCSTATE_OPERATIONAL,
+ OHCI_CONTROL_FUNCSTATE_SUSPEND
+};
+
+enum {
+ OHCI_CONTROL_CONTROL_BULK_RATIO = 3, ///< This specifies the service ratio between Control and Bulk EDs. 0 = 1:1, 3 = 4:1
+ OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = TU_BIT(2),
+ OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = TU_BIT(3),
+ OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = TU_BIT(4),
+ OHCI_CONTROL_LIST_BULK_ENABLE_MASK = TU_BIT(5),
+};
+
+enum {
+ OHCI_FMINTERVAL_FI = 0x2EDF, // 7.3.1 nominal (reset) value
+ OHCI_FMINTERVAL_FSMPS = (6*(OHCI_FMINTERVAL_FI-210)) / 7, // 5.4 calculated based on maximum overhead + bit stuffing
+};
+
+enum {
+ OHCI_PERIODIC_START = 0x3E67
+};
+
+enum {
+ OHCI_INT_SCHEDULING_OVERUN_MASK = TU_BIT(0),
+ OHCI_INT_WRITEBACK_DONEHEAD_MASK = TU_BIT(1),
+ OHCI_INT_SOF_MASK = TU_BIT(2),
+ OHCI_INT_RESUME_DETECTED_MASK = TU_BIT(3),
+ OHCI_INT_UNRECOVERABLE_ERROR_MASK = TU_BIT(4),
+ OHCI_INT_FRAME_OVERFLOW_MASK = TU_BIT(5),
+ OHCI_INT_RHPORT_STATUS_CHANGE_MASK = TU_BIT(6),
+
+ OHCI_INT_OWNERSHIP_CHANGE_MASK = TU_BIT(30),
+ OHCI_INT_MASTER_ENABLE_MASK = TU_BIT(31),
+};
+
+enum {
+ OHCI_RHPORT_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0),
+ OHCI_RHPORT_PORT_ENABLE_STATUS_MASK = TU_BIT(1),
+ OHCI_RHPORT_PORT_SUSPEND_STATUS_MASK = TU_BIT(2),
+ OHCI_RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = TU_BIT(3),
+ OHCI_RHPORT_PORT_RESET_STATUS_MASK = TU_BIT(4), ///< write '1' to reset port
+
+ OHCI_RHPORT_PORT_POWER_STATUS_MASK = TU_BIT(8),
+ OHCI_RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = TU_BIT(9),
+
+ OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK = TU_BIT(16),
+ OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK = TU_BIT(17),
+ OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK = TU_BIT(18),
+ OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK = TU_BIT(19),
+ OHCI_RHPORT_PORT_RESET_CHANGE_MASK = TU_BIT(20),
+
+ OHCI_RHPORT_ALL_CHANGE_MASK = OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK | OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK |
+ OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK | OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK | OHCI_RHPORT_PORT_RESET_CHANGE_MASK
+};
+
+enum {
+ OHCI_CCODE_NO_ERROR = 0,
+ OHCI_CCODE_CRC = 1,
+ OHCI_CCODE_BIT_STUFFING = 2,
+ OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3,
+ OHCI_CCODE_STALL = 4,
+ OHCI_CCODE_DEVICE_NOT_RESPONDING = 5,
+ OHCI_CCODE_PID_CHECK_FAILURE = 6,
+ OHCI_CCODE_UNEXPECTED_PID = 7,
+ OHCI_CCODE_DATA_OVERRUN = 8,
+ OHCI_CCODE_DATA_UNDERRUN = 9,
+ OHCI_CCODE_BUFFER_OVERRUN = 12,
+ OHCI_CCODE_BUFFER_UNDERRUN = 13,
+ OHCI_CCODE_NOT_ACCESSED = 14,
+};
+
+enum {
+ OHCI_INT_ON_COMPLETE_YES = 0,
+ OHCI_INT_ON_COMPLETE_NO = TU_BIN8(111)
+};
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data;
+
+static ohci_ed_t * const p_ed_head[] =
+{
+ [TUSB_XFER_CONTROL] = &ohci_data.control[0].ed,
+ [TUSB_XFER_BULK ] = &ohci_data.bulk_head_ed,
+ [TUSB_XFER_INTERRUPT] = &ohci_data.period_head_ed,
+ [TUSB_XFER_ISOCHRONOUS] = NULL // TODO Isochronous
+};
+
+static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
+static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// USBH-HCD API
+//--------------------------------------------------------------------+
+// Initialization according to 5.1.1.4
+bool hcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ //------------- Data Structure init -------------//
+ tu_memclr(&ohci_data, sizeof(ohci_data_t));
+ for(uint8_t i=0; i<32; i++)
+ { // assign all interrupt pointes to period head ed
+ ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed;
+ }
+
+ ohci_data.control[0].ed.skip = 1;
+ ohci_data.bulk_head_ed.skip = 1;
+ ohci_data.period_head_ed.skip = 1;
+
+ // reset controller
+ OHCI_REG->command_status_bit.controller_reset = 1;
+ while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us
+
+ //------------- init ohci registers -------------//
+ OHCI_REG->control_head_ed = (uint32_t) &ohci_data.control[0].ed;
+ OHCI_REG->bulk_head_ed = (uint32_t) &ohci_data.bulk_head_ed;
+ OHCI_REG->hcca = (uint32_t) &ohci_data.hcca;
+
+ OHCI_REG->interrupt_disable = OHCI_REG->interrupt_enable; // disable all interrupts
+ OHCI_REG->interrupt_status = OHCI_REG->interrupt_status; // clear current set bits
+ OHCI_REG->interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK |
+ OHCI_INT_UNRECOVERABLE_ERROR_MASK | OHCI_INT_FRAME_OVERFLOW_MASK | OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
+ OHCI_INT_MASTER_ENABLE_MASK;
+
+ OHCI_REG->control |= OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
+ OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK; // TODO Isochronous
+
+ OHCI_REG->frame_interval = (OHCI_FMINTERVAL_FSMPS << 16) | OHCI_FMINTERVAL_FI;
+ OHCI_REG->periodic_start = (OHCI_FMINTERVAL_FI * 9) / 10; // Periodic start is 90% of frame interval
+
+ OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power)
+ OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports
+
+ return true;
+}
+
+uint32_t hcd_uframe_number(uint8_t rhport)
+{
+ (void) rhport;
+ return (ohci_data.frame_number_hi << 16 | OHCI_REG->frame_number) << 3;
+}
+
+
+//--------------------------------------------------------------------+
+// PORT API
+//--------------------------------------------------------------------+
+void hcd_port_reset(uint8_t hostid)
+{
+ (void) hostid;
+ OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK;
+}
+
+bool hcd_port_connect_status(uint8_t hostid)
+{
+ (void) hostid;
+ return OHCI_REG->rhport_status_bit[0].current_connect_status;
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t hostid)
+{
+ (void) hostid;
+ return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
+}
+
+// endpoints are tied to an address, which only reclaim after a long delay when enumerating
+// thus there is no need to make sure ED is not in HC's cahed as it will not for sure
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+ // TODO OHCI
+ (void) rhport;
+
+ // addr0 serves as static head --> only set skip bit
+ if ( dev_addr == 0 )
+ {
+ ohci_data.control[0].ed.skip = 1;
+ }else
+ {
+ // remove control
+ ed_list_remove_by_addr( p_ed_head[TUSB_XFER_CONTROL], dev_addr);
+
+ // remove bulk
+ ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr);
+
+ // remove interrupt
+ ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr);
+
+ // TODO remove ISO
+ }
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// CONTROL PIPE API
+//--------------------------------------------------------------------+
+static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed)
+{
+ return (p_ed->ep_number == 0 ) ? TUSB_XFER_CONTROL :
+ (p_ed->is_iso ) ? TUSB_XFER_ISOCHRONOUS :
+ (p_ed->is_interrupt_xfer ) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK;
+}
+
+static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type, uint8_t interval)
+{
+ (void) interval;
+
+ // address 0 is used as async head, which always on the list --> cannot be cleared
+ if (dev_addr != 0)
+ {
+ tu_memclr(p_ed, sizeof(ohci_ed_t));
+ }
+
+ p_ed->dev_addr = dev_addr;
+ p_ed->ep_number = endpoint_addr & 0x0F;
+ p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? OHCI_PID_SETUP : ( (endpoint_addr & TUSB_DIR_IN_MASK) ? OHCI_PID_IN : OHCI_PID_OUT );
+ p_ed->speed = _usbh_devices[dev_addr].speed;
+ p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
+ p_ed->max_packet_size = max_packet_size;
+
+ p_ed->used = 1;
+ p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
+}
+
+static void gtd_init(ohci_gtd_t* p_td, void* data_ptr, uint16_t total_bytes)
+{
+ tu_memclr(p_td, sizeof(ohci_gtd_t));
+
+ p_td->used = 1;
+ p_td->expected_bytes = total_bytes;
+
+ p_td->buffer_rounding = 1; // less than queued length is not a error
+ p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
+ p_td->condition_code = OHCI_CCODE_NOT_ACCESSED;
+
+ p_td->current_buffer_pointer = data_ptr;
+ p_td->buffer_end = total_bytes ? (((uint8_t*) data_ptr) + total_bytes-1) : NULL;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+ (void) rhport;
+
+ ohci_ed_t* p_ed = &ohci_data.control[dev_addr].ed;
+ ohci_gtd_t *p_setup = &ohci_data.control[dev_addr].gtd;
+
+ gtd_init(p_setup, (void*) setup_packet, 8);
+ p_setup->index = dev_addr;
+ p_setup->pid = OHCI_PID_SETUP;
+ p_setup->data_toggle = TU_BIN8(10); // DATA0
+ p_setup->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
+
+ //------------- Attach TDs list to Control Endpoint -------------//
+ p_ed->td_head.address = (uint32_t) p_setup;
+
+ OHCI_REG->command_status_bit.control_list_filled = 1;
+
+ return true;
+}
+
+// TODO move around
+static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr);
+static ohci_gtd_t * gtd_find_free(void);
+static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd);
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ // FIXME control only for now
+ if ( epnum == 0 )
+ {
+ ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed;
+ ohci_gtd_t *p_data = &ohci_data.control[dev_addr].gtd;
+
+ gtd_init(p_data, buffer, buflen);
+
+ p_data->index = dev_addr;
+ p_data->pid = dir ? OHCI_PID_IN : OHCI_PID_OUT;
+ p_data->data_toggle = TU_BIN8(11); // DATA1
+ p_data->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
+
+ p_ed->td_head.address = (uint32_t) p_data;
+
+ OHCI_REG->command_status_bit.control_list_filled = 1;
+ }else
+ {
+ ohci_ed_t * p_ed = ed_from_addr(dev_addr, ep_addr);
+ ohci_gtd_t* p_gtd = gtd_find_free();
+
+ TU_ASSERT(p_gtd);
+
+ gtd_init(p_gtd, buffer, buflen);
+ p_gtd->index = p_ed-ohci_data.ed_pool;
+ p_gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
+
+ td_insert_to_ed(p_ed, p_gtd);
+
+ tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) );
+ if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1;
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// BULK/INT/ISO PIPE API
+//--------------------------------------------------------------------+
+static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
+{
+ if ( tu_edpt_number(ep_addr) == 0 ) return &ohci_data.control[dev_addr].ed;
+
+ ohci_ed_t* ed_pool = ohci_data.ed_pool;
+
+ for(uint32_t i=0; i<HCD_MAX_ENDPOINT; i++)
+ {
+ if ( (ed_pool[i].dev_addr == dev_addr) &&
+ ep_addr == tu_edpt_addr(ed_pool[i].ep_number, ed_pool[i].pid == OHCI_PID_IN) )
+ {
+ return &ed_pool[i];
+ }
+ }
+
+ return NULL;
+}
+
+static ohci_ed_t * ed_find_free(void)
+{
+ ohci_ed_t* ed_pool = ohci_data.ed_pool;
+
+ for(uint8_t i = 0; i < HCD_MAX_ENDPOINT; i++)
+ {
+ if ( !ed_pool[i].used ) return &ed_pool[i];
+ }
+
+ return NULL;
+}
+
+static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
+{
+ p_ed->next = p_pre->next;
+ p_pre->next = (uint32_t) p_ed;
+}
+
+static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
+{
+ ohci_ed_t* p_prev = p_head;
+
+ while( p_prev->next )
+ {
+ ohci_ed_t* ed = (ohci_ed_t*) p_prev->next;
+
+ if (ed->dev_addr == dev_addr)
+ {
+ // unlink ed
+ p_prev->next = ed->next;
+
+ // point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
+ ed->next = (uint32_t) p_head;
+ ed->used = 0;
+ }
+
+ // check next valid since we could remove it
+ if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next;
+ }
+}
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void) rhport;
+
+ // TODO iso support
+ TU_ASSERT(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
+
+ //------------- Prepare Queue Head -------------//
+ ohci_ed_t * p_ed;
+
+ if ( ep_desc->bEndpointAddress == 0 )
+ {
+ p_ed = &ohci_data.control[dev_addr].ed;
+ }else
+ {
+ p_ed = ed_find_free();
+ }
+ TU_ASSERT(p_ed);
+
+ ed_init( p_ed, dev_addr, ep_desc->wMaxPacketSize.size, ep_desc->bEndpointAddress,
+ ep_desc->bmAttributes.xfer, ep_desc->bInterval );
+
+ // control of dev0 is used as static async head
+ if ( dev_addr == 0 )
+ {
+ p_ed->skip = 0; // only need to clear skip bit
+ return true;
+ }
+
+ ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed );
+
+ return true;
+}
+
+static ohci_gtd_t * gtd_find_free(void)
+{
+ for(uint8_t i=0; i < HCD_MAX_XFER; i++)
+ {
+ if ( !ohci_data.gtd_pool[i].used ) return &ohci_data.gtd_pool[i];
+ }
+
+ return NULL;
+}
+
+static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
+{
+ // tail is always NULL
+ if ( tu_align16(p_ed->td_head.address) == 0 )
+ { // TD queue is empty --> head = TD
+ p_ed->td_head.address |= (uint32_t) p_gtd;
+ }
+ else
+ { // TODO currently only support queue up to 2 TD each endpoint at a time
+ ((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd;
+ }
+}
+
+static bool pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
+{
+ ohci_ed_t* const p_ed = ed_from_addr(dev_addr, ep_addr);
+
+ // not support ISO yet
+ TU_VERIFY ( !p_ed->is_iso );
+
+ ohci_gtd_t * const p_gtd = gtd_find_free();
+ TU_ASSERT(p_gtd); // not enough gtd
+
+ gtd_init(p_gtd, buffer, total_bytes);
+ p_gtd->index = p_ed-ohci_data.ed_pool;
+
+ if ( int_on_complete ) p_gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
+
+ td_insert_to_ed(p_ed, p_gtd);
+
+ return true;
+}
+
+bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes)
+{
+ return pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, false);
+}
+
+bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
+{
+ (void) int_on_complete;
+ TU_ASSERT( pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, true) );
+
+ tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) );
+
+ if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1;
+
+ return true;
+}
+
+bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr);
+ return tu_align16(p_ed->td_head.address) != tu_align16(p_ed->td_tail);
+}
+
+bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr);
+ return p_ed->td_head.halted && p_ed->is_stalled;
+}
+
+bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
+{
+ ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr);
+
+ p_ed->is_stalled = 0;
+ p_ed->td_tail &= 0x0Ful; // set tail pointer back to NULL
+
+ p_ed->td_head.toggle = 0; // reset data toggle
+ p_ed->td_head.halted = 0;
+
+ if ( TUSB_XFER_BULK == ed_get_xfer_type(p_ed) ) OHCI_REG->command_status_bit.bulk_list_filled = 1;
+
+ return true;
+}
+
+
+//--------------------------------------------------------------------+
+// OHCI Interrupt Handler
+//--------------------------------------------------------------------+
+static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
+{
+ ohci_td_item_t* td_reverse_head = NULL;
+
+ while(td_head != NULL)
+ {
+ uint32_t next = td_head->next;
+
+ // make current's item become reverse's first item
+ td_head->next = (uint32_t) td_reverse_head;
+ td_reverse_head = td_head;
+
+ td_head = (ohci_td_item_t*) next; // advance to next item
+ }
+
+ return td_reverse_head;
+}
+
+static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd)
+{
+ return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout
+}
+
+static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
+{
+ if ( gtd_is_control(p_qtd) )
+ {
+ return &ohci_data.control[p_qtd->index].ed;
+ }else
+ {
+ return &ohci_data.ed_pool[p_qtd->index];
+ }
+}
+
+static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
+{ // 5.2.9 OHCI sample code
+ return (tu_align4k(buffer_end ^ current_buffer) ? 0x1000 : 0) +
+ tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1;
+}
+
+static void done_queue_isr(uint8_t hostid)
+{
+ (void) hostid;
+
+ // done head is written in reversed order of completion --> need to reverse the done queue first
+ ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) );
+
+ while( td_head != NULL )
+ {
+ // TODO check if td_head is iso td
+ //------------- Non ISO transfer -------------//
+ ohci_gtd_t * const p_qtd = (ohci_gtd_t *) td_head;
+ xfer_result_t const event = (p_qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS :
+ (p_qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED;
+
+ p_qtd->used = 0; // free TD
+ if ( (p_qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
+ {
+ ohci_ed_t * const p_ed = gtd_get_ed(p_qtd);
+
+ uint32_t const xferred_bytes = p_qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) p_qtd->buffer_end, (uint32_t) p_qtd->current_buffer_pointer);
+
+ // NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
+ // When there is a error resulting this ED is halted, and this EP still has other queued TD
+ // --> the Bulk list only has this halted EP queueing TDs (remaining)
+ // --> Bulk list will be considered as not empty by HC !!! while there is no attempt transaction on this list
+ // --> HC will not process Control list (due to service ratio when Bulk list not empty)
+ // To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
+ // the TailP must be set back to NULL for processing remaining TDs
+ if ((event != XFER_RESULT_SUCCESS))
+ {
+ p_ed->td_tail &= 0x0Ful;
+ p_ed->td_tail |= tu_align16(p_ed->td_head.address); // mark halted EP as empty queue
+ if ( event == XFER_RESULT_STALLED ) p_ed->is_stalled = 1;
+ }
+
+ hcd_event_xfer_complete(p_ed->dev_addr,
+ tu_edpt_addr(p_ed->ep_number, p_ed->pid == OHCI_PID_IN),
+ xferred_bytes, event, true);
+ }
+
+ td_head = (ohci_td_item_t*) td_head->next;
+ }
+}
+
+void hcd_int_handler(uint8_t hostid)
+{
+ uint32_t const int_en = OHCI_REG->interrupt_enable;
+ uint32_t const int_status = OHCI_REG->interrupt_status & int_en;
+
+ if (int_status == 0) return;
+
+ // Frame number overflow
+ if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
+ {
+ ohci_data.frame_number_hi++;
+ }
+
+ //------------- RootHub status -------------//
+ if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
+ {
+ uint32_t const rhport_status = OHCI_REG->rhport_status[0] & OHCI_RHPORT_ALL_CHANGE_MASK;
+
+ // TODO dual port is not yet supported
+ if ( rhport_status & OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK )
+ {
+ // TODO check if remote wake-up
+ if ( OHCI_REG->rhport_status_bit[0].current_connect_status )
+ {
+ // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
+ OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK;
+ hcd_event_device_attach(hostid, true);
+ }else
+ {
+ hcd_event_device_remove(hostid, true);
+ }
+ }
+
+ if ( rhport_status & OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK)
+ {
+
+ }
+
+ OHCI_REG->rhport_status[0] = rhport_status; // acknowledge all interrupt
+ }
+
+ //------------- Transfer Complete -------------//
+ if ( int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK)
+ {
+ done_queue_isr(hostid);
+ }
+
+ OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt
+}
+//--------------------------------------------------------------------+
+// HELPER
+//--------------------------------------------------------------------+
+
+
+#endif
+
diff --git a/sw/Core/Src/tinyusb/src/portable/ohci/ohci.h b/sw/Core/Src/tinyusb/src/portable/ohci/ohci.h
new file mode 100755
index 0000000..7f9e55b
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/ohci/ohci.h
@@ -0,0 +1,291 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup Group_HCD
+ * @{
+ * \defgroup OHCI
+ * \brief OHCI driver. All documents sources mentioned here (eg section 3.5) is referring to OHCI Specs unless state otherwise
+ * @{ */
+
+#ifndef _TUSB_OHCI_H_
+#define _TUSB_OHCI_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// OHCI CONFIGURATION & CONSTANTS
+//--------------------------------------------------------------------+
+#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed
+#define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS)
+
+// TODO merge OHCI with EHCI
+enum {
+ OHCI_MAX_ITD = 4
+};
+
+enum {
+ OHCI_PID_SETUP = 0,
+ OHCI_PID_OUT,
+ OHCI_PID_IN,
+};
+
+//--------------------------------------------------------------------+
+// OHCI Data Structure
+//--------------------------------------------------------------------+
+typedef struct {
+ uint32_t interrupt_table[32];
+ volatile uint16_t frame_number;
+ volatile uint16_t frame_pad;
+ volatile uint32_t done_head;
+ uint8_t reserved[116+4]; // TODO try to make use of this area if possible, extra 4 byte to make the whole struct size = 256
+}ohci_hcca_t; // TU_ATTR_ALIGNED(256)
+
+TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
+
+typedef struct {
+ uint32_t reserved[2];
+ volatile uint32_t next;
+ uint32_t reserved2;
+}ohci_td_item_t;
+
+
+typedef struct TU_ATTR_ALIGNED(16)
+{
+ // Word 0
+ uint32_t used : 1;
+ uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer
+ uint32_t expected_bytes : 13; // TODO available for hcd
+
+ uint32_t buffer_rounding : 1;
+ uint32_t pid : 2;
+ uint32_t delay_interrupt : 3;
+ volatile uint32_t data_toggle : 2;
+ volatile uint32_t error_count : 2;
+ volatile uint32_t condition_code : 4;
+
+ // Word 1
+ volatile uint8_t* current_buffer_pointer;
+
+ // Word 2 : next TD
+ volatile uint32_t next;
+
+ // Word 3
+ uint8_t* buffer_end;
+} ohci_gtd_t;
+
+TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" );
+
+typedef struct TU_ATTR_ALIGNED(16)
+{
+ // Word 0
+ uint32_t dev_addr : 7;
+ uint32_t ep_number : 4;
+ uint32_t pid : 2; // 00b from TD, 01b Out, 10b In
+ uint32_t speed : 1;
+ uint32_t skip : 1;
+ uint32_t is_iso : 1;
+ uint32_t max_packet_size : 11;
+ // HCD: make use of 5 reserved bits
+ uint32_t used : 1;
+ uint32_t is_interrupt_xfer : 1;
+ uint32_t is_stalled : 1;
+ uint32_t : 2;
+
+ // Word 1
+ uint32_t td_tail;
+
+ // Word 2
+ volatile union {
+ uint32_t address;
+ struct {
+ uint32_t halted : 1;
+ uint32_t toggle : 1;
+ uint32_t : 30;
+ };
+ }td_head;
+
+ // Word 3: next ED
+ uint32_t next;
+} ohci_ed_t;
+
+TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" );
+
+typedef struct TU_ATTR_ALIGNED(32)
+{
+ /*---------- Word 1 ----------*/
+ uint32_t starting_frame : 16;
+ uint32_t : 5; // can be used
+ uint32_t delay_interrupt : 3;
+ uint32_t frame_count : 3;
+ uint32_t : 1; // can be used
+ volatile uint32_t condition_code : 4;
+
+ /*---------- Word 2 ----------*/
+ uint32_t buffer_page0; // 12 lsb bits can be used
+
+ /*---------- Word 3 ----------*/
+ volatile uint32_t next;
+
+ /*---------- Word 4 ----------*/
+ uint32_t buffer_end;
+
+ /*---------- Word 5-8 ----------*/
+ volatile uint16_t offset_packetstatus[8];
+} ochi_itd_t;
+
+TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
+
+// structure with member alignment required from large to small
+typedef struct TU_ATTR_ALIGNED(256)
+{
+ ohci_hcca_t hcca;
+
+ ohci_ed_t bulk_head_ed; // static bulk head (dummy)
+ ohci_ed_t period_head_ed; // static periodic list head (dummy)
+
+ // control endpoints has reserved resources
+ struct {
+ ohci_ed_t ed;
+ ohci_gtd_t gtd;
+ }control[CFG_TUSB_HOST_DEVICE_MAX+1];
+
+ // ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
+ ohci_ed_t ed_pool[HCD_MAX_ENDPOINT];
+ ohci_gtd_t gtd_pool[HCD_MAX_XFER];
+
+ volatile uint16_t frame_number_hi;
+
+} ohci_data_t;
+
+//--------------------------------------------------------------------+
+// OHCI Operational Register
+//--------------------------------------------------------------------+
+
+
+//--------------------------------------------------------------------+
+// OHCI Data Organization
+//--------------------------------------------------------------------+
+typedef volatile struct
+{
+ uint32_t revision;
+
+ union {
+ uint32_t control;
+ struct {
+ uint32_t control_bulk_service_ratio : 2;
+ uint32_t periodic_list_enable : 1;
+ uint32_t isochronous_enable : 1;
+ uint32_t control_list_enable : 1;
+ uint32_t bulk_list_enable : 1;
+ uint32_t hc_functional_state : 2;
+ uint32_t interrupt_routing : 1;
+ uint32_t remote_wakeup_connected : 1;
+ uint32_t remote_wakeup_enale : 1;
+ uint32_t TU_RESERVED : 21;
+ }control_bit;
+ };
+
+ union {
+ uint32_t command_status;
+ struct {
+ uint32_t controller_reset : 1;
+ uint32_t control_list_filled : 1;
+ uint32_t bulk_list_filled : 1;
+ uint32_t ownership_change_request : 1;
+ uint32_t : 12;
+ uint32_t scheduling_overrun_count : 2;
+ }command_status_bit;
+ };
+
+ uint32_t interrupt_status;
+ uint32_t interrupt_enable;
+ uint32_t interrupt_disable;
+
+ uint32_t hcca;
+ uint32_t period_current_ed;
+ uint32_t control_head_ed;
+ uint32_t control_current_ed;
+ uint32_t bulk_head_ed;
+ uint32_t bulk_current_ed;
+ uint32_t done_head;
+
+ uint32_t frame_interval;
+ uint32_t frame_remaining;
+ uint32_t frame_number;
+ uint32_t periodic_start;
+ uint32_t lowspeed_threshold;
+
+ uint32_t rh_descriptorA;
+ uint32_t rh_descriptorB;
+
+ union {
+ uint32_t rh_status;
+ struct {
+ uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power
+ uint32_t over_current_indicator : 1;
+ uint32_t : 13;
+ uint32_t device_remote_wakeup_enable : 1;
+ uint32_t local_power_status_change : 1;
+ uint32_t over_current_indicator_change : 1;
+ uint32_t : 13;
+ uint32_t clear_remote_wakeup_enable : 1;
+ }rh_status_bit;
+ };
+
+ union {
+ uint32_t rhport_status[2]; // TODO NXP OHCI controller only has 2 ports
+ struct {
+ uint32_t current_connect_status : 1;
+ uint32_t port_enable_status : 1;
+ uint32_t port_suspend_status : 1;
+ uint32_t port_over_current_indicator : 1;
+ uint32_t port_reset_status : 1;
+ uint32_t : 3;
+ uint32_t port_power_status : 1;
+ uint32_t low_speed_device_attached : 1;
+ uint32_t : 6;
+ uint32_t connect_status_change : 1;
+ uint32_t port_enable_status_change : 1;
+ uint32_t port_suspend_status_change : 1;
+ uint32_t port_over_current_indicator_change : 1;
+ uint32_t port_reset_status_change : 1;
+ uint32_t TU_RESERVED : 11;
+ }rhport_status_bit[2];
+ };
+}ohci_registers_t;
+
+TU_VERIFY_STATIC( sizeof(ohci_registers_t) == 0x5c, "size is not correct");
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_OHCI_H_ */
+
+/** @} */
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c
new file mode 100755
index 0000000..f844a0c
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -0,0 +1,532 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
+
+#include "pico.h"
+#include "rp2040_usb.h"
+
+#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
+#include "pico/fix/rp2040_usb_device_enumeration.h"
+#endif
+
+
+#include "device/dcd.h"
+
+/*------------------------------------------------------------------*/
+/* Low level controller
+ *------------------------------------------------------------------*/
+
+#define usb_hw_set hw_set_alias(usb_hw)
+#define usb_hw_clear hw_clear_alias(usb_hw)
+
+// Init these in dcd_init
+static uint8_t *next_buffer_ptr;
+
+// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
+static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2] = {0};
+
+static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
+{
+ return &hw_endpoints[num][dir];
+}
+
+static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
+{
+ uint8_t num = tu_edpt_number(ep_addr);
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ return hw_endpoint_get_by_num(num, dir);
+}
+
+static void _hw_endpoint_alloc(struct hw_endpoint *ep)
+{
+ uint16_t size = tu_min16(64, ep->wMaxPacketSize);
+
+ // Assumes single buffered for now
+ ep->hw_data_buf = next_buffer_ptr;
+ next_buffer_ptr += size;
+ // Bits 0-5 are ignored by the controller so make sure these are 0
+ if ((uintptr_t)next_buffer_ptr & 0b111111u)
+ {
+ // Round up to the next 64
+ uint32_t fixptr = (uintptr_t)next_buffer_ptr;
+ fixptr &= ~0b111111u;
+ fixptr += 64;
+ pico_info("Rounding non 64 byte boundary buffer up from %x to %x\n", (uintptr_t)next_buffer_ptr, fixptr);
+ next_buffer_ptr = (uint8_t*)fixptr;
+ }
+ assert(((uintptr_t)next_buffer_ptr & 0b111111u) == 0);
+ uint dpram_offset = hw_data_offset(ep->hw_data_buf);
+ assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
+
+ pico_info("Alloced %d bytes at offset 0x%x (0x%p) for ep %d %s\n",
+ size,
+ dpram_offset,
+ ep->hw_data_buf,
+ ep->num,
+ ep_dir_string[ep->in]);
+
+ // Fill in endpoint control register with buffer offset
+ uint32_t reg = EP_CTRL_ENABLE_BITS
+ | EP_CTRL_INTERRUPT_PER_BUFFER
+ | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
+ | dpram_offset;
+
+ *ep->endpoint_control = reg;
+}
+
+static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type)
+{
+ const uint8_t num = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ ep->ep_addr = ep_addr;
+ // For device, IN is a tx transfer and OUT is an rx transfer
+ ep->rx = (dir == TUSB_DIR_OUT);
+ // Response to a setup packet on EP0 starts with pid of 1
+ ep->next_pid = num == 0 ? 1u : 0u;
+
+ // Add some checks around the max packet size
+ if (transfer_type == TUSB_XFER_ISOCHRONOUS)
+ {
+ if (wMaxPacketSize > USB_MAX_ISO_PACKET_SIZE)
+ {
+ panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
+ }
+ }
+ else
+ {
+ if (wMaxPacketSize > USB_MAX_PACKET_SIZE)
+ {
+ panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
+ }
+ }
+
+ ep->wMaxPacketSize = wMaxPacketSize;
+ ep->transfer_type = transfer_type;
+
+ // Every endpoint has a buffer control register in dpram
+ if (dir == TUSB_DIR_IN)
+ {
+ ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
+ }
+ else
+ {
+ ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
+ }
+
+ // Clear existing buffer control state
+ *ep->buffer_control = 0;
+
+ if (num == 0)
+ {
+ // EP0 has no endpoint control register because
+ // the buffer offsets are fixed
+ ep->endpoint_control = NULL;
+
+ // Buffer offset is fixed
+ ep->hw_data_buf = (uint8_t*)&usb_dpram->ep0_buf_a[0];
+ }
+ else
+ {
+ // Set the endpoint control register (starts at EP1, hence num-1)
+ if (dir == TUSB_DIR_IN)
+ {
+ ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].in;
+ }
+ else
+ {
+ ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].out;
+ }
+
+ // Now if it hasn't already been done
+ //alloc a buffer and fill in endpoint control register
+ if(!(ep->configured))
+ {
+ _hw_endpoint_alloc(ep);
+ }
+ }
+
+ ep->configured = true;
+}
+
+#if 0 // todo unused
+static void _hw_endpoint_close(struct hw_endpoint *ep)
+{
+ // Clear hardware registers and then zero the struct
+ // Clears endpoint enable
+ *ep->endpoint_control = 0;
+ // Clears buffer available, etc
+ *ep->buffer_control = 0;
+ // Clear any endpoint state
+ memset(ep, 0, sizeof(struct hw_endpoint));
+}
+
+static void hw_endpoint_close(uint8_t ep_addr)
+{
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ _hw_endpoint_close(ep);
+}
+#endif
+
+static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t bmAttributes)
+{
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ _hw_endpoint_init(ep, ep_addr, wMaxPacketSize, bmAttributes);
+}
+
+static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool start)
+{
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ _hw_endpoint_xfer(ep, buffer, total_bytes, start);
+}
+
+static void hw_handle_buff_status(void)
+{
+ uint32_t remaining_buffers = usb_hw->buf_status;
+ pico_trace("buf_status 0x%08x\n", remaining_buffers);
+ uint bit = 1u;
+ for (uint i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
+ {
+ if (remaining_buffers & bit)
+ {
+ uint __unused which = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0;
+ // Should be single buffered
+ assert(which == 0);
+ // clear this in advance
+ usb_hw_clear->buf_status = bit;
+ // IN transfer for even i, OUT transfer for odd i
+ struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, !(i & 1u));
+ // Continue xfer
+ bool done = _hw_endpoint_xfer_continue(ep);
+ if (done)
+ {
+ // Notify
+ dcd_event_xfer_complete(0, ep->ep_addr, ep->len, XFER_RESULT_SUCCESS, true);
+ hw_endpoint_reset_transfer(ep);
+ }
+ remaining_buffers &= ~bit;
+ }
+ bit <<= 1u;
+ }
+}
+
+static void reset_ep0(void)
+{
+ // If we have finished this transfer on EP0 set pid back to 1 for next
+ // setup transfer. Also clear a stall in case
+ uint8_t addrs[] = {0x0, 0x80};
+ for (uint i = 0 ; i < TU_ARRAY_SIZE(addrs); i++)
+ {
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
+ ep->next_pid = 1u;
+ ep->stalled = 0;
+ }
+}
+
+static void ep0_0len_status(void)
+{
+ // Send 0len complete response on EP0 IN
+ reset_ep0();
+ hw_endpoint_xfer(0x80, NULL, 0, true);
+}
+
+static void _hw_endpoint_stall(struct hw_endpoint *ep)
+{
+ assert(!ep->stalled);
+ if (tu_edpt_number(ep->ep_addr) == 0)
+ {
+ // A stall on EP0 has to be armed so it can be cleared on the next setup packet
+ usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
+ }
+ _hw_endpoint_buffer_control_set_mask32(ep, USB_BUF_CTRL_STALL);
+ ep->stalled = true;
+}
+
+static void hw_endpoint_stall(uint8_t ep_addr)
+{
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ _hw_endpoint_stall(ep);
+}
+
+static void _hw_endpoint_clear_stall(struct hw_endpoint *ep)
+{
+ if (tu_edpt_number(ep->ep_addr) == 0)
+ {
+ // Probably already been cleared but no harm
+ usb_hw_clear->ep_stall_arm = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
+ }
+ _hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
+ ep->stalled = false;
+}
+
+static void hw_endpoint_clear_stall(uint8_t ep_addr)
+{
+ struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ _hw_endpoint_clear_stall(ep);
+}
+
+static void dcd_rp2040_irq(void)
+{
+ uint32_t status = usb_hw->ints;
+ uint32_t handled = 0;
+
+ if (status & USB_INTS_SETUP_REQ_BITS)
+ {
+ handled |= USB_INTS_SETUP_REQ_BITS;
+ uint8_t const *setup = (uint8_t const *)&usb_dpram->setup_packet;
+ // Clear stall bits and reset pid
+ reset_ep0();
+ // Pass setup packet to tiny usb
+ dcd_event_setup_received(0, setup, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
+ }
+
+ if (status & USB_INTS_BUFF_STATUS_BITS)
+ {
+ handled |= USB_INTS_BUFF_STATUS_BITS;
+ hw_handle_buff_status();
+ }
+
+ // SE0 for 2 us or more, usually together with Bus Reset
+ if (status & USB_INTS_DEV_CONN_DIS_BITS)
+ {
+ handled |= USB_INTS_DEV_CONN_DIS_BITS;
+
+ if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
+ {
+ // Connected: nothing to do
+ }else
+ {
+ // Disconnected
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
+ }
+
+ usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
+ }
+
+ // SE0 for 2.5 us or more
+ if (status & USB_INTS_BUS_RESET_BITS)
+ {
+ pico_trace("BUS RESET\n");
+ usb_hw->dev_addr_ctrl = 0;
+ handled |= USB_INTS_BUS_RESET_BITS;
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
+
+#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
+ // Only run enumeration walk-around if pull up is enabled
+ if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS )
+ {
+ rp2040_usb_device_enumeration_fix();
+ }
+#endif
+ }
+
+#if 0
+ // TODO Enable SUSPEND & RESUME interrupt and test later on with/without VBUS detection
+
+ /* Note from pico datasheet 4.1.2.6.4 (v1.2)
+ * If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
+ * the device is first connected but the bus is idle. The bus can be idle for a few ms before
+ * the host begins sending start of frame packets. You will also see a suspend interrupt
+ * when the device is disconnected if you do not have a VBUS detect circuit connected. This is
+ * because without VBUS detection, it is impossible to tell the difference between
+ * being disconnected and suspended.
+ */
+ if (status & USB_INTS_DEV_SUSPEND_BITS)
+ {
+ handled |= USB_INTS_DEV_SUSPEND_BITS;
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
+ }
+
+ if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS)
+ {
+ handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
+ }
+#endif
+
+ if (status ^ handled)
+ {
+ panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
+ }
+}
+
+#define USB_INTS_ERROR_BITS ( \
+ USB_INTS_ERROR_DATA_SEQ_BITS | \
+ USB_INTS_ERROR_BIT_STUFF_BITS | \
+ USB_INTS_ERROR_CRC_BITS | \
+ USB_INTS_ERROR_RX_OVERFLOW_BITS | \
+ USB_INTS_ERROR_RX_TIMEOUT_BITS)
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init (uint8_t rhport)
+{
+ pico_trace("dcd_init %d\n", rhport);
+ assert(rhport == 0);
+
+ // Reset hardware to default state
+ rp2040_usb_init();
+
+ irq_set_exclusive_handler(USBCTRL_IRQ, dcd_rp2040_irq);
+ memset(hw_endpoints, 0, sizeof(hw_endpoints));
+ next_buffer_ptr = &usb_dpram->epx_data[0];
+
+ // EP0 always exists so init it now
+ // EP0 OUT
+ hw_endpoint_init(0x0, 64, 0);
+ // EP0 IN
+ hw_endpoint_init(0x80, 64, 0);
+
+ // Initializes the USB peripheral for device mode and enables it.
+ // Don't need to enable the pull up here. Force VBUS
+ usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
+
+ // Enable individual controller IRQS here. Processor interrupt enable will be used
+ // for the global interrupt enable...
+ // TODO Enable SUSPEND & RESUME interrupt
+ usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
+ usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
+ USB_INTS_DEV_CONN_DIS_BITS /* | USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS */ ;
+
+ dcd_connect(rhport);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ assert(rhport == 0);
+ irq_set_enabled(USBCTRL_IRQ, true);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ assert(rhport == 0);
+ irq_set_enabled(USBCTRL_IRQ, false);
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ pico_trace("dcd_set_address %d %d\n", rhport, dev_addr);
+ assert(rhport == 0);
+
+ // Can't set device address in hardware until status xfer has complete
+ ep0_0len_status();
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ pico_info("dcd_remote_wakeup %d\n", rhport);
+ assert(rhport == 0);
+ usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
+}
+
+// disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+ pico_info("dcd_disconnect %d\n", rhport);
+ assert(rhport == 0);
+ usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
+}
+
+// connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+ pico_info("dcd_connect %d\n", rhport);
+ assert(rhport == 0);
+ usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
+}
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
+{
+ pico_trace("dcd_edpt0_status_complete %d\n", rhport);
+ assert(rhport == 0);
+
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS)
+ {
+ pico_trace("Set HW address %d\n", assigned_address);
+ usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
+ }
+
+ reset_ep0();
+}
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+ pico_info("dcd_edpt_open %d %02x\n", rhport, desc_edpt->bEndpointAddress);
+ assert(rhport == 0);
+ hw_endpoint_init(desc_edpt->bEndpointAddress, desc_edpt->wMaxPacketSize.size, desc_edpt->bmAttributes.xfer);
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ assert(rhport == 0);
+ // True means start new xfer
+ hw_endpoint_xfer(ep_addr, buffer, total_bytes, true);
+ return true;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ pico_trace("dcd_edpt_stall %d %02x\n", rhport, ep_addr);
+ assert(rhport == 0);
+ hw_endpoint_stall(ep_addr);
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ pico_trace("dcd_edpt_clear_stall %d %02x\n", rhport, ep_addr);
+ assert(rhport == 0);
+ hw_endpoint_clear_stall(ep_addr);
+}
+
+
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+{
+ // usbd.c says: In progress transfers on this EP may be delivered after this call
+ pico_trace("dcd_edpt_close %d %02x\n", rhport, ep_addr);
+
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+ dcd_rp2040_irq();
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/hcd_rp2040.c
new file mode 100755
index 0000000..224f6ef
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -0,0 +1,553 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_HOST_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
+
+#include "pico.h"
+#include "rp2040_usb.h"
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "osal/osal.h"
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "host/usbh_hcd.h"
+
+#define ROOT_PORT 0
+
+//--------------------------------------------------------------------+
+// Low level rp2040 controller functions
+//--------------------------------------------------------------------+
+
+#ifndef PICO_USB_HOST_INTERRUPT_ENDPOINTS
+#define PICO_USB_HOST_INTERRUPT_ENDPOINTS (USB_MAX_ENDPOINTS - 1)
+#endif
+static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, "");
+
+// Host mode uses one shared endpoint register for non-interrupt endpoint
+struct hw_endpoint eps[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
+#define epx (eps[0])
+
+#define usb_hw_set hw_set_alias(usb_hw)
+#define usb_hw_clear hw_clear_alias(usb_hw)
+
+// Used for hcd pipe busy.
+// todo still a bit wasteful
+// top bit set if valid
+uint8_t dev_ep_map[CFG_TUSB_HOST_DEVICE_MAX][1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS][2];
+
+// Flags we set by default in sie_ctrl (we add other bits on top)
+static uint32_t sie_ctrl_base = USB_SIE_CTRL_SOF_EN_BITS |
+ USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |
+ USB_SIE_CTRL_PULLDOWN_EN_BITS |
+ USB_SIE_CTRL_EP0_INT_1BUF_BITS;
+
+static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
+{
+ uint8_t num = tu_edpt_number(ep_addr);
+ if (num == 0) {
+ return &epx;
+ }
+ uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
+ uint mapping = dev_ep_map[dev_addr-1][num][in];
+ pico_trace("Get dev addr %d ep %d = %d\n", dev_addr, ep_addr, mapping);
+ return mapping >= 128 ? eps + (mapping & 0x7fu) : NULL;
+}
+
+static void set_dev_ep(uint8_t dev_addr, uint8_t ep_addr, struct hw_endpoint *ep)
+{
+ uint8_t num = tu_edpt_number(ep_addr);
+ uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
+ uint32_t index = ep - eps;
+ hard_assert(index < TU_ARRAY_SIZE(eps));
+ // todo revisit why dev_addr can be 0 here
+ if (dev_addr) {
+ dev_ep_map[dev_addr-1][num][in] = 128u | index;
+ }
+ pico_trace("Set dev addr %d ep %d = %d\n", dev_addr, ep_addr, index);
+}
+
+static inline uint8_t dev_speed(void)
+{
+ return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
+}
+
+static bool need_pre(uint8_t dev_addr)
+{
+ // If this device is different to the speed of the root device
+ // (i.e. is a low speed device on a full speed hub) then need pre
+ return hcd_port_speed_get(0) != tuh_device_get_speed(dev_addr);
+}
+
+static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result)
+{
+ // Mark transfer as done before we tell the tinyusb stack
+ uint8_t dev_addr = ep->dev_addr;
+ uint8_t ep_addr = ep->ep_addr;
+ uint total_len = ep->total_len;
+ hw_endpoint_reset_transfer(ep);
+ hcd_event_xfer_complete(dev_addr, ep_addr, total_len, xfer_result, true);
+}
+
+static void _handle_buff_status_bit(uint bit, struct hw_endpoint *ep)
+{
+ usb_hw_clear->buf_status = bit;
+ bool done = _hw_endpoint_xfer_continue(ep);
+ if (done)
+ {
+ hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
+ }
+}
+
+static void hw_handle_buff_status(void)
+{
+ uint32_t remaining_buffers = usb_hw->buf_status;
+ pico_trace("buf_status 0x%08x\n", remaining_buffers);
+
+ // Check EPX first
+ uint bit = 0b1;
+ if (remaining_buffers & bit)
+ {
+ remaining_buffers &= ~bit;
+ struct hw_endpoint *ep = &epx;
+ _handle_buff_status_bit(bit, ep);
+ }
+
+ // Check interrupt endpoints
+ for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++)
+ {
+ // EPX is bit 0
+ // IEP1 is bit 2
+ // IEP2 is bit 4
+ // IEP3 is bit 6
+ // etc
+ bit = 1 << (i*2);
+
+ if (remaining_buffers & bit)
+ {
+ remaining_buffers &= ~bit;
+ _handle_buff_status_bit(bit, &eps[i]);
+ }
+ }
+
+ if (remaining_buffers)
+ {
+ panic("Unhandled buffer %d\n", remaining_buffers);
+ }
+}
+
+static void hw_trans_complete(void)
+{
+ struct hw_endpoint *ep = &epx;
+ assert(ep->active);
+
+ if (ep->sent_setup)
+ {
+ pico_trace("Sent setup packet\n");
+ hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
+ }
+ else
+ {
+ // Don't care. Will handle this in buff status
+ return;
+ }
+}
+
+static void hcd_rp2040_irq(void)
+{
+ uint32_t status = usb_hw->ints;
+ uint32_t handled = 0;
+
+ if (status & USB_INTS_HOST_CONN_DIS_BITS)
+ {
+ handled |= USB_INTS_HOST_CONN_DIS_BITS;
+
+ if (dev_speed())
+ {
+ hcd_event_device_attach(ROOT_PORT, true);
+ }
+ else
+ {
+ hcd_event_device_remove(ROOT_PORT, true);
+ }
+
+ // Clear speed change interrupt
+ usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
+ }
+
+ if (status & USB_INTS_TRANS_COMPLETE_BITS)
+ {
+ handled |= USB_INTS_TRANS_COMPLETE_BITS;
+ usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
+ hw_trans_complete();
+ }
+
+ if (status & USB_INTS_BUFF_STATUS_BITS)
+ {
+ handled |= USB_INTS_BUFF_STATUS_BITS;
+ hw_handle_buff_status();
+ }
+
+ if (status & USB_INTS_STALL_BITS)
+ {
+ // We have rx'd a stall from the device
+ pico_trace("Stall REC\n");
+ handled |= USB_INTS_STALL_BITS;
+ usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
+ hw_xfer_complete(&epx, XFER_RESULT_STALLED);
+ }
+
+ if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS)
+ {
+ handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
+ usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
+ }
+
+ if (status & USB_INTS_ERROR_DATA_SEQ_BITS)
+ {
+ usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
+ panic("Data Seq Error \n");
+ }
+
+ if (status ^ handled)
+ {
+ panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
+ }
+}
+
+static struct hw_endpoint *_next_free_interrupt_ep(void)
+{
+ struct hw_endpoint *ep = NULL;
+ for (uint i = 1; i < TU_ARRAY_SIZE(eps); i++)
+ {
+ ep = &eps[i];
+ if (!ep->configured)
+ {
+ // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
+ ep->interrupt_num = i - 1;
+ return ep;
+ }
+ }
+ return ep;
+}
+
+static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
+{
+ struct hw_endpoint *ep = NULL;
+ if (transfer_type == TUSB_XFER_INTERRUPT)
+ {
+ ep = _next_free_interrupt_ep();
+ pico_info("Allocate interrupt ep %d\n", ep->interrupt_num);
+ assert(ep);
+ ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
+ ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
+ // 0x180 for epx
+ // 0x1c0 for intep0
+ // 0x200 for intep1
+ // etc
+ ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 1)];
+ }
+ else
+ {
+ ep = &epx;
+ ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
+ ep->endpoint_control = &usbh_dpram->epx_ctrl;
+ ep->hw_data_buf = &usbh_dpram->epx_data[0];
+ }
+ return ep;
+}
+
+static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
+{
+ // Already has data buffer, endpoint control, and buffer control allocated at this point
+ assert(ep->endpoint_control);
+ assert(ep->buffer_control);
+ assert(ep->hw_data_buf);
+
+ uint8_t const num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ bool in = ep_addr & TUSB_DIR_IN_MASK;
+ ep->ep_addr = ep_addr;
+ ep->dev_addr = dev_addr;
+
+ // For host, IN to host == RX, anything else rx == false
+ ep->rx = (dir == TUSB_DIR_IN);
+
+ // Response to a setup packet on EP0 starts with pid of 1
+ ep->next_pid = num == 0 ? 1u : 0u;
+ ep->wMaxPacketSize = wMaxPacketSize;
+ ep->transfer_type = transfer_type;
+
+ pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, ep->num, ep_dir_string[ep->in], ep->transfer_type);
+ pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, ep->num, ep_dir_string[ep->in], ep->hw_data_buf);
+ uint dpram_offset = hw_data_offset(ep->hw_data_buf);
+ // Bits 0-5 should be 0
+ assert(!(dpram_offset & 0b111111));
+
+ // Fill in endpoint control register with buffer offset
+ uint32_t ep_reg = EP_CTRL_ENABLE_BITS
+ | EP_CTRL_INTERRUPT_PER_BUFFER
+ | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
+ | dpram_offset;
+ ep_reg |= bmInterval ? (bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB : 0;
+ *ep->endpoint_control = ep_reg;
+ pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
+ ep->configured = true;
+
+ if (bmInterval)
+ {
+ // This is an interrupt endpoint
+ // so need to set up interrupt endpoint address control register with:
+ // device address
+ // endpoint number / direction
+ // preamble
+ uint32_t reg = dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB);
+ // Assert the interrupt endpoint is IN_TO_HOST
+ assert(dir == TUSB_DIR_IN);
+
+ if (need_pre(dev_addr))
+ {
+ reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
+ }
+ usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
+
+ // Finally, enable interrupt that endpoint
+ usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
+
+ // If it's an interrupt endpoint we need to set up the buffer control
+ // register
+
+ }
+}
+
+static void hw_endpoint_init(uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc)
+{
+ // Allocated differently based on if it's an interrupt endpoint or not
+ struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
+ _hw_endpoint_init(ep,
+ dev_addr,
+ ep_desc->bEndpointAddress,
+ ep_desc->wMaxPacketSize.size,
+ ep_desc->bmAttributes.xfer,
+ ep_desc->bInterval);
+ // Map this struct to ep@device address
+ set_dev_ep(dev_addr, ep_desc->bEndpointAddress, ep);
+}
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+bool hcd_init(uint8_t rhport)
+{
+ pico_trace("hcd_init %d\n", rhport);
+ assert(rhport == 0);
+
+ // Reset any previous state
+ rp2040_usb_init();
+
+ irq_set_exclusive_handler(USBCTRL_IRQ, hcd_rp2040_irq);
+
+ // clear epx and interrupt eps
+ memset(&eps, 0, sizeof(eps));
+
+ // Enable in host mode with SOF / Keep alive on
+ usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS;
+ usb_hw->sie_ctrl = sie_ctrl_base;
+ usb_hw->inte = USB_INTE_BUFF_STATUS_BITS |
+ USB_INTE_HOST_CONN_DIS_BITS |
+ USB_INTE_HOST_RESUME_BITS |
+ USB_INTE_STALL_BITS |
+ USB_INTE_TRANS_COMPLETE_BITS |
+ USB_INTE_ERROR_RX_TIMEOUT_BITS |
+ USB_INTE_ERROR_DATA_SEQ_BITS ;
+
+ return true;
+}
+
+void hcd_port_reset(uint8_t rhport)
+{
+ pico_trace("hcd_port_reset\n");
+ assert(rhport == 0);
+ // TODO: Nothing to do here yet. Perhaps need to reset some state?
+}
+
+bool hcd_port_connect_status(uint8_t rhport)
+{
+ pico_trace("hcd_port_connect_status\n");
+ assert(rhport == 0);
+ return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS;
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+ pico_trace("hcd_port_speed_get\n");
+ assert(rhport == 0);
+ // TODO: Should enumval this register
+ switch (dev_speed())
+ {
+ case 1:
+ return TUSB_SPEED_LOW;
+ case 2:
+ return TUSB_SPEED_FULL;
+ default:
+ panic("Invalid speed\n");
+ }
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+ pico_trace("hcd_device_close %d\n", dev_addr);
+}
+
+void hcd_int_enable(uint8_t rhport)
+{
+ assert(rhport == 0);
+ irq_set_enabled(USBCTRL_IRQ, true);
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+ // todo we should check this is disabling from the correct core; note currently this is never called
+ assert(rhport == 0);
+ irq_set_enabled(USBCTRL_IRQ, false);
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
+{
+ pico_info("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
+
+ // Get appropriate ep. Either EPX or interrupt endpoint
+ struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
+ assert(ep);
+
+ if (ep_addr != ep->ep_addr)
+ {
+ // Direction has flipped so re init it but with same properties
+ _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
+ }
+
+ // True indicates this is the start of the transfer
+ _hw_endpoint_xfer(ep, buffer, buflen, true);
+
+ // If a normal transfer (non-interrupt) then initiate using
+ // sie ctrl registers. Otherwise interrupt ep registers should
+ // already be configured
+ if (ep == &epx) {
+ // That has set up buffer control, endpoint control etc
+ // for host we have to initiate the transfer
+ usb_hw->dev_addr_ctrl = dev_addr | (tu_edpt_number(ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB);
+ uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | sie_ctrl_base;
+ flags |= ep->rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS;
+ // Set pre if we are a low speed device on full speed hub
+ flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
+ usb_hw->sie_ctrl = flags;
+ }
+
+ return true;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+ pico_info("hcd_setup_send dev_addr %d\n", dev_addr);
+
+ // Copy data into setup packet buffer
+ memcpy((void*)&usbh_dpram->setup_packet[0], setup_packet, 8);
+
+ // Configure EP0 struct with setup info for the trans complete
+ struct hw_endpoint *ep = _hw_endpoint_allocate(0);
+ // EP0 out
+ _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
+ assert(ep->configured);
+ assert(ep->num == 0 && !ep->in);
+ ep->total_len = 8;
+ ep->transfer_size = 8;
+ ep->active = true;
+ ep->sent_setup = true;
+
+ // Set device address
+ usb_hw->dev_addr_ctrl = dev_addr;
+ // Set pre if we are a low speed device on full speed hub
+ uint32_t flags = sie_ctrl_base | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS;
+ flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
+ usb_hw->sie_ctrl = flags;
+ return true;
+}
+
+uint32_t hcd_uframe_number(uint8_t rhport)
+{
+ // Microframe number is (125us) but we are max full speed so return miliseconds * 8
+ return usb_hw->sof_rd * 8;
+}
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+ pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
+ hw_endpoint_init(dev_addr, ep_desc);
+ return true;
+}
+
+bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
+{
+ // EPX is shared, so multiple device addresses and endpoint addresses share that
+ // so if any transfer is active on epx, we are busy. Interrupt endpoints have their own
+ // EPX so ep->active will only be busy if there is a pending transfer on that interrupt endpoint
+ // on that device
+ pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\n", dev_addr, ep_addr);
+ struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
+ assert(ep);
+ bool busy = ep->active;
+ pico_trace("busy == %d\n", busy);
+ return busy;
+}
+
+bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
+{
+ panic("hcd_pipe_stalled");
+}
+
+bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
+{
+ panic("hcd_clear_stall");
+ return true;
+}
+
+bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
+{
+ pico_trace("hcd_pipe_xfer dev_addr %d, ep_addr 0x%x, total_bytes %d, int_on_complete %d\n",
+ dev_addr, ep_addr, total_bytes, int_on_complete);
+
+ // Same logic as hcd_edpt_xfer as far as I am concerned
+ hcd_edpt_xfer(0, dev_addr, ep_addr, buffer, total_bytes);
+
+ return true;
+}
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c
new file mode 100755
index 0000000..98ffea9
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -0,0 +1,294 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+
+#include <stdlib.h>
+#include "rp2040_usb.h"
+
+// Direction strings for debug
+const char *ep_dir_string[] = {
+ "out",
+ "in",
+};
+
+static inline void _hw_endpoint_lock_update(struct hw_endpoint *ep, int delta) {
+ // todo add critsec as necessary to prevent issues between worker and IRQ...
+ // note that this is perhaps as simple as disabling IRQs because it would make
+ // sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
+}
+
+#if TUSB_OPT_HOST_ENABLED
+static inline void _hw_endpoint_update_last_buf(struct hw_endpoint *ep)
+{
+ ep->last_buf = ep->len + ep->transfer_size == ep->total_len;
+}
+#endif
+
+void rp2040_usb_init(void)
+{
+ // Reset usb controller
+ reset_block(RESETS_RESET_USBCTRL_BITS);
+ unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+
+ // Clear any previous state just in case
+ memset(usb_hw, 0, sizeof(*usb_hw));
+ memset(usb_dpram, 0, sizeof(*usb_dpram));
+
+ // Mux the controller to the onboard usb phy
+ usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
+
+ // Force VBUS detect so the device thinks it is plugged into a host
+ // TODO support VBUs detect
+ usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
+}
+
+void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
+{
+ ep->stalled = false;
+ ep->active = false;
+#if TUSB_OPT_HOST_ENABLED
+ ep->sent_setup = false;
+#endif
+ ep->total_len = 0;
+ ep->len = 0;
+ ep->transfer_size = 0;
+ ep->user_buf = 0;
+}
+
+void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
+ uint32_t value = 0;
+ if (and_mask) {
+ value = *ep->buffer_control & and_mask;
+ }
+ if (or_mask) {
+ value |= or_mask;
+ if (or_mask & USB_BUF_CTRL_AVAIL) {
+ if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
+ panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+ }
+ *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
+ // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
+ // Don't need delay in host mode as host is in charge
+#if !TUSB_OPT_HOST_ENABLED
+ __asm volatile (
+ "b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1: b 1f\n"
+ "1:\n"
+ : : : "memory");
+#endif
+ }
+ }
+ *ep->buffer_control = value;
+}
+
+void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
+{
+ // Prepare buffer control register value
+ uint32_t val = ep->transfer_size | USB_BUF_CTRL_AVAIL;
+
+ if (!ep->rx)
+ {
+ // Copy data from user buffer to hw buffer
+ memcpy(ep->hw_data_buf, &ep->user_buf[ep->len], ep->transfer_size);
+ // Mark as full
+ val |= USB_BUF_CTRL_FULL;
+ }
+
+ // PID
+ val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
+ ep->next_pid ^= 1u;
+
+#if TUSB_OPT_HOST_ENABLED
+ // Is this the last buffer? Only really matters for host mode. Will trigger
+ // the trans complete irq but also stop it polling. We only really care about
+ // trans complete for setup packets being sent
+ if (ep->last_buf)
+ {
+ pico_trace("Last buf (%d bytes left)\n", ep->transfer_size);
+ val |= USB_BUF_CTRL_LAST;
+ }
+#endif
+
+ // Finally, write to buffer_control which will trigger the transfer
+ // the next time the controller polls this dpram address
+ _hw_endpoint_buffer_control_set_value32(ep, val);
+ pico_trace("buffer control (0x%p) <- 0x%x\n", ep->buffer_control, val);
+}
+
+
+void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
+{
+ _hw_endpoint_lock_update(ep, 1);
+ pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+ if (ep->active)
+ {
+ // TODO: Is this acceptable for interrupt packets?
+ pico_warn("WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+
+ hw_endpoint_reset_transfer(ep);
+ }
+
+ // Fill in info now that we're kicking off the hw
+ ep->total_len = total_len;
+ ep->len = 0;
+ ep->transfer_size = tu_min16(total_len, ep->wMaxPacketSize);
+ ep->active = true;
+ ep->user_buf = buffer;
+#if TUSB_OPT_HOST_ENABLED
+ // Recalculate if this is the last buffer
+ _hw_endpoint_update_last_buf(ep);
+ ep->buf_sel = 0;
+#endif
+
+ _hw_endpoint_start_next_buffer(ep);
+ _hw_endpoint_lock_update(ep, -1);
+}
+
+void _hw_endpoint_xfer_sync(struct hw_endpoint *ep)
+{
+ // Update hw endpoint struct with info from hardware
+ // after a buff status interrupt
+
+ // Get the buffer state and amount of bytes we have
+ // transferred
+ uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
+ uint16_t transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
+
+#if TUSB_OPT_HOST_ENABLED
+ // tag::host_buf_sel_fix[]
+ if (ep->buf_sel == 1)
+ {
+ // Host can erroneously write status to top half of buf_ctrl register
+ buf_ctrl = buf_ctrl >> 16;
+ }
+ // Flip buf sel for host
+ ep->buf_sel ^= 1u;
+ // end::host_buf_sel_fix[]
+#endif
+
+ // We are continuing a transfer here. If we are TX, we have successfullly
+ // sent some data can increase the length we have sent
+ if (!ep->rx)
+ {
+ assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
+ pico_trace("tx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
+ ep->len += transferred_bytes;
+ }
+ else
+ {
+ // If we are OUT we have recieved some data, so can increase the length
+ // we have recieved AFTER we have copied it to the user buffer at the appropriate
+ // offset
+ pico_trace("rx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
+ assert(buf_ctrl & USB_BUF_CTRL_FULL);
+ memcpy(&ep->user_buf[ep->len], ep->hw_data_buf, transferred_bytes);
+ ep->len += transferred_bytes;
+ }
+
+ // Sometimes the host will send less data than we expect...
+ // If this is a short out transfer update the total length of the transfer
+ // to be the current length
+ if ((ep->rx) && (transferred_bytes < ep->transfer_size))
+ {
+ pico_trace("Short rx transfer\n");
+ // Reduce total length as this is last packet
+ ep->total_len = ep->len;
+ }
+}
+
+// Returns true if transfer is complete
+bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
+{
+ _hw_endpoint_lock_update(ep, 1);
+ // Part way through a transfer
+ if (!ep->active)
+ {
+ panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string);
+ }
+
+ // Update EP struct from hardware state
+ _hw_endpoint_xfer_sync(ep);
+
+ // Now we have synced our state with the hardware. Is there more data to transfer?
+ uint16_t remaining_bytes = ep->total_len - ep->len;
+ ep->transfer_size = tu_min16(remaining_bytes, ep->wMaxPacketSize);
+#if TUSB_OPT_HOST_ENABLED
+ _hw_endpoint_update_last_buf(ep);
+#endif
+
+ // Can happen because of programmer error so check for it
+ if (ep->len > ep->total_len)
+ {
+ panic("Transferred more data than expected");
+ }
+
+ // If we are done then notify tinyusb
+ if (ep->len == ep->total_len)
+ {
+ pico_trace("Completed transfer of %d bytes on ep %d %s\n",
+ ep->len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+ // Notify caller we are done so it can notify the tinyusb
+ // stack
+ _hw_endpoint_lock_update(ep, -1);
+ return true;
+ }
+ else
+ {
+ _hw_endpoint_start_next_buffer(ep);
+ }
+
+ _hw_endpoint_lock_update(ep, -1);
+ // More work to do
+ return false;
+}
+
+void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start)
+{
+ // Trace
+ pico_trace("hw_endpoint_xfer ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+ pico_trace(" total_len %d, start=%d\n", total_len, start);
+
+ assert(ep->configured);
+
+
+ if (start)
+ {
+ _hw_endpoint_xfer_start(ep, buffer, total_len);
+ }
+ else
+ {
+ _hw_endpoint_xfer_continue(ep);
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.h b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.h
new file mode 100755
index 0000000..bb88d97
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.h
@@ -0,0 +1,122 @@
+#ifndef RP2040_COMMON_H_
+#define RP2040_COMMON_H_
+
+#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
+#error TinyUSB device and host mode not supported at the same time
+#endif
+
+#include "common/tusb_common.h"
+
+#include "pico.h"
+#include "hardware/structs/usb.h"
+#include "hardware/irq.h"
+#include "hardware/resets.h"
+
+#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
+#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
+#endif
+
+// For memset
+#include <string.h>
+
+#if false && !defined(NDEBUG)
+#define pico_trace(format,args...) printf(format, ## args)
+#else
+#define pico_trace(format,...) ((void)0)
+#endif
+
+#if false && !defined(NDEBUG)
+#define pico_info(format,args...) printf(format, ## args)
+#else
+#define pico_info(format,...) ((void)0)
+#endif
+
+#if false && !defined(NDEBUG)
+#define pico_warn(format,args...) printf(format, ## args)
+#else
+#define pico_warn(format,...) ((void)0)
+#endif
+
+// Hardware information per endpoint
+struct hw_endpoint
+{
+ // Is this a valid struct
+ bool configured;
+
+ // Transfer direction (i.e. IN is rx for host but tx for device)
+ // allows us to common up transfer functions
+ bool rx;
+
+ uint8_t ep_addr;
+ uint8_t next_pid;
+
+ // Endpoint control register
+ io_rw_32 *endpoint_control;
+ // Buffer control register
+ io_rw_32 *buffer_control;
+
+ // Buffer pointer in usb dpram
+ uint8_t *hw_data_buf;
+
+ // Have we been stalled
+ bool stalled;
+
+ // Current transfer information
+ bool active;
+ uint16_t total_len;
+ uint16_t len;
+ // Amount of data with the hardware
+ uint16_t transfer_size;
+ // User buffer in main memory
+ uint8_t *user_buf;
+
+ // Data needed from EP descriptor
+ uint16_t wMaxPacketSize;
+ // Interrupt, bulk, etc
+ uint8_t transfer_type;
+
+#if TUSB_OPT_HOST_ENABLED
+ // Only needed for host mode
+ bool last_buf;
+ // HOST BUG. Host will incorrect write status to top half of buffer
+ // control register when doing transfers > 1 packet
+ uint8_t buf_sel;
+ // Only needed for host
+ uint8_t dev_addr;
+ bool sent_setup;
+ // If interrupt endpoint
+ uint8_t interrupt_num;
+#endif
+};
+
+void rp2040_usb_init(void);
+
+void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
+void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start);
+void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
+void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
+void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
+bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep);
+void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
+static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) {
+ return *ep->buffer_control;
+}
+static inline void _hw_endpoint_buffer_control_set_value32(struct hw_endpoint *ep, uint32_t value) {
+ return _hw_endpoint_buffer_control_update32(ep, 0, value);
+}
+static inline void _hw_endpoint_buffer_control_set_mask32(struct hw_endpoint *ep, uint32_t value) {
+ return _hw_endpoint_buffer_control_update32(ep, ~value, value);
+}
+static inline void _hw_endpoint_buffer_control_clear_mask32(struct hw_endpoint *ep, uint32_t value) {
+ return _hw_endpoint_buffer_control_update32(ep, ~value, 0);
+}
+
+static inline uintptr_t hw_data_offset(uint8_t *buf)
+{
+ // Remove usb base from buffer pointer
+ return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
+}
+
+extern const char *ep_dir_string[];
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/renesas/usba/dcd_usba.c b/sw/Core/Src/tinyusb/src/portable/renesas/usba/dcd_usba.c
new file mode 100755
index 0000000..06ea1a3
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/renesas/usba/dcd_usba.c
@@ -0,0 +1,736 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Koji Kitayama
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X )
+
+#include "device/dcd.h"
+#include "iodefine.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+#define SYSTEM_PRCR_PRC1 (1<<1)
+#define SYSTEM_PRCR_PRKEY (0xA5u<<8)
+
+#define USB_FIFOSEL_TX ((uint16_t)(1u<<5))
+#define USB_FIFOSEL_MBW_8 ((uint16_t)(0u<<10))
+#define USB_FIFOSEL_MBW_16 ((uint16_t)(1u<<10))
+#define USB_IS0_CTSQ ((uint16_t)(7u))
+#define USB_IS0_DVSQ ((uint16_t)(7u<<4))
+#define USB_IS0_VALID ((uint16_t)(1u<<3))
+#define USB_IS0_BRDY ((uint16_t)(1u<<8))
+#define USB_IS0_NRDY ((uint16_t)(1u<<9))
+#define USB_IS0_BEMP ((uint16_t)(1u<<10))
+#define USB_IS0_CTRT ((uint16_t)(1u<<11))
+#define USB_IS0_DVST ((uint16_t)(1u<<12))
+#define USB_IS0_SOFR ((uint16_t)(1u<<13))
+#define USB_IS0_RESM ((uint16_t)(1u<<14))
+#define USB_IS0_VBINT ((uint16_t)(1u<<15))
+#define USB_IS1_SACK ((uint16_t)(1u<<4))
+#define USB_IS1_SIGN ((uint16_t)(1u<<5))
+#define USB_IS1_EOFERR ((uint16_t)(1u<<6))
+#define USB_IS1_ATTCH ((uint16_t)(1u<<11))
+#define USB_IS1_DTCH ((uint16_t)(1u<<12))
+#define USB_IS1_BCHG ((uint16_t)(1u<<14))
+#define USB_IS1_OVRCR ((uint16_t)(1u<<15))
+
+#define USB_IS0_CTSQ_MSK (7u)
+#define USB_IS0_CTSQ_SETUP (1u)
+#define USB_IS0_DVSQ_DEF (1u<<4)
+#define USB_IS0_DVSQ_ADDR (2u<<4)
+#define USB_IS0_DVSQ_SUSP (4u<<4)
+
+#define USB_PIPECTR_PID_NAK (0u)
+#define USB_PIPECTR_PID_BUF (1u)
+#define USB_PIPECTR_PID_STALL (2u)
+#define USB_PIPECTR_CCPL (1u<<2)
+#define USB_PIPECTR_SQMON (1u<<6)
+#define USB_PIPECTR_SQCLR (1u<<8)
+#define USB_PIPECTR_ACLRM (1u<<9)
+#define USB_PIPECTR_INBUFM (1u<<14)
+#define USB_PIPECTR_BSTS (1u<<15)
+
+#define USB_FIFOCTR_DTLN (0x1FF)
+#define USB_FIFOCTR_FRDY (1u<<13)
+#define USB_FIFOCTR_BCLR (1u<<14)
+#define USB_FIFOCTR_BVAL (1u<<15)
+
+#define USB_PIPECFG_SHTNAK (1u<<7)
+#define USB_PIPECFG_DBLB (1u<<9)
+#define USB_PIPECFG_BULK (1u<<14)
+#define USB_PIPECFG_ISO (3u<<14)
+#define USB_PIPECFG_INT (2u<<14)
+
+#define FIFO_REQ_CLR (1u)
+#define FIFO_COMPLETE (1u<<1)
+
+typedef struct {
+ union {
+ struct {
+ uint16_t : 8;
+ uint16_t TRCLR: 1;
+ uint16_t TRENB: 1;
+ uint16_t : 0;
+ };
+ uint16_t TRE;
+ };
+ uint16_t TRN;
+} reg_pipetre_t;
+
+typedef union {
+ struct {
+ volatile uint16_t u8: 8;
+ volatile uint16_t : 0;
+ };
+ volatile uint16_t u16;
+} hw_fifo_t;
+
+typedef struct TU_ATTR_PACKED
+{
+ uintptr_t addr; /* the start address of a transfer data buffer */
+ uint16_t length; /* the number of bytes in the buffer */
+ uint16_t remaining; /* the number of bytes remaining in the buffer */
+ struct {
+ uint32_t ep : 8; /* an assigned endpoint address */
+ uint32_t : 0;
+ };
+} pipe_state_t;
+
+typedef struct
+{
+ pipe_state_t pipe[9];
+ uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */
+} dcd_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION static dcd_data_t _dcd;
+
+static uint32_t disable_interrupt(void)
+{
+ uint32_t pswi;
+ pswi = __builtin_rx_mvfc(0) & 0x010000;
+ __builtin_rx_clrpsw('I');
+ return pswi;
+}
+
+static void enable_interrupt(uint32_t pswi)
+{
+ __builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi);
+}
+
+static unsigned find_pipe(unsigned xfer)
+{
+ switch (xfer) {
+ case TUSB_XFER_ISOCHRONOUS:
+ for (int i = 1; i <= 2; ++i) {
+ if (0 == _dcd.pipe[i].ep) return i;
+ }
+ break;
+ case TUSB_XFER_BULK:
+ for (int i = 3; i <= 5; ++i) {
+ if (0 == _dcd.pipe[i].ep) return i;
+ }
+ for (int i = 1; i <= 1; ++i) {
+ if (0 == _dcd.pipe[i].ep) return i;
+ }
+ break;
+ case TUSB_XFER_INTERRUPT:
+ for (int i = 6; i <= 9; ++i) {
+ if (0 == _dcd.pipe[i].ep) return i;
+ }
+ break;
+ default:
+ /* No support for control transfer */
+ break;
+ }
+ return 0;
+}
+
+static volatile uint16_t* get_pipectr(unsigned num)
+{
+ volatile uint16_t *ctr = NULL;
+ if (num) {
+ ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD;
+ ctr += num - 1;
+ } else {
+ ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD;
+ }
+ return ctr;
+}
+
+static volatile reg_pipetre_t* get_pipetre(unsigned num)
+{
+ volatile reg_pipetre_t* tre = NULL;
+ if ((1 <= num) && (num <= 5)) {
+ tre = (volatile reg_pipetre_t*)&USB0.PIPE1TRE.WORD;
+ tre += num - 1;
+ }
+ return tre;
+}
+
+static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
+{
+ (void)rhport;
+ volatile uint16_t *ctr = NULL;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ if (epn) {
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned num = _dcd.ep[dir][epn];
+ if (num) {
+ ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD;
+ ctr += num - 1;
+ }
+ } else {
+ ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD;
+ }
+ return ctr;
+}
+
+static unsigned wait_for_pipe_ready(void)
+{
+ unsigned ctr;
+ do {
+ ctr = USB0.D0FIFOCTR.WORD;
+ } while (!(ctr & USB_FIFOCTR_FRDY));
+ return ctr;
+}
+
+static unsigned select_pipe(unsigned num, unsigned attr)
+{
+ USB0.PIPESEL.WORD = num;
+ USB0.D0FIFOSEL.WORD = num | attr;
+ while (!(USB0.D0FIFOSEL.BIT.CURPIPE != num)) ;
+ return wait_for_pipe_ready();
+}
+
+/* 1 less than mps bytes were written to FIFO
+ * 2 no bytes were written to FIFO
+ * 0 mps bytes were written to FIFO */
+static int fifo_write(volatile void *fifo, pipe_state_t* pipe, unsigned mps)
+{
+ unsigned rem = pipe->remaining;
+ if (!rem) return 2;
+ unsigned len = TU_MIN(rem, mps);
+
+ hw_fifo_t *reg = (hw_fifo_t*)fifo;
+ uintptr_t addr = pipe->addr + pipe->length - rem;
+ if (addr & 1u) {
+ /* addr is not 2-byte aligned */
+ reg->u8 = *(const uint8_t *)addr;
+ ++addr;
+ --len;
+ }
+ while (len >= 2) {
+ reg->u16 = *(const uint16_t *)addr;
+ addr += 2;
+ len -= 2;
+ }
+ if (len) {
+ reg->u8 = *(const uint8_t *)addr;
+ ++addr;
+ }
+ if (rem < mps) return 1;
+ return 0;
+}
+
+/* 1 less than mps bytes were read from FIFO
+ * 2 the end of the buffer reached.
+ * 0 mps bytes were read from FIFO */
+static int fifo_read(volatile void *fifo, pipe_state_t* pipe, unsigned mps, size_t len)
+{
+ unsigned rem = pipe->remaining;
+ if (!rem) return 2;
+ if (rem < len) len = rem;
+ pipe->remaining = rem - len;
+
+ hw_fifo_t *reg = (hw_fifo_t*)fifo;
+ uintptr_t addr = pipe->addr;
+ unsigned loop = len;
+ while (loop--) {
+ *(uint8_t *)addr = reg->u8;
+ ++addr;
+ }
+ pipe->addr = addr;
+ if (rem < mps) return 1;
+ if (rem == len) return 2;
+ return 0;
+}
+
+static void process_setup_packet(uint8_t rhport)
+{
+ uint16_t setup_packet[4];
+ if (0 == (USB0.INTSTS0.WORD & USB_IS0_VALID)) return;
+ USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
+ setup_packet[0] = USB0.USBREQ.WORD;
+ setup_packet[1] = USB0.USBVAL;
+ setup_packet[2] = USB0.USBINDX;
+ setup_packet[3] = USB0.USBLENG;
+ USB0.INTSTS0.WORD = ~USB_IS0_VALID;
+ dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet[0], true);
+}
+
+static void process_status_completion(uint8_t rhport)
+{
+ uint8_t ep_addr;
+ /* Check the data stage direction */
+ if (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) {
+ /* IN transfer. */
+ ep_addr = tu_edpt_addr(0, TUSB_DIR_IN);
+ } else {
+ /* OUT transfer. */
+ ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT);
+ }
+ dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true);
+}
+
+static bool process_edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ (void)rhport;
+
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ /* configure fifo direction and access unit settings */
+ if (ep_addr) { /* IN, 2 bytes */
+ USB0.CFIFOSEL.WORD = USB_FIFOSEL_TX | USB_FIFOSEL_MBW_16;
+ while (!(USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX)) ;
+ } else { /* OUT, a byte */
+ USB0.CFIFOSEL.WORD = USB_FIFOSEL_MBW_8;
+ while (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) ;
+ }
+ if (total_bytes) {
+ pipe->addr = (uintptr_t)buffer;
+ pipe->length = total_bytes;
+ pipe->remaining = total_bytes;
+ if (ep_addr) { /* IN */
+ TU_ASSERT(USB0.DCPCTR.BIT.BSTS && (USB0.USBREQ.WORD & 0x80));
+ if (fifo_write(&USB0.CFIFO.WORD, pipe, 64)) {
+ USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
+ }
+ }
+ USB0.DCPCTR.WORD = USB_PIPECTR_PID_BUF;
+ } else {
+ /* ZLP */
+ pipe->addr = 0;
+ pipe->length = 0;
+ pipe->remaining = 0;
+ USB0.DCPCTR.WORD = USB_PIPECTR_CCPL | USB_PIPECTR_PID_BUF;
+ }
+ return true;
+}
+
+static void process_edpt0_bemp(uint8_t rhport)
+{
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ const unsigned rem = pipe->remaining;
+ if (rem > 64) {
+ pipe->remaining = rem - 64;
+ int r = fifo_write(&USB0.CFIFO.WORD, &_dcd.pipe[0], 64);
+ if (r) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
+ return;
+ }
+ pipe->addr = 0;
+ pipe->remaining = 0;
+ dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN),
+ pipe->length, XFER_RESULT_SUCCESS, true);
+}
+
+static void process_edpt0_brdy(uint8_t rhport)
+{
+ size_t len = USB0.CFIFOCTR.BIT.DTLN;
+ int cplt = fifo_read(&USB0.CFIFO.WORD, &_dcd.pipe[0], 64, len);
+ if (cplt || (len < 64)) {
+ if (2 != cplt) {
+ USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
+ }
+ dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
+ _dcd.pipe[0].length - _dcd.pipe[0].remaining,
+ XFER_RESULT_SUCCESS, true);
+ }
+}
+
+static bool process_pipe_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ (void)rhport;
+
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned num = _dcd.ep[dir][epn];
+
+ TU_ASSERT(num);
+
+ pipe_state_t *pipe = &_dcd.pipe[num];
+ pipe->addr = (uintptr_t)buffer;
+ pipe->length = total_bytes;
+ pipe->remaining = total_bytes;
+
+ USB0.PIPESEL.WORD = num;
+ const unsigned mps = USB0.PIPEMAXP.WORD;
+ if (dir) { /* IN */
+ USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_16;
+ while (!(USB0.D0FIFOSEL.BIT.CURPIPE != num)) ;
+ int r = fifo_write(&USB0.D0FIFO.WORD, pipe, mps);
+ if (r) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
+ USB0.D0FIFOSEL.WORD = 0;
+ } else {
+ volatile reg_pipetre_t *pt = get_pipetre(num);
+ if (pt) {
+ volatile uint16_t *ctr = get_pipectr(num);
+ if (*ctr & 0x3) *ctr = USB_PIPECTR_PID_NAK;
+ pt->TRE = TU_BIT(8);
+ pt->TRN = (total_bytes + mps - 1) / mps;
+ pt->TRENB = 1;
+ *ctr = USB_PIPECTR_PID_BUF;
+ }
+ }
+ // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
+ return true;
+}
+
+static void process_pipe_brdy(uint8_t rhport, unsigned num)
+{
+ pipe_state_t *pipe = &_dcd.pipe[num];
+ if (tu_edpt_dir(pipe->ep)) { /* IN */
+ select_pipe(num, USB_FIFOSEL_MBW_16);
+ const unsigned mps = USB0.PIPEMAXP.WORD;
+ unsigned rem = pipe->remaining;
+ rem -= TU_MIN(rem, mps);
+ pipe->remaining = rem;
+ if (rem) {
+ int r = 0;
+ r = fifo_write(&USB0.D0FIFO.WORD, pipe, mps);
+ if (r) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
+ USB0.D0FIFOSEL.WORD = 0;
+ return;
+ }
+ USB0.D0FIFOSEL.WORD = 0;
+ pipe->addr = 0;
+ pipe->remaining = 0;
+ dcd_event_xfer_complete(rhport, pipe->ep, pipe->length,
+ XFER_RESULT_SUCCESS, true);
+ } else {
+ const unsigned ctr = select_pipe(num, USB_FIFOSEL_MBW_8);
+ const unsigned len = ctr & USB_FIFOCTR_DTLN;
+ const unsigned mps = USB0.PIPEMAXP.WORD;
+ int cplt = fifo_read(&USB0.D0FIFO.WORD, pipe, mps, len);
+ if (cplt || (len < mps)) {
+ if (2 != cplt) {
+ USB0.D0FIFO.WORD = USB_FIFOCTR_BCLR;
+ }
+ USB0.D0FIFOSEL.WORD = 0;
+ dcd_event_xfer_complete(rhport, pipe->ep,
+ pipe->length - pipe->remaining,
+ XFER_RESULT_SUCCESS, true);
+ return;
+ }
+ USB0.D0FIFOSEL.WORD = 0;
+ }
+}
+
+static void process_bus_reset(uint8_t rhport)
+{
+ USB0.BEMPENB.WORD = 1;
+ USB0.BRDYENB.WORD = 1;
+ USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
+ USB0.D0FIFOSEL.WORD = 0;
+ USB0.D1FIFOSEL.WORD = 0;
+ volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1CTR.WORD));
+ volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1TRE.WORD));
+ for (int i = 1; i <= 5; ++i) {
+ USB0.PIPESEL.WORD = i;
+ USB0.PIPECFG.WORD = 0;
+ *ctr = USB_PIPECTR_ACLRM;
+ *ctr = 0;
+ ++ctr;
+ *tre = TU_BIT(8);
+ tre += 2;
+ }
+ for (int i = 6; i <= 9; ++i) {
+ USB0.PIPESEL.WORD = i;
+ USB0.PIPECFG.WORD = 0;
+ *ctr = USB_PIPECTR_ACLRM;
+ *ctr = 0;
+ ++ctr;
+ }
+ tu_varclr(&_dcd);
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+}
+
+static void process_set_address(uint8_t rhport)
+{
+ const uint32_t addr = USB0.USBADDR.BIT.USBADDR;
+ if (!addr) return;
+ const tusb_control_request_t setup_packet = {
+ .bmRequestType = 0,
+ .bRequest = 5,
+ .wValue = addr,
+ .wIndex = 0,
+ .wLength = 0,
+ };
+ dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet, true);
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+void dcd_init(uint8_t rhport)
+{
+ (void)rhport;
+ /* Enable USB0 */
+ uint32_t pswi = disable_interrupt();
+ SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
+ MSTP(USB0) = 0;
+ SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
+ enable_interrupt(pswi);
+ USB0.SYSCFG.BIT.SCKE = 1;
+ while (!USB0.SYSCFG.BIT.SCKE) ;
+ USB0.SYSCFG.BIT.DRPD = 0;
+ USB0.SYSCFG.BIT.DCFM = 0;
+ USB0.SYSCFG.BIT.USBE = 1;
+
+ IR(USB0, USBI0) = 0;
+
+ /* Setup default control pipe */
+ USB0.DCPMAXP.BIT.MXPS = 64;
+ USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP | USB_IS0_DVST | USB_IS0_CTRT;
+ USB0.BEMPENB.WORD = 1;
+ USB0.BRDYENB.WORD = 1;
+
+ if (USB0.INTSTS0.BIT.VBSTS) {
+ dcd_connect(rhport);
+ }
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+ (void)rhport;
+ IEN(USB0, USBI0) = 1;
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void)rhport;
+ IEN(USB0, USBI0) = 0;
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void)rhport;
+ (void)dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void)rhport;
+ /* TODO */
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void)rhport;
+ USB0.SYSCFG.BIT.DPRPU = 1;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void)rhport;
+ USB0.SYSCFG.BIT.DPRPU = 0;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void)rhport;
+
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned xfer = ep_desc->bmAttributes.xfer;
+
+ const unsigned mps = ep_desc->wMaxPacketSize.size;
+ if (xfer == TUSB_XFER_ISOCHRONOUS && mps > 256) {
+ /* USBa supports up to 256 bytes */
+ return false;
+ }
+
+ const unsigned num = find_pipe(xfer);
+ if (!num) return false;
+ _dcd.pipe[num].ep = ep_addr;
+ _dcd.ep[dir][epn] = num;
+
+ /* setup pipe */
+ dcd_int_disable(rhport);
+ USB0.PIPESEL.WORD = num;
+ USB0.PIPEMAXP.WORD = mps;
+ volatile uint16_t *ctr = get_pipectr(num);
+ *ctr = USB_PIPECTR_ACLRM;
+ *ctr = 0;
+ unsigned cfg = (dir << 4) | epn;
+ if (xfer == TUSB_XFER_BULK) {
+ cfg |= USB_PIPECFG_BULK | USB_PIPECFG_SHTNAK | USB_PIPECFG_DBLB;
+ } else if (xfer == TUSB_XFER_INTERRUPT) {
+ cfg |= USB_PIPECFG_INT;
+ } else {
+ cfg |= USB_PIPECFG_ISO | USB_PIPECFG_DBLB;
+ }
+ USB0.PIPECFG.WORD = cfg;
+ USB0.BRDYSTS.WORD = 0x1FFu ^ TU_BIT(num);
+ USB0.BRDYENB.WORD |= TU_BIT(num);
+ if (dir || (xfer != TUSB_XFER_BULK)) {
+ *ctr = USB_PIPECTR_PID_BUF;
+ }
+ // TU_LOG1("O %d %x %x\r\n", USB0.PIPESEL.WORD, USB0.PIPECFG.WORD, USB0.PIPEMAXP.WORD);
+ dcd_int_enable(rhport);
+
+ return true;
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+ (void)rhport;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned num = _dcd.ep[dir][epn];
+
+ USB0.BRDYENB.WORD &= ~TU_BIT(num);
+ volatile uint16_t *ctr = get_pipectr(num);
+ *ctr = 0;
+ USB0.PIPESEL.WORD = num;
+ USB0.PIPECFG.WORD = 0;
+ _dcd.pipe[num].ep = 0;
+ _dcd.ep[dir][epn] = 0;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ bool r;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ dcd_int_disable(rhport);
+ if (0 == epn) {
+ r = process_edpt0_xfer(rhport, ep_addr, buffer, total_bytes);
+ } else {
+ r = process_pipe_xfer(rhport, ep_addr, buffer, total_bytes);
+ }
+ dcd_int_enable(rhport);
+ return r;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
+ if (!ctr) return;
+ dcd_int_disable(rhport);
+ const uint32_t pid = *ctr & 0x3;
+ *ctr = pid | USB_PIPECTR_PID_STALL;
+ *ctr = USB_PIPECTR_PID_STALL;
+ dcd_int_enable(rhport);
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
+ if (!ctr) return;
+ dcd_int_disable(rhport);
+ *ctr = USB_PIPECTR_SQCLR;
+
+ if (tu_edpt_dir(ep_addr)) { /* IN */
+ *ctr = USB_PIPECTR_PID_BUF;
+ } else {
+ const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)];
+ USB0.PIPESEL.WORD = num;
+ if (USB0.PIPECFG.BIT.TYPE != 1) {
+ *ctr = USB_PIPECTR_PID_BUF;
+ }
+ }
+ dcd_int_enable(rhport);
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void)rhport;
+
+ unsigned is0 = USB0.INTSTS0.WORD;
+ /* clear bits except VALID */
+ USB0.INTSTS0.WORD = USB_IS0_VALID;
+ if (is0 & USB_IS0_VBINT) {
+ if (USB0.INTSTS0.BIT.VBSTS) {
+ dcd_connect(rhport);
+ } else {
+ dcd_disconnect(rhport);
+ }
+ }
+ if (is0 & USB_IS0_DVST) {
+ switch (is0 & USB_IS0_DVSQ) {
+ case USB_IS0_DVSQ_DEF:
+ process_bus_reset(rhport);
+ break;
+ case USB_IS0_DVSQ_ADDR:
+ process_set_address(rhport);
+ break;
+ default:
+ break;
+ }
+ }
+ if (is0 & USB_IS0_CTRT) {
+ if (is0 & USB_IS0_CTSQ_SETUP) {
+ /* A setup packet has been received. */
+ process_setup_packet(rhport);
+ } else if (0 == (is0 & USB_IS0_CTSQ_MSK)) {
+ /* A ZLP has been sent/received. */
+ process_status_completion(rhport);
+ }
+ }
+ if (is0 & USB_IS0_BEMP) {
+ const unsigned s = USB0.BEMPSTS.WORD;
+ USB0.BEMPSTS.WORD = 0;
+ if (s & 1) {
+ process_edpt0_bemp(rhport);
+ }
+ }
+ if (is0 & USB_IS0_BRDY) {
+ const unsigned m = USB0.BRDYENB.WORD;
+ unsigned s = USB0.BRDYSTS.WORD & m;
+ USB0.BRDYSTS.WORD = 0;
+ if (s & 1) {
+ process_edpt0_brdy(rhport);
+ s &= ~1;
+ }
+ while (s) {
+ const unsigned num = __builtin_ctz(s);
+ process_pipe_brdy(rhport, num);
+ s &= ~TU_BIT(num);
+ }
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/silabs/efm32/dcd_efm32.c b/sw/Core/Src/tinyusb/src/portable/silabs/efm32/dcd_efm32.c
new file mode 100755
index 0000000..bd1f32e
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/silabs/efm32/dcd_efm32.c
@@ -0,0 +1,931 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Rafael Silva (@perigoso)
+ * Copyright (c) 2021 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && ( \
+ (CFG_TUSB_MCU == OPT_MCU_EFM32GG) || \
+ (CFG_TUSB_MCU == OPT_MCU_EFM32GG11) || \
+ (CFG_TUSB_MCU == OPT_MCU_EFM32GG12) )
+
+/* Silabs */
+#include "em_device.h"
+
+#include "device/dcd.h"
+
+/*
+ * Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
+ * We disable SOF for now until needed later on
+ */
+#define USE_SOF 0
+
+/*
+ * Number of endpoints
+ * 12 software-configurable endpoints (6 IN, 6 OUT) in addition to endpoint 0
+ */
+#define EP_COUNT 7
+
+/* FIFO size in bytes */
+#define EP_FIFO_SIZE 2048
+
+/* Max number of IN EP FIFOs */
+#define EP_FIFO_NUM 7
+
+/* */
+typedef struct {
+ uint8_t *buffer;
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t max_size;
+ bool short_packet;
+} xfer_ctl_t;
+
+static uint32_t _setup_packet[2];
+
+#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
+static xfer_ctl_t xfer_status[EP_COUNT][2];
+
+/* Keep count of how many FIFOs are in use */
+static uint8_t _allocated_fifos = 1; /* FIFO0 is always in use */
+
+static volatile uint32_t* tx_fifo[EP_FIFO_NUM] = {
+ USB->FIFO0D,
+ USB->FIFO1D,
+ USB->FIFO2D,
+ USB->FIFO3D,
+ USB->FIFO4D,
+ USB->FIFO5D,
+ USB->FIFO6D,
+};
+
+/* Register Helpers */
+#define DCTL_WO_BITMASK (USB_DCTL_CGOUTNAK | USB_DCTL_SGOUTNAK | USB_DCTL_CGNPINNAK | USB_DCTL_SGNPINNAK)
+#define GUSBCFG_WO_BITMASK (USB_GUSBCFG_CORRUPTTXPKT)
+#define DEPCTL_WO_BITMASK (USB_DIEP_CTL_CNAK | USB_DIEP_CTL_SNAK | USB_DIEP_CTL_SETD0PIDEF | USB_DIEP_CTL_SETD1PIDOF)
+
+/* Will either return an unused FIFO number, or 0 if all are used. */
+static uint8_t get_free_fifo(void)
+{
+ if(_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++;
+ return 0;
+}
+
+/*
+static void flush_rx_fifo(void)
+{
+ USB->GRSTCTL = USB_GRSTCTL_RXFFLSH;
+ while(USB->GRSTCTL & USB_GRSTCTL_RXFFLSH);
+}
+*/
+
+static void flush_tx_fifo(uint8_t fifo_num)
+{
+ USB->GRSTCTL = USB_GRSTCTL_TXFFLSH | (fifo_num << _USB_GRSTCTL_TXFNUM_SHIFT);
+ while(USB->GRSTCTL & USB_GRSTCTL_TXFFLSH);
+}
+
+/* Setup the control endpoint 0. */
+static void bus_reset(void)
+{
+ USB->DOEP0CTL |= USB_DIEP_CTL_SNAK;
+ for(uint8_t i = 0; i < EP_COUNT - 1; i++)
+ {
+ USB->DOEP[i].CTL |= USB_DIEP_CTL_SNAK;
+ }
+
+ /* reset address */
+ USB->DCFG &= ~_USB_DCFG_DEVADDR_MASK;
+
+ USB->DAINTMSK |= USB_DAINTMSK_OUTEPMSK0 | USB_DAINTMSK_INEPMSK0;
+ USB->DOEPMSK |= USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK;
+ USB->DIEPMSK |= USB_DIEPMSK_TIMEOUTMSK | USB_DIEPMSK_XFERCOMPLMSK;
+
+ /*
+ * - All EP OUT shared a unique OUT FIFO which uses
+ * * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets).
+ * * 2 locations for OUT endpoint control words.
+ * * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes)
+ * * 1 location for global NAK (not required/used here).
+ * * It is recommended to allocate 2 times the largest packet size, therefore
+ * Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52
+ */
+ flush_tx_fifo(_USB_GRSTCTL_TXFNUM_FALL); // Flush All
+ USB->GRXFSIZ = 52;
+
+ /* Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) */
+ USB->GNPTXFSIZ = (16 << _USB_GNPTXFSIZ_NPTXFINEPTXF0DEP_SHIFT) | (USB->GRXFSIZ & _USB_GNPTXFSIZ_NPTXFSTADDR_MASK);
+
+ /* Ready to receive SETUP packet */
+ USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_SUPCNT_SHIFT);
+
+ USB->GINTMSK |= USB_GINTMSK_IEPINTMSK | USB_GINTMSK_OEPINTMSK;
+}
+
+static void enum_done_processing(void)
+{
+ /* Maximum packet size for EP 0 is set for both directions by writing DIEPCTL */
+ if((USB->DSTS & _USB_DSTS_ENUMSPD_MASK) == USB_DSTS_ENUMSPD_FS)
+ {
+ /* Full Speed (PHY on 48 MHz) */
+ USB->DOEP0CTL = (USB->DOEP0CTL & ~_USB_DOEP0CTL_MPS_MASK) | _USB_DOEP0CTL_MPS_64B; /* Maximum Packet Size 64 bytes */
+ USB->DOEP0CTL &= ~_USB_DOEP0CTL_STALL_MASK; /* clear Stall */
+ xfer_status[0][TUSB_DIR_OUT].max_size = 64;
+ xfer_status[0][TUSB_DIR_IN].max_size = 64;
+ }
+ else
+ {
+ /* Low Speed (PHY on 6 MHz) */
+ USB->DOEP0CTL = (USB->DOEP0CTL & ~_USB_DOEP0CTL_MPS_MASK) | _USB_DOEP0CTL_MPS_8B; /* Maximum Packet Size 64 bytes */
+ USB->DOEP0CTL &= ~_USB_DOEP0CTL_STALL_MASK; /* clear Stall */
+ xfer_status[0][TUSB_DIR_OUT].max_size = 8;
+ xfer_status[0][TUSB_DIR_IN].max_size = 8;
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+/* Controller API */
+/*------------------------------------------------------------------*/
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ /* Reset Core */
+ USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK;
+ USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE);
+
+ /* Core Soft Reset */
+ USB->GRSTCTL |= USB_GRSTCTL_CSFTRST;
+ while(USB->GRSTCTL & USB_GRSTCTL_CSFTRST);
+
+ while(!(USB->GRSTCTL & USB_GRSTCTL_AHBIDLE));
+
+ /* Enable PHY pins */
+ USB->ROUTE = USB_ROUTE_PHYPEN;
+
+ dcd_disconnect(rhport);
+
+ /*
+ * Set device speed (Full speed PHY)
+ * Stall on non-zero len status OUT packets (ctrl transfers)
+ * periodic frame interval to 80%
+ */
+ USB->DCFG = (USB->DCFG & ~(_USB_DCFG_DEVSPD_MASK | _USB_DCFG_PERFRINT_MASK)) | USB_DCFG_DEVSPD_FS | USB_DCFG_NZSTSOUTHSHK;
+
+ /* Enable Global Interrupts */
+ USB->GAHBCFG = (USB->GAHBCFG & ~_USB_GAHBCFG_HBSTLEN_MASK) | USB_GAHBCFG_GLBLINTRMSK;
+
+ /* Force Device Mode */
+ USB->GUSBCFG = (USB->GUSBCFG & ~(GUSBCFG_WO_BITMASK | USB_GUSBCFG_FORCEHSTMODE)) | USB_GUSBCFG_FORCEDEVMODE;
+
+ /* No Overrides */
+ USB->GOTGCTL &= ~(USB_GOTGCTL_BVALIDOVVAL | USB_GOTGCTL_BVALIDOVEN | USB_GOTGCTL_VBVALIDOVVAL);
+
+ /* Ignore frame numbers on ISO transfers. */
+ USB->DCTL = (USB->DCTL & ~DCTL_WO_BITMASK) | USB_DCTL_IGNRFRMNUM;
+
+ /* Setting SNAKs */
+ USB->DOEP0CTL |= USB_DIEP_CTL_SNAK;
+ for(uint8_t i = 0; i < EP_COUNT - 1; i++)
+ {
+ USB->DOEP[i].CTL |= USB_DIEP_CTL_SNAK;
+ }
+
+ /* D. Interruption masking */
+ /* Disable all device interrupts */
+ USB->DIEPMSK = 0;
+ USB->DOEPMSK = 0;
+ USB->DAINTMSK = 0;
+ USB->DIEPEMPMSK = 0;
+ USB->GINTMSK = 0;
+ USB->GOTGINT = ~0U; /* clear OTG ints */
+ USB->GINTSTS = ~0U; /* clear pending ints */
+ USB->GINTMSK = USB_GINTMSK_MODEMISMSK |
+ #if USE_SOF
+ USB_GINTMSK_SOFMSK |
+ #endif
+ USB_GINTMSK_ERLYSUSPMSK |
+ USB_GINTMSK_USBSUSPMSK |
+ USB_GINTMSK_USBRSTMSK |
+ USB_GINTMSK_ENUMDONEMSK |
+ USB_GINTMSK_RESETDETMSK |
+ USB_GINTMSK_DISCONNINTMSK;
+
+ NVIC_ClearPendingIRQ(USB_IRQn);
+
+ dcd_connect(rhport);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+
+ USB->DCFG = (USB->DCFG & ~_USB_DCFG_DEVADDR_MASK) | (dev_addr << _USB_DCFG_DEVADDR_SHIFT);
+
+ /* Response with status after changing device address */
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+
+ /* connect by enabling internal pull-up resistor on D+/D- */
+ USB->DCTL &= ~(DCTL_WO_BITMASK | USB_DCTL_SFTDISCON);
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+
+ /* disconnect by disabling internal pull-up resistor on D+/D- */
+ USB->DCTL = (USB->DCTL & ~(DCTL_WO_BITMASK)) | USB_DCTL_SFTDISCON;
+}
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint Port */
+/*------------------------------------------------------------------*/
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if(dir == TUSB_DIR_IN)
+ {
+ if(epnum == 0)
+ {
+ USB->DIEP0CTL = (USB->DIEP0CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP0CTL_SNAK | USB_DIEP0CTL_STALL;
+
+ flush_tx_fifo(_USB_GRSTCTL_TXFNUM_F0);
+ }
+ else
+ {
+ /* Only disable currently enabled non-control endpoint */
+ if(USB->DIEP[epnum - 1].CTL & USB_DIEP_CTL_EPENA)
+ {
+ USB->DIEP[epnum - 1].CTL = (USB->DIEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_EPDIS | USB_DIEP_CTL_SNAK | USB_DIEP_CTL_STALL;
+ while(!(USB->DIEP[epnum - 1].INT & USB_DIEP_INT_EPDISBLD));
+ USB->DIEP[epnum - 1].INT |= USB_DIEP_INT_EPDISBLD;
+ }
+ else
+ {
+ USB->DIEP[epnum - 1].CTL = (USB->DIEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_SNAK | USB_DIEP_CTL_STALL;
+ }
+
+ /* Flush the FIFO */
+ uint8_t const fifo_num = ((USB->DIEP[epnum - 1].CTL & _USB_DIEP_CTL_TXFNUM_MASK) >> _USB_DIEP_CTL_TXFNUM_SHIFT);
+ flush_tx_fifo(fifo_num);
+ }
+ }
+ else
+ {
+ if(epnum == 0)
+ {
+ USB->DOEP0CTL = (USB->DOEP0CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP0CTL_STALL;
+ }
+ else
+ {
+ /* Only disable currently enabled non-control endpoint */
+ if(USB->DOEP[epnum - 1].CTL & USB_DIEP_CTL_EPENA)
+ {
+ /* Asserting GONAK is required to STALL an OUT endpoint. */
+ USB->DCTL |= USB_DCTL_SGOUTNAK;
+ while(!(USB->GINTSTS & USB_GINTSTS_GOUTNAKEFF));
+
+ /* Disable the endpoint. Note that only STALL and not SNAK is set here. */
+ USB->DOEP[epnum - 1].CTL = (USB->DOEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_EPDIS | USB_DIEP_CTL_STALL;
+ while(USB->DOEP[epnum - 1].INT & USB_DIEP_INT_EPDISBLD);
+ USB->DOEP[epnum - 1].INT |= USB_DIEP_INT_EPDISBLD;
+
+ /* Allow other OUT endpoints to keep receiving. */
+ USB->DCTL |= USB_DCTL_CGOUTNAK;
+ }
+ else
+ {
+ USB->DIEP[epnum - 1].CTL = (USB->DIEP[epnum - 1].CTL & ~DEPCTL_WO_BITMASK) | USB_DIEP_CTL_STALL;
+ }
+ }
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if(dir == TUSB_DIR_IN)
+ {
+ if(epnum == 0)
+ {
+ USB->DIEP0CTL &= ~(DEPCTL_WO_BITMASK | USB_DIEP0CTL_STALL);
+ }
+ else
+ {
+ USB->DIEP[epnum - 1].CTL &= ~(DEPCTL_WO_BITMASK | USB_DIEP_CTL_STALL);
+
+ /* Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints. */
+ uint8_t eptype = (USB->DIEP[epnum - 1].CTL & _USB_DIEP_CTL_EPTYPE_MASK) >> _USB_DIEP_CTL_EPTYPE_SHIFT;
+
+ if((eptype == _USB_DIEP_CTL_EPTYPE_BULK) || (eptype == _USB_DIEP_CTL_EPTYPE_INT))
+ {
+ USB->DIEP[epnum - 1].CTL |= USB_DIEP_CTL_SETD0PIDEF;
+ }
+ }
+ }
+ else
+ {
+ if(epnum == 0)
+ {
+ USB->DOEP0CTL &= ~(DEPCTL_WO_BITMASK | USB_DOEP0CTL_STALL);
+ }
+ else
+ {
+ USB->DOEP[epnum - 1].CTL &= ~(DEPCTL_WO_BITMASK | USB_DOEP_CTL_STALL);
+
+ /* Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints. */
+ uint8_t eptype = (USB->DOEP[epnum - 1].CTL & _USB_DOEP_CTL_EPTYPE_MASK) >> _USB_DOEP_CTL_EPTYPE_SHIFT;
+
+ if((eptype == _USB_DOEP_CTL_EPTYPE_BULK) || (eptype == _USB_DOEP_CTL_EPTYPE_INT))
+ {
+ USB->DOEP[epnum - 1].CTL |= USB_DOEP_CTL_SETD0PIDEF;
+ }
+ }
+ }
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void)rhport;
+
+ uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+
+ TU_ASSERT(p_endpoint_desc->wMaxPacketSize.size <= 64);
+ TU_ASSERT(epnum < EP_COUNT);
+ TU_ASSERT(epnum != 0);
+
+ xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->max_size = p_endpoint_desc->wMaxPacketSize.size;
+
+ if(dir == TUSB_DIR_OUT)
+ {
+ USB->DOEP[epnum - 1].CTL |= USB_DOEP_CTL_USBACTEP |
+ (p_endpoint_desc->bmAttributes.xfer << _USB_DOEP_CTL_EPTYPE_SHIFT) |
+ (p_endpoint_desc->wMaxPacketSize.size << _USB_DOEP_CTL_MPS_SHIFT);
+ USB->DAINTMSK |= (1 << (_USB_DAINTMSK_OUTEPMSK0_SHIFT + epnum));
+ }
+ else
+ {
+ uint8_t fifo_num = get_free_fifo();
+ TU_ASSERT(fifo_num != 0);
+
+ USB->DIEP[epnum - 1].CTL &= ~(_USB_DIEP_CTL_TXFNUM_MASK | _USB_DIEP_CTL_EPTYPE_MASK | USB_DIEP_CTL_SETD0PIDEF | _USB_DIEP_CTL_MPS_MASK);
+ USB->DIEP[epnum - 1].CTL |= USB_DIEP_CTL_USBACTEP |
+ (fifo_num << _USB_DIEP_CTL_TXFNUM_SHIFT) |
+ (p_endpoint_desc->bmAttributes.xfer << _USB_DIEP_CTL_EPTYPE_SHIFT) |
+ ((p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) ? USB_DIEP_CTL_SETD0PIDEF : 0) |
+ (p_endpoint_desc->wMaxPacketSize.size << 0);
+
+ USB->DAINTMSK |= (1 << epnum);
+
+ /* Both TXFD and TXSA are in unit of 32-bit words. */
+ /* IN FIFO 0 was configured during enumeration, hence the "+ 16". */
+ uint16_t const allocated_size = (USB->GRXFSIZ & _USB_GRXFSIZ_RXFDEP_MASK) + 16;
+ uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1);
+ uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1);
+
+ /* DIEPTXF starts at FIFO #1. */
+ volatile uint32_t* usb_dieptxf = &USB->DIEPTXF1;
+ usb_dieptxf[epnum - 1] = (fifo_size << _USB_DIEPTXF1_INEPNTXFDEP_SHIFT) | fifo_offset;
+ }
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ (void)rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->short_packet = false;
+
+ uint16_t num_packets = (total_bytes / xfer->max_size);
+ uint8_t short_packet_size = total_bytes % xfer->max_size;
+
+ // Zero-size packet is special case.
+ if(short_packet_size > 0 || (total_bytes == 0))
+ {
+ num_packets++;
+ }
+
+ // IN and OUT endpoint xfers are interrupt-driven, we just schedule them
+ // here.
+ if(dir == TUSB_DIR_IN)
+ {
+ if(epnum == 0)
+ {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ USB->DIEP0TSIZ = (num_packets << _USB_DIEP0TSIZ_PKTCNT_SHIFT) | total_bytes;
+ USB->DIEP0CTL |= USB_DIEP0CTL_EPENA | USB_DIEP0CTL_CNAK; // Enable | CNAK
+ }
+ else
+ {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ USB->DIEP[epnum - 1].TSIZ = (num_packets << _USB_DIEP_TSIZ_PKTCNT_SHIFT) | total_bytes;
+ USB->DIEP[epnum - 1].CTL |= USB_DIEP_CTL_EPENA | USB_DIEP_CTL_CNAK; // Enable | CNAK
+ }
+
+ // Enable fifo empty interrupt only if there are something to put in the fifo.
+ if(total_bytes != 0)
+ {
+ USB->DIEPEMPMSK |= (1 << epnum);
+ }
+ }
+ else
+ {
+ if(epnum == 0)
+ {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP0TSIZ_XFERSIZE_MASK) << _USB_DOEP0TSIZ_XFERSIZE_SHIFT);
+ USB->DOEP0CTL |= USB_DOEP0CTL_EPENA | USB_DOEP0CTL_CNAK;
+ }
+ else
+ {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ USB->DOEP[epnum - 1].TSIZ |= (1 << _USB_DOEP_TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP_TSIZ_XFERSIZE_MASK) << _USB_DOEP_TSIZ_XFERSIZE_SHIFT);
+ USB->DOEP[epnum - 1].CTL |= USB_DOEP_CTL_EPENA | USB_DOEP_CTL_CNAK;
+ }
+ }
+ return true;
+}
+
+/*------------------------------------------------------------------*/
+/* IRQ */
+/*------------------------------------------------------------------*/
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+static void receive_packet(xfer_ctl_t *xfer, uint16_t xfer_size)
+{
+ uint16_t remaining = xfer->total_len - xfer->queued_len;
+ uint16_t to_recv_size;
+
+ if(remaining <= xfer->max_size)
+ {
+ /* Avoid buffer overflow. */
+ to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
+ }
+ else
+ {
+ /* Room for full packet, choose recv_size based on what the microcontroller claims. */
+ to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
+ }
+
+ uint8_t to_recv_rem = to_recv_size % 4;
+ uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
+
+ /* Do not assume xfer buffer is aligned. */
+ uint8_t *base = (xfer->buffer + xfer->queued_len);
+
+ /* This for loop always runs at least once- skip if less than 4 bytes to collect. */
+ if(to_recv_size >= 4)
+ {
+ for(uint16_t i = 0; i < to_recv_size_aligned; i += 4)
+ {
+ uint32_t tmp = (*USB->FIFO0D);
+ base[i] = tmp & 0x000000FF;
+ base[i + 1] = (tmp & 0x0000FF00) >> 8;
+ base[i + 2] = (tmp & 0x00FF0000) >> 16;
+ base[i + 3] = (tmp & 0xFF000000) >> 24;
+ }
+ }
+
+ /* Do not read invalid bytes from RX FIFO. */
+ if(to_recv_rem != 0)
+ {
+ uint32_t tmp = (*USB->FIFO0D);
+ uint8_t *last_32b_bound = base + to_recv_size_aligned;
+
+ last_32b_bound[0] = tmp & 0x000000FF;
+ if(to_recv_rem > 1)
+ {
+ last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
+ }
+ if(to_recv_rem > 2)
+ {
+ last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
+ }
+ }
+
+ xfer->queued_len += xfer_size;
+
+ /* Per USB spec, a short OUT packet (including length 0) is always */
+ /* indicative of the end of a transfer (at least for ctl, bulk, int). */
+ xfer->short_packet = (xfer_size < xfer->max_size);
+}
+
+static void transmit_packet(xfer_ctl_t *xfer, uint8_t fifo_num)
+{
+ uint16_t remaining;
+ if(fifo_num == 0)
+ {
+ remaining = (USB->DIEP0TSIZ & 0x7FFFFU) >> _USB_DIEP0TSIZ_XFERSIZE_SHIFT;
+ }
+ else
+ {
+ remaining = (USB->DIEP[fifo_num - 1].TSIZ & 0x7FFFFU) >> _USB_DIEP_TSIZ_XFERSIZE_SHIFT;
+ }
+ xfer->queued_len = xfer->total_len - remaining;
+
+ uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
+ uint8_t to_xfer_rem = to_xfer_size % 4;
+ uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
+
+ /* Buffer might not be aligned to 32b, so we need to force alignment by copying to a temp var. */
+ uint8_t *base = (xfer->buffer + xfer->queued_len);
+
+ /* This for loop always runs at least once- skip if less than 4 bytes to send off. */
+ if(to_xfer_size >= 4)
+ {
+ for(uint16_t i = 0; i < to_xfer_size_aligned; i += 4)
+ {
+ uint32_t tmp = base[i] | (base[i + 1] << 8) | (base[i + 2] << 16) | (base[i + 3] << 24);
+ *tx_fifo[fifo_num] = tmp;
+ }
+ }
+
+ /* Do not read beyond end of buffer if not divisible by 4. */
+ if(to_xfer_rem != 0)
+ {
+ uint32_t tmp = 0;
+ uint8_t *last_32b_bound = base + to_xfer_size_aligned;
+
+ tmp |= last_32b_bound[0];
+ if(to_xfer_rem > 1)
+ {
+ tmp |= (last_32b_bound[1] << 8);
+ }
+ if(to_xfer_rem > 2)
+ {
+ tmp |= (last_32b_bound[2] << 16);
+ }
+
+ *tx_fifo[fifo_num] = tmp;
+ }
+}
+
+static void read_rx_fifo(void)
+{
+ /*
+ * Pop control word off FIFO (completed xfers will have 2 control words,
+ * we only pop one ctl word each interrupt).
+ */
+ uint32_t const ctl_word = USB->GRXSTSP;
+ uint8_t const pktsts = (ctl_word & _USB_GRXSTSP_PKTSTS_MASK) >> _USB_GRXSTSP_PKTSTS_SHIFT;
+ uint8_t const epnum = (ctl_word & _USB_GRXSTSP_CHNUM_MASK ) >> _USB_GRXSTSP_CHNUM_SHIFT;
+ uint16_t const bcnt = (ctl_word & _USB_GRXSTSP_BCNT_MASK ) >> _USB_GRXSTSP_BCNT_SHIFT;
+
+ switch(pktsts)
+ {
+ case 0x01: /* Global OUT NAK (Interrupt) */
+ break;
+
+ case 0x02:
+ {
+ /* Out packet recvd */
+ xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
+ receive_packet(xfer, bcnt);
+ }
+ break;
+
+ case 0x03:
+ /* Out packet done (Interrupt) */
+ break;
+
+ case 0x04:
+ /* Step 2: Setup transaction completed (Interrupt) */
+ /* After this event, OEPINT interrupt will occur with SETUP bit set */
+ if(epnum == 0)
+ {
+ USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_SUPCNT_SHIFT);
+ }
+
+ break;
+
+ case 0x06:
+ {
+ /* Step1: Setup data packet received */
+
+ /*
+ * We can receive up to three setup packets in succession, but
+ * only the last one is valid. Therefore we just overwrite it
+ */
+ _setup_packet[0] = (*USB->FIFO0D);
+ _setup_packet[1] = (*USB->FIFO0D);
+ }
+ break;
+
+ default:
+ /* Invalid, breakpoint. */
+ TU_BREAKPOINT();
+ break;
+ }
+}
+
+static void handle_epout_ints(void)
+{
+ // GINTSTS will be cleared with DAINT == 0
+ // DAINT for a given EP clears when DOEPINTx is cleared.
+ // DOEPINT will be cleared when DAINT's out bits are cleared.
+
+ for(uint8_t n = 0; n < EP_COUNT; n++)
+ {
+ xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
+
+ if(n == 0)
+ {
+ if(USB->DAINT & (1 << (_USB_DAINT_OUTEPINT0_SHIFT + n)))
+ {
+ // SETUP packet Setup Phase done.
+ if((USB->DOEP0INT & USB_DOEP0INT_SETUP))
+ {
+ USB->DOEP0INT = USB_DOEP0INT_STUPPKTRCVD | USB_DOEP0INT_SETUP; // clear
+ dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
+ }
+
+ // OUT XFER complete (single packet).q
+ if(USB->DOEP0INT & USB_DOEP0INT_XFERCOMPL)
+ {
+ USB->DOEP0INT = USB_DOEP0INT_XFERCOMPL;
+
+ // Transfer complete if short packet or total len is transferred
+ if(xfer->short_packet || (xfer->queued_len == xfer->total_len))
+ {
+ xfer->short_packet = false;
+ dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Schedule another packet to be received.
+ USB->DOEP0TSIZ |= (1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP0TSIZ_XFERSIZE_MASK) << _USB_DOEP0TSIZ_XFERSIZE_SHIFT);
+ USB->DOEP0CTL |= USB_DOEP0CTL_EPENA | USB_DOEP0CTL_CNAK;
+ }
+ }
+ }
+ }
+ else
+ {
+ if(USB->DAINT & (1 << (_USB_DAINT_OUTEPINT0_SHIFT + n)))
+ {
+ // SETUP packet Setup Phase done.
+ if((USB->DOEP[n - 1].INT & USB_DOEP_INT_SETUP))
+ {
+ USB->DOEP[n - 1].INT = USB_DOEP_INT_STUPPKTRCVD | USB_DOEP_INT_SETUP; // clear
+ dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
+ }
+
+ // OUT XFER complete (single packet).q
+ if(USB->DOEP[n - 1].INT & USB_DOEP_INT_XFERCOMPL)
+ {
+ USB->DOEP[n - 1].INT = USB_DOEP_INT_XFERCOMPL;
+
+ // Transfer complete if short packet or total len is transferred
+ if(xfer->short_packet || (xfer->queued_len == xfer->total_len))
+ {
+ xfer->short_packet = false;
+ dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Schedule another packet to be received.
+ USB->DOEP[n - 1].TSIZ |= (1 << _USB_DOEP_TSIZ_PKTCNT_SHIFT) | ((xfer->max_size & _USB_DOEP_TSIZ_XFERSIZE_MASK) << _USB_DOEP_TSIZ_XFERSIZE_SHIFT);
+ USB->DOEP[n - 1].CTL |= USB_DOEP_CTL_EPENA | USB_DOEP_CTL_CNAK;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void handle_epin_ints(void)
+{
+
+ for(uint32_t n = 0; n < EP_COUNT; n++)
+ {
+ xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN];
+
+ if(n == 0)
+ {
+ if(USB->DAINT & (1 << n))
+ {
+ /* IN XFER complete (entire xfer). */
+ if(USB->DIEP0INT & USB_DIEP0INT_XFERCOMPL)
+ {
+ USB->DIEP0INT = USB_DIEP0INT_XFERCOMPL;
+ dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+
+ /* XFER FIFO empty */
+ if(USB->DIEP0INT & USB_DIEP0INT_TXFEMP)
+ {
+ USB->DIEP0INT = USB_DIEP0INT_TXFEMP;
+ transmit_packet(xfer, n);
+
+ /* Turn off TXFE if all bytes are written. */
+ if(xfer->queued_len == xfer->total_len)
+ {
+ USB->DIEPEMPMSK &= ~(1 << n);
+ }
+ }
+
+ /* XFER Timeout */
+ if(USB->DIEP0INT & USB_DIEP0INT_TIMEOUT)
+ {
+ /* Clear interrupt or enpoint will hang. */
+ USB->DIEP0INT = USB_DIEP0INT_TIMEOUT;
+ }
+ }
+ }
+ else
+ {
+ if(USB->DAINT & (1 << n))
+ {
+ /* IN XFER complete (entire xfer). */
+ if(USB->DIEP[n - 1].INT & USB_DIEP_INT_XFERCOMPL)
+ {
+ USB->DIEP[n - 1].INT = USB_DIEP_INT_XFERCOMPL;
+ dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+
+ /* XFER FIFO empty */
+ if(USB->DIEP[n - 1].INT & USB_DIEP_INT_TXFEMP)
+ {
+ USB->DIEP[n - 1].INT = USB_DIEP_INT_TXFEMP;
+ transmit_packet(xfer, n);
+
+ /* Turn off TXFE if all bytes are written. */
+ if(xfer->queued_len == xfer->total_len)
+ {
+ USB->DIEPEMPMSK &= ~(1 << n);
+ }
+ }
+
+ /* XFER Timeout */
+ if(USB->DIEP[n - 1].INT & USB_DIEP_INT_TIMEOUT)
+ {
+ /* Clear interrupt or enpoint will hang. */
+ USB->DIEP[n - 1].INT = USB_DIEP_INT_TIMEOUT;
+ }
+ }
+ }
+ }
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ const uint32_t int_status = USB->GINTSTS;
+
+ /* USB Reset */
+ if(int_status & USB_GINTSTS_USBRST)
+ {
+ /* start of reset */
+ USB->GINTSTS = USB_GINTSTS_USBRST;
+ /* FIFOs will be reassigned when the endpoints are reopen */
+ _allocated_fifos = 1;
+ bus_reset();
+ }
+
+ /* Reset detected Interrupt */
+ if(int_status & USB_GINTSTS_RESETDET)
+ {
+ USB->GINTSTS = USB_GINTSTS_RESETDET;
+ bus_reset();
+ }
+
+ /* Enumeration Done */
+ if(int_status & USB_GINTSTS_ENUMDONE)
+ {
+ /* This interrupt is considered the end of reset. */
+ USB->GINTSTS = USB_GINTSTS_ENUMDONE;
+ enum_done_processing();
+ dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+ }
+
+ /* OTG Interrupt */
+ if(int_status & USB_GINTSTS_OTGINT)
+ {
+ /* OTG INT bit is read-only */
+
+ uint32_t const otg_int = USB->GOTGINT;
+
+ if(otg_int & USB_GOTGINT_SESENDDET)
+ {
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
+ }
+
+ USB->GOTGINT = otg_int;
+ }
+
+ #if USE_SOF
+ if(int_status & USB_GINTSTS_SOF)
+ {
+ USB->GINTSTS = USB_GINTSTS_SOF;
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+ #endif
+
+ /* RxFIFO Non-Empty */
+ if(int_status & USB_GINTSTS_RXFLVL)
+ {
+ /* RXFLVL bit is read-only */
+
+ /* Mask out RXFLVL while reading data from FIFO */
+ USB->GINTMSK &= ~USB_GINTMSK_RXFLVLMSK;
+ read_rx_fifo();
+ USB->GINTMSK |= USB_GINTMSK_RXFLVLMSK;
+ }
+
+ /* OUT Endpoints Interrupt */
+ if(int_status & USB_GINTMSK_OEPINTMSK)
+ {
+ /* OEPINT is read-only */
+ handle_epout_ints();
+ }
+
+ /* IN Endpoints Interrupt */
+ if(int_status & USB_GINTMSK_IEPINTMSK)
+ {
+ /* IEPINT bit read-only */
+ handle_epin_ints();
+ }
+
+ /* unhandled */
+ USB->GINTSTS |= USB_GINTSTS_CURMOD |
+ USB_GINTSTS_MODEMIS |
+ USB_GINTSTS_OTGINT |
+ USB_GINTSTS_NPTXFEMP |
+ USB_GINTSTS_GINNAKEFF |
+ USB_GINTSTS_GOUTNAKEFF |
+ USB_GINTSTS_ERLYSUSP |
+ USB_GINTSTS_USBSUSP |
+ USB_GINTSTS_ISOOUTDROP |
+ USB_GINTSTS_EOPF |
+ USB_GINTSTS_EPMIS |
+ USB_GINTSTS_INCOMPISOIN |
+ USB_GINTSTS_INCOMPLP |
+ USB_GINTSTS_FETSUSP |
+ USB_GINTSTS_PTXFEMP;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/sony/cxd56/dcd_cxd56.c b/sw/Core/Src/tinyusb/src/portable/sony/cxd56/dcd_cxd56.c
new file mode 100755
index 0000000..5495818
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/sony/cxd56/dcd_cxd56.c
@@ -0,0 +1,409 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright 2019 Sony Semiconductor Solutions Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_CXD56
+
+#include <errno.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/arch.h>
+
+#include "device/dcd.h"
+#include "osal/osal.h"
+
+#define CXD56_EPNUM (7)
+#define CXD56_SETUP_QUEUE_DEPTH (4)
+#define CXD56_MAX_DATA_OUT_SIZE (64)
+
+OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _setup_queue_def, CXD56_SETUP_QUEUE_DEPTH, struct usb_ctrlreq_s);
+
+struct usbdcd_driver_s
+{
+ struct usbdevclass_driver_s usbdevclass_driver;
+ FAR struct usbdev_ep_s *ep[CXD56_EPNUM];
+ FAR struct usbdev_req_s *req[CXD56_EPNUM];
+ osal_queue_t setup_queue;
+ bool setup_processed;
+ FAR uint8_t dataout[CXD56_MAX_DATA_OUT_SIZE];
+ size_t outlen;
+};
+
+static struct usbdcd_driver_s usbdcd_driver;
+static struct usbdev_s *usbdev;
+
+static int _dcd_bind (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
+static void _dcd_unbind (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
+static int _dcd_setup (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen);
+static void _dcd_disconnect (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
+static void _dcd_suspend (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
+static void _dcd_resume (FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev);
+
+static const struct usbdevclass_driverops_s g_driverops =
+{
+ _dcd_bind, /* bind */
+ _dcd_unbind, /* unbind */
+ _dcd_setup, /* setup */
+ _dcd_disconnect, /* disconnect */
+ _dcd_suspend, /* suspend */
+ _dcd_resume, /* resume */
+};
+
+static void usbdcd_ep0incomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
+{
+ (void) ep;
+
+ uint8_t ep_addr = (uint32_t)req->priv;
+
+ if (req->result || req->xfrd != req->len)
+ {
+ if (req->len)
+ {
+ dcd_event_xfer_complete(0, ep_addr, req->xfrd, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ else
+ {
+ if (req->xfrd)
+ {
+ dcd_event_xfer_complete(0, ep_addr, req->xfrd, XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+
+static int _dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
+{
+ (void) driver;
+
+ usbdev = dev;
+ usbdcd_driver.ep[0] = dev->ep0;
+
+ usbdcd_driver.req[0] = EP_ALLOCREQ(usbdcd_driver.ep[0]);
+ if (usbdcd_driver.req[0] != NULL)
+ {
+ usbdcd_driver.req[0]->len = 64;
+ usbdcd_driver.req[0]->buf = EP_ALLOCBUFFER(usbdcd_driver.ep[0], 64);
+ if (!usbdcd_driver.req[0]->buf)
+ {
+ EP_FREEREQ(usbdcd_driver.ep[0], usbdcd_driver.req[0]);
+ usbdcd_driver.req[0] = NULL;
+ }
+ }
+
+ usbdcd_driver.req[0]->callback = usbdcd_ep0incomplete;
+
+ DEV_CONNECT(dev);
+ return 0;
+}
+
+static void _dcd_unbind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
+{
+ (void) driver;
+ (void) dev;
+}
+
+static int _dcd_setup(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev,
+ FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout, size_t outlen)
+{
+ (void) driver;
+ (void) dev;
+
+ if (usbdcd_driver.setup_processed)
+ {
+ usbdcd_driver.setup_processed = false;
+ dcd_event_setup_received(0, (uint8_t *) ctrl, true);
+ }
+ else
+ {
+ osal_queue_send(usbdcd_driver.setup_queue, ctrl, true);
+ }
+
+ if (outlen > 0 && outlen <= CXD56_MAX_DATA_OUT_SIZE)
+ {
+ memcpy(usbdcd_driver.dataout, dataout, outlen);
+ usbdcd_driver.outlen = outlen;
+ }
+
+ return 0;
+}
+
+static void _dcd_disconnect(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
+{
+ (void) driver;
+
+ tusb_speed_t speed;
+
+ switch (dev->speed)
+ {
+ case USB_SPEED_LOW:
+ speed = TUSB_SPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ speed = TUSB_SPEED_FULL;
+ break;
+ case USB_SPEED_HIGH:
+ speed = TUSB_SPEED_HIGH;
+ break;
+ default:
+ speed = TUSB_SPEED_HIGH;
+ break;
+ }
+
+ dcd_event_bus_reset(0, speed, true);
+ DEV_CONNECT(dev);
+}
+
+static void _dcd_suspend(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
+{
+ (void) driver;
+ (void) dev;
+
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+}
+
+static void _dcd_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_s *dev)
+{
+ (void) driver;
+ (void) dev;
+
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+}
+
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ usbdcd_driver.usbdevclass_driver.speed = USB_SPEED_HIGH;
+ usbdcd_driver.usbdevclass_driver.ops = &g_driverops;
+ usbdcd_driver.setup_processed = true;
+ usbdcd_driver.setup_queue = osal_queue_create(&_setup_queue_def);
+
+ usbdev_register(&usbdcd_driver.usbdevclass_driver);
+}
+
+// Enable device interrupt
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+
+ up_enable_irq(CXD56_IRQ_USB_INT);
+}
+
+// Disable device interrupt
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+
+ up_disable_irq(CXD56_IRQ_USB_INT);
+}
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ (void) dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+
+ DEV_WAKEUP(usbdev);
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ DEV_CONNECT(usbdev);
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ DEV_DISCONNECT(usbdev);
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc)
+{
+ (void) rhport;
+
+ uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ uint8_t xfrtype = 0;
+ struct usb_epdesc_s epdesc;
+
+ if (epnum >= CXD56_EPNUM)
+ {
+ return false;
+ }
+
+ switch (p_endpoint_desc->bmAttributes.xfer)
+ {
+ case 1:
+ xfrtype = USB_EP_ATTR_XFER_ISOC;
+ break;
+ case 2:
+ xfrtype = USB_EP_ATTR_XFER_BULK;
+ break;
+ case 3:
+ xfrtype = USB_EP_ATTR_XFER_INT;
+ break;
+ }
+
+ usbdcd_driver.ep[epnum] = DEV_ALLOCEP(usbdev, epnum, dir == TUSB_DIR_IN, xfrtype);
+ if (usbdcd_driver.ep[epnum] == NULL)
+ {
+ return false;
+ }
+
+ usbdcd_driver.req[epnum] = NULL;
+ usbdcd_driver.req[epnum] = EP_ALLOCREQ(usbdcd_driver.ep[epnum]);
+ if (usbdcd_driver.req[epnum] != NULL)
+ {
+ usbdcd_driver.req[epnum]->len = p_endpoint_desc->wMaxPacketSize.size;
+ }
+ else
+ {
+ return false;
+ }
+
+ usbdcd_driver.req[epnum]->callback = usbdcd_ep0incomplete;
+
+ epdesc.len = p_endpoint_desc->bLength;
+ epdesc.type = p_endpoint_desc->bDescriptorType;
+ epdesc.addr = p_endpoint_desc->bEndpointAddress;
+ epdesc.attr = xfrtype;
+ epdesc.mxpacketsize[0] = LSBYTE(p_endpoint_desc->wMaxPacketSize.size);
+ epdesc.mxpacketsize[1] = MSBYTE(p_endpoint_desc->wMaxPacketSize.size);
+ epdesc.interval = p_endpoint_desc->bInterval;
+
+ if (EP_CONFIGURE(usbdcd_driver.ep[epnum], &epdesc, false) < 0)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ bool ret = true;
+ uint8_t epnum = tu_edpt_number(ep_addr);
+
+ if (epnum >= CXD56_EPNUM)
+ {
+ return false;
+ }
+
+ if (epnum == 0)
+ {
+ if (total_bytes == 0)
+ {
+ usbdcd_driver.setup_processed = true;
+ dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false);
+ }
+ else if (ep_addr == 0x00 && total_bytes == usbdcd_driver.outlen)
+ {
+ memcpy(buffer, usbdcd_driver.dataout, usbdcd_driver.outlen);
+ dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false);
+ usbdcd_driver.outlen = 0;
+ }
+ else
+ {
+ usbdcd_driver.req[epnum]->len = total_bytes;
+ usbdcd_driver.req[epnum]->priv = (void *)((uint32_t)ep_addr);
+ usbdcd_driver.req[epnum]->flags = total_bytes < usbdcd_driver.ep[epnum]->maxpacket ? USBDEV_REQFLAGS_NULLPKT : 0;
+ usbdcd_driver.req[epnum]->buf = buffer;
+
+ if (EP_SUBMIT(usbdcd_driver.ep[epnum], usbdcd_driver.req[epnum]) < 0)
+ {
+ ret = false;
+ }
+ }
+
+ struct usb_ctrlreq_s ctrl;
+
+ if (usbdcd_driver.setup_processed)
+ {
+ if (osal_queue_receive(usbdcd_driver.setup_queue, &ctrl))
+ {
+ usbdcd_driver.setup_processed = false;
+ dcd_event_setup_received(0, (uint8_t *)&ctrl, false);
+ }
+ }
+ }
+ else
+ {
+ usbdcd_driver.req[epnum]->len = total_bytes;
+ usbdcd_driver.req[epnum]->priv = (void *)((uint32_t)ep_addr);
+ usbdcd_driver.req[epnum]->flags = total_bytes < usbdcd_driver.ep[epnum]->maxpacket ? USBDEV_REQFLAGS_NULLPKT : 0;
+ usbdcd_driver.req[epnum]->buf = buffer;
+
+ if (EP_SUBMIT(usbdcd_driver.ep[epnum], usbdcd_driver.req[epnum]) < 0)
+ {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t epnum = tu_edpt_number(ep_addr);
+
+ if (epnum >= CXD56_EPNUM)
+ {
+ return;
+ }
+
+ EP_STALL(usbdcd_driver.ep[epnum]);
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t epnum = tu_edpt_number(ep_addr);
+
+ if (epnum >= CXD56_EPNUM)
+ {
+ return;
+ }
+
+ EP_RESUME(usbdcd_driver.ep[epnum]);
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/sw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
new file mode 100755
index 0000000..18f0bc8
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -0,0 +1,959 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Nathan Conrad
+ *
+ * Portions:
+ * Copyright (c) 2016 STMicroelectronics
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/**********************************************
+ * This driver has been tested with the following MCUs:
+ * - F070, F072, L053, F042F6
+ *
+ * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This
+ * covers:
+ *
+ * F04x, F072, F078, 070x6/B 1024 byte buffer
+ * F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?)
+ * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up
+ * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up
+ * L0x2, L0x3 1024 byte buffer
+ * L1 512 byte buffer
+ * L4x2, L4x3 1024 byte buffer
+ *
+ * To use this driver, you must:
+ * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS)
+ * - Remap pins to be D+/D- on devices that they are shared (for example: F042Fx)
+ * - This is different to the normal "alternate function" GPIO interface, needs to go through SYSCFG->CFGRx register
+ * - Enable USB clock; Perhaps use __HAL_RCC_USB_CLK_ENABLE();
+ * - (Optionally configure GPIO HAL to tell it the USB driver is using the USB pins)
+ * - call tusb_init();
+ * - periodically call tusb_task();
+ *
+ * Assumptions of the driver:
+ * - You are not using CAN (it must share the packet buffer)
+ * - APB clock is >= 10 MHz
+ * - On some boards, series resistors are required, but not on others.
+ * - On some boards, D+ pull up resistor (1.5kohm) is required, but not on others.
+ * - You don't have long-running interrupts; some USB packets must be quickly responded to.
+ * - You have the ST CMSIS library linked into the project. HAL is not used.
+ *
+ * Current driver limitations (i.e., a list of features for you to add):
+ * - STALL handled, but not tested.
+ * - Does it work? No clue.
+ * - All EP BTABLE buffers are created based on max packet size of first EP opened with that address.
+ * - No isochronous endpoints
+ * - Endpoint index is the ID of the endpoint
+ * - This means that priority is given to endpoints with lower ID numbers
+ * - Code is mixing up EP IX with EP ID. Everywhere.
+ * - Packet buffer memory is copied in the interrupt.
+ * - This is better for performance, but means interrupts are disabled for longer
+ * - DMA may be the best choice, but it could also be pushed to the USBD task.
+ * - No double-buffering
+ * - No DMA
+ * - Minimal error handling
+ * - Perhaps error interrupts should be reported to the stack, or cause a device reset?
+ * - Assumes a single USB peripheral; I think that no hardware has multiple so this is fine.
+ * - Add a callback for enabling/disabling the D+ PU on devices without an internal PU.
+ * - F3 models use three separate interrupts. I think we could only use the LP interrupt for
+ * everything? However, the interrupts are configurable so the DisableInt and EnableInt
+ * below functions could be adjusting the wrong interrupts (if they had been reconfigured)
+ * - LPM is not used correctly, or at all?
+ *
+ * USB documentation and Reference implementations
+ * - STM32 Reference manuals
+ * - STM32 USB Hardware Guidelines AN4879
+ *
+ * - STM32 HAL (much of this driver is based on this)
+ * - libopencm3/lib/stm32/common/st_usbfs_core.c
+ * - Keil USB Device http://www.keil.com/pack/doc/mw/USB/html/group__usbd.html
+ *
+ * - YouTube OpenTechLab 011; https://www.youtube.com/watch?v=4FOkJLp_PUw
+ *
+ * Advantages over HAL driver:
+ * - Tiny (saves RAM, assumes a single USB peripheral)
+ *
+ * Notes:
+ * - The buffer table is allocated as endpoints are opened. The allocation is only
+ * cleared when the device is reset. This may be bad if the USB device needs
+ * to be reconfigured.
+ */
+
+#include "tusb_option.h"
+
+#if defined(STM32F102x6) || defined(STM32F102xB) || \
+ defined(STM32F103x6) || defined(STM32F103xB) || \
+ defined(STM32F103xE) || defined(STM32F103xG)
+#define STM32F1_FSDEV
+#endif
+
+#if (TUSB_OPT_DEVICE_ENABLED) && ( \
+ (CFG_TUSB_MCU == OPT_MCU_STM32F0 ) || \
+ (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_FSDEV)) || \
+ (CFG_TUSB_MCU == OPT_MCU_STM32F3 ) || \
+ (CFG_TUSB_MCU == OPT_MCU_STM32L0 ) \
+ )
+
+// In order to reduce the dependance on HAL, we undefine this.
+// Some definitions are copied to our private include file.
+#undef USE_HAL_DRIVER
+
+#include "device/dcd.h"
+#include "portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h"
+
+
+/*****************************************************
+ * Configuration
+ *****************************************************/
+
+// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM
+// (8u here would mean 8 IN and 8 OUT)
+#ifndef MAX_EP_COUNT
+# define MAX_EP_COUNT 8U
+#endif
+
+// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
+// Both of these MUST be a multiple of 2, and are in byte units.
+#ifndef DCD_STM32_BTABLE_BASE
+# define DCD_STM32_BTABLE_BASE 0U
+#endif
+
+#ifndef DCD_STM32_BTABLE_LENGTH
+# define DCD_STM32_BTABLE_LENGTH (PMA_LENGTH - DCD_STM32_BTABLE_BASE)
+#endif
+
+// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
+// We disable SOF for now until needed later on
+#ifndef USE_SOF
+# define USE_SOF 0
+#endif
+
+/***************************************************
+ * Checks, structs, defines, function definitions, etc.
+ */
+
+TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
+
+TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_LENGTH))<=(PMA_LENGTH),
+ "BTABLE does not fit in PMA RAM");
+
+TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
+
+// One of these for every EP IN & OUT, uses a bit of RAM....
+typedef struct
+{
+ uint8_t * buffer;
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t pma_ptr;
+ uint8_t max_packet_size;
+ uint8_t pma_alloc_size;
+} xfer_ctl_t;
+
+static xfer_ctl_t xfer_status[MAX_EP_COUNT][2];
+
+static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t epnum, uint32_t dir)
+{
+ return &xfer_status[epnum][dir];
+}
+
+static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6];
+
+static uint8_t remoteWakeCountdown; // When wake is requested
+
+// into the stack.
+static void dcd_handle_bus_reset(void);
+static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix);
+static void dcd_ep_ctr_handler(void);
+
+// PMA allocation/access
+static uint8_t open_ep_count;
+static uint16_t ep_buf_ptr; ///< Points to first free memory location
+static void dcd_pma_alloc_reset(void);
+static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length);
+static void dcd_pma_free(uint8_t ep_addr);
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes);
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes);
+
+// Using a function due to better type checks
+// This seems better than having to do type casts everywhere else
+static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
+ *reg = (uint16_t)(*reg & ~mask);
+}
+
+// Bits in ISTR are cleared upon writing 0
+static inline void clear_istr_bits(uint16_t mask) {
+ USB->ISTR = ~mask;
+}
+
+void dcd_init (uint8_t rhport)
+{
+ /* Clocks should already be enabled */
+ /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */
+
+ /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL.
+ * Here, the RM is followed. */
+
+ for(uint32_t i = 0; i<200; i++) // should be a few us
+ {
+ asm("NOP");
+ }
+ // Perform USB peripheral reset
+ USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
+ for(uint32_t i = 0; i<200; i++) // should be a few us
+ {
+ asm("NOP");
+ }
+ reg16_clear_bits(&USB->CNTR, USB_CNTR_PDWN);// Remove powerdown
+ // Wait startup time, for F042 and F070, this is <= 1 us.
+ for(uint32_t i = 0; i<200; i++) // should be a few us
+ {
+ asm("NOP");
+ }
+ USB->CNTR = 0; // Enable USB
+
+ USB->BTABLE = DCD_STM32_BTABLE_BASE;
+
+ USB->ISTR = 0; // Clear pending interrupts
+
+ // Reset endpoints to disabled
+ for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
+ {
+ // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED.
+ pcd_set_endpoint(USB,i,0u);
+ }
+
+ USB->CNTR |= USB_CNTR_RESETM | (USE_SOF ? USB_CNTR_SOFM : 0) | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
+ dcd_handle_bus_reset();
+
+ // Enable pull-up if supported
+ if ( dcd_connect ) dcd_connect(rhport);
+}
+
+// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
+#if defined(USB_BCDR_DPPU)
+
+// Disable internal D+ PU
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ USB->BCDR &= ~(USB_BCDR_DPPU);
+}
+
+// Enable internal D+ PU
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ USB->BCDR |= USB_BCDR_DPPU;
+}
+
+#endif
+
+// Enable device interrupt
+void dcd_int_enable (uint8_t rhport)
+{
+ (void)rhport;
+ // Member here forces write to RAM before allowing ISR to execute
+ __DSB();
+ __ISB();
+#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
+ NVIC_EnableIRQ(USB_IRQn);
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ #ifdef SYSCFG_CFGR1_USB_IT_RMP
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
+ {
+ NVIC_EnableIRQ(USB_HP_IRQn);
+ NVIC_EnableIRQ(USB_LP_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
+ }
+ else
+ #endif
+ {
+ NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn);
+ NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_IRQn);
+ }
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+ NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
+ NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_IRQn);
+#else
+ #error Unknown arch in USB driver
+#endif
+}
+
+// Disable device interrupt
+void dcd_int_disable(uint8_t rhport)
+{
+ (void)rhport;
+
+#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
+ NVIC_DisableIRQ(USB_IRQn);
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to disable the right IRQs.
+ #ifdef SYSCFG_CFGR1_USB_IT_RMP
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
+ {
+ NVIC_DisableIRQ(USB_HP_IRQn);
+ NVIC_DisableIRQ(USB_LP_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+ }
+ else
+ #endif
+ {
+ NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn);
+ NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_IRQn);
+ }
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+ NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
+ NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_IRQn);
+#else
+ #error Unknown arch in USB driver
+#endif
+
+ // CMSIS has a membar after disabling interrupts
+}
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ (void) dev_addr;
+
+ // Respond with status
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+ // DCD can only set address after status for this request is complete.
+ // do it at dcd_edpt0_status_complete()
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+
+ USB->CNTR |= (uint16_t) USB_CNTR_RESUME;
+ remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms.
+}
+
+static const tusb_desc_endpoint_t ep0OUT_desc =
+{
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+
+ .bEndpointAddress = 0x00,
+ .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
+ .wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
+ .bInterval = 0
+};
+
+static const tusb_desc_endpoint_t ep0IN_desc =
+{
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+
+ .bEndpointAddress = 0x80,
+ .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
+ .wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE },
+ .bInterval = 0
+};
+
+static void dcd_handle_bus_reset(void)
+{
+ //__IO uint16_t * const epreg = &(EPREG(0));
+ USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag
+
+ // Clear all EPREG (or maybe this is automatic? I'm not sure)
+ for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
+ {
+ pcd_set_endpoint(USB,i,0u);
+ }
+
+ dcd_pma_alloc_reset();
+ dcd_edpt_open (0, &ep0OUT_desc);
+ dcd_edpt_open (0, &ep0IN_desc);
+
+ USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero.
+}
+
+// Handle CTR interrupt for the TX/IN direction
+//
+// Upon call, (wIstr & USB_ISTR_DIR) == 0U
+static void dcd_ep_ctr_tx_handler(uint32_t wIstr)
+{
+ uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
+ uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
+
+ // Verify the CTR_TX bit is set. This was in the ST Micro code,
+ // but I'm not sure it's actually necessary?
+ if((wEPRegVal & USB_EP_CTR_TX) == 0U)
+ {
+ return;
+ }
+
+ /* clear int flag */
+ pcd_clear_tx_ep_ctr(USB, EPindex);
+
+ xfer_ctl_t * xfer = xfer_ctl_ptr(EPindex,TUSB_DIR_IN);
+ if((xfer->total_len != xfer->queued_len)) /* TX not complete */
+ {
+ dcd_transmit_packet(xfer, EPindex);
+ }
+ else /* TX Complete */
+ {
+ dcd_event_xfer_complete(0, (uint8_t)(0x80 + EPindex), xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+}
+
+// Handle CTR interrupt for the RX/OUT direction
+//
+// Upon call, (wIstr & USB_ISTR_DIR) == 0U
+static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
+{
+ uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
+ uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
+ uint32_t count = pcd_get_ep_rx_cnt(USB,EPindex);
+
+ xfer_ctl_t *xfer = xfer_ctl_ptr(EPindex,TUSB_DIR_OUT);
+
+ // Verify the CTR_RX bit is set. This was in the ST Micro code,
+ // but I'm not sure it's actually necessary?
+ if((wEPRegVal & USB_EP_CTR_RX) == 0U)
+ {
+ return;
+ }
+
+ if((EPindex == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */
+ {
+ // The setup_received function uses memcpy, so this must first copy the setup data into
+ // user memory, to allow for the 32-bit access that memcpy performs.
+ uint8_t userMemBuf[8];
+ /* Get SETUP Packet*/
+ if(count == 8) // Setup packet should always be 8 bytes. If not, ignore it, and try again.
+ {
+ // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
+ pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
+ pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
+ dcd_read_packet_memory(userMemBuf, *pcd_ep_rx_address_ptr(USB,EPindex), 8);
+ dcd_event_setup_received(0, (uint8_t*)userMemBuf, true);
+ }
+ }
+ else
+ {
+ // Clear RX CTR interrupt flag
+ if(EPindex != 0u)
+ {
+ pcd_clear_rx_ep_ctr(USB, EPindex);
+ }
+
+ if (count != 0U)
+ {
+ dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]),
+ *pcd_ep_rx_address_ptr(USB,EPindex), count);
+ xfer->queued_len = (uint16_t)(xfer->queued_len + count);
+ }
+
+ if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len))
+ {
+ /* RX COMPLETE */
+ dcd_event_xfer_complete(0, EPindex, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ // Though the host could still send, we don't know.
+ // Does the bulk pipe need to be reset to valid to allow for a ZLP?
+ }
+ else
+ {
+ uint32_t remaining = (uint32_t)xfer->total_len - (uint32_t)xfer->queued_len;
+ if(remaining >= xfer->max_packet_size) {
+ pcd_set_ep_rx_cnt(USB, EPindex,xfer->max_packet_size);
+ } else {
+ pcd_set_ep_rx_cnt(USB, EPindex,remaining);
+ }
+ pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID);
+ }
+ }
+
+ // For EP0, prepare to receive another SETUP packet.
+ // Clear CTR last so that a new packet does not overwrite the packing being read.
+ // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared)
+ if(EPindex == 0u)
+ {
+ // Always be prepared for a status packet...
+ pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE);
+ pcd_clear_rx_ep_ctr(USB, EPindex);
+ }
+}
+
+static void dcd_ep_ctr_handler(void)
+{
+ uint32_t wIstr;
+
+ /* stay in loop while pending interrupts */
+ while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U)
+ {
+
+ if ((wIstr & USB_ISTR_DIR) == 0U) /* TX/IN */
+ {
+ dcd_ep_ctr_tx_handler(wIstr);
+ }
+ else /* RX/OUT*/
+ {
+ dcd_ep_ctr_rx_handler(wIstr);
+ }
+ }
+}
+
+void dcd_int_handler(uint8_t rhport) {
+
+ (void) rhport;
+
+ uint32_t int_status = USB->ISTR;
+ //const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP
+ // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF;
+ // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ )
+
+ // The ST driver loops here on the CTR bit, but that loop has been moved into the
+ // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't
+ // be triggered repeatedly.
+
+ if(int_status & USB_ISTR_RESET) {
+ // USBRST is start of reset.
+ clear_istr_bits(USB_ISTR_RESET);
+ dcd_handle_bus_reset();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ return; // Don't do the rest of the things here; perhaps they've been cleared?
+ }
+
+ if (int_status & USB_ISTR_CTR)
+ {
+ /* servicing of the endpoint correct transfer interrupt */
+ /* clear of the CTR flag into the sub */
+ dcd_ep_ctr_handler();
+ }
+
+ if (int_status & USB_ISTR_WKUP)
+ {
+ reg16_clear_bits(&USB->CNTR, USB_CNTR_LPMODE);
+ reg16_clear_bits(&USB->CNTR, USB_CNTR_FSUSP);
+ clear_istr_bits(USB_ISTR_WKUP);
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ }
+
+ if (int_status & USB_ISTR_SUSP)
+ {
+ /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring,
+ * these events cannot be differentiated, so we only trigger suspend. */
+
+ /* Force low-power mode in the macrocell */
+ USB->CNTR |= USB_CNTR_FSUSP;
+ USB->CNTR |= USB_CNTR_LPMODE;
+
+ /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
+ clear_istr_bits(USB_ISTR_SUSP);
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ }
+
+#if USE_SOF
+ if(int_status & USB_ISTR_SOF) {
+ clear_istr_bits(USB_ISTR_SOF);
+ dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ }
+#endif
+
+ if(int_status & USB_ISTR_ESOF) {
+ if(remoteWakeCountdown == 1u)
+ {
+ USB->CNTR &= (uint16_t)(~USB_CNTR_RESUME);
+ }
+ if(remoteWakeCountdown > 0u)
+ {
+ remoteWakeCountdown--;
+ }
+ clear_istr_bits(USB_ISTR_ESOF);
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Invoked when a control transfer's status stage is complete.
+// May help DCD to prepare for next control transfer, this API is optional.
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
+{
+ (void) rhport;
+
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS )
+ {
+ uint8_t const dev_addr = (uint8_t) request->wValue;
+
+ // Setting new address after the whole request is complete
+ reg16_clear_bits(&USB->DADDR, USB_DADDR_ADD);
+ USB->DADDR = (uint16_t)(USB->DADDR | dev_addr); // leave the enable bit set
+ }
+}
+
+static void dcd_pma_alloc_reset(void)
+{
+ ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT; // 8 bytes per endpoint (two TX and two RX words, each)
+ //TU_LOG2("dcd_pma_alloc_reset()\r\n");
+ for(uint32_t i=0; i<MAX_EP_COUNT; i++)
+ {
+ xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_alloc_size = 0U;
+ xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_alloc_size = 0U;
+ xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_ptr = 0U;
+ xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_ptr = 0U;
+ }
+}
+
+/***
+ * Allocate a section of PMA
+ *
+ * If the EP number has already been allocated, and the new allocation
+ * is larger than the old allocation, then this will fail with a TU_ASSERT.
+ * (This is done to simplify the code. More complicated algorithms could be used)
+ *
+ * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually.
+ */
+static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t* epXferCtl = xfer_ctl_ptr(epnum,dir);
+
+ if(epXferCtl->pma_alloc_size != 0U)
+ {
+ //TU_LOG2("dcd_pma_alloc(%x,%x)=%x (cached)\r\n",ep_addr,length,epXferCtl->pma_ptr);
+ // Previously allocated
+ TU_ASSERT(length <= epXferCtl->pma_alloc_size, 0xFFFF); // Verify no larger than previous alloc
+ return epXferCtl->pma_ptr;
+ }
+
+ uint16_t addr = ep_buf_ptr;
+ ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
+
+ // Verify no overflow
+ TU_ASSERT(ep_buf_ptr <= PMA_LENGTH, 0xFFFF);
+
+ epXferCtl->pma_ptr = addr;
+ epXferCtl->pma_alloc_size = length;
+ //TU_LOG2("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr);
+
+ return addr;
+}
+
+/***
+ * Free a block of PMA space
+ */
+static void dcd_pma_free(uint8_t ep_addr)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ // Presently, this should never be called for EP0 IN/OUT
+ TU_ASSERT(open_ep_count > 2, /**/);
+ TU_ASSERT(xfer_ctl_ptr(epnum,dir)->max_packet_size != 0, /**/);
+ open_ep_count--;
+
+ // If count is 2, only EP0 should be open, so allocations can be mostly reset.
+
+ if(open_ep_count == 2)
+ {
+ ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT + 2*CFG_TUD_ENDPOINT0_SIZE; // 8 bytes per endpoint (two TX and two RX words, each), and EP0
+
+ // Skip EP0
+ for(uint32_t i=1; i<MAX_EP_COUNT; i++)
+ {
+ xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_alloc_size = 0U;
+ xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_alloc_size = 0U;
+ xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_ptr = 0U;
+ xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_ptr = 0U;
+ }
+ }
+}
+
+// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers,
+// so I'm using the #define from HAL here, instead.
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void)rhport;
+ uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ const uint16_t epMaxPktSize = p_endpoint_desc->wMaxPacketSize.size;
+ uint16_t pma_addr;
+ uint32_t wType;
+
+ // Isochronous not supported (yet), and some other driver assumptions.
+ TU_ASSERT(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
+ TU_ASSERT(epnum < MAX_EP_COUNT);
+
+ // Set type
+ switch(p_endpoint_desc->bmAttributes.xfer) {
+ case TUSB_XFER_CONTROL:
+ wType = USB_EP_CONTROL;
+ break;
+#if (0)
+ case TUSB_XFER_ISOCHRONOUS: // FIXME: Not yet supported
+ wType = USB_EP_ISOCHRONOUS;
+ break;
+#endif
+
+ case TUSB_XFER_BULK:
+ wType = USB_EP_CONTROL;
+ break;
+
+ case TUSB_XFER_INTERRUPT:
+ wType = USB_EP_INTERRUPT;
+ break;
+
+ default:
+ TU_ASSERT(false);
+ }
+
+ pcd_set_eptype(USB, epnum, wType);
+ pcd_set_ep_address(USB, epnum, epnum);
+ // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint)
+ // or being double-buffered (bulk endpoints)
+ pcd_clear_ep_kind(USB,0);
+
+ pma_addr = dcd_pma_alloc(p_endpoint_desc->bEndpointAddress, p_endpoint_desc->wMaxPacketSize.size);
+
+ if(dir == TUSB_DIR_IN)
+ {
+ *pcd_ep_tx_address_ptr(USB, epnum) = pma_addr;
+ pcd_set_ep_tx_cnt(USB, epnum, p_endpoint_desc->wMaxPacketSize.size);
+ pcd_clear_tx_dtog(USB, epnum);
+ pcd_set_ep_tx_status(USB,epnum,USB_EP_TX_NAK);
+ }
+ else
+ {
+ *pcd_ep_rx_address_ptr(USB, epnum) = pma_addr;
+ pcd_set_ep_rx_cnt(USB, epnum, p_endpoint_desc->wMaxPacketSize.size);
+ pcd_clear_rx_dtog(USB, epnum);
+ pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_NAK);
+ }
+
+ xfer_ctl_ptr(epnum, dir)->max_packet_size = epMaxPktSize;
+
+ return true;
+}
+
+/**
+ * Close an endpoint.
+ *
+ * This function may be called with interrupts enabled or disabled.
+ *
+ * This also clears transfers in progress, should there be any.
+ */
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+{
+ (void)rhport;
+ uint32_t const epnum = tu_edpt_number(ep_addr);
+ uint32_t const dir = tu_edpt_dir(ep_addr);
+
+ if(dir == TUSB_DIR_IN)
+ {
+ pcd_set_ep_tx_status(USB,epnum,USB_EP_TX_DIS);
+ }
+ else
+ {
+ pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_DIS);
+ }
+
+ dcd_pma_free(ep_addr);
+}
+
+// Currently, single-buffered, and only 64 bytes at a time (max)
+
+static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
+{
+ uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len);
+
+ if(len > xfer->max_packet_size) // max packet size for FS transfer
+ {
+ len = xfer->max_packet_size;
+ }
+ uint16_t oldAddr = *pcd_ep_tx_address_ptr(USB,ep_ix);
+ dcd_write_packet_memory(oldAddr, &(xfer->buffer[xfer->queued_len]), len);
+ xfer->queued_len = (uint16_t)(xfer->queued_len + len);
+
+ pcd_set_ep_tx_cnt(USB,ep_ix,len);
+ pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID);
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
+
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+
+ if ( dir == TUSB_DIR_OUT )
+ {
+ // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid
+ // buffer for the control endpoint.
+ if (epnum == 0 && buffer == NULL)
+ {
+ xfer->buffer = (uint8_t*)_setup_packet;
+ }
+ if(total_bytes > xfer->max_packet_size)
+ {
+ pcd_set_ep_rx_cnt(USB,epnum,xfer->max_packet_size);
+ } else {
+ pcd_set_ep_rx_cnt(USB,epnum,total_bytes);
+ }
+ pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
+ }
+ else // IN
+ {
+ dcd_transmit_packet(xfer,epnum);
+ }
+ return true;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void)rhport;
+
+ if (ep_addr & 0x80)
+ { // IN
+ pcd_set_ep_tx_status(USB, ep_addr & 0x7F, USB_EP_TX_STALL);
+ }
+ else
+ { // OUT
+ pcd_set_ep_rx_status(USB, ep_addr, USB_EP_RX_STALL);
+ }
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void)rhport;
+
+ if (ep_addr & 0x80)
+ { // IN
+ ep_addr &= 0x7F;
+
+ pcd_set_ep_tx_status(USB,ep_addr, USB_EP_TX_NAK);
+
+ /* Reset to DATA0 if clearing stall condition. */
+ pcd_clear_tx_dtog(USB,ep_addr);
+ }
+ else
+ { // OUT
+ /* Reset to DATA0 if clearing stall condition. */
+ pcd_clear_rx_dtog(USB,ep_addr);
+
+ pcd_set_ep_rx_status(USB,ep_addr, USB_EP_RX_NAK);
+ }
+}
+
+// Packet buffer access can only be 8- or 16-bit.
+/**
+ * @brief Copy a buffer from user memory area to packet memory area (PMA).
+ * This uses byte-access for user memory (so support non-aligned buffers)
+ * and 16-bit access for packet memory.
+ * @param dst, byte address in PMA; must be 16-bit aligned
+ * @param src pointer to user memory area.
+ * @param wPMABufAddr address into PMA.
+ * @param wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes)
+{
+ uint32_t n = ((uint32_t)wNBytes + 1U) >> 1U;
+ uint32_t i;
+ uint16_t temp1, temp2;
+ const uint8_t * srcVal;
+
+ // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
+ // it volatile so that it won't do that.
+ __IO uint16_t *pdwVal;
+
+ srcVal = src;
+ pdwVal = &pma[PMA_STRIDE*(dst>>1)];
+
+ for (i = n; i != 0; i--)
+ {
+ temp1 = (uint16_t) *srcVal;
+ srcVal++;
+ temp2 = temp1 | ((uint16_t)((uint16_t) ((*srcVal) << 8U))) ;
+ *pdwVal = temp2;
+ pdwVal += PMA_STRIDE;
+ srcVal++;
+ }
+ return true;
+}
+
+/**
+ * @brief Copy a buffer from user memory area to packet memory area (PMA).
+ * Uses byte-access of system memory and 16-bit access of packet memory
+ * @param wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes)
+{
+ uint32_t n = (uint32_t)wNBytes >> 1U;
+ uint32_t i;
+ // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
+ // it volatile so that it won't do that.
+ __IO const uint16_t *pdwVal;
+ uint32_t temp;
+
+ pdwVal = &pma[PMA_STRIDE*(src>>1)];
+ uint8_t *dstVal = (uint8_t*)dst;
+
+ for (i = n; i != 0U; i--)
+ {
+ temp = *pdwVal;
+ pdwVal += PMA_STRIDE;
+ *dstVal++ = ((temp >> 0) & 0xFF);
+ *dstVal++ = ((temp >> 8) & 0xFF);
+ }
+
+ if (wNBytes % 2)
+ {
+ temp = *pdwVal;
+ pdwVal += PMA_STRIDE;
+ *dstVal++ = ((temp >> 0) & 0xFF);
+ }
+ return true;
+}
+
+#endif
+
diff --git a/sw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h b/sw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
new file mode 100755
index 0000000..d26c700
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
@@ -0,0 +1,407 @@
+/**
+ ******************************************************************************
+ * @file dcd_stm32f0_pvt_st.h
+ * @brief DCD utilities from ST code
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+ * <h2><center>&copy; parts COPYRIGHT(c) N Conrad</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********/
+
+// This file contains source copied from ST's HAL, and thus should have their copyright statement.
+
+// PMA_LENGTH is PMA buffer size in bytes.
+// On 512-byte devices, access with a stride of two words (use every other 16-bit address)
+// On 1024-byte devices, access with a stride of one word (use every 16-bit address)
+
+#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
+#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
+
+#if defined(STM32F042x6) || \
+ defined(STM32F070x6) || defined(STM32F070xB) || \
+ defined(STM32F072xB) || \
+ defined(STM32F078xx)
+ #include "stm32f0xx.h"
+ #define PMA_LENGTH (1024u)
+ // F0x2 models are crystal-less
+ // All have internal D+ pull-up
+ // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
+ // PMA dedicated to USB (no sharing with CAN)
+
+#elif defined(STM32F1_FSDEV)
+ #include "stm32f1xx.h"
+ #define PMA_LENGTH (512u)
+ // NO internal Pull-ups
+ // *B, and *C: 2 x 16 bits/word
+
+ // F1 names this differently from the rest
+ #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
+
+#elif defined(STM32F302xB) || defined(STM32F302xC) || \
+ defined(STM32F303xB) || defined(STM32F303xC) || \
+ defined(STM32F373xC)
+ #include "stm32f3xx.h"
+ #define PMA_LENGTH (512u)
+ // NO internal Pull-ups
+ // *B, and *C: 1 x 16 bits/word
+ // PMA dedicated to USB (no sharing with CAN)
+
+#elif defined(STM32F302x6) || defined(STM32F302x8) || \
+ defined(STM32F302xD) || defined(STM32F302xE) || \
+ defined(STM32F303xD) || defined(STM32F303xE)
+ #include "stm32f3xx.h"
+ #define PMA_LENGTH (1024u)
+ // NO internal Pull-ups
+ // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
+ // When CAN clock is enabled, USB can use first 768 bytes ONLY.
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
+ #include "stm32l0xx.h"
+ #define PMA_LENGTH (1024u)
+
+#else
+ #error You are using an untested or unimplemented STM32 variant. Please update the driver.
+ // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4
+#endif
+
+// For purposes of accessing the packet
+#if ((PMA_LENGTH) == 512u)
+ #define PMA_STRIDE (2u)
+#elif ((PMA_LENGTH) == 1024u)
+ #define PMA_STRIDE (1u)
+#endif
+
+// And for type-safety create a new macro for the volatile address of PMAADDR
+// The compiler should warn us if we cast it to a non-volatile type?
+// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
+static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
+
+// prototypes
+static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum);
+static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum);
+static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wRegValue);
+
+
+/* SetENDPOINT */
+static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wRegValue)
+{
+ __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpNum*2u);
+ *reg = (uint16_t)wRegValue;
+}
+
+/* GetENDPOINT */
+static inline uint16_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpNum) {
+ __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpNum*2u);
+ return *reg;
+}
+
+static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wType)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= (uint32_t)USB_EP_T_MASK;
+ regVal |= wType;
+ regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+}
+
+static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EP_T_FIELD;
+ return regVal;
+}
+/**
+ * @brief Clears bit CTR_RX / CTR_TX in the endpoint register.
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @retval None
+ */
+static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPREG_MASK;
+ regVal &= ~USB_EP_CTR_RX;
+ regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0)
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+}
+static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPREG_MASK;
+ regVal &= ~USB_EP_CTR_TX;
+ regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0)
+ pcd_set_endpoint(USBx, bEpNum,regVal);
+}
+/**
+ * @brief gets counter of the tx buffer.
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @retval Counter value
+ */
+static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpNum);
+ return *regPtr & 0x3ffU;
+}
+
+static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpNum);
+ return *regPtr & 0x3ffU;
+}
+
+/**
+ * @brief Sets counter of rx buffer with no. of blocks.
+ * @param dwReg Register
+ * @param wCount Counter.
+ * @param wNBlocks no. of Blocks.
+ * @retval None
+ */
+
+static inline void pcd_set_ep_cnt_rx_reg(__O uint16_t * pdwReg, size_t wCount) {
+ uint32_t wNBlocks;
+ if(wCount > 62u)
+ {
+ wNBlocks = wCount >> 5u;
+ if((wCount & 0x1fU) == 0u)
+ {
+ wNBlocks--;
+ }
+ wNBlocks = wNBlocks << 10u;
+ wNBlocks |= 0x8000u; // Mark block size as 32byte
+ *pdwReg = (uint16_t)wNBlocks;
+ }
+ else
+ {
+ wNBlocks = wCount >> 1u;
+ if((wCount & 0x1U) != 0u)
+ {
+ wNBlocks++;
+ }
+ *pdwReg = (uint16_t)((wNBlocks) << 10u);
+ }
+}
+
+
+/**
+ * @brief Sets address in an endpoint register.
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @param bAddr Address.
+ * @retval None
+ */
+static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t bAddr)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPREG_MASK;
+ regVal |= bAddr;
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
+ pcd_set_endpoint(USBx, bEpNum,regVal);
+}
+
+static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
+{
+ size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
+ total_word_offset *= PMA_STRIDE;
+ return &(pma[total_word_offset]);
+}
+
+// Pointers to the PMA table entries (using the ARM address space)
+static inline __IO uint16_t* pcd_ep_tx_address_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 0u);
+}
+static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 1u);
+}
+
+static inline __IO uint16_t* pcd_ep_rx_address_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 2u);
+}
+
+static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 3u);
+}
+
+static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wCount)
+{
+ *pcd_ep_tx_cnt_ptr(USBx, bEpNum) = (uint16_t)wCount;
+}
+
+static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wCount)
+{
+ __IO uint16_t *pdwReg = pcd_ep_rx_cnt_ptr((USBx),(bEpNum));
+ pcd_set_ep_cnt_rx_reg(pdwReg, wCount);
+}
+
+/**
+ * @brief sets the status for tx transfer (bits STAT_TX[1:0]).
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @param wState new state
+ * @retval None
+ */
+static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wState)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPTX_DTOGMASK;
+
+ /* toggle first bit ? */
+ if((USB_EPTX_DTOG1 & (wState))!= 0U)
+ {
+ regVal ^= USB_EPTX_DTOG1;
+ }
+ /* toggle second bit ? */
+ if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U)
+ {
+ regVal ^= USB_EPTX_DTOG2;
+ }
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+} /* pcd_set_ep_tx_status */
+
+/**
+ * @brief sets the status for rx transfer (bits STAT_TX[1:0])
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @param wState new state
+ * @retval None
+ */
+
+static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wState)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPRX_DTOGMASK;
+
+ /* toggle first bit ? */
+ if((USB_EPRX_DTOG1 & wState)!= 0U)
+ {
+ regVal ^= USB_EPRX_DTOG1;
+ }
+ /* toggle second bit ? */
+ if((USB_EPRX_DTOG2 & wState)!= 0U)
+ {
+ regVal ^= USB_EPRX_DTOG2;
+ }
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+} /* pcd_set_ep_rx_status */
+
+static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ return (regVal & USB_EPRX_STAT) >> (12u);
+} /* pcd_get_ep_rx_status */
+
+
+/**
+ * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register.
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @retval None
+ */
+static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPREG_MASK;
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX;
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+}
+
+static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPREG_MASK;
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX;
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+}
+
+/**
+ * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register.
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @retval None
+ */
+
+static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ if((regVal & USB_EP_DTOG_RX) != 0)
+ {
+ pcd_rx_dtog(USBx,bEpNum);
+ }
+}
+
+static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ if((regVal & USB_EP_DTOG_TX) != 0)
+ {
+ pcd_tx_dtog(USBx,bEpNum);
+ }
+}
+
+/**
+ * @brief set & clear EP_KIND bit.
+ * @param USBx USB peripheral instance register address.
+ * @param bEpNum Endpoint Number.
+ * @retval None
+ */
+
+static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal |= USB_EP_KIND;
+ regVal &= USB_EPREG_MASK;
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+}
+static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpNum)
+{
+ uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
+ regVal &= USB_EPKIND_MASK;
+ regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
+ pcd_set_endpoint(USBx, bEpNum, regVal);
+}
+
+// This checks if the device has "LPM"
+#if defined(USB_ISTR_L1REQ)
+#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
+#else
+#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
+#endif
+
+#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \
+ USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED )
+
+// Number of endpoints in hardware
+#define STFSDEV_EP_COUNT (8u)
+
+#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */
diff --git a/sw/Core/Src/tinyusb/src/portable/st/synopsys/dcd_synopsys.c b/sw/Core/Src/tinyusb/src/portable/st/synopsys/dcd_synopsys.c
new file mode 100755
index 0000000..548438b
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/st/synopsys/dcd_synopsys.c
@@ -0,0 +1,1106 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Jan Duempelmann
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
+// We disable SOF for now until needed later on
+#define USE_SOF 0
+
+#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
+ defined (STM32F107xB) || defined (STM32F107xC)
+#define STM32F1_SYNOPSYS
+#endif
+
+#if defined (STM32L475xx) || defined (STM32L476xx) || \
+ defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
+ defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
+ defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
+#define STM32L4_SYNOPSYS
+#endif
+
+#if TUSB_OPT_DEVICE_ENABLED && \
+ ( (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
+ (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) \
+ )
+
+// EP_MAX : Max number of bi-directional endpoints including EP0
+// EP_FIFO_SIZE : Size of dedicated USB SRAM
+#if CFG_TUSB_MCU == OPT_MCU_STM32F1
+#include "stm32f1xx.h"
+#define EP_MAX_FS 4
+#define EP_FIFO_SIZE_FS 1280
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F2
+#include "stm32f2xx.h"
+#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
+#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
+#include "stm32f4xx.h"
+#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
+#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
+#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
+#define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7
+#include "stm32h7xx.h"
+#define EP_MAX_FS 9
+#define EP_FIFO_SIZE_FS 4096
+#define EP_MAX_HS 9
+#define EP_FIFO_SIZE_HS 4096
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
+#include "stm32f7xx.h"
+#define EP_MAX_FS 6
+#define EP_FIFO_SIZE_FS 1280
+#define EP_MAX_HS 9
+#define EP_FIFO_SIZE_HS 4096
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
+#include "stm32l4xx.h"
+#define EP_MAX_FS 6
+#define EP_FIFO_SIZE_FS 1280
+
+#else
+#error "Unsupported MCUs"
+
+#endif
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM
+//--------------------------------------------------------------------+
+
+// On STM32 we associate Port0 to OTG_FS, and Port1 to OTG_HS
+#if TUD_OPT_RHPORT == 0
+#define EP_MAX EP_MAX_FS
+#define EP_FIFO_SIZE EP_FIFO_SIZE_FS
+#define RHPORT_REGS_BASE USB_OTG_FS_PERIPH_BASE
+#define RHPORT_IRQn OTG_FS_IRQn
+
+#else
+#define EP_MAX EP_MAX_HS
+#define EP_FIFO_SIZE EP_FIFO_SIZE_HS
+#define RHPORT_REGS_BASE USB_OTG_HS_PERIPH_BASE
+#define RHPORT_IRQn OTG_HS_IRQn
+
+#endif
+
+#define GLOBAL_BASE(_port) ((USB_OTG_GlobalTypeDef*) RHPORT_REGS_BASE)
+#define DEVICE_BASE(_port) (USB_OTG_DeviceTypeDef *) (RHPORT_REGS_BASE + USB_OTG_DEVICE_BASE)
+#define OUT_EP_BASE(_port) (USB_OTG_OUTEndpointTypeDef *) (RHPORT_REGS_BASE + USB_OTG_OUT_ENDPOINT_BASE)
+#define IN_EP_BASE(_port) (USB_OTG_INEndpointTypeDef *) (RHPORT_REGS_BASE + USB_OTG_IN_ENDPOINT_BASE)
+#define FIFO_BASE(_port, _x) ((volatile uint32_t *) (RHPORT_REGS_BASE + USB_OTG_FIFO_BASE + (_x) * USB_OTG_FIFO_SIZE))
+
+enum
+{
+ DCD_HIGH_SPEED = 0, // Highspeed mode
+ DCD_FULL_SPEED_USE_HS = 1, // Full speed in Highspeed port (probably with internal PHY)
+ DCD_FULL_SPEED = 3, // Full speed with internal PHY
+};
+
+static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
+
+typedef struct {
+ uint8_t * buffer;
+ uint16_t total_len;
+ uint16_t max_size;
+ uint8_t interval;
+} xfer_ctl_t;
+
+typedef volatile uint32_t * usb_fifo_t;
+
+xfer_ctl_t xfer_status[EP_MAX][2];
+#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
+
+// EP0 transfers are limited to 1 packet - larger sizes has to be split
+static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
+
+// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from usb_otg->GRXFSIZ
+static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
+static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
+
+// Calculate the RX FIFO size according to recommendations from reference manual
+static inline uint16_t calc_rx_ff_size(uint16_t ep_size)
+{
+ return 15 + 2*(ep_size/4) + 2*EP_MAX;
+}
+
+static void update_grxfsiz(uint8_t rhport)
+{
+ (void) rhport;
+
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+
+ // Determine largest EP size for RX FIFO
+ uint16_t max_epsize = 0;
+ for (uint8_t epnum = 0; epnum < EP_MAX; epnum++)
+ {
+ max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
+ }
+
+ // Update size of RX FIFO
+ usb_otg->GRXFSIZ = calc_rx_ff_size(max_epsize);
+}
+
+// Setup the control endpoint 0.
+static void bus_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
+ USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
+
+ tu_memclr(xfer_status, sizeof(xfer_status));
+ _out_ep_closed = false;
+
+ for(uint8_t n = 0; n < EP_MAX; n++) {
+ out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
+ }
+
+ dev->DAINTMSK |= (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos);
+ dev->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM;
+ dev->DIEPMSK |= USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM;
+
+ // "USB Data FIFOs" section in reference manual
+ // Peripheral FIFO architecture
+ //
+ // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start.
+ // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located
+ // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard
+ // configuration done below.
+ //
+ // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed.
+ // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a
+ // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually
+ // opened when the host sends an additional command: setInterface. At this point in time
+ // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size
+ // an additional memory
+ //
+ // --------------- 320 or 1024 ( 1280 or 4096 bytes )
+ // | IN FIFO 0 |
+ // --------------- (320 or 1024) - 16
+ // | IN FIFO 1 |
+ // --------------- (320 or 1024) - 16 - x
+ // | . . . . |
+ // --------------- (320 or 1024) - 16 - x - y - ... - z
+ // | IN FIFO MAX |
+ // ---------------
+ // | FREE |
+ // --------------- GRXFSIZ
+ // | OUT FIFO |
+ // | ( Shared ) |
+ // --------------- 0
+ //
+ // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits):
+ // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN
+ //
+ // - All EP OUT shared a unique OUT FIFO which uses
+ // - 13 for setup packets + control words (up to 3 setup packets).
+ // - 1 for global NAK (not required/used here).
+ // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1"
+ // - 2 for each used OUT endpoint
+ //
+ // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum
+ // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x EP_MAX = 47 + 2 x EP_MAX
+ // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x EP_MAX = 271 + 2 x EP_MAX
+ //
+ // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge
+ // of the overall picture yet. We will use the worst scenario: largest possible + EP_MAX
+ //
+ // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
+ // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to
+ // overwrite this.
+
+ usb_otg->GRXFSIZ = calc_rx_ff_size(TUD_OPT_HIGH_SPEED ? 512 : 64);
+
+ _allocated_fifo_words_tx = 16;
+
+ // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
+ usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
+
+ // Fixed control EP0 size to 64 bytes
+ in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos);
+ xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64;
+
+ out_ep[0].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos);
+
+ usb_otg->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT;
+}
+
+// Set turn-around timeout according to link speed
+extern uint32_t SystemCoreClock;
+static void set_turnaround(USB_OTG_GlobalTypeDef * usb_otg, tusb_speed_t speed)
+{
+ usb_otg->GUSBCFG &= ~USB_OTG_GUSBCFG_TRDT;
+
+ if ( speed == TUSB_SPEED_HIGH )
+ {
+ // Use fixed 0x09 for Highspeed
+ usb_otg->GUSBCFG |= (0x09 << USB_OTG_GUSBCFG_TRDT_Pos);
+ }
+ else
+ {
+ // Turnaround timeout depends on the MCU clock
+ uint32_t turnaround;
+
+ if ( SystemCoreClock >= 32000000U )
+ turnaround = 0x6U;
+ else if ( SystemCoreClock >= 27500000U )
+ turnaround = 0x7U;
+ else if ( SystemCoreClock >= 24000000U )
+ turnaround = 0x8U;
+ else if ( SystemCoreClock >= 21800000U )
+ turnaround = 0x9U;
+ else if ( SystemCoreClock >= 20000000U )
+ turnaround = 0xAU;
+ else if ( SystemCoreClock >= 18500000U )
+ turnaround = 0xBU;
+ else if ( SystemCoreClock >= 17200000U )
+ turnaround = 0xCU;
+ else if ( SystemCoreClock >= 16000000U )
+ turnaround = 0xDU;
+ else if ( SystemCoreClock >= 15000000U )
+ turnaround = 0xEU;
+ else
+ turnaround = 0xFU;
+
+ // Fullspeed depends on MCU clocks, but we will use 0x06 for 32+ Mhz
+ usb_otg->GUSBCFG |= (turnaround << USB_OTG_GUSBCFG_TRDT_Pos);
+ }
+}
+
+static tusb_speed_t get_speed(uint8_t rhport)
+{
+ (void) rhport;
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ uint32_t const enum_spd = (dev->DSTS & USB_OTG_DSTS_ENUMSPD_Msk) >> USB_OTG_DSTS_ENUMSPD_Pos;
+ return (enum_spd == DCD_HIGH_SPEED) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
+}
+
+static void set_speed(uint8_t rhport, tusb_speed_t speed)
+{
+ uint32_t bitvalue;
+
+ if ( rhport == 1 )
+ {
+ bitvalue = ((TUSB_SPEED_HIGH == speed) ? DCD_HIGH_SPEED : DCD_FULL_SPEED_USE_HS);
+ }
+ else
+ {
+ bitvalue = DCD_FULL_SPEED;
+ }
+
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+
+ // Clear and set speed bits
+ dev->DCFG &= ~(3 << USB_OTG_DCFG_DSPD_Pos);
+ dev->DCFG |= (bitvalue << USB_OTG_DCFG_DSPD_Pos);
+}
+
+#if defined(USB_HS_PHYC)
+static bool USB_HS_PHYCInit(void)
+{
+ USB_HS_PHYC_GlobalTypeDef *usb_hs_phyc = (USB_HS_PHYC_GlobalTypeDef*) USB_HS_PHYC_CONTROLLER_BASE;
+
+ // Enable LDO
+ usb_hs_phyc->USB_HS_PHYC_LDO |= USB_HS_PHYC_LDO_ENABLE;
+
+ // Wait until LDO ready
+ while ( 0 == (usb_hs_phyc->USB_HS_PHYC_LDO & USB_HS_PHYC_LDO_STATUS) ) {}
+
+ uint32_t phyc_pll = 0;
+
+ // TODO Try to get HSE_VALUE from registers instead of depending CFLAGS
+ switch ( HSE_VALUE )
+ {
+ case 12000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12MHZ ; break;
+ case 12500000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12_5MHZ ; break;
+ case 16000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_16MHZ ; break;
+ case 24000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_24MHZ ; break;
+ case 25000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_25MHZ ; break;
+ case 32000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_Msk ; break; // Value not defined in header
+ default:
+ TU_ASSERT(0);
+ }
+ usb_hs_phyc->USB_HS_PHYC_PLL = phyc_pll;
+
+ // Control the tuning interface of the High Speed PHY
+ // Use magic value (USB_HS_PHYC_TUNE_VALUE) from ST driver
+ usb_hs_phyc->USB_HS_PHYC_TUNE |= 0x00000F13U;
+
+ // Enable PLL internal PHY
+ usb_hs_phyc->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN;
+
+ // Original ST code has 2 ms delay for PLL stabilization.
+ // Primitive test shows that more than 10 USB un/replug cycle showed no error with enumeration
+
+ return true;
+}
+#endif
+
+static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
+ USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
+
+ // EP0 is limited to one packet each xfer
+ // We use multiple transaction of xfer->max_size length to get a whole transfer done
+ if(epnum == 0) {
+ xfer_ctl_t * const xfer = XFER_CTL_BASE(epnum, dir);
+ total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
+ ep0_pending[dir] -= total_bytes;
+ }
+
+ // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here.
+ if(dir == TUSB_DIR_IN) {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ in_ep[epnum].DIEPTSIZ = (num_packets << USB_OTG_DIEPTSIZ_PKTCNT_Pos) |
+ ((total_bytes << USB_OTG_DIEPTSIZ_XFRSIZ_Pos) & USB_OTG_DIEPTSIZ_XFRSIZ_Msk);
+
+ in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
+ // For ISO endpoint set correct odd/even bit for next frame.
+ if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1)
+ {
+ // Take odd/even bit from frame counter.
+ uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos));
+ in_ep[epnum].DIEPCTL |= (odd_frame_now ? USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DIEPCTL_SODDFRM_Msk);
+ }
+ // Enable fifo empty interrupt only if there are something to put in the fifo.
+ if(total_bytes != 0) {
+ dev->DIEPEMPMSK |= (1 << epnum);
+ }
+ } else {
+ // A full OUT transfer (multiple packets, possibly) triggers XFRC.
+ out_ep[epnum].DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT_Msk | USB_OTG_DOEPTSIZ_XFRSIZ);
+ out_ep[epnum].DOEPTSIZ |= (num_packets << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
+ ((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk);
+
+ out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ if ((out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP) == USB_OTG_DOEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1)
+ {
+ // Take odd/even bit from frame counter.
+ uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos));
+ out_ep[epnum].DOEPCTL |= (odd_frame_now ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DOEPCTL_SODDFRM_Msk);
+ }
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init (uint8_t rhport)
+{
+ // Programming model begins in the last section of the chapter on the USB
+ // peripheral in each Reference Manual.
+
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+
+ // No HNP/SRP (no OTG support), program timeout later.
+ if ( rhport == 1 )
+ {
+ // On selected MCUs HS port1 can be used with external PHY via ULPI interface
+#if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED
+ // deactivate internal PHY
+ usb_otg->GCCFG &= ~USB_OTG_GCCFG_PWRDWN;
+
+ // Init The UTMI Interface
+ usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_TSDPS | USB_OTG_GUSBCFG_ULPIFSLS | USB_OTG_GUSBCFG_PHYSEL);
+
+ // Select default internal VBUS Indicator and Drive for ULPI
+ usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_ULPIEVBUSD | USB_OTG_GUSBCFG_ULPIEVBUSI);
+#else
+ usb_otg->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL;
+#endif
+
+#if defined(USB_HS_PHYC)
+ // Highspeed with embedded UTMI PHYC
+
+ // Select UTMI Interface
+ usb_otg->GUSBCFG &= ~USB_OTG_GUSBCFG_ULPI_UTMI_SEL;
+ usb_otg->GCCFG |= USB_OTG_GCCFG_PHYHSEN;
+
+ // Enables control of a High Speed USB PHY
+ USB_HS_PHYCInit();
+#endif
+ } else
+ {
+ // Enable internal PHY
+ usb_otg->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL;
+ }
+
+ // Reset core after selecting PHY
+ // Wait AHB IDLE, reset then wait until it is cleared
+ while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U) {}
+ usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_CSRST;
+ while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST) {}
+
+ // Restart PHY clock
+ *((volatile uint32_t *)(RHPORT_REGS_BASE + USB_OTG_PCGCCTL_BASE)) = 0;
+
+ // Clear all interrupts
+ usb_otg->GINTSTS |= usb_otg->GINTSTS;
+
+ // Required as part of core initialization.
+ // TODO: How should mode mismatch be handled? It will cause
+ // the core to stop working/require reset.
+ usb_otg->GINTMSK |= USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_MMISM;
+
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+
+ // If USB host misbehaves during status portion of control xfer
+ // (non zero-length packet), send STALL back and discard.
+ dev->DCFG |= USB_OTG_DCFG_NZLSOHSK;
+
+ set_speed(rhport, TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL);
+
+ // Enable internal USB transceiver, unless using HS core (port 1) with external PHY.
+ if (!(rhport == 1 && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED))) usb_otg->GCCFG |= USB_OTG_GCCFG_PWRDWN;
+
+ usb_otg->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
+ USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM |
+ USB_OTG_GINTMSK_RXFLVLM | (USE_SOF ? USB_OTG_GINTMSK_SOFM : 0);
+
+ // Enable global interrupt
+ usb_otg->GAHBCFG |= USB_OTG_GAHBCFG_GINT;
+
+ dcd_connect(rhport);
+}
+
+void dcd_int_enable (uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(RHPORT_IRQn);
+}
+
+void dcd_int_disable (uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(RHPORT_IRQn);
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ dev->DCFG |= (dev_addr << USB_OTG_DCFG_DAD_Pos) & USB_OTG_DCFG_DAD_Msk;
+
+ // Response with status after changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+
+ // TODO must manually clear this bit after 1-15 ms
+ // USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ // dev->DCTL |= USB_OTG_DCTL_RWUSIG;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ dev->DCTL &= ~USB_OTG_DCTL_SDIS;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ dev->DCTL |= USB_OTG_DCTL_SDIS;
+}
+
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+ (void) rhport;
+
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
+ USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
+
+ uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+
+ TU_ASSERT(epnum < EP_MAX);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->max_size = desc_edpt->wMaxPacketSize.size;
+ xfer->interval = desc_edpt->bInterval;
+
+ uint16_t const fifo_size = (desc_edpt->wMaxPacketSize.size + 3) / 4; // Round up to next full word
+
+ if(dir == TUSB_DIR_OUT)
+ {
+ // Calculate required size of RX FIFO
+ uint16_t const sz = calc_rx_ff_size(4*fifo_size);
+
+ // If size_rx needs to be extended check if possible and if so enlarge it
+ if (usb_otg->GRXFSIZ < sz)
+ {
+ TU_ASSERT(sz + _allocated_fifo_words_tx <= EP_FIFO_SIZE/4);
+
+ // Enlarge RX FIFO
+ usb_otg->GRXFSIZ = sz;
+ }
+
+ out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
+ (desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
+ (desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos);
+
+ dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_OEPM_Pos + epnum));
+ }
+ else
+ {
+ // "USB Data FIFOs" section in reference manual
+ // Peripheral FIFO architecture
+ //
+ // --------------- 320 or 1024 ( 1280 or 4096 bytes )
+ // | IN FIFO 0 |
+ // --------------- (320 or 1024) - 16
+ // | IN FIFO 1 |
+ // --------------- (320 or 1024) - 16 - x
+ // | . . . . |
+ // --------------- (320 or 1024) - 16 - x - y - ... - z
+ // | IN FIFO MAX |
+ // ---------------
+ // | FREE |
+ // --------------- GRXFSIZ
+ // | OUT FIFO |
+ // | ( Shared ) |
+ // --------------- 0
+ //
+ // In FIFO is allocated by following rules:
+ // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
+
+ // Check if free space is available
+ TU_ASSERT(_allocated_fifo_words_tx + fifo_size + usb_otg->GRXFSIZ <= EP_FIFO_SIZE/4);
+
+ _allocated_fifo_words_tx += fifo_size;
+
+ // DIEPTXF starts at FIFO #1.
+ // Both TXFD and TXSA are in unit of 32-bit words.
+ usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
+
+ in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) |
+ (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
+ (desc_edpt->bmAttributes.xfer << USB_OTG_DIEPCTL_EPTYP_Pos) |
+ (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) |
+ (desc_edpt->wMaxPacketSize.size << USB_OTG_DIEPCTL_MPSIZ_Pos);
+
+ dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_IEPM_Pos + epnum));
+ }
+
+ return true;
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+
+ // EP0 can only handle one packet
+ if(epnum == 0) {
+ ep0_pending[dir] = total_bytes;
+ // Schedule the first transaction for EP0 transfer
+ edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]);
+ return true;
+ }
+
+ uint16_t num_packets = (total_bytes / xfer->max_size);
+ uint8_t const short_packet_size = total_bytes % xfer->max_size;
+
+ // Zero-size packet is special case.
+ if(short_packet_size > 0 || (total_bytes == 0)) {
+ num_packets++;
+ }
+
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+
+ return true;
+}
+
+static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall)
+{
+ (void) rhport;
+
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
+ USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if(dir == TUSB_DIR_IN) {
+ // Only disable currently enabled non-control endpoint
+ if ( (epnum == 0) || !(in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPENA) ){
+ in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SNAK | (stall ? USB_OTG_DIEPCTL_STALL : 0);
+ } else {
+ // Stop transmitting packets and NAK IN xfers.
+ in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
+ while((in_ep[epnum].DIEPINT & USB_OTG_DIEPINT_INEPNE) == 0);
+
+ // Disable the endpoint.
+ in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPDIS | (stall ? USB_OTG_DIEPCTL_STALL : 0);
+ while((in_ep[epnum].DIEPINT & USB_OTG_DIEPINT_EPDISD_Msk) == 0);
+ in_ep[epnum].DIEPINT = USB_OTG_DIEPINT_EPDISD;
+ }
+
+ // Flush the FIFO, and wait until we have confirmed it cleared.
+ usb_otg->GRSTCTL |= (epnum << USB_OTG_GRSTCTL_TXFNUM_Pos);
+ usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_TXFFLSH;
+ while((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH_Msk) != 0);
+ } else {
+ // Only disable currently enabled non-control endpoint
+ if ( (epnum == 0) || !(out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPENA) ){
+ out_ep[epnum].DOEPCTL |= stall ? USB_OTG_DOEPCTL_STALL : 0;
+ } else {
+ // Asserting GONAK is required to STALL an OUT endpoint.
+ // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
+ // anyway, and it can't be cleared by user code. If this while loop never
+ // finishes, we have bigger problems than just the stack.
+ dev->DCTL |= USB_OTG_DCTL_SGONAK;
+ while((usb_otg->GINTSTS & USB_OTG_GINTSTS_BOUTNAKEFF_Msk) == 0);
+
+ // Ditto here- disable the endpoint.
+ out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPDIS | (stall ? USB_OTG_DOEPCTL_STALL : 0);
+ while((out_ep[epnum].DOEPINT & USB_OTG_DOEPINT_EPDISD_Msk) == 0);
+ out_ep[epnum].DOEPINT = USB_OTG_DOEPINT_EPDISD;
+
+ // Allow other OUT endpoints to keep receiving.
+ dev->DCTL |= USB_OTG_DCTL_CGONAK;
+ }
+ }
+}
+
+/**
+ * Close an endpoint.
+ */
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+{
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ dcd_edpt_disable(rhport, ep_addr, false);
+
+ // Update max_size
+ xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation
+
+ if (dir == TUSB_DIR_IN)
+ {
+ uint16_t const fifo_size = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXFD_Msk) >> USB_OTG_DIEPTXF_INEPTXFD_Pos;
+ uint16_t const fifo_start = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXSA_Msk) >> USB_OTG_DIEPTXF_INEPTXSA_Pos;
+ // For now only the last opened endpoint can be closed without fuss.
+ TU_ASSERT(fifo_start == EP_FIFO_SIZE/4 - _allocated_fifo_words_tx,);
+ _allocated_fifo_words_tx -= fifo_size;
+ }
+ else
+ {
+ _out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
+ }
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ dcd_edpt_disable(rhport, ep_addr, true);
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
+ USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if(dir == TUSB_DIR_IN) {
+ in_ep[epnum].DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
+
+ uint8_t eptype = (in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP_Msk) >> USB_OTG_DIEPCTL_EPTYP_Pos;
+ // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints.
+ if(eptype == 2 || eptype == 3) {
+ in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
+ }
+ } else {
+ out_ep[epnum].DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
+
+ uint8_t eptype = (out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP_Msk) >> USB_OTG_DOEPCTL_EPTYP_Pos;
+ // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt and bulk endpoints.
+ if(eptype == 2 || eptype == 3) {
+ out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
+ }
+ }
+}
+
+/*------------------------------------------------------------------*/
+
+// Read a single data packet from receive FIFO
+static void read_fifo_packet(uint8_t rhport, uint8_t * dst, uint16_t len)
+{
+ (void) rhport;
+
+ usb_fifo_t rx_fifo = FIFO_BASE(rhport, 0);
+
+ // Reading full available 32 bit words from fifo
+ uint16_t full_words = len >> 2;
+ for(uint16_t i = 0; i < full_words; i++) {
+ uint32_t tmp = *rx_fifo;
+ dst[0] = tmp & 0x000000FF;
+ dst[1] = (tmp & 0x0000FF00) >> 8;
+ dst[2] = (tmp & 0x00FF0000) >> 16;
+ dst[3] = (tmp & 0xFF000000) >> 24;
+ dst += 4;
+ }
+
+ // Read the remaining 1-3 bytes from fifo
+ uint8_t bytes_rem = len & 0x03;
+ if(bytes_rem != 0) {
+ uint32_t tmp = *rx_fifo;
+ dst[0] = tmp & 0x000000FF;
+ if(bytes_rem > 1) {
+ dst[1] = (tmp & 0x0000FF00) >> 8;
+ }
+ if(bytes_rem > 2) {
+ dst[2] = (tmp & 0x00FF0000) >> 16;
+ }
+ }
+}
+
+// Write a single data packet to EPIN FIFO
+static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t * src, uint16_t len)
+{
+ (void) rhport;
+
+ usb_fifo_t tx_fifo = FIFO_BASE(rhport, fifo_num);
+
+ // Pushing full available 32 bit words to fifo
+ uint16_t full_words = len >> 2;
+ for(uint16_t i = 0; i < full_words; i++){
+ *tx_fifo = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
+ src += 4;
+ }
+
+ // Write the remaining 1-3 bytes into fifo
+ uint8_t bytes_rem = len & 0x03;
+ if(bytes_rem){
+ uint32_t tmp_word = 0;
+ tmp_word |= src[0];
+ if(bytes_rem > 1){
+ tmp_word |= src[1] << 8;
+ }
+ if(bytes_rem > 2){
+ tmp_word |= src[2] << 16;
+ }
+ *tx_fifo = tmp_word;
+ }
+}
+
+static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_ep) {
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+ usb_fifo_t rx_fifo = FIFO_BASE(rhport, 0);
+
+ // Pop control word off FIFO
+ uint32_t ctl_word = usb_otg->GRXSTSP;
+ uint8_t pktsts = (ctl_word & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos;
+ uint8_t epnum = (ctl_word & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos;
+ uint16_t bcnt = (ctl_word & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos;
+
+ switch(pktsts) {
+ case 0x01: // Global OUT NAK (Interrupt)
+ break;
+
+ case 0x02: // Out packet recvd
+ {
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
+
+ // Read packet off RxFIFO
+ read_fifo_packet(rhport, xfer->buffer, bcnt);
+
+ // Increment pointer to xfer data
+ xfer->buffer += bcnt;
+
+ // Truncate transfer length in case of short packet
+ if(bcnt < xfer->max_size) {
+ xfer->total_len -= (out_ep[epnum].DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
+ if(epnum == 0) {
+ xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
+ ep0_pending[TUSB_DIR_OUT] = 0;
+ }
+ }
+ }
+ break;
+
+ case 0x03: // Out packet done (Interrupt)
+ break;
+
+ case 0x04: // Setup packet done (Interrupt)
+ out_ep[epnum].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos);
+ break;
+
+ case 0x06: // Setup packet recvd
+ // We can receive up to three setup packets in succession, but
+ // only the last one is valid.
+ _setup_packet[0] = (* rx_fifo);
+ _setup_packet[1] = (* rx_fifo);
+ break;
+
+ default: // Invalid
+ TU_BREAKPOINT();
+ break;
+ }
+}
+
+static void handle_epout_ints(uint8_t rhport, USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTypeDef * out_ep) {
+ // DAINT for a given EP clears when DOEPINTx is cleared.
+ // OEPINT will be cleared when DAINT's out bits are cleared.
+ for(uint8_t n = 0; n < EP_MAX; n++) {
+ xfer_ctl_t * xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
+
+ if(dev->DAINT & (1 << (USB_OTG_DAINT_OEPINT_Pos + n))) {
+ // SETUP packet Setup Phase done.
+ if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_STUP) {
+ out_ep[n].DOEPINT = USB_OTG_DOEPINT_STUP;
+ dcd_event_setup_received(rhport, (uint8_t*) &_setup_packet[0], true);
+ }
+
+ // OUT XFER complete
+ if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) {
+ out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC;
+
+ // EP0 can only handle one packet
+ if((n == 0) && ep0_pending[TUSB_DIR_OUT]) {
+ // Schedule another packet to be received.
+ edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
+ } else {
+ dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+ }
+}
+
+static void handle_epin_ints(uint8_t rhport, USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointTypeDef * in_ep) {
+ // DAINT for a given EP clears when DIEPINTx is cleared.
+ // IEPINT will be cleared when DAINT's out bits are cleared.
+ for ( uint8_t n = 0; n < EP_MAX; n++ )
+ {
+ xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN);
+
+ if ( dev->DAINT & (1 << (USB_OTG_DAINT_IEPINT_Pos + n)) )
+ {
+ // IN XFER complete (entire xfer).
+ if ( in_ep[n].DIEPINT & USB_OTG_DIEPINT_XFRC )
+ {
+ in_ep[n].DIEPINT = USB_OTG_DIEPINT_XFRC;
+
+ // EP0 can only handle one packet
+ if((n == 0) && ep0_pending[TUSB_DIR_IN]) {
+ // Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
+ } else {
+ dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+
+ // XFER FIFO empty
+ if ( (in_ep[n].DIEPINT & USB_OTG_DIEPINT_TXFE) && (dev->DIEPEMPMSK & (1 << n)) )
+ {
+ // DIEPINT's TXFE bit is read-only, software cannot clear it.
+ // It will only be cleared by hardware when written bytes is more than
+ // - 64 bytes or
+ // - Half of TX FIFO size (configured by DIEPTXF)
+
+ uint16_t remaining_packets = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
+
+ // Process every single packet (only whole packets can be written to fifo)
+ for(uint16_t i = 0; i < remaining_packets; i++){
+ uint16_t remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
+ // Packet can not be larger than ep max size
+ uint16_t packet_size = tu_min16(remaining_bytes, xfer->max_size);
+
+ // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
+ // EP has to be checked if the buffer can take another WHOLE packet
+ if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)){
+ break;
+ }
+
+ // Push packet to Tx-FIFO
+ write_fifo_packet(rhport, n, xfer->buffer, packet_size);
+
+ // Increment pointer to xfer data
+ xfer->buffer += packet_size;
+ }
+
+ // Turn off TXFE if all bytes are written.
+ if (((in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos) == 0)
+ {
+ dev->DIEPEMPMSK &= ~(1 << n);
+ }
+ }
+ }
+ }
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+ USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
+ USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
+ USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
+
+ uint32_t int_status = usb_otg->GINTSTS;
+
+ if(int_status & USB_OTG_GINTSTS_USBRST)
+ {
+ // USBRST is start of reset.
+ usb_otg->GINTSTS = USB_OTG_GINTSTS_USBRST;
+ bus_reset(rhport);
+ }
+
+ if(int_status & USB_OTG_GINTSTS_ENUMDNE)
+ {
+ // ENUMDNE is the end of reset where speed of the link is detected
+
+ usb_otg->GINTSTS = USB_OTG_GINTSTS_ENUMDNE;
+
+ tusb_speed_t const speed = get_speed(rhport);
+
+ set_turnaround(usb_otg, speed);
+ dcd_event_bus_reset(rhport, speed, true);
+ }
+
+ if(int_status & USB_OTG_GINTSTS_USBSUSP)
+ {
+ usb_otg->GINTSTS = USB_OTG_GINTSTS_USBSUSP;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+ }
+
+ if(int_status & USB_OTG_GINTSTS_WKUINT)
+ {
+ usb_otg->GINTSTS = USB_OTG_GINTSTS_WKUINT;
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+ }
+
+ if(int_status & USB_OTG_GINTSTS_OTGINT)
+ {
+ // OTG INT bit is read-only
+ uint32_t const otg_int = usb_otg->GOTGINT;
+
+ if (otg_int & USB_OTG_GOTGINT_SEDET)
+ {
+ dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
+ }
+
+ usb_otg->GOTGINT = otg_int;
+ }
+
+#if USE_SOF
+ if(int_status & USB_OTG_GINTSTS_SOF)
+ {
+ usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ }
+#endif
+
+ // RxFIFO non-empty interrupt handling.
+ if(int_status & USB_OTG_GINTSTS_RXFLVL)
+ {
+ // RXFLVL bit is read-only
+
+ // Mask out RXFLVL while reading data from FIFO
+ usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
+
+ // Loop until all available packets were handled
+ do
+ {
+ handle_rxflvl_ints(rhport, out_ep);
+ int_status = usb_otg->GINTSTS;
+ } while(int_status & USB_OTG_GINTSTS_RXFLVL);
+
+ // Manage RX FIFO size
+ if (_out_ep_closed)
+ {
+ update_grxfsiz(rhport);
+
+ // Disable flag
+ _out_ep_closed = false;
+ }
+
+ usb_otg->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
+ }
+
+ // OUT endpoint interrupt handling.
+ if(int_status & USB_OTG_GINTSTS_OEPINT)
+ {
+ // OEPINT is read-only
+ handle_epout_ints(rhport, dev, out_ep);
+ }
+
+ // IN endpoint interrupt handling.
+ if(int_status & USB_OTG_GINTSTS_IEPINT)
+ {
+ // IEPINT bit read-only
+ handle_epin_ints(rhport, dev, in_ep);
+ }
+
+ // // Check for Incomplete isochronous IN transfer
+ // if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
+ // printf(" IISOIXFR!\r\n");
+ //// TU_LOG2(" IISOIXFR!\r\n");
+ // }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/template/dcd_template.c b/sw/Core/Src/tinyusb/src/portable/template/dcd_template.c
new file mode 100755
index 0000000..6188124
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/template/dcd_template.c
@@ -0,0 +1,121 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_NONE
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+
+// Initialize controller to device mode
+void dcd_init (uint8_t rhport)
+{
+ (void) rhport;
+}
+
+// Enable device interrupt
+void dcd_int_enable (uint8_t rhport)
+{
+ (void) rhport;
+}
+
+// Disable device interrupt
+void dcd_int_disable (uint8_t rhport)
+{
+ (void) rhport;
+}
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+ (void) dev_addr;
+}
+
+// Wake up host
+void dcd_remote_wakeup (uint8_t rhport)
+{
+ (void) rhport;
+}
+
+// Connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+// Disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Configure endpoint's registers according to descriptor
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void) rhport;
+ (void) ep_desc;
+ return false;
+}
+
+// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+ (void) ep_addr;
+ (void) buffer;
+ (void) total_bytes;
+ return false;
+}
+
+// Stall endpoint
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ (void) ep_addr;
+}
+
+// clear stall, data toggle is also reset to DATA0
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ (void) ep_addr;
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/sw/Core/Src/tinyusb/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
new file mode 100755
index 0000000..e08c353
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -0,0 +1,642 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2020 William D. Jones
+ * Copyright (c) 2019-2020 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MSP430x5xx )
+
+#include "msp430.h"
+#include "device/dcd.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+// usbpllir_mirror and usbmaintl_mirror can be added later if needed.
+static volatile uint16_t usbiepie_mirror = 0;
+static volatile uint16_t usboepie_mirror = 0;
+static volatile uint8_t usbie_mirror = 0;
+static volatile uint16_t usbpwrctl_mirror = 0;
+static bool in_isr = false;
+
+uint8_t _setup_packet[8];
+
+// Xfer control
+typedef struct
+{
+ uint8_t * buffer;
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t max_size;
+ bool short_packet;
+} xfer_ctl_t;
+
+xfer_ctl_t xfer_status[8][2];
+#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
+
+// Accessing endpoint regs
+typedef volatile uint8_t * ep_regs_t;
+
+typedef enum
+{
+ CNF = 0,
+ BBAX = 1,
+ BCTX = 2,
+ BBAY = 5,
+ BCTY = 6,
+ SIZXY = 7
+} ep_regs_index_t;
+
+#define EP_REGS(epnum, dir) ((ep_regs_t) ((uintptr_t)&USBOEPCNF_1 + 64*dir + 8*(epnum - 1)))
+
+static void bus_reset(void)
+{
+ // Hardcoded into the USB core.
+ xfer_status[0][TUSB_DIR_OUT].max_size = 8;
+ xfer_status[0][TUSB_DIR_IN].max_size = 8;
+
+ USBKEYPID = USBKEY;
+
+ // Enable the control EP 0. Also enable Indication Enable- a guard flag
+ // separate from the Interrupt Enable mask.
+ USBOEPCNF_0 |= (UBME | USBIIE);
+ USBIEPCNF_0 |= (UBME | USBIIE);
+
+ // Enable interrupts for this endpoint.
+ USBOEPIE |= BIT0;
+ USBIEPIE |= BIT0;
+
+ // Clear NAK until a setup packet is received.
+ USBOEPCNT_0 &= ~NAK;
+ USBIEPCNT_0 &= ~NAK;
+
+ USBCTL |= FEN; // Enable responding to packets.
+
+ // Dedicated buffers in hardware for SETUP and EP0, no setup needed.
+ // Now safe to respond to SETUP packets.
+ USBIE |= SETUPIE;
+
+ USBKEYPID = 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init (uint8_t rhport)
+{
+ (void) rhport;
+
+ USBKEYPID = USBKEY;
+
+ // Enable the module (required to write config regs)!
+ USBCNF |= USB_EN;
+
+ // Reset used interrupts
+ USBOEPIE = 0;
+ USBIEPIE = 0;
+ USBIE = 0;
+ USBOEPIFG = 0;
+ USBIEPIFG = 0;
+ USBIFG = 0;
+ USBPWRCTL &= ~(VUOVLIE | VBONIE | VBOFFIE | VUOVLIFG | VBONIFG | VBOFFIFG);
+ usboepie_mirror = 0;
+ usbiepie_mirror = 0;
+ usbie_mirror = 0;
+ usbpwrctl_mirror = 0;
+
+ USBVECINT = 0;
+
+ // Enable reset and wait for it before continuing.
+ USBIE |= RSTRIE;
+
+ // Enable pullup.
+ USBCNF |= PUR_EN;
+
+ USBKEYPID = 0;
+}
+
+// There is no "USB peripheral interrupt disable" bit on MSP430, so we have
+// to save the relevant registers individually.
+// WARNING: Unlike the ARM/NVIC routines, these functions are _not_ idempotent
+// if you modified the registers saved in between calls so they don't match
+// the mirrors; mirrors will be updated to reflect most recent register
+// contents.
+void dcd_int_enable (uint8_t rhport)
+{
+ (void) rhport;
+
+ __bic_SR_register(GIE); // Unlikely to be called in ISR, but let's be safe.
+ // Also, this cleanly disables all USB interrupts
+ // atomically from application's POV.
+
+ // This guard is required because tinyusb can enable interrupts without
+ // having disabled them first.
+ if(in_isr)
+ {
+ USBOEPIE = usboepie_mirror;
+ USBIEPIE = usbiepie_mirror;
+ USBIE = usbie_mirror;
+ USBPWRCTL |= usbpwrctl_mirror;
+ }
+
+ in_isr = false;
+ __bis_SR_register(GIE);
+}
+
+void dcd_int_disable (uint8_t rhport)
+{
+ (void) rhport;
+
+ __bic_SR_register(GIE);
+ usboepie_mirror = USBOEPIE;
+ usbiepie_mirror = USBIEPIE;
+ usbie_mirror = USBIE;
+ usbpwrctl_mirror = (USBPWRCTL & (VUOVLIE | VBONIE | VBOFFIE));
+ USBOEPIE = 0;
+ USBIEPIE = 0;
+ USBIE = 0;
+ USBPWRCTL &= ~(VUOVLIE | VBONIE | VBOFFIE);
+ in_isr = true;
+ __bis_SR_register(GIE);
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+
+ USBFUNADR = dev_addr;
+
+ // Response with status after changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ dcd_int_disable(rhport);
+
+ USBKEYPID = USBKEY;
+ USBCNF |= PUR_EN; // Enable pullup.
+ USBKEYPID = 0;
+
+ dcd_int_enable(rhport);
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ dcd_int_disable(rhport);
+
+ USBKEYPID = USBKEY;
+ USBCNF &= ~PUR_EN; // Disable pullup.
+ USBKEYPID = 0;
+
+ dcd_int_enable(rhport);
+}
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+
+ // Unsupported endpoint numbers or type (Iso not supported. Control
+ // not supported on nonzero endpoints).
+ if( (epnum > 7) || \
+ (desc_edpt->bmAttributes.xfer == 0) || \
+ (desc_edpt->bmAttributes.xfer == 1)) {
+ return false;
+ }
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->max_size = desc_edpt->wMaxPacketSize.size;
+
+ // Buffer allocation scheme:
+ // For simplicity, only single buffer for now, since tinyusb currently waits
+ // for an xfer to complete before scheduling another one. This means only
+ // the X buffer is used.
+ //
+ // 1904 bytes are available, the max endpoint size supported on msp430 is
+ // 64 bytes. This is enough RAM for all 14 endpoints enabled _with_ double
+ // bufferring (64*14*2 = 1792 bytes). Extra RAM exists for triple and higher
+ // order bufferring, which must be maintained in software.
+ //
+ // For simplicity, each endpoint gets a hardcoded 64 byte chunk (regardless
+ // of actual wMaxPacketSize) whose start address is the following:
+ // addr = 128 * (epnum - 1) + 64 * dir.
+ //
+ // Double buffering equation:
+ // x_addr = 256 * (epnum - 1) + 128 * dir
+ // y_addr = x_addr + 64
+ // Address is right-shifted by 3 to fit into 8 bits.
+
+ uint8_t buf_base = (128 * (epnum - 1) + 64 * dir) >> 3;
+
+ // IN and OUT EP registers have the same structure.
+ ep_regs_t ep_regs = EP_REGS(epnum, dir);
+
+ // FIXME: I was able to get into a situation where OUT EP 3 would stall
+ // while debugging, despite stall code never being called. It appears
+ // these registers don't get cleared on reset, being part of RAM.
+ // Investigate and see if I can duplicate.
+ // Also, DBUF got set on OUT EP 2 while debugging. Only OUT EPs seem to be
+ // affected at this time. USB RAM directly precedes main RAM; perhaps I'm
+ // overwriting registers via buffer overflow w/ my debugging code?
+ ep_regs[SIZXY] = desc_edpt->wMaxPacketSize.size;
+ ep_regs[BCTX] |= NAK;
+ ep_regs[BBAX] = buf_base;
+ ep_regs[CNF] &= ~(TOGGLE | STALL | DBUF); // ISO xfers not supported on
+ // MSP430, so no need to gate DATA0/1 and frame
+ // behavior. Clear stall and double buffer bit as
+ // well- see above comment.
+ ep_regs[CNF] |= (UBME | USBIIE);
+
+ USBKEYPID = USBKEY;
+ if(dir == TUSB_DIR_OUT)
+ {
+ USBOEPIE |= (1 << epnum);
+ }
+ else
+ {
+ USBIEPIE |= (1 << epnum);
+ }
+ USBKEYPID = 0;
+
+ return true;
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->short_packet = false;
+
+ if(epnum == 0)
+ {
+ if(dir == TUSB_DIR_OUT)
+ {
+ // Interrupt will notify us when data was received.
+ USBCTL &= ~DIR;
+ USBOEPCNT_0 &= ~NAK;
+ }
+ else
+ {
+ // Kickstart the IN packet handler by queuing initial data and calling
+ // the ISR to transmit the first packet.
+ // Interrupt only fires on completed xfer.
+ USBCTL |= DIR;
+ USBIEPIFG |= BIT0;
+ }
+ }
+ else
+ {
+ ep_regs_t ep_regs = EP_REGS(epnum, dir);
+
+ if(dir == TUSB_DIR_OUT)
+ {
+ ep_regs[BCTX] &= ~NAK;
+ }
+ else
+ {
+ USBIEPIFG |= (1 << epnum);
+ }
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if(epnum == 0)
+ {
+ if(dir == TUSB_DIR_OUT)
+ {
+ USBOEPCNT_0 |= NAK;
+ USBOEPCNF_0 |= STALL;
+ }
+ else
+ {
+ USBIEPCNT_0 |= NAK;
+ USBIEPCNF_0 |= STALL;
+ }
+ }
+ else
+ {
+ ep_regs_t ep_regs = EP_REGS(epnum, dir);
+ ep_regs[CNF] |= STALL;
+ }
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if(epnum == 0)
+ {
+ if(dir == TUSB_DIR_OUT)
+ {
+ USBOEPCNF_0 &= ~STALL;
+ }
+ else
+ {
+ USBIEPCNF_0 &= ~STALL;
+ }
+ }
+ else
+ {
+ ep_regs_t ep_regs = EP_REGS(epnum, dir);
+ // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
+ // and bulk endpoints.
+ ep_regs[CNF] &= ~(STALL + TOGGLE);
+ }
+}
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
+{
+ (void) rhport;
+ (void) request;
+
+ // FIXME: Per manual, we should be clearing the NAK bits of EP0 after the
+ // Status Phase of a control xfer is done, in preparation of another possible
+ // SETUP packet. However, from my own testing, SETUP packets _are_ correctly
+ // handled by the USB core without clearing the NAKs.
+ //
+ // Right now, clearing NAKs in this callbacks causes a direction mismatch
+ // between host and device on EP0. Figure out why and come back to this.
+ // USBOEPCNT_0 &= ~NAK;
+ // USBIEPCNT_0 &= ~NAK;
+}
+
+/*------------------------------------------------------------------*/
+
+static void receive_packet(uint8_t ep_num)
+{
+ xfer_ctl_t * xfer = XFER_CTL_BASE(ep_num, TUSB_DIR_OUT);
+ ep_regs_t ep_regs = EP_REGS(ep_num, TUSB_DIR_OUT);
+ uint8_t xfer_size;
+
+ if(ep_num == 0)
+ {
+ xfer_size = USBOEPCNT_0 & 0x0F;
+ }
+ else
+ {
+ xfer_size = ep_regs[BCTX] & 0x7F;
+ }
+
+ uint16_t remaining = xfer->total_len - xfer->queued_len;
+ uint16_t to_recv_size;
+
+ if(remaining <= xfer->max_size) {
+ // Avoid buffer overflow.
+ to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
+ } else {
+ // Room for full packet, choose recv_size based on what the microcontroller
+ // claims.
+ to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
+ }
+
+ uint8_t * base = (xfer->buffer + xfer->queued_len);
+
+ if(ep_num == 0)
+ {
+ volatile uint8_t * ep0out_buf = &USBOEP0BUF;
+ for(uint16_t i = 0; i < to_recv_size; i++)
+ {
+ base[i] = ep0out_buf[i];
+ }
+ }
+ else
+ {
+ volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
+ for(uint16_t i = 0; i < to_recv_size ; i++)
+ {
+ base[i] = ep_buf[i];
+ }
+ }
+
+ xfer->queued_len += xfer_size;
+
+ xfer->short_packet = (xfer_size < xfer->max_size);
+ if((xfer->total_len == xfer->queued_len) || xfer->short_packet)
+ {
+ dcd_event_xfer_complete(0, ep_num, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Schedule to receive another packet.
+ if(ep_num == 0)
+ {
+ USBOEPCNT_0 &= ~NAK;
+ }
+ else
+ {
+ ep_regs[BCTX] &= ~NAK;
+ }
+ }
+}
+
+static void transmit_packet(uint8_t ep_num)
+{
+ xfer_ctl_t * xfer = XFER_CTL_BASE(ep_num, TUSB_DIR_IN);
+
+ // First, determine whether we should even send a packet or finish
+ // up the xfer.
+ bool zlp = (xfer->total_len == 0); // By necessity, xfer->total_len will
+ // equal xfer->queued_len for ZLPs.
+ // Of course a ZLP is a short packet.
+ if((!zlp && (xfer->total_len == xfer->queued_len)) || xfer->short_packet)
+ {
+ dcd_event_xfer_complete(0, ep_num | TUSB_DIR_IN_MASK, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ return;
+ }
+
+ // Then actually commit to transmit a packet.
+ uint8_t * base = (xfer->buffer + xfer->queued_len);
+ uint16_t remaining = xfer->total_len - xfer->queued_len;
+ uint8_t xfer_size = (xfer->max_size < xfer->total_len) ? xfer->max_size : remaining;
+
+ xfer->queued_len += xfer_size;
+ if(xfer_size < xfer->max_size)
+ {
+ // Next "xfer complete interrupt", the transfer will end.
+ xfer->short_packet = true;
+ }
+
+ if(ep_num == 0)
+ {
+ volatile uint8_t * ep0in_buf = &USBIEP0BUF;
+ for(uint16_t i = 0; i < xfer_size; i++)
+ {
+ ep0in_buf[i] = base[i];
+ }
+
+ USBIEPCNT_0 = (USBIEPCNT_0 & 0xF0) + xfer_size;
+ USBIEPCNT_0 &= ~NAK;
+ }
+ else
+ {
+ ep_regs_t ep_regs = EP_REGS(ep_num, TUSB_DIR_IN);
+ volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
+
+ for(int i = 0; i < xfer_size; i++)
+ {
+ ep_buf[i] = base[i];
+ }
+
+ ep_regs[BCTX] = (ep_regs[BCTX] & 0x80) + (xfer_size & 0x7F);
+ ep_regs[BCTX] &= ~NAK;
+ }
+}
+
+static void handle_setup_packet(void)
+{
+ volatile uint8_t * setup_buf = &USBSUBLK;
+
+ for(int i = 0; i < 8; i++)
+ {
+ _setup_packet[i] = setup_buf[i];
+ }
+
+ // Clearing SETUPIFG by reading USBVECINT does not set NAK, so now that we
+ // have a SETUP packet, force NAKs until tinyusb can handle the SETUP
+ // packet and prepare for a new xfer.
+ USBIEPCNT_0 |= NAK;
+ USBOEPCNT_0 |= NAK;
+ dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true);
+}
+
+void dcd_int_handler(uint8_t rhport)
+{
+ (void) rhport;
+
+ // Setup is special- reading USBVECINT to handle setup packets is done to
+ // stop hardware-generated NAKs on EP0.
+ uint8_t setup_status = USBIFG & SETUPIFG;
+
+ if(setup_status)
+ {
+ handle_setup_packet();
+ }
+
+ uint16_t curr_vector = USBVECINT;
+
+ switch(curr_vector)
+ {
+ case USBVECINT_RSTR:
+ bus_reset();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ break;
+
+ // Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet
+ // is received. At this point, even though the hardware is no longer
+ // forcing NAKs, the EP0 NAK bits should still be set to avoid
+ // sending/receiving data before tinyusb is ready.
+ //
+ // Furthermore, it's possible for the hardware to STALL in the middle of
+ // a control xfer if the EP0 NAK bits aren't set properly.
+ // See: https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/845259
+ // From my testing, if all of the following hold:
+ // * OUT EP0 NAK is cleared.
+ // * IN EP0 NAK is set.
+ // * DIR bit in USBCTL is clear.
+ // and an IN packet is received on EP0, the USB core will STALL. Setting
+ // both EP0 NAKs manually when a SETUP packet is received, as is done
+ // in handle_setup_packet(), avoids meeting STALL conditions.
+ //
+ // TODO: Figure out/explain why the STALL condition can be reached in the
+ // first place. When I first noticed the STALL, the only two places I
+ // touched the NAK bits were in dcd_edpt_xfer() and to _set_ (sic) them in
+ // bus_reset(). SETUP packet handling should've been unaffected.
+ case USBVECINT_SETUP_PACKET_RECEIVED:
+ break;
+
+ case USBVECINT_INPUT_ENDPOINT0:
+ transmit_packet(0);
+ break;
+
+ case USBVECINT_OUTPUT_ENDPOINT0:
+ receive_packet(0);
+ break;
+
+ case USBVECINT_INPUT_ENDPOINT1:
+ case USBVECINT_INPUT_ENDPOINT2:
+ case USBVECINT_INPUT_ENDPOINT3:
+ case USBVECINT_INPUT_ENDPOINT4:
+ case USBVECINT_INPUT_ENDPOINT5:
+ case USBVECINT_INPUT_ENDPOINT6:
+ case USBVECINT_INPUT_ENDPOINT7:
+ {
+ uint8_t ep = ((curr_vector - USBVECINT_INPUT_ENDPOINT1) >> 1) + 1;
+ transmit_packet(ep);
+ }
+ break;
+
+ case USBVECINT_OUTPUT_ENDPOINT1:
+ case USBVECINT_OUTPUT_ENDPOINT2:
+ case USBVECINT_OUTPUT_ENDPOINT3:
+ case USBVECINT_OUTPUT_ENDPOINT4:
+ case USBVECINT_OUTPUT_ENDPOINT5:
+ case USBVECINT_OUTPUT_ENDPOINT6:
+ case USBVECINT_OUTPUT_ENDPOINT7:
+ {
+ uint8_t ep = ((curr_vector - USBVECINT_OUTPUT_ENDPOINT1) >> 1) + 1;
+ receive_packet(ep);
+ }
+ break;
+
+ default:
+ while(true);
+ break;
+ }
+
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.c b/sw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.c
new file mode 100755
index 0000000..b68f04f
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.c
@@ -0,0 +1,643 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+#ifndef LOG_USB
+#define LOG_USB 0
+#endif
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_VALENTYUSB_EPTRI)
+
+#include "device/dcd.h"
+#include "dcd_eptri.h"
+#include "csr.h"
+#include "irq.h"
+void fomu_error(uint32_t line);
+
+#if LOG_USB
+struct usb_log {
+ uint8_t ep_num;
+ uint8_t size;
+ uint8_t data[66];
+};
+__attribute__((used))
+struct usb_log usb_log[128];
+__attribute__((used))
+uint8_t usb_log_offset;
+
+struct xfer_log {
+ uint8_t ep_num;
+ uint16_t size;
+};
+__attribute__((used))
+struct xfer_log xfer_log[64];
+__attribute__((used))
+uint8_t xfer_log_offset;
+
+__attribute__((used))
+struct xfer_log queue_log[64];
+__attribute__((used))
+uint8_t queue_log_offset;
+#endif
+
+//--------------------------------------------------------------------+
+// SIE Command
+//--------------------------------------------------------------------+
+
+#define EP_SIZE 64
+
+uint16_t volatile rx_buffer_offset[16];
+uint8_t* volatile rx_buffer[16];
+uint16_t volatile rx_buffer_max[16];
+
+volatile uint8_t tx_ep;
+volatile bool tx_active;
+volatile uint16_t tx_buffer_offset[16];
+uint8_t* volatile tx_buffer[16];
+volatile uint16_t tx_buffer_max[16];
+volatile uint8_t reset_count;
+
+#if DEBUG
+__attribute__((used)) uint8_t volatile * last_tx_buffer;
+__attribute__((used)) volatile uint8_t last_tx_ep;
+uint8_t setup_packet_bfr[10];
+#endif
+
+//--------------------------------------------------------------------+
+// PIPE HELPER
+//--------------------------------------------------------------------+
+
+static bool advance_tx_ep(void) {
+ // Move on to the next transmit buffer in a round-robin manner
+ uint8_t prev_tx_ep = tx_ep;
+ for (tx_ep = (tx_ep + 1) & 0xf; tx_ep != prev_tx_ep; tx_ep = ((tx_ep + 1) & 0xf)) {
+ if (tx_buffer[tx_ep])
+ return true;
+ }
+ if (!tx_buffer[tx_ep])
+ return false;
+ return true;
+}
+
+#if LOG_USB
+void xfer_log_append(uint8_t ep_num, uint16_t sz) {
+ xfer_log[xfer_log_offset].ep_num = ep_num;
+ xfer_log[xfer_log_offset].size = sz;
+ xfer_log_offset++;
+ if (xfer_log_offset >= sizeof(xfer_log)/sizeof(*xfer_log))
+ xfer_log_offset = 0;
+}
+
+void queue_log_append(uint8_t ep_num, uint16_t sz) {
+ queue_log[queue_log_offset].ep_num = ep_num;
+ queue_log[queue_log_offset].size = sz;
+ queue_log_offset++;
+ if (queue_log_offset >= sizeof(queue_log)/sizeof(*queue_log))
+ queue_log_offset = 0;
+}
+#endif
+
+static void tx_more_data(void) {
+ // Send more data
+ uint8_t added_bytes;
+ for (added_bytes = 0; (added_bytes < EP_SIZE) && (tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) {
+#if LOG_USB
+ usb_log[usb_log_offset].data[added_bytes] = tx_buffer[tx_ep][tx_buffer_offset[tx_ep]];
+#endif
+ usb_in_data_write(tx_buffer[tx_ep][tx_buffer_offset[tx_ep]++]);
+ }
+
+#if LOG_USB
+ usb_log[usb_log_offset].ep_num = tu_edpt_addr(tx_ep, TUSB_DIR_IN);
+ usb_log[usb_log_offset].size = added_bytes;
+ usb_log_offset++;
+ if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log))
+ usb_log_offset = 0;
+#endif
+
+ // Updating the epno queues the data
+ usb_in_ctrl_write(tx_ep & 0xf);
+}
+
+static void process_tx(void) {
+#if DEBUG
+ // If the system isn't idle, then something is very wrong.
+ uint8_t in_status = usb_in_status_read();
+ if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET)))
+ fomu_error(__LINE__);
+#endif
+
+ // If the buffer is now empty, search for the next buffer to fill.
+ if (!tx_buffer[tx_ep]) {
+ if (advance_tx_ep())
+ tx_more_data();
+ else
+ tx_active = false;
+ return;
+ }
+
+ if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) {
+#if DEBUG
+ last_tx_buffer = tx_buffer[tx_ep];
+ last_tx_ep = tx_ep;
+#endif
+ tx_buffer[tx_ep] = NULL;
+ uint16_t xferred_bytes = tx_buffer_max[tx_ep];
+ uint8_t xferred_ep = tx_ep;
+
+ if (!advance_tx_ep())
+ tx_active = false;
+#if LOG_USB
+ xfer_log_append(tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes);
+#endif
+ dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true);
+ if (!tx_active)
+ return;
+ }
+
+ tx_more_data();
+ return;
+}
+
+static void process_rx(void) {
+ uint8_t out_status = usb_out_status_read();
+#if DEBUG
+ // If the OUT handler is still waiting to send, don't do anything.
+ if (!(out_status & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)))
+ fomu_error(__LINE__);
+ // return;
+#endif
+ uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf;
+
+ // If the destination buffer doesn't exist, don't drain the hardware
+ // fifo. Note that this can cause deadlocks if the host is waiting
+ // on some other endpoint's data!
+#if DEBUG
+ if (rx_buffer[rx_ep] == NULL) {
+ fomu_error(__LINE__);
+ return;
+ }
+#endif
+
+ // Drain the FIFO into the destination buffer
+ uint32_t total_read = 0;
+ uint32_t current_offset = rx_buffer_offset[rx_ep];
+#if DEBUG
+ uint8_t test_buffer[256];
+ memset(test_buffer, 0, sizeof(test_buffer));
+ if (current_offset > rx_buffer_max[rx_ep])
+ fomu_error(__LINE__);
+#endif
+#if LOG_USB
+ usb_log[usb_log_offset].ep_num = tu_edpt_addr(rx_ep, TUSB_DIR_OUT);
+ usb_log[usb_log_offset].size = 0;
+#endif
+ while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) {
+ uint8_t c = usb_out_data_read();
+#if DEBUG
+ test_buffer[total_read] = c;
+#endif
+ total_read++;
+ if (current_offset < rx_buffer_max[rx_ep]) {
+#if LOG_USB
+ usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c;
+#endif
+ if (rx_buffer[rx_ep] != (volatile uint8_t *)0xffffffff)
+ rx_buffer[rx_ep][current_offset++] = c;
+ }
+ }
+#if LOG_USB
+ usb_log_offset++;
+ if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log))
+ usb_log_offset = 0;
+#endif
+#if DEBUG
+ if (total_read > 66)
+ fomu_error(__LINE__);
+ if (total_read < 2)
+ total_read = 2;
+ // fomu_error(__LINE__);
+#endif
+
+ // Strip off the CRC16
+ rx_buffer_offset[rx_ep] += (total_read - 2);
+ if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep])
+ rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep];
+
+ // If there's no more data, complete the transfer to tinyusb
+ if ((rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep])
+ // ZLP with less than the total amount of data
+ || ((total_read == 2) && ((rx_buffer_offset[rx_ep] & 63) == 0))
+ // Short read, but not a full packet
+ || (((rx_buffer_offset[rx_ep] & 63) != 0) && (total_read < 66))) {
+#if DEBUG
+ if (rx_buffer[rx_ep] == NULL)
+ fomu_error(__LINE__);
+#endif
+
+ // Free up this buffer.
+ rx_buffer[rx_ep] = NULL;
+ uint16_t len = rx_buffer_offset[rx_ep];
+
+#if DEBUG
+ // Validate that all enabled endpoints have buffers,
+ // and no disabled endpoints have buffers.
+ uint16_t ep_en_mask = usb_out_enable_status_read();
+ int i;
+ for (i = 0; i < 16; i++) {
+ if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) {
+ uint8_t new_status = usb_out_status_read();
+ // Another IRQ came in while we were processing, so ignore this endpoint.
+ if ((new_status & 0x20) && ((new_status & 0xf) == i))
+ continue;
+ fomu_error(__LINE__);
+ }
+ }
+#endif
+#if LOG_USB
+ xfer_log_append(tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len);
+#endif
+ dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, true);
+ }
+ else {
+ // If there's more data, re-enable data reception on this endpoint
+ usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep);
+ }
+
+ // Now that the buffer is drained, clear the pending IRQ.
+ usb_out_ev_pending_write(usb_out_ev_pending_read());
+}
+
+//--------------------------------------------------------------------+
+// CONTROLLER API
+//--------------------------------------------------------------------+
+
+static void dcd_reset(void)
+{
+ reset_count++;
+ usb_setup_ev_enable_write(0);
+ usb_in_ev_enable_write(0);
+ usb_out_ev_enable_write(0);
+
+ usb_address_write(0);
+
+ // Reset all three FIFO handlers
+ usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET);
+ usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET);
+ usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET);
+
+ memset((void *)rx_buffer, 0, sizeof(rx_buffer));
+ memset((void *)rx_buffer_max, 0, sizeof(rx_buffer_max));
+ memset((void *)rx_buffer_offset, 0, sizeof(rx_buffer_offset));
+
+ memset((void *)tx_buffer, 0, sizeof(tx_buffer));
+ memset((void *)tx_buffer_max, 0, sizeof(tx_buffer_max));
+ memset((void *)tx_buffer_offset, 0, sizeof(tx_buffer_offset));
+ tx_ep = 0;
+ tx_active = false;
+
+ // Enable all event handlers and clear their contents
+ usb_setup_ev_pending_write(0xff);
+ usb_in_ev_pending_write(0xff);
+ usb_out_ev_pending_write(0xff);
+ usb_in_ev_enable_write(1);
+ usb_out_ev_enable_write(1);
+ usb_setup_ev_enable_write(3);
+
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+}
+
+// Initializes the USB peripheral for device mode and enables it.
+void dcd_init(uint8_t rhport)
+{
+ (void) rhport;
+
+ usb_pullup_out_write(0);
+
+ // Enable all event handlers and clear their contents
+ usb_setup_ev_pending_write(usb_setup_ev_pending_read());
+ usb_in_ev_pending_write(usb_in_ev_pending_read());
+ usb_out_ev_pending_write(usb_out_ev_pending_read());
+ usb_in_ev_enable_write(1);
+ usb_out_ev_enable_write(1);
+ usb_setup_ev_enable_write(3);
+
+ // Turn on the external pullup
+ usb_pullup_out_write(1);
+}
+
+// Enables or disables the USB device interrupt(s). May be used to
+// prevent concurrency issues when mutating data structures shared
+// between main code and the interrupt handler.
+void dcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ irq_setmask(irq_getmask() | (1 << USB_INTERRUPT));
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT));
+}
+
+// Called when the device is given a new bus address.
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ // Respond with ACK status first before changing device address
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+
+ // Wait for the response packet to get sent
+ while (tx_active)
+ ;
+
+ // Activate the new address
+ usb_address_write(dev_addr);
+}
+
+// Called to remote wake up host when suspended (e.g hid keyboard)
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_pullup_out_write(1);
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ usb_pullup_out_write(0);
+}
+
+
+//--------------------------------------------------------------------+
+// DCD Endpoint Port
+//--------------------------------------------------------------------+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
+{
+ (void) rhport;
+ uint8_t ep_num = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ uint8_t ep_dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+
+ if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
+ return false; // Not supported
+
+ if (ep_dir == TUSB_DIR_OUT) {
+ rx_buffer_offset[ep_num] = 0;
+ rx_buffer_max[ep_num] = 0;
+ rx_buffer[ep_num] = NULL;
+ }
+
+ else if (ep_dir == TUSB_DIR_IN) {
+ tx_buffer_offset[ep_num] = 0;
+ tx_buffer_max[ep_num] = 0;
+ tx_buffer[ep_num] = NULL;
+ }
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
+ uint8_t enable = 0;
+ if (rx_buffer[ep_addr])
+ enable = 1;
+ usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr));
+ }
+ else
+ usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr));
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
+ uint8_t enable = 0;
+ if (rx_buffer[ep_addr])
+ enable = 1;
+ usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr));
+ }
+ // IN endpoints will get unstalled when more data is written.
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ (void)rhport;
+ uint8_t ep_num = tu_edpt_number(ep_addr);
+ uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ TU_ASSERT(ep_num < 16);
+
+ // Give a nonzero buffer when we transmit 0 bytes, so that the
+ // system doesn't think the endpoint is idle.
+ if ((buffer == NULL) && (total_bytes == 0)) {
+ buffer = (uint8_t *)0xffffffff;
+ }
+
+ TU_ASSERT(buffer != NULL);
+
+ if (ep_dir == TUSB_DIR_IN) {
+ // Wait for the tx pipe to free up
+ uint8_t previous_reset_count = reset_count;
+ // Continue until the buffer is empty, the system is idle, and the fifo is empty.
+ while (tx_buffer[ep_num] != NULL)
+ ;
+
+ dcd_int_disable(0);
+#if LOG_USB
+ queue_log_append(ep_addr, total_bytes);
+#endif
+ // If a reset happens while we're waiting, abort the transfer
+ if (previous_reset_count != reset_count)
+ return true;
+
+ TU_ASSERT(tx_buffer[ep_num] == NULL);
+ tx_buffer_offset[ep_num] = 0;
+ tx_buffer_max[ep_num] = total_bytes;
+ tx_buffer[ep_num] = buffer;
+
+ // If the current buffer is NULL, then that means the tx logic is idle.
+ // Update the tx_ep to point to our endpoint number and queue the data.
+ // Otherwise, let it be and it'll get picked up after the next transfer
+ // finishes.
+ if (!tx_active) {
+ tx_ep = ep_num;
+ tx_active = true;
+ tx_more_data();
+ }
+ dcd_int_enable(0);
+ }
+
+ else if (ep_dir == TUSB_DIR_OUT) {
+ while (rx_buffer[ep_num] != NULL)
+ ;
+
+ TU_ASSERT(rx_buffer[ep_num] == NULL);
+ dcd_int_disable(0);
+#if LOG_USB
+ queue_log_append(ep_addr, total_bytes);
+#endif
+ rx_buffer[ep_num] = buffer;
+ rx_buffer_offset[ep_num] = 0;
+ rx_buffer_max[ep_num] = total_bytes;
+
+ // Enable receiving on this particular endpoint
+ usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num);
+#if DEBUG
+ uint16_t ep_en_mask = usb_out_enable_status_read();
+ int i;
+ for (i = 0; i < 16; i++) {
+ if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) {
+ if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i)
+ continue;
+ fomu_error(__LINE__);
+ }
+ }
+#endif
+ dcd_int_enable(0);
+ }
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+
+static void handle_out(void)
+{
+ // An "OUT" transaction just completed so we have new data.
+ // (But only if we can accept the data)
+#if DEBUG
+ if (!usb_out_ev_pending_read())
+ fomu_error(__LINE__);
+ if (!usb_out_ev_enable_read())
+ fomu_error(__LINE__);
+#endif
+ process_rx();
+}
+
+static void handle_in(void)
+{
+#if DEBUG
+ if (!usb_in_ev_pending_read())
+ fomu_error(__LINE__);
+ if (!usb_in_ev_enable_read())
+ fomu_error(__LINE__);
+#endif
+ usb_in_ev_pending_write(usb_in_ev_pending_read());
+ process_tx();
+}
+
+static void handle_reset(void)
+{
+#if DEBUG
+ uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read();
+ if (!(setup_pending & 2))
+ fomu_error(__LINE__);
+#endif
+ usb_setup_ev_pending_write(2);
+
+ // This event means a bus reset occurred. Reset everything, and
+ // abandon any further processing.
+ dcd_reset();
+}
+
+static void handle_setup(void)
+{
+#if !DEBUG
+ uint8_t setup_packet_bfr[10];
+#endif
+
+#if DEBUG
+ uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read();
+ if (!(setup_pending & 1))
+ fomu_error(__LINE__);
+#endif
+
+ // We got a SETUP packet. Copy it to the setup buffer and clear
+ // the "pending" bit.
+ // Setup packets are always 8 bytes, plus two bytes of crc16.
+ uint32_t setup_length = 0;
+
+#if DEBUG
+ if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)))
+ fomu_error(__LINE__);
+#endif
+
+ while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) {
+ uint8_t c = usb_setup_data_read();
+ if (setup_length < sizeof(setup_packet_bfr))
+ setup_packet_bfr[setup_length] = c;
+ setup_length++;
+ }
+
+ // If we have 10 bytes, that's a full SETUP packet plus CRC16.
+ // Otherwise, it was an RX error.
+ if (setup_length == 10) {
+ dcd_event_setup_received(0, setup_packet_bfr, true);
+ }
+#if DEBUG
+ else {
+ fomu_error(__LINE__);
+ }
+#endif
+
+ usb_setup_ev_pending_write(1);
+}
+void dcd_int_handler(uint8_t rhport)
+{
+ (void)rhport;
+ uint8_t next_ev;
+ while ((next_ev = usb_next_ev_read())) {
+ switch (next_ev) {
+ case 1 << CSR_USB_NEXT_EV_IN_OFFSET:
+ handle_in();
+ break;
+ case 1 << CSR_USB_NEXT_EV_OUT_OFFSET:
+ handle_out();
+ break;
+ case 1 << CSR_USB_NEXT_EV_SETUP_OFFSET:
+ handle_setup();
+ break;
+ case 1 << CSR_USB_NEXT_EV_RESET_OFFSET:
+ handle_reset();
+ break;
+ }
+ }
+}
+
+#endif
diff --git a/sw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.h b/sw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.h
new file mode 100755
index 0000000..0fa6ecc
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.h
@@ -0,0 +1,39 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_DCD_VALENTYUSB_EPTRI_H_
+#define _TUSB_DCD_VALENTYUSB_EPTRI_H_
+
+#include "common/tusb_common.h"
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_DCD_VALENTYUSB_EPTRI_H_ */
diff --git a/sw/Core/Src/tinyusb/src/tusb.c b/sw/Core/Src/tinyusb/src/tusb.c
new file mode 100755
index 0000000..31452e8
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/tusb.c
@@ -0,0 +1,150 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_HOST_ENABLED || TUSB_OPT_DEVICE_ENABLED
+
+#include "tusb.h"
+
+static bool _initialized = false;
+
+// TODO clean up
+#if TUSB_OPT_DEVICE_ENABLED
+#include "device/usbd_pvt.h"
+#endif
+
+bool tusb_init(void)
+{
+ // skip if already initialized
+ if (_initialized) return true;
+
+#if TUSB_OPT_HOST_ENABLED
+ TU_ASSERT( tuh_init() ); // init host stack
+#endif
+
+#if TUSB_OPT_DEVICE_ENABLED
+ TU_ASSERT ( tud_init() ); // init device stack
+#endif
+
+ _initialized = true;
+
+ return true;
+}
+
+bool tusb_inited(void)
+{
+ return _initialized;
+}
+
+/*------------------------------------------------------------------*/
+/* Debug
+ *------------------------------------------------------------------*/
+#if CFG_TUSB_DEBUG
+#include <ctype.h>
+
+char const* const tusb_strerr[TUSB_ERROR_COUNT] = { ERROR_TABLE(ERROR_STRING) };
+
+static void dump_str_line(uint8_t const* buf, uint16_t count)
+{
+ tu_printf(" |");
+
+ // each line is 16 bytes
+ for(uint16_t i=0; i<count; i++)
+ {
+ const char ch = buf[i];
+ tu_printf("%c", isprint(ch) ? ch : '.');
+ }
+
+ tu_printf("|\r\n");
+}
+
+/* Print out memory contents
+ * - buf : buffer
+ * - count : number of item
+ * - indent: prefix spaces on every line
+ */
+void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
+{
+ uint8_t const size = 1; // fixed 1 byte for now
+
+ if ( !buf || !count )
+ {
+ tu_printf("NULL\r\n");
+ return;
+ }
+
+ uint8_t const *buf8 = (uint8_t const *) buf;
+
+ char format[] = "%00X";
+ format[2] += 2*size;
+
+ const uint8_t item_per_line = 16 / size;
+
+ for(unsigned int i=0; i<count; i++)
+ {
+ unsigned int value=0;
+
+ if ( i%item_per_line == 0 )
+ {
+ // Print Ascii
+ if ( i != 0 )
+ {
+ dump_str_line(buf8-16, 16);
+ }
+
+ for(uint8_t s=0; s < indent; s++) tu_printf(" ");
+
+ // print offset or absolute address
+ tu_printf("%04X: ", 16*i/item_per_line);
+ }
+
+ memcpy(&value, buf8, size);
+ buf8 += size;
+
+ tu_printf(" ");
+ tu_printf(format, value);
+ }
+
+ // fill up last row to 16 for printing ascii
+ const uint32_t remain = count%16;
+ uint8_t nback = (remain ? remain : 16);
+
+ if ( remain )
+ {
+ for(uint32_t i=0; i< 16-remain; i++)
+ {
+ tu_printf(" ");
+ for(int j=0; j<2*size; j++) tu_printf(" ");
+ }
+ }
+
+ dump_str_line(buf8-nback, nback);
+}
+
+#endif
+
+#endif // host or device enabled
diff --git a/sw/Core/Src/tinyusb/src/tusb.h b/sw/Core/Src/tinyusb/src/tusb.h
new file mode 100755
index 0000000..cc82c44
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/tusb.h
@@ -0,0 +1,132 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_H_
+#define _TUSB_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "common/tusb_common.h"
+#include "osal/osal.h"
+#include "common/tusb_fifo.h"
+
+//------------- HOST -------------//
+#if TUSB_OPT_HOST_ENABLED
+ #include "host/usbh.h"
+
+ #if HOST_CLASS_HID
+ #include "class/hid/hid_host.h"
+ #endif
+
+ #if CFG_TUH_MSC
+ #include "class/msc/msc_host.h"
+ #endif
+
+ #if CFG_TUH_CDC
+ #include "class/cdc/cdc_host.h"
+ #endif
+
+ #if CFG_TUH_VENDOR
+ #include "class/vendor/vendor_host.h"
+ #endif
+
+#endif
+
+//------------- DEVICE -------------//
+#if TUSB_OPT_DEVICE_ENABLED
+ #include "device/usbd.h"
+
+ #if CFG_TUD_HID
+ #include "class/hid/hid_device.h"
+ #endif
+
+ #if CFG_TUD_CDC
+ #include "class/cdc/cdc_device.h"
+ #endif
+
+ #if CFG_TUD_MSC
+ #include "class/msc/msc_device.h"
+ #endif
+
+#if CFG_TUD_AUDIO
+ #include "class/audio/audio_device.h"
+#endif
+
+ #if CFG_TUD_MIDI
+ #include "class/midi/midi_device.h"
+ #endif
+
+ #if CFG_TUD_VENDOR
+ #include "class/vendor/vendor_device.h"
+ #endif
+
+ #if CFG_TUD_USBTMC
+ #include "class/usbtmc/usbtmc_device.h"
+ #endif
+
+ #if CFG_TUD_DFU_RUNTIME
+ #include "class/dfu/dfu_rt_device.h"
+ #endif
+
+ #if CFG_TUD_NET
+ #include "class/net/net_device.h"
+ #endif
+
+ #if CFG_TUD_BTH
+ #include "class/bth/bth_device.h"
+ #endif
+#endif
+
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+/** \ingroup group_application_api
+ * @{ */
+
+// Initialize device/host stack
+// Note: when using with RTOS, this should be called after scheduler/kernel is started.
+// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+bool tusb_init(void);
+
+// Check if stack is initialized
+bool tusb_inited(void);
+
+// TODO
+// bool tusb_teardown(void);
+
+/** @} */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_H_ */
diff --git a/sw/Core/Src/tinyusb/src/tusb_config.h b/sw/Core/Src/tinyusb/src/tusb_config.h
new file mode 100755
index 0000000..f256c3b
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/tusb_config.h
@@ -0,0 +1,120 @@
+/*
+ * 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
+//--------------------------------------------------------------------
+
+#define CFG_TUSB_MCU OPT_MCU_STM32L4
+#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
+
+// defined by board.mk
+#ifndef CFG_TUSB_MCU
+ #error CFG_TUSB_MCU must be defined
+#endif
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_DEVICE_RHPORT_NUM
+ #define BOARD_DEVICE_RHPORT_NUM 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
+#ifndef BOARD_DEVICE_RHPORT_SPEED
+ #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
+ CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56)
+ #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
+ #else
+ #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
+ #endif
+#endif
+
+// Device mode with rhport and speed defined by board.mk
+#if BOARD_DEVICE_RHPORT_NUM == 0
+ #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
+#elif BOARD_DEVICE_RHPORT_NUM == 1
+ #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
+#else
+ #error "Incorrect RHPort configuration"
+#endif
+
+// This example doesn't use an RTOS
+#define CFG_TUSB_OS OPT_OS_NONE
+
+// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
+// #define CFG_TUSB_DEBUG 0
+
+/* 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_CDC 0
+#define CFG_TUD_MSC 0
+#define CFG_TUD_HID 0
+#define CFG_TUD_MIDI 1
+#define CFG_TUD_VENDOR 1
+
+// MIDI FIFO size of TX and RX
+#define CFG_TUD_MIDI_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_MIDI_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+ // Vendor FIFO size of TX and RX
+ // If not configured vendor endpoints will not be buffered
+ #define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+ #define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/sw/Core/Src/tinyusb/src/tusb_option.h b/sw/Core/Src/tinyusb/src/tusb_option.h
new file mode 100755
index 0000000..644208a
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/tusb_option.h
@@ -0,0 +1,290 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_OPTION_H_
+#define _TUSB_OPTION_H_
+
+#define TUSB_VERSION_MAJOR 0
+#define TUSB_VERSION_MINOR 9
+#define TUSB_VERSION_REVISION 0
+#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
+
+/** \defgroup group_mcu Supported MCU
+ * \ref CFG_TUSB_MCU must be defined to one of these
+ * @{ */
+
+#define OPT_MCU_NONE 0
+
+// LPC
+#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx
+#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx
+#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx
+#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x
+#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x
+#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
+#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
+#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
+#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x
+#define OPT_MCU_LPC54XXX 10 ///< NXP LPC54xxx
+#define OPT_MCU_LPC55XX 11 ///< NXP LPC55xx
+
+// NRF
+#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
+
+// SAM
+#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11
+#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21
+#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51
+#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x
+#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series
+
+// STM32
+#define OPT_MCU_STM32F0 300 ///< ST STM32F0
+#define OPT_MCU_STM32F1 301 ///< ST STM32F1
+#define OPT_MCU_STM32F2 302 ///< ST STM32F2
+#define OPT_MCU_STM32F3 303 ///< ST STM32F3
+#define OPT_MCU_STM32F4 304 ///< ST STM32F4
+#define OPT_MCU_STM32F7 305 ///< ST STM32F7
+#define OPT_MCU_STM32H7 306 ///< ST STM32H7
+#define OPT_MCU_STM32L0 307 ///< ST STM32L0
+#define OPT_MCU_STM32L1 308 ///< ST STM32L1
+#define OPT_MCU_STM32L4 309 ///< ST STM32L4
+
+// Sony
+#define OPT_MCU_CXD56 400 ///< SONY CXD56
+
+// TI MSP430
+#define OPT_MCU_MSP430x5xx 500 ///< TI MSP430x5xx
+
+// ValentyUSB eptri
+#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config
+
+// NXP iMX RT
+#define OPT_MCU_MIMXRT10XX 700 ///< NXP iMX RT10xx
+
+// Nuvoton
+#define OPT_MCU_NUC121 800
+#define OPT_MCU_NUC126 801
+#define OPT_MCU_NUC120 802
+#define OPT_MCU_NUC505 803
+
+// Espressif
+#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
+
+// Dialog
+#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
+
+// Raspberry Pi
+#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040
+
+// NXP Kinetis
+#define OPT_MCU_MKL25ZXX 1200 ///< NXP MKL25Zxx
+
+// Silabs
+#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG
+#define OPT_MCU_EFM32GG11 1301 ///< Silabs EFM32GG11
+#define OPT_MCU_EFM32GG12 1302 ///< Silabs EFM32GG12
+
+// Renesas RX
+#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631
+
+/** @} */
+
+/** \defgroup group_supported_os Supported RTOS
+ * \ref CFG_TUSB_OS must be defined to one of these
+ * @{ */
+#define OPT_OS_NONE 1 ///< No RTOS
+#define OPT_OS_FREERTOS 2 ///< FreeRTOS
+#define OPT_OS_MYNEWT 3 ///< Mynewt OS
+#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application
+#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK
+#define OPT_OS_RTTHREAD 6 ///< RT-Thread
+/** @} */
+
+
+// Allow to use command line to change the config name/location
+#ifdef CFG_TUSB_CONFIG_FILE
+ #include CFG_TUSB_CONFIG_FILE
+#else
+ #include "tusb_config.h"
+#endif
+
+
+
+/** \addtogroup group_configuration
+ * @{ */
+
+
+//--------------------------------------------------------------------
+// RootHub Mode Configuration
+// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
+//--------------------------------------------------------------------
+
+// Lower 4-bit is operational mode
+#define OPT_MODE_NONE 0x00 ///< Disabled
+#define OPT_MODE_DEVICE 0x01 ///< Device Mode
+#define OPT_MODE_HOST 0x02 ///< Host Mode
+
+// Higher 4-bit is max operational speed (corresponding to tusb_speed_t)
+#define OPT_MODE_FULL_SPEED 0x00 ///< Max Full Speed
+#define OPT_MODE_LOW_SPEED 0x10 ///< Max Low Speed
+#define OPT_MODE_HIGH_SPEED 0x20 ///< Max High Speed
+
+
+#ifndef CFG_TUSB_RHPORT0_MODE
+ #define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE
+#endif
+
+
+#ifndef CFG_TUSB_RHPORT1_MODE
+ #define CFG_TUSB_RHPORT1_MODE OPT_MODE_NONE
+#endif
+
+#if (((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST ) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST )) || \
+ (((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE))
+ #error "TinyUSB currently does not support same modes on more than 1 roothub port"
+#endif
+
+// Which roothub port is configured as host
+#define TUH_OPT_RHPORT ( ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST) ? 0 : (((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST) ? 1 : -1) )
+#define TUSB_OPT_HOST_ENABLED ( TUH_OPT_RHPORT >= 0 )
+
+// Which roothub port is configured as device
+#define TUD_OPT_RHPORT ( ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) ? 0 : (((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE) ? 1 : -1) )
+
+#if TUD_OPT_RHPORT == 0
+#define TUD_OPT_HIGH_SPEED ( (CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HIGH_SPEED )
+#else
+#define TUD_OPT_HIGH_SPEED ( (CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HIGH_SPEED )
+#endif
+
+#define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 )
+
+//--------------------------------------------------------------------+
+// COMMON OPTIONS
+//--------------------------------------------------------------------+
+
+// Debug enable to print out error message
+#ifndef CFG_TUSB_DEBUG
+ #define CFG_TUSB_DEBUG 0
+#endif
+
+// place data in accessible RAM for usb controller
+#ifndef CFG_TUSB_MEM_SECTION
+ #define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+ #define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
+#endif
+
+#ifndef CFG_TUSB_OS
+ #define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE OPTIONS
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+ #define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+#ifndef CFG_TUD_CDC
+ #define CFG_TUD_CDC 0
+#endif
+
+#ifndef CFG_TUD_MSC
+ #define CFG_TUD_MSC 0
+#endif
+
+#ifndef CFG_TUD_HID
+ #define CFG_TUD_HID 0
+#endif
+
+#ifndef CFG_TUD_AUDIO
+ #define CFG_TUD_AUDIO 0
+#endif
+
+#ifndef CFG_TUD_MIDI
+ #define CFG_TUD_MIDI 0
+#endif
+
+#ifndef CFG_TUD_VENDOR
+ #define CFG_TUD_VENDOR 0
+#endif
+
+#ifndef CFG_TUD_USBTMC
+ #define CFG_TUD_USBTMC 0
+#endif
+
+#ifndef CFG_TUD_DFU_RUNTIME
+ #define CFG_TUD_DFU_RUNTIME 0
+#endif
+
+#ifndef CFG_TUD_NET
+ #define CFG_TUD_NET 0
+#endif
+
+#ifndef CFG_TUD_BTH
+ #define CFG_TUD_BTH 0
+#endif
+
+//--------------------------------------------------------------------
+// HOST OPTIONS
+//--------------------------------------------------------------------
+#if TUSB_OPT_HOST_ENABLED
+ #ifndef CFG_TUSB_HOST_DEVICE_MAX
+ #define CFG_TUSB_HOST_DEVICE_MAX 1
+ #warning CFG_TUSB_HOST_DEVICE_MAX is not defined, default value is 1
+ #endif
+
+ //------------- HUB CLASS -------------//
+ #if CFG_TUH_HUB && (CFG_TUSB_HOST_DEVICE_MAX == 1)
+ #error there is no benefit enable hub with max device is 1. Please disable hub or increase CFG_TUSB_HOST_DEVICE_MAX
+ #endif
+
+ //------------- HID CLASS -------------//
+ #define HOST_CLASS_HID ( CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC )
+
+ #ifndef CFG_TUSB_HOST_ENUM_BUFFER_SIZE
+ #define CFG_TUSB_HOST_ENUM_BUFFER_SIZE 256
+ #endif
+
+ //------------- CLASS -------------//
+#endif // TUSB_OPT_HOST_ENABLED
+
+
+//------------------------------------------------------------------
+// Configuration Validation
+//------------------------------------------------------------------
+#if CFG_TUD_ENDPOINT0_SIZE > 64
+ #error Control Endpoint Max Packet Size cannot be larger than 64
+#endif
+
+#endif /* _TUSB_OPTION_H_ */
+
+/** @} */
diff --git a/sw/Core/Src/tinyusb/src/usb_descriptors.c b/sw/Core/Src/tinyusb/src/usb_descriptors.c
new file mode 100755
index 0000000..3d1c892
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/usb_descriptors.c
@@ -0,0 +1,267 @@
+/*
+ * 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 "tusb.h"
+
+enum
+{
+ VENDOR_REQUEST_WEBUSB = 1,
+ VENDOR_REQUEST_MICROSOFT = 2
+};
+
+
+
+
+/* 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] 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) )
+extern uint32_t serialno;
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device =
+{
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x0210, // 2.1 for webusb
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = 0xCafe,
+ .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_MIDI = 0,
+ ITF_NUM_MIDI_STREAMING,
+// ITF_NUM_CDC,
+// ITF_NUM_CDC_DATA,
+ ITF_NUM_VENDOR,
+ ITF_NUM_TOTAL
+};
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI_DESC_LEN + /*TUD_CDC_DESC_LEN + */TUD_VENDOR_DESC_LEN)
+
+ #define EPNUM_MIDI 0x01
+//#define EPNUM_CDC 2
+#define EPNUM_VENDOR 2
+
+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, 150),
+
+ // Interface number, string index, EP Out & EP In address, EP size
+ TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 64),
+
+// // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+//TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 5, 0x81, 8, EPNUM_CDC, 0x80 | EPNUM_CDC, TUD_OPT_HIGH_SPEED ? 512 : 64),
+//
+// Interface number, string index, EP Out & IN address, EP size
+TUD_VENDOR_DESCRIPTOR(ITF_NUM_VENDOR, 4, EPNUM_VENDOR, 0x80 | EPNUM_VENDOR, TUD_OPT_HIGH_SPEED ? 512 : 64)
+};
+
+#if TUD_OPT_HIGH_SPEED
+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, 150),
+
+ // Interface number, string index, EP Out & EP In address, EP size
+ TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 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
+}
+
+//--------------------------------------------------------------------+
+// 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_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+
+#define MS_OS_20_DESC_LEN 0xB2
+
+// BOS Descriptor is required for webUSB
+uint8_t const desc_bos[] =
+{
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
+
+ // Vendor Code, iLandingPage
+ TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
+
+ // Microsoft OS 2.0 descriptor
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
+};
+
+uint8_t const* 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),
+
+ // 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),
+
+ // 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_VENDOR, 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', '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 - 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: �{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}�.
+ '{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 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");
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// array of pointer to string descriptors
+char const* string_desc_arr [] =
+{
+ (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+ "Plinky", // 1: Manufacturer
+ "PlinkySynth MIDI", // 2: Product
+ "", // 3: Serials, should use chip ID
+ "TinyUSB WebUSB" // 4: Vendor Interface
+// "TinyUSB CDC", // 5: CDC Interface // unused
+};
+
+static uint16_t _desc_str[32];
+
+// 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;
+
+ uint8_t chr_count;
+
+ if ( index == 0)
+ {
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ }else
+ {
+ // 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);
+ if ( chr_count > 31 ) chr_count = 31;
+
+ char serialbuf[10];
+ if (index==3) {
+ chr_count = sprintf(serialbuf,"%08x", (unsigned int)serialno);
+ str=serialbuf;
+ }
+ // Convert ASCII string into UTF-16
+ for(uint8_t i=0; i<chr_count; i++)
+ {
+ _desc_str[1+i] = str[i];
+ }
+ }
+
+ // first byte is length (including header), second byte is string type
+ _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
+
+ return _desc_str;
+}
diff --git a/sw/Core/Src/tinyusb/src/usbmidi.c b/sw/Core/Src/tinyusb/src/usbmidi.c
new file mode 100755
index 0000000..39bf514
--- /dev/null
+++ b/sw/Core/Src/tinyusb/src/usbmidi.c
@@ -0,0 +1,385 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tusb.h"
+#include "main.h"
+
+
+enum
+{
+ VENDOR_REQUEST_WEBUSB = 1,
+ VENDOR_REQUEST_MICROSOFT = 2
+};
+
+extern uint8_t const desc_ms_os_20[];
+
+static bool web_serial_connected = false;
+
+
+#define URL "www.plinkysynth.com/webusb"
+
+const tusb_desc_webusb_url_t desc_url =
+{
+ .bLength = 3 + sizeof(URL) - 1,
+ .bDescriptorType = 3, // WEBUSB URL type
+ .bScheme = 1, // 0: http, 1: https
+ .url = URL
+};
+
+
+
+void OTG_FS_IRQHandler(void)
+{
+ tud_int_handler(0);
+}
+
+//int HAL_GetTick(void);
+#define millis HAL_GetTick
+typedef unsigned char u8;
+extern u8 led_ram[9][8];
+void board_led_write(bool state) {
+// led_ram[0][0]=state?255:0;
+ if (state)
+ GPIOD->BSRR=1;
+ else
+ GPIOD->BRR=1;
+}
+
+/* This MIDI example send sequence of note (on/off) repeatedly. To test on PC, you need to install
+ * synth software and midi connection management software. On
+ * - Linux (Ubuntu): install qsynth, qjackctl. Then connect TinyUSB output port to FLUID Synth input port
+ * - Windows: install MIDI-OX
+ * - MacOS: SimpleSynth
+ */
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+/* 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;
+void led_blinking_task(void);
+//void midi_task(void);
+
+extern TIM_HandleTypeDef htim1;
+
+static inline uint32_t mix(uint32_t a,uint32_t b,uint32_t c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+ return c;
+}
+uint32_t serialno;
+
+/*------------- MAIN -------------*/
+void midiinit(void)
+{
+#if 0
+
+ // based on https://github.com/hathach/tinyusb/blob/fix-stm32l4/hw/bsp/stm32l476disco/stm32l476disco.c
+ /* Enable Power Clock*/
+ __HAL_RCC_PWR_CLK_ENABLE();
+
+ /* Enable USB power on Pwrctrl CR2 register */
+ HAL_PWREx_EnableVddUSB();
+
+ GPIO_InitTypeDef GPIO_InitStruct;
+ // USB
+ /* Configure DM DP Pins */
+ GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12);
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+ /* Configure VBUS Pin */
+ GPIO_InitStruct.Pin = GPIO_PIN_11;
+ GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+ /* Enable USB FS Clock */
+ __HAL_RCC_USB_OTG_FS_CLK_ENABLE();
+
+ // L476Disco use general GPIO PC11 for VBUS sensing instead of dedicated PA9 as others
+ // Disable VBUS Sense and force device mode
+ USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_VBDEN;
+
+ USB_OTG_FS->GUSBCFG &= ~USB_OTG_GUSBCFG_FHMOD;
+ USB_OTG_FS->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD;
+
+ USB_OTG_FS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN;
+ USB_OTG_FS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL;
+#endif
+
+ uint32_t uid0=HAL_GetUIDw0 ();
+ uint32_t uid1=HAL_GetUIDw1 ();
+ uint32_t uid2=HAL_GetUIDw2 ();
+ serialno = mix(uid0,uid1,uid2);
+
+ tusb_init();
+}
+
+void webserial_task(void);
+
+void usb_midi_update(void) {
+ tud_task();
+// cdc_task();
+ webserial_task();
+}
+bool midi_receive(unsigned char packet[4]) {
+ return tud_midi_available() && tud_midi_packet_read(packet);
+}
+bool usb_midi_write(const uint8_t packet[4]) {
+ return tud_midi_packet_write(packet);
+}
+
+/*
+int miditest(void) {
+ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
+ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
+ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
+ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
+ GPIOD->MODER|=1;
+
+ while (1)
+ {
+ tud_task(); // tinyusb device task
+ led_blinking_task();
+ midi_task();
+ }
+ return 0;
+}*/
+
+
+//--------------------------------------------------------------------+
+// Device callbacks
+//--------------------------------------------------------------------+
+
+// 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 = BLINK_MOUNTED;
+}
+
+//--------------------------------------------------------------------+
+// WebUSB use vendor class
+//--------------------------------------------------------------------+
+
+// 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;
+
+ switch (request->bRequest)
+ {
+ case VENDOR_REQUEST_WEBUSB:
+ // match vendor request in BOS descriptor
+ // Get landing page url
+ return tud_control_xfer(rhport, request, (void*)&desc_url, desc_url.bLength);
+
+ case VENDOR_REQUEST_MICROSOFT:
+ if (request->wIndex == 7)
+ {
+ // Get Microsoft OS 2.0 compatible descriptor
+ uint16_t total_len;
+ memcpy(&total_len, desc_ms_os_20 + 8, 2);
+
+ return tud_control_xfer(rhport, request, (void*)desc_ms_os_20, total_len);
+ }
+ else
+ {
+ return false;
+ }
+
+ case 0x22:
+ // Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to
+ // connect and disconnect.
+ web_serial_connected = (request->wValue != 0);
+
+ // Always lit LED if connected
+ if (web_serial_connected)
+ {
+ board_led_write(true);
+// blink_interval_ms = BLINK_ALWAYS_ON;
+
+ //tud_vendor_write_str("\r\nTinyUSB WebUSB device example\r\n");
+ }
+ else
+ {
+ blink_interval_ms = BLINK_MOUNTED;
+ }
+
+ // response with status OK
+ return tud_control_status(rhport, request);
+
+ default:
+ // stall unknown request
+ return false;
+ }
+
+ return true;
+}
+void PumpWebUSB(void);
+
+void webserial_task(void)
+{
+ if (web_serial_connected)
+ {
+ // if (tud_vendor_available())
+ {
+ /*
+ uint8_t buf[64];
+ uint32_t count = tud_vendor_read(buf, sizeof(buf));
+
+ // echo back to both web serial and cdc
+ for (int c1=0;c1<count;++c1)
+ buf[c1]&=~0x20;
+ tud_vendor_write(buf, count);*/
+ PumpWebUSB();
+ }
+ }
+}
+
+/*
+//--------------------------------------------------------------------+
+// 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)
+{
+ (void)itf;
+
+ // connected
+ if (dtr && rts)
+ {
+ // print initial message when connected
+ tud_cdc_write_str("\r\nTinyUSB WebUSB device example\r\n");
+ }
+}
+
+// Invoked when CDC interface received data from host
+void tud_cdc_rx_cb(uint8_t itf)
+{
+ (void)itf;
+}
+*/
+/*
+//--------------------------------------------------------------------+
+// MIDI Task
+//--------------------------------------------------------------------+
+
+void DebugLog(const char *fmt, ...);
+
+void midi_task(void)
+{
+ static uint32_t start_ms = 0;
+
+ if (tud_midi_available()) {
+ unsigned char packet[4]={};
+ if (tud_midi_receive(packet)) {
+ // DebugLog("%02x %02x %02x %02x\r\n", packet[0],packet[1],packet[2],packet[3]);
+ }
+ }
+
+ // send note every 1000 ms
+ if (millis() - start_ms < 286) return; // not enough time
+ start_ms += 286;
+
+ // Previous positions in the note sequence.
+ int previous = note_pos - 1;
+
+ // 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;
+
+ // Send Note On for current position at full velocity (127) on channel 1.
+ tudi_midi_write24(0, 0x90, note_sequence[note_pos], 127);
+
+ // Send Note Off for previous note.
+ tudi_midi_write24(0, 0x80, note_sequence[previous], 0);
+
+ // Increment position
+ note_pos++;
+
+ // If we are at the end of the sequence, start over.
+ if (note_pos >= sizeof(note_sequence)) note_pos = 0;
+}
+
+//--------------------------------------------------------------------+
+// BLINKING TASK
+//--------------------------------------------------------------------+
+void led_blinking_task(void)
+{
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
+
+ // Blink every interval ms
+ if ( millis() - 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
+}
+*/
diff --git a/sw/Core/Src/tinyusb/tinyusb.Doxyfile b/sw/Core/Src/tinyusb/tinyusb.Doxyfile
new file mode 100755
index 0000000..4e380f1
--- /dev/null
+++ b/sw/Core/Src/tinyusb/tinyusb.Doxyfile
@@ -0,0 +1,2384 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = tinyusb
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = 0.4
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ../../web/gh_page
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = doxygen \
+ readme.markdown \
+ tinyusb \
+ boards \
+ tests/readme.md \
+ tools/readme.md \
+ demos/readme.md \
+ demos/device/readme.md \
+ demos/host/readme.md
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.f90 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf \
+ *.as \
+ *.js \
+ *.txt
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = readme.markdown
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# compiled with the --with-libclang option.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = 0.4
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER = doxygen/header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = CFG_TUSB_CONTROLLER_0_MODE=TUSB_MODE_HOST \
+ CFG_TUSB_CONTROLLER_1_MODE=TUSB_MODE_NONE \
+ CFG_TUSB_HOST_HID_KEYBOARD=1 \
+ ATTR_PACKED_STRUCT(x)=x \
+ CFG_TUSB_HOST_CDC_RNDIS=1 \
+ ATTR_ALIGNED(x)= \
+ CFG_TUSB_OS=TUSB_OS_NONE
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/sw/Core/Src/tinyusb/version.yml b/sw/Core/Src/tinyusb/version.yml
new file mode 100755
index 0000000..db103d8
--- /dev/null
+++ b/sw/Core/Src/tinyusb/version.yml
@@ -0,0 +1,3 @@
+# Newt uses this file to determine the version of a checked out repo.
+# This should always be 0.0.0 in the master branch.
+repo.version: 0.0.0