Skip to content

machine/stm32: add OTG FS USB driver for F4/F7#5404

Merged
deadprogram merged 1 commit into
tinygo-org:devfrom
helvionics:de_stm32_usb_support
Jul 12, 2026
Merged

machine/stm32: add OTG FS USB driver for F4/F7#5404
deadprogram merged 1 commit into
tinygo-org:devfrom
helvionics:de_stm32_usb_support

Conversation

@digitalentity

Copy link
Copy Markdown
Contributor

STM32F4 and F7 share the same OTG FS IP but have no USB driver in TinyGo. This adds a full device-mode driver covering CDC, HID, and MSC with working examples.

  • OTG FS has 4 physical EPs (0–3); virtual indices 4–7 fold onto them via physEP() so the existing machine/usb API is unchanged
  • F4 bypasses VBUS sensing via GCCFG.NOVBUSSENS; F7 uses the USB voltage regulator + GOTGCTL B-valid override instead
  • STM32F7 PLL_Q changed 2→9 to produce the 48 MHz clock required by USB/RNG/SDMMC; CK48MSEL cleared to select main PLL as source
  • HID and MSC descriptors remapped at init() to physical endpoint addresses (EP2/EP1 for HID, EP2/EP3 for MSC)
  • usb-storage example replaced machine.Flash with a FAT12 RAM disk so the host mounts without reformatting
  • MSC sendCSW sets queuedBytes before state transition to fix a missed byte-count on the status phase

Tested on CDC, HID and MSC examples.

CDC example, echo verified to work:

$ tinygo flash -target nucleo-f722ze -serial usb src/examples/echo/echo.go

2026-05-15T22:46:55.857388+02:00 homestation kernel: usb 3-3.2: new full-speed USB device number 32 using xhci_hcd
2026-05-15T22:46:55.998410+02:00 homestation kernel: usb 3-3.2: New USB device found, idVendor=239a, idProduct=0001, bcdDevice= 1.00
2026-05-15T22:46:55.998419+02:00 homestation kernel: usb 3-3.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
2026-05-15T22:46:55.998420+02:00 homestation kernel: usb 3-3.2: Product: STM32 USB Device
2026-05-15T22:46:55.998435+02:00 homestation kernel: usb 3-3.2: Manufacturer: TinyGo
2026-05-15T22:46:56.008407+02:00 homestation kernel: cdc_acm 3-3.2:1.0: ttyACM1: USB ACM device

HID example:

$ tinygo flash -target nucleo-f722ze src/examples/hid-keyboard/main.go

2026-05-15T22:46:26.183398+02:00 homestation kernel: usb 3-3.2: USB disconnect, device number 30
2026-05-15T22:46:26.578409+02:00 homestation kernel: usb 3-3.2: new full-speed USB device number 31 using xhci_hcd
2026-05-15T22:46:26.719416+02:00 homestation kernel: usb 3-3.2: New USB device found, idVendor=239a, idProduct=0001, bcdDevice= 1.00
2026-05-15T22:46:26.719425+02:00 homestation kernel: usb 3-3.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
2026-05-15T22:46:26.719426+02:00 homestation kernel: usb 3-3.2: Product: STM32 USB Device
2026-05-15T22:46:26.719427+02:00 homestation kernel: usb 3-3.2: Manufacturer: TinyGo
2026-05-15T22:46:26.734374+02:00 homestation kernel: cdc_acm 3-3.2:1.0: ttyACM1: USB ACM device
2026-05-15T22:46:26.736365+02:00 homestation kernel: input: TinyGo STM32 USB Device Keyboard as /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.2/3-3.2:1.2/0003:239A:0001.0016/input/input43
2026-05-15T22:46:26.811393+02:00 homestation kernel: input: TinyGo STM32 USB Device Mouse as /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.2/3-3.2:1.2/0003:239A:0001.0016/input/input44
2026-05-15T22:46:26.811403+02:00 homestation kernel: hid-generic 0003:239A:0001.0016: input,hidraw13: USB HID v1.11 Keyboard [TinyGo STM32 USB Device] on usb-0000:00:14.0-3.2/input2

MSC example, RAM disk verified to handle write/read correctly:

$ tinygo flash -target nucleo-f722ze src/examples/usb-storage/main.go

2026-05-15T22:45:41.127390+02:00 homestation kernel: usb 3-3.2: USB disconnect, device number 28
2026-05-15T22:45:41.136726+02:00 homestation kernel: umount: attempt to access beyond end of device
2026-05-15T22:45:41.136739+02:00 homestation kernel: sdd: rw=0, sector=0, nr_sectors = 1 limit=0
2026-05-15T22:45:41.136740+02:00 homestation kernel: FAT-fs (sdd): unable to read boot sector to mark fs as dirty
2026-05-15T22:45:42.897391+02:00 homestation kernel: usb 3-3.2: new full-speed USB device number 29 using xhci_hcd
2026-05-15T22:45:43.038395+02:00 homestation kernel: usb 3-3.2: New USB device found, idVendor=239a, idProduct=0001, bcdDevice= 1.00
2026-05-15T22:45:43.038403+02:00 homestation kernel: usb 3-3.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
2026-05-15T22:45:43.038404+02:00 homestation kernel: usb 3-3.2: Product: STM32 USB Device
2026-05-15T22:45:43.038404+02:00 homestation kernel: usb 3-3.2: Manufacturer: TinyGo
2026-05-15T22:45:43.053488+02:00 homestation kernel: cdc_acm 3-3.2:1.0: ttyACM1: USB ACM device
2026-05-15T22:45:43.053494+02:00 homestation kernel: usb-storage 3-3.2:1.2: USB Mass Storage device detected
2026-05-15T22:45:43.054367+02:00 homestation kernel: scsi host2: usb-storage 3-3.2:1.2
2026-05-15T22:45:44.101372+02:00 homestation kernel: scsi 2:0:0:0: Direct-Access     TinyGo   Mass Storage     1.0  PQ: 0 ANSI: 0
2026-05-15T22:45:44.101380+02:00 homestation kernel: sd 2:0:0:0: Attached scsi generic sg3 type 0
2026-05-15T22:45:44.103369+02:00 homestation kernel: sd 2:0:0:0: [sdd] 128 512-byte logical blocks: (65.5 kB/64.0 KiB)
2026-05-15T22:45:44.105369+02:00 homestation kernel: sd 2:0:0:0: [sdd] Write Protect is off
2026-05-15T22:45:44.105376+02:00 homestation kernel: sd 2:0:0:0: [sdd] Mode Sense: 03 00 00 00
2026-05-15T22:45:44.106370+02:00 homestation kernel: sd 2:0:0:0: [sdd] No Caching mode page found
2026-05-15T22:45:44.106376+02:00 homestation kernel: sd 2:0:0:0: [sdd] Assuming drive cache: write through
2026-05-15T22:45:44.121371+02:00 homestation kernel:  sdd:
2026-05-15T22:45:44.121379+02:00 homestation kernel: sd 2:0:0:0: [sdd] Attached SCSI removable disk

Fixes #4751

@digitalentity
digitalentity force-pushed the de_stm32_usb_support branch 2 times, most recently from 5e806ae to 3aaf8e5 Compare May 16, 2026 08:23
@deadprogram

Copy link
Copy Markdown
Member

Hello @digitalentity thank you very much for working on this.

I would suggest breaking this into a few different PRs so that it is easier to review the changes. For example:

  • PR for HID and MSC descriptor remap.
  • PR for the usb-storage example changes.
  • PR for adding the OTG support for F4/F7

Very importantly, at present we do not have any code in the machine/usb package that is platform specific, it just describes the protocols.

You should figure out how to isolate your changes so that platform code does not end up in that package or sub-packages. Probably after you split off the non-platform specific changes, this will be a lot easier to achieve.

@digitalentity

digitalentity commented May 17, 2026

Copy link
Copy Markdown
Contributor Author

Hey, thanks for the review.

Very importantly, at present we do not have any code in the machine/usb package that is platform specific, it just describes the protocols.

The HID and MSC descriptor remap stems from the fact that STM32 has only 4 EPs and the TinyGo system currently assumes 8. I see three possible options:

  1. Allow platform-specific code in the machine/usb package. I totally agree that this is very suboptimal.
  2. Change the descriptors globally to fit into the 4 EPs on all platforms (essentially change the default assumption about 8 EPs). The benefit of this is that we can also get rid of physEP() mapping which is an ugly hack TBH.
  3. Move the descriptors entirely to the application space - supplying the correct descriptors would be the application's responsibility.

I suggest we do the (2). This comes with some risk of breaking existing applications that use USB though.

@deadprogram

Copy link
Copy Markdown
Member

The HID and MSC descriptor remap stems from the fact that STM32 has only 4 EPs and the TinyGo system currently assumes 8. I see three possible options:

  1. Allow platform-specific code in the machine/usb package. I totally agree that this is very suboptimal.
  2. Change the descriptors globally to fit into the 4 EPs on all platforms (essentially change the default assumption about 8 EPs). The benefit of this is that we can also get rid of physEP() mapping which is an ugly hack TBH.
  3. Move the descriptors entirely to the application space - supplying the correct descriptors would be the application's responsibility.

I suggest we do the (2). This comes with some risk of breaking existing applications that use USB though.

Perhaps some of the serious USB people like @sago35 @mikesmitty and others of course can offer their feedback on this question.

If option number 2 is practical on all platforms that sounds like the best choice, but I have not looked into that question yet.

@sago35

sago35 commented May 17, 2026

Copy link
Copy Markdown
Member

This PR is related.
#5014

I think option 2 is good.

@digitalentity

Copy link
Copy Markdown
Contributor Author

I had a crack at it in #5405, @sago35 PTAL, thanks!

@digitalentity

Copy link
Copy Markdown
Contributor Author

On Option 2 - EPs are bidirectional, nothing is preventing us from doing mix & match - with 4 EPs (effectively 8 unidirectional streams) we can fit HID (2), MSC (2) and CDC (3) into the same device in principle.

@deadprogram

Copy link
Copy Markdown
Member

Hello @digitalentity any chance of getting this PR back in action now that the endpoint changes have been merged? Please and thank you! 😸

@digitalentity

Copy link
Copy Markdown
Contributor Author

Hey @deadprogram, yes, I plan to get back to this, been too busy with other things lately 🧑‍🏭

@digitalentity
digitalentity force-pushed the de_stm32_usb_support branch 2 times, most recently from 1d9d344 to 7b5155e Compare July 9, 2026 11:15
@digitalentity

Copy link
Copy Markdown
Contributor Author

So, this is a rebase which builds, but is completely untested. I still need to address the flash support.

@digitalentity
digitalentity force-pushed the de_stm32_usb_support branch from 7b5155e to f329e11 Compare July 9, 2026 18:07
@deadprogram

Copy link
Copy Markdown
Member

@digitalentity please see formatting error:

Unformatted:
make: *** [GNUmakefile:213: fmt-check] Error 1
  src/machine/machine_stm32_otgfs_usb.go
Error: Process completed with exit code 2.

@digitalentity

Copy link
Copy Markdown
Contributor Author

Weird, I definitely ran make test and go fmt, but whaterever :)

@deadprogram

Copy link
Copy Markdown
Member

Not sure but perhaps you also need to rebase the latest dev into this branch? The Windows build failure is very odd, really do not know why that is happening.

@digitalentity

Copy link
Copy Markdown
Contributor Author

Not sure but perhaps you also need to rebase the latest dev into this branch? The Windows build failure is very odd, really do not know why that is happening.

This is rebased onto the current dev. Looks like it's a runner issue:

Error: C:\Users\runneradmin\go\pkg\mod\tinygo.org\x\go-llvm@v0.0.0-20260422095634-06c6725fe5e6\analysis.go:16:10: fatal error: llvm-c/Analysis.h: No such file or directory
   16 | #include "llvm-c/Analysis.h" // If you are getting an error here you need to build or install LLVM, see https://tinygo.org/docs/guides/build/
      |          ^~~~~~~~~~~~~~~~~~~
compilation terminated.

@deadprogram

Copy link
Copy Markdown
Member

Other Windows runner jobs are passing, so ?

@digitalentity

Copy link
Copy Markdown
Contributor Author

Other Windows runner jobs are passing, so ?

The windows builds for PR #5511 also fail. Definitely a runner or cached llvm issue.

@deadprogram

Copy link
Copy Markdown
Member

Other Windows runner jobs are passing, so ?

The windows builds for PR #5511 also fail. Definitely a runner or cached llvm issue.

I saw that a few minutes ago with another build and just tried deleting some caches. GH fun and games!

@digitalentity
digitalentity force-pushed the de_stm32_usb_support branch from fc909b3 to e2f6ed0 Compare July 10, 2026 17:53
@digitalentity

Copy link
Copy Markdown
Contributor Author

All 3 common examples work:

  • src/examples/echo/echo.go
  • src/examples/hid-keyboard/main.go
  • src/examples/usb-storage/main.go

@digitalentity

Copy link
Copy Markdown
Contributor Author

@deadprogram should I factor out the usb-storage example changes (use tiny RAM disk instead of flash) or is this good as-is?

@deadprogram

Copy link
Copy Markdown
Member

@deadprogram should I factor out the usb-storage example changes (use tiny RAM disk instead of flash) or is this good as-is?

I think the RAM disk change is probably fine.

Now that the CI has been fixed using #5512 can you please rebase just once more so the tests can run? Thanks and sorry about the inconvenience.

STM32F4 and F7 share the same OTG FS IP but have no USB driver in
TinyGo. This adds a full device-mode driver covering CDC, HID, and
MSC with working examples.

- OTG FS has 4 physical EPs (0–3); virtual indices 4–7 fold onto
  them via physEP() so the existing machine/usb API is unchanged
- F4 bypasses VBUS sensing via GCCFG.NOVBUSSENS; F7 uses the USB
  voltage regulator + GOTGCTL B-valid override instead
- STM32F7 PLL_Q changed 2→9 to produce the 48 MHz clock required
  by USB/RNG/SDMMC; CK48MSEL cleared to select main PLL as source
- HID and MSC descriptors remapped at init() to physical endpoint
  addresses (EP2/EP1 for HID, EP2/EP3 for MSC)
- usb-storage example replaced machine.Flash with a FAT12 RAM disk
  so the host mounts without reformatting
- MSC sendCSW sets queuedBytes before state transition to fix a
  missed byte-count on the status phase
@digitalentity
digitalentity force-pushed the de_stm32_usb_support branch from e2f6ed0 to 6e56f02 Compare July 12, 2026 07:18
@digitalentity

Copy link
Copy Markdown
Contributor Author

@deadprogram done, CI/CD tests passing

@deadprogram

Copy link
Copy Markdown
Member

Thank you very much for all your work on this @digitalentity now merging!

@deadprogram
deadprogram merged commit c1a4ed1 into tinygo-org:dev Jul 12, 2026
20 checks passed
@sago35

sago35 commented Jul 12, 2026

Copy link
Copy Markdown
Member

great work! Thank You.

@digitalentity
digitalentity deleted the de_stm32_usb_support branch July 12, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

stm32 needs USB implementation

3 participants