Skip to content

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

Open
digitalentity wants to merge 1 commit into
tinygo-org:devfrom
helvionics:de_stm32_usb_support
Open

machine/stm32: add OTG FS USB driver for F4/F7#5404
digitalentity wants to merge 1 commit into
tinygo-org:devfrom
helvionics:de_stm32_usb_support

Conversation

@digitalentity
Copy link
Copy Markdown

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 from c4c2e31 to 5e806ae Compare May 16, 2026 08:21
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 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
Copy link
Copy Markdown
Author

digitalentity commented May 17, 2026

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
Copy link
Copy Markdown
Member

sago35 commented May 17, 2026

This PR is related.
#5014

I think option 2 is good.

@digitalentity
Copy link
Copy Markdown
Author

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

@digitalentity
Copy link
Copy Markdown
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.

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