Skip to content

cpu/stm32/usbdev_fs: allow inverted disconnect GPIO#21718

Merged
maribu merged 1 commit intoRIOT-OS:masterfrom
maribu:cpu/stm32/usbdev_fs/inverted-disconnect-pin
Oct 7, 2025
Merged

cpu/stm32/usbdev_fs: allow inverted disconnect GPIO#21718
maribu merged 1 commit intoRIOT-OS:masterfrom
maribu:cpu/stm32/usbdev_fs/inverted-disconnect-pin

Conversation

@maribu
Copy link
Copy Markdown
Member

@maribu maribu commented Sep 14, 2025

Contribution description

The STM32F3 requires a dedicated digital signal to emulate a disconnect event by pulling D+ down via a 1.5 kOhm resistor. Some boards, such as the OLIMEXINO-STM32F3, do not directly connect a GPIO but place a transistor in between. Depending on the exact implementation, the logic level may end up being inverted compared to directly connecting a GPIO.

This adds a flag member to the stm32_usbdev_fs_config_t and a new flag to indicate inverted logic. In addition the members in the struct are sorted by alignment, as this is a foolproof algorithm to prevent wasting memory on unneeded padding.

Finally, the USB driver is adapted to honor the flag.

Testing procedure

No regressions on STM32 boards with the usbdev_fs USB peripheral (e.g. Nucleo-F303Ze, Bluepill with STM32F1, p-Nucleo-WB55).

We do not have a board upstream that has an inverted disconnect pin yet, but I pinky-promise I tested this with one downstream for which this fixed an issue of USB not re-enumerating on reboot.

Issues/PRs references

None

@maribu maribu added Type: new feature The issue requests / The PR implemements a new feature for RIOT CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Sep 14, 2025
@github-actions github-actions bot added Platform: ARM Platform: This PR/issue effects ARM-based platforms Area: cpu Area: CPU/MCU ports labels Sep 14, 2025
@riot-ci
Copy link
Copy Markdown

riot-ci commented Sep 14, 2025

Murdock results

✔️ PASSED

26d8fe4 cpu/stm32/usbdev_fs: allow inverted disconnect GPIO

Success Failures Total Runtime
10511 0 10514 14m:26s

Artifacts

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Sep 15, 2025

We do not have a board upstream that has an inverted disconnect pin yet, but I pinky-promise I tested this with one downstream for which this fixed an issue of USB not re-enumerating on reboot.

Did you also test it with a board where the pin is not inverted? One of the upstream boards?

I think I have a board with USB peripheral somewhere to test this.

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Sep 15, 2025

No, I "tested" this by "staring hard at the diff". But I'm having a Bluepill somewhere in reach, I'll give it a spin in a sec.

Comment on lines +62 to +63
uint8_t irqn; /**< IRQ channel */
uint8_t apb; /**< APB bus */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change the order in the struct? 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See commit message / PR description:

In addition the members in the struct are sorted by alignment, as this is a foolproof algorithm to prevent wasting memory on unneeded padding. (It does not yield savings here, but it is a good practice.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. consider:

#include <stdio.h>
#include <stdint.h>

struct foo1 {
    uint8_t a;
    uint32_t b;
    uint8_t c;
    uint16_t d;
};

struct foo2 {
    uint32_t b;
    uint16_t d;
    uint8_t a;
    uint8_t c;
};

int main(void)
{
    printf("sizeof(struct foo1) = %zu, sizeof(struct foo2) = %zu\n",
           sizeof(struct foo1), sizeof(struct foo2));
    return 0;
}
~ ➜ gcc -Os -o test test.c                      
~ ➜ ./test                           
sizeof(struct foo1) = 12, sizeof(struct foo2) = 8

Both structs would be identical for the given use case, but one wastes 4 B of RAM.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, the gpio_af_t actually can be implemented as uint8_t and sorting that as well. With that sorted in correctly, the struct actually will be smaller than before, despite having one field more :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern was that some part of the code might be dependant on the order of the struct, but I guess for the internal STM32 configuration structure, there is no wild casting going on from one struct type to another.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there would be no reason to cast config structs. One could embed one in another and use container_of() to almost safely cast back to the "outer/child struct".

Just casting between to unrelated structs would be asking for trouble.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern was that some part of the code might be dependant on the order of the struct,

Well 🤣

image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh man, C++ is a pain in the ass

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Sep 15, 2025

Tested just now:

Type '/exit' to exit.
help
2025-09-15 11:15:59,643 # in(): This is mhelp
2025-09-15 11:15:59,644 # shell: command not found: main():
> 
2025-09-15 11:16:00,504 # help
2025-09-15 11:16:00,505 # Command              Description
2025-09-15 11:16:00,505 # ---------------------------------------
2025-09-15 11:16:00,506 # pm                   interact with layered PM subsystem
2025-09-15 11:16:00,507 # ps                   Prints information about running threads.
2025-09-15 11:16:00,510 # rtc                  control RTC peripheral interface
2025-09-15 11:16:00,511 # saul                 interact with sensors and actuators using SAUL
2025-09-15 11:16:00,512 # version              Prints current RIOT_VERSION
2025-09-15 11:16:00,514 # reboot               Reboot the node
> reboot
2025-09-15 11:16:02,079 # reboot
2025-09-15 11:16:02,679 # Serial port disconnected, waiting to get reconnected...
2025-09-15 11:16:03,680 # Try to reconnect to /dev/ttyACM0 again...
2025-09-15 11:16:03,684 # Reconnected to serial port /dev/ttyACM0
help
2025-09-15 11:16:10,005 # : This is  emain(help
2025-09-15 11:16:10,006 # shell: command not found: main():
> 
2025-09-15 11:16:11,812 # help
2025-09-15 11:16:11,812 # Command              Description
2025-09-15 11:16:11,813 # ---------------------------------------
2025-09-15 11:16:11,814 # pm                   interact with layered PM subsystem
2025-09-15 11:16:11,815 # ps                   Prints information about running threads.
2025-09-15 11:16:11,817 # rtc                  control RTC peripheral interface
2025-09-15 11:16:11,819 # saul                 interact with sensors and actuators using SAUL
2025-09-15 11:16:11,820 # version              Prints current RIOT_VERSION
2025-09-15 11:16:11,821 # reboot               Reboot the node
[ 9952.018172] usb 1-1: new full-speed USB device number 13 using xhci_hcd
[ 9952.170525] usb 1-1: New USB device found, idVendor=1209, idProduct=7d00, bcdDevice= 1.00
[ 9952.170539] usb 1-1: New USB device strings: Mfr=3, Product=2, SerialNumber=4
[ 9952.170546] usb 1-1: Product: bluepill-stm32f103c8
[ 9952.170551] usb 1-1: Manufacturer: RIOT-os.org
[ 9952.170556] usb 1-1: SerialNumber: 6B28815B8E2570ED
[ 9952.260484] cdc_acm 1-1:1.0: ttyACM0: USB ACM device
[ 9952.260527] usbcore: registered new interface driver cdc_acm
[ 9952.260530] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[ 9966.425346] usb 1-1: USB disconnect, device number 13
[ 9966.666215] usb 1-1: new full-speed USB device number 14 using xhci_hcd
[ 9966.818565] usb 1-1: New USB device found, idVendor=1209, idProduct=7d00, bcdDevice= 1.00
[ 9966.818579] usb 1-1: New USB device strings: Mfr=3, Product=2, SerialNumber=4
[ 9966.818586] usb 1-1: Product: bluepill-stm32f103c8
[ 9966.818591] usb 1-1: Manufacturer: RIOT-os.org
[ 9966.818596] usb 1-1: SerialNumber: 6B28815B8E2570ED
[ 9966.872578] cdc_acm 1-1:1.0: ttyACM0: USB ACM device

So even re-enumeration after reboot (the important part) does still work.

Copy link
Copy Markdown
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#define USBDEV_CPU_DMA_REQUIREMENTS __attribute__((aligned(USBDEV_CPU_DMA_ALIGNMENT)))

/**
* @name Flags used in @ref stm32_usbdev_fs_config_t
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maribu maribu force-pushed the cpu/stm32/usbdev_fs/inverted-disconnect-pin branch from 2e0f128 to 3b8fef7 Compare September 15, 2025 14:33
@maribu
Copy link
Copy Markdown
Member Author

maribu commented Sep 15, 2025

Ah, we could do better having the different two flavors of the USB peripheral more prominently appear in the doc. I'll rework it here soonish

@maribu maribu force-pushed the cpu/stm32/usbdev_fs/inverted-disconnect-pin branch from 3b8fef7 to 3e6e965 Compare September 15, 2025 18:27
@maribu maribu added this pull request to the merge queue Oct 7, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 7, 2025
@maribu maribu force-pushed the cpu/stm32/usbdev_fs/inverted-disconnect-pin branch from 3e6e965 to b1b2664 Compare October 7, 2025 12:49
@github-actions github-actions bot added the Area: boards Area: Board ports label Oct 7, 2025
@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 7, 2025

Ah, we could do better having the different two flavors of the USB peripheral more prominently appear in the doc. I'll rework it here soonish

Is that still planned for this PR?

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 7, 2025

It actually is already distinct, I just didn't track the development close enough to be aware that the synopsis driver has been split out and used by different CPUs.

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 7, 2025

You can squash and then we try to merge again 🤔

The STM32F3 requires a dedicated digital signal to emulate a disconnect
event by pulling D+ down via a 1.5 kOhm resistor. Some boards, such as
the OLIMEXINO-STM32F3, do not directly connect a GPIO but place a
transistor in between. Depending on the exact implementation, the logic
level may end up being inverted compared to directly connecting a
GPIO.

This adds a flag member to the `stm32_usbdev_fs_config_t` and a new flag
to indicate inverted logic. In addition the members in the struct are
sorted by alignment, as this is a foolproof algorithm to prevent wasting
memory on unneeded padding.

Finally, the USB driver is adapted to honor the flag.

Co-authored-by: crasbe <[email protected]>
@maribu maribu force-pushed the cpu/stm32/usbdev_fs/inverted-disconnect-pin branch from 64f356d to 26d8fe4 Compare October 7, 2025 15:42
@maribu maribu enabled auto-merge October 7, 2025 15:42
@maribu maribu added this pull request to the merge queue Oct 7, 2025
Merged via the queue into RIOT-OS:master with commit 9cbd344 Oct 7, 2025
25 checks passed
@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 8, 2025

Whoohoo! Time to open a PR that needs this magic 🎉

@maribu maribu deleted the cpu/stm32/usbdev_fs/inverted-disconnect-pin branch October 8, 2025 04:28
@benpicco benpicco added this to the Release 2025.10 milestone Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: boards Area: Board ports Area: cpu Area: CPU/MCU ports CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ARM Platform: This PR/issue effects ARM-based platforms Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants