Skip to content

machine: add USBDevice.Attach and USBDevice.Detach - #5563

Merged
deadprogram merged 4 commits into
devfrom
usbdev-attach-detach
Aug 20, 2026
Merged

machine: add USBDevice.Attach and USBDevice.Detach#5563
deadprogram merged 4 commits into
devfrom
usbdev-attach-detach

Conversation

@sago35

@sago35 sago35 commented Jul 28, 2026

Copy link
Copy Markdown
Member

This PR adds machine.USBDev.Attach() and machine.USBDev.Detach(), exposing
USB soft-connect control (the D+ pull-up) to user code.

Motivation

The USB device is attached to the bus automatically during startup, before
user code has had a chance to finish its USB configuration (usb.VendorID /
usb.ProductID / usb.Product, additional interfaces registered through
machine.ConfigureUSBEndpoint, ...). For composite devices such as keyboards
this means the host may enumerate a half-configured device: identifiers and
interfaces set from main() can arrive after the host has already read the
descriptors.

With this API, an application or library can stay detached until its
configuration is complete, so the host enumerates the finished device exactly
once:

func init() {
      machine.USBDev.Detach() // stay invisible to the host for now
}

func main() {
      usb.Product = "my-keyboard"
      // ... register HID interfaces, set VID/PID, ...
      machine.USBDev.Attach() // now let the host enumerate
}

It can also be used to force re-enumeration without replugging the cable
(Detach() → Attach()).

Prior art

Soft-connect control is a well-established primitive in embedded USB stacks,
exposed for exactly these use cases (delaying enumeration until configuration
is complete, and forcing re-enumeration from software):

  • Arduino: USBDevice.detach() / USBDevice.attach() (AVR and SAMD
    cores).
  • TinyUSB: tud_disconnect() / tud_connect().
  • Zephyr: usb_dc_detach() / usb_dc_attach() in the device-controller
    driver API.
  • STM32 HAL: HAL_PCD_DevDisconnect() / HAL_PCD_DevConnect().

The Attach/Detach naming follows the USB specification's bus-state wording
(a device is "attached" to the bus), which is also where the SAMD register
bit name (CTRLB.DETACH) comes from — and matches the Arduino API, easing
migration for users coming from that ecosystem.

Implementation

One-register operation per chip family, following each reference manual:

┌────────────────────┬─────────────────────────────┐
│        Chip        │          Mechanism          │
├────────────────────┼─────────────────────────────┤
│ atsamd21, atsamd51 │ USB_DEVICE.CTRLB DETACH bit │
├────────────────────┼─────────────────────────────┤
│ nrf52840           │ USBD.USBPULLUP              │
├────────────────────┼─────────────────────────────┤
│ rp2040, rp2350     │ SIE_CTRL PULLUP_EN bit      │
└────────────────────┴─────────────────────────────┘

The ESP32 variants are not covered: their USB is a Serial/JTAG peripheral
with no equivalent soft-connect concept. Existing behavior is unchanged —
the methods are purely additive.

Testing

  • Compile-tested a program calling both methods for all five chip families
    (xiao-ble, xiao, wioterminal, pico, pico2).
  • Verified on hardware with a XIAO nRF52840 Sense running a Vial keyboard
    firmware (tinygo-keyboard)
    that detaches in a package init() and attaches after configuration:
    the host enumerates the device once, with the complete configuration
    (CDC + keyboard/mouse/raw HID interfaces and a custom ProductID).**

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

Size difference with the dev branch:

Binary size difference
 flash                          ram
 before   after   diff          before   after   diff
  19684   19684      0   0.00%    7052    7052      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/adafruit4650
  63228   63228      0   0.00%    6788    6788      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
  10024   10024      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
  14628   14628      0   0.00%    7396    7396      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx
  10184   10184      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
  12592   12592      0   0.00%    7172    7172      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/apds9960/proximity/main.go
  11136   11136      0   0.00%    5360    5360      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
   7588    7588      0   0.00%    2312    2312      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
   9272    9272      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
   8660    8660      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
  71408   71408      0   0.00%    3656    3656      0   0.00% tinygo build -size short -o ./build/test.hex -target=pinetime     ./examples/bma42x/main.go
  67120   67120      0   0.00%    6804    6804      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
  28952   28952      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
  65720   65720      0   0.00%    6828    6828      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
  13080   13080      0   0.00%    5412    5412      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bmp388/main.go
  24024   24024      0   0.00%    6220    6220      0   0.00% tinygo build -size short -o ./build/test.hex -target=metro-rp2350 ./examples/bno08x/i2c/main.go
   8008    8008      0   0.00%    3344    3344      0   0.00% tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
  22116   22116      0   0.00%    3548    3548      0   0.00% tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
  30556   30556      0   0.00%    5576    5576      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/alarms/main.go
  44492   44492      0   0.00%    5576    5576      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/basic/main.go
   4592    4592      0   0.00%    2272    2272      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go
  71884   71884      0   0.00%    7588    7588      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi
  67996   67996      0   0.00%    9628    9628      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi
   7128    7128      0   0.00%    2276    2276      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/gc9a01/main.go
  69296   69296      0   0.00%    6964    6964      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/i2c/main.go
  69984   69984      0   0.00%    7108    7108      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/uart/main.go
   9684    9684      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/hcsr04/main.go
   5700    5700      0   0.00%    2272    2272      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/customchar/main.go
   5740    5740      0   0.00%    2272    2272      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
  11600   11600      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
  15460   15460      0   0.00%    7172    7172      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
  16184   16184      0   0.00%    2356    2356      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
  11224   11224      0   0.00%    7516    7516      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
  12300   12300      0   0.00%    5476    5476      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/basic
  30528   30528      0   0.00%   38676   38676      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/pyportal_boing
  11276   11276      0   0.00%    7516    7516      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/scroll
  12396   12396      0   0.00%    5476    5476      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/scroll
 264200  264200      0   0.00%   47348   47348      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/slideshow
  11740   11740      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
  14868   14868      0   0.00%    7172    7172      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
  26720   26720      0   0.00%    2320    2320      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
  29360   29360      0   0.00%    7428    7428      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/lsm303dlhc/main.go
  13592   13592      0   0.00%    5388    5388      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
  11272   11272      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
  10228   10228      0   0.00%    5372    5372      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
  10672   10672      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017-multiple/main.go
  10680   10680      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
  73304   73304      0   0.00%    6844    6844      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp2515/main.go
  28368   28368      0   0.00%    4080    4080      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
  28384   28384      0   0.00%    6124    6124      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit-v2 ./examples/microbitmatrix/main.go
   8724    8724      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
   8628    8628      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
  78096   78096      0   0.00%    8056    8056      0   0.00% tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/main.go
  15104   15104      0   0.00%    6232    6232      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
   6428    6428      0   0.00%    3284    3284      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
   4812    4812      0   0.00%    2276    2276      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
  13160   13160      0   0.00%    6208    6208      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw/soil-sensor
  14504   14504      0   0.00%    6216    6216      0   0.00% tinygo build -size short -o ./build/test.hex -target=qtpy-rp2040 ./examples/seesaw/rotary-encoder
   2873    2873      0   0.00%     560     560      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
  16636   16636      0   0.00%    6280    6280      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico     ./examples/sgp30
   9100    9100      0   0.00%    7388    7388      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
  57904   57904      0   0.00%    3688    3688      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
  57904   57904      0   0.00%    3696    3696      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht4x/main.go
  57904   57904      0   0.00%    3688    3688      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
  13572   13572      0   0.00%    8972    8972      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao-ble ./examples/ssd1306/
  14424   14424      0   0.00%    6160    6160      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao-rp2040 ./examples/ssd1306/
  14524   14524      0   0.00%    6160    6160      0   0.00% tinygo build -size short -o ./build/test.hex -target=thumby ./examples/ssd1306/
   6084    6084      0   0.00%    2276    2276      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
   6932    6932      0   0.00%    2276    2276      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
   6660    6660      0   0.00%    2276    2276      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
  18048   18048      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
  10880   10880      0   0.00%    5140    5140      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-bluefruit ./examples/tone
  11256   11256      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/tm1637/main.go
  13708   13708      0   0.00%    6220    6220      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/touch/capacitive
   9936    9936      0   0.00%    7380    7380      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
  13404   13404      0   0.00%    7576    7576      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
  17028   17028      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
  15600   15600      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl6180x/main.go
  25876   25876      0   0.00%   14336   14336      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-nrf52840-sense ./examples/waveshare-epd/epd1in54/main.go
   6544    6544      0   0.00%    2316    2316      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
   6132    6132      0   0.00%    2308    2308      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
   6436    6436      0   0.00%    2316    2316      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
  29092   29092      0   0.00%   19296   19296      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/waveshare-epd/epd2in66b/main.go
   8340    8340      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
   5978    5978      0   0.00%    8448    8448      0   0.00% tinygo build -size short -o ./build/test.bin -target=m5stamp-c3          ./examples/ws2812
  64104   64104      0   0.00%    6548    6548      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
   1757    1757      0   0.00%     600     600      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino   ./examples/ws2812
   1264    1264      0   0.00%     182     182      0   0.00% tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
   7320    7320      0   0.00%    8728    8728      0   0.00% tinygo build -size short -o ./build/test.bin -target=xiao-esp32c3 ./examples/ws2812
  33172   33172      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
  17616   17616      0   0.00%    5324    5324      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
  13020   13020      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go
  13520   13520      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/veml6070/main.go
   8092    8092      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/simple/main.go
  10004   10004      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/speed/main.go
   8052    8052      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/simple/main.go
  10428   10428      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/speed/main.go
   7552    7552      0   0.00%    3316    3316      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-f103rb ./examples/shiftregister/main.go
   7144    7144      0   0.00%    2272    2272      0   0.00% tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351/main.go
  14892   14892      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
  12516   12516      0   0.00%    5364    5364      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
  78576   78576      0   0.00%    6940    6940      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
  38924   38924      0   0.00%    6872    6872      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
  72988   72988      0   0.00%    6948    6948      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
   8436    8436      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
  72492   72492      0   0.00%    6944    6944      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
  72884   72884      0   0.00%    6948    6948      0   0.00% tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
  14800   14800      0   0.00%    6184    6184      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
  13908   13908      0   0.00%    6168    6168      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8591/
   9960    9960      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
  13468   13468      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina219/main.go
   9632    9632      0   0.00%    5248    5248      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
  75060   75060      0   0.00%   11368   11368      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
  63252   63252      0   0.00%    8836    8836      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/
  11268   11268      0   0.00%    7388    7388      0   0.00% tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/
  11828   11828      0   0.00%    6156    6156      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/xpt2046/main.go
  13480   13480      0   0.00%    4932    4932      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
  44284   44284      0   0.00%    9484    9484      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/sx127x/lora_rxtx/
  35104   35104      0   0.00%    7936    7936      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/ssd1289/main.go
  14196   14196      0   0.00%    7148    7148      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/irremote/main.go
  14784   14784      0   0.00%    6240    6240      0   0.00% tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/uc8151/main.go
  15204   15204      0   0.00%    6676    6676      0   0.00% tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/waveshare-epd/epd2in9v2/main.go
  13104   13104      0   0.00%    6236    6236      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd4x/main.go
   9212    9212      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=circuitplay-express ./examples/makeybutton/main.go
  10684   10684      0   0.00%    5364    5364      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ds18b20/main.go
 124060  124060      0   0.00%    8384    8384      0   0.00% tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/lora/lorawan/atcmd/
  19148   19148      0   0.00%    7808    7808      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/as560x/main.go
  12944   12944      0   0.00%    6176    6176      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu6886/main.go
   9028    9028      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ttp229/main.go
  70284   70284      0   0.00%    7704    7704      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/ndir/main_ndir.go
  62512   62512      0   0.00%    3784    3784      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/main_ndir.go
  66736   66736      0   0.00%    6860    6860      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go
  12464   12464      0   0.00%    6168    6168      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go
  14484   14484      0   0.00%    6204    6204      0   0.00% tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
  11304   11304      0   0.00%    6640    6640      0   0.00% tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt
  69972   69972      0   0.00%    7672    7672      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mcp9808/main.go
  46052   46052      0   0.00%    8268    8268      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/main.go
  14796   14796      0   0.00%    6164    6164      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go
  13312   13312      0   0.00%    5152    5152      0   0.00% tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go
  63860   63860      0   0.00%    6576    6576      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/max6675/main.go
  15428   15428      0   0.00%    6224    6224      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/ens160/main.go
  19192   19192      0   0.00%    6240    6240      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/si5351/main.go
  24152   24152      0   0.00%    7536    7536      0   0.00% tinygo build -size short -o ./build/test.hex -target=pico ./examples/w5500/main.go
  90132   90132      0   0.00%    8024    8024      0   0.00% tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
 375432  375432      0   0.00%   17888   17888      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal -stack-size 8kb ./examples/net/http-get/
 122884  122884      0   0.00%    8940    8940      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-nano33 -stack-size 8kb ./examples/net/tcpclient/
 355252  355252      0   0.00%   17188   17188      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb ./examples/net/websocket/dial/
 105444  105444      0   0.00%   10920   10920      0   0.00% tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift -stack-size 8kb ./examples/net/socket/
 419536  419536      0   0.00%   19916   19916      0   0.00% tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 -stack-size 8kb ./examples/net/webstatic/
 169596  169596      0   0.00%   10940   10940      0   0.00% tinygo build -size short -o ./build/test.hex -target=arduino-mkrwifi1010 -stack-size 8kb ./examples/net/tlsclient/
 159332  159332      0   0.00%    9920    9920      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb ./examples/net/mqttclient/natiu/
 119192  119192      0   0.00%   14216   14216      0   0.00% tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webclient/
 370044  370044      0   0.00%   23028   23028      0   0.00% tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webserver/
 370980  370980      0   0.00%   22944   22944      0   0.00% tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/mqttclient/paho/
 177448  177448      0   0.00%   15036   15036      0   0.00% tinygo build -size short -o ./build/test.hex -target=elecrow-rp2040 -stack-size 8kb ./examples/net/tlsclient/
 122692  122692      0   0.00%   13036   13036      0   0.00% tinygo build -size short -o ./build/test.hex -target=elecrow-rp2350 -stack-size 8kb ./examples/net/ntpclient/
6716736 6716736      0   0.00% 1094506 1094506      0   0.00%

@rdon-key rdon-key left a comment

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.

I left one comment about using the RP2040/RP2350 atomic set/clear aliases for SIE_CTRL.PULLUP_EN.

Comment thread src/machine/machine_rp2040_usb.go
@deadprogram

Copy link
Copy Markdown
Member

Hello @sago35 thanks for working on this.

Here are a couple of items extracted from an automated code review.

  1. nRF52840: Detach() isn't sticky. The IRQ handler unconditionally re-enables the pull-up on every USBEVENT/EVENTCAUSE_READY:
// machine_nrf52840_usb.go:105-107
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
nrf.USBD.USBPULLUP.Set(1)

Unlike the other four chips, the pull-up here is owned by the ISR rather than Configure(), so a later power-ready event can silently re-attach. Could a detached bool on USBDevice guard that line? (The Detach-in-init → Attach-in-main flow you tested wouldn't necessarily hit it.)

  1. Missing platforms. usb.go also builds for stm32f4/f7/h7, and esp32c3/c6/xx declare their own USBDevice, so machine.USBDev.Attach() won't compile there. stm32 isn't mentioned in the description but is the easy win — dctlSDIS (machine_stm32_otgfs_usb.go:98) and DCTL_SDIS (machine_stm32h7_usb.go:120) already exist and are already toggled in Configure. For esp32, machine_esp32xx_usb.go:65-68 sets the no-op-stub precedent (SetStallEPIn etc.), so no-op methods there would keep user code portable.

What do you think?

@sago35

sago35 commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

I will check later.

@dgryski dgryski added this to the 0.42.0 milestone Aug 19, 2026
sago35 added 4 commits August 20, 2026 09:56
The USB device is attached to the bus automatically during startup,
before user code has a chance to finish its USB configuration (device
identifiers, extra HID interfaces, ...). Composite devices such as
keyboards may therefore be enumerated by the host with an incomplete
configuration.

Attach and Detach expose the soft-connect control (DP pull-up) so that
an application or library can detach in an init function, complete its
configuration, and attach again to let the host enumerate the finished
device. They can also be used to force re-enumeration without
replugging the cable.

Implemented for atsamd21, atsamd51, nrf52840, rp2040 and rp2350.
The USB IRQ handler re-enables the DP pull-up on every power-ready
event, silently undoing an earlier Detach. Guard the pull-up write with
a detached flag so the device stays off the bus until Attach is called.
- stm32f4, stm32f7, stm32h7: implement Attach and Detach using the
  DCTL soft-disconnect bit that Configure already toggles.
- esp32c3, esp32c6, esp32s3: add no-op stubs to keep user code
  portable; the fixed-function USB Serial/JTAG controller has no
  software-controlled soft-connect.
Replace the direct soft-connect register writes in Configure with the
equivalent Attach and Detach calls on atsamd21, atsamd51, rp2040,
rp2350, stm32f4, stm32f7 and stm32h7.
@sago35
sago35 force-pushed the usbdev-attach-detach branch from 2a56d5e to e23373e Compare August 20, 2026 13:26
@sago35

sago35 commented Aug 20, 2026

Copy link
Copy Markdown
Member Author

@deadprogram
Both points are addressed, split into three commits so they can be judged (or dropped) individually. The branch is also rebased onto the current dev.

  1. nRF52840: sticky Detach (c16ff7b)

Added a usbDetached flag. Detach() sets it, Attach() clears it, and the IRQ handler now only re-enables the DP pull-up on a power-ready event when the flag is not set, so a later USBEVENT/EVENTCAUSE_READY can no longer silently re-attach the device. The flag is written before the register in both methods, so an event racing with Detach() still ends up detached.

  1. Missing platforms (858a56f)
  • stm32f4 / stm32f7 / stm32h7: implemented Attach/Detach with the DCTL soft-disconnect bit (dctlSDIS / DCTL_SDIS). Note that I have no stm32 hardware at hand, so these are compile-tested only — but they reuse the exact soft-disconnect sequence Configure already performs on these chips, so they should behave the same.
  • esp32c3 / esp32c6 / esp32s3: added no-op stubs next to the existing SetStallEPIn etc. stubs, so machine.USBDev.Attach() stays portable. The fixed-function USB Serial/JTAG controller has no software-controlled soft-connect.
  1. Refactor (e23373e, optional)

Replaced the direct soft-connect register writes inside Configure with the equivalent dev.Attach() / dev.Detach() calls on atsamd21, atsamd51, rp2040, rp2350 and the three stm32 families. This one is purely cosmetic — feel free to ask me to drop it if you prefer the explicit register writes in Configure.

One deliberate exception: the USBPULLUP.Set(0) at the start of the nRF52840 Configure is not replaced with Detach(), because that would set the sticky flag and prevent the IRQ handler from ever attaching during normal enumeration.

Verified by compiling a program that calls machine.USBDev.Detach() / Attach() for all affected targets: circuitplay-express, feather-m4, feather-nrf52840, pico, pico2, feather-stm32f405, nucleo-f722ze, nucleo-h753zi, esp32c3-12f, esp32s3-generic and xiao-esp32c6.

@deadprogram

Copy link
Copy Markdown
Member

Thank you very much for adding this @sago35 now squash/merging.

@deadprogram
deadprogram merged commit 93bc654 into dev Aug 20, 2026
26 checks passed
@deadprogram
deadprogram deleted the usbdev-attach-detach branch August 20, 2026 15:17
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.

4 participants