From 2a56d5e216754b614bb631943b421641164b1749 Mon Sep 17 00:00:00 2001 From: sago35 Date: Tue, 28 Jul 2026 21:29:37 +0900 Subject: [PATCH] machine: add USBDevice.Attach and USBDevice.Detach 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. --- src/machine/machine_atsamd21_usb.go | 14 ++++++++++++++ src/machine/machine_atsamd51_usb.go | 14 ++++++++++++++ src/machine/machine_nrf52840_usb.go | 15 +++++++++++++++ src/machine/machine_rp2040_usb.go | 15 +++++++++++++++ src/machine/machine_rp2350_usb.go | 15 +++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/src/machine/machine_atsamd21_usb.go b/src/machine/machine_atsamd21_usb.go index 45ba18d454..51f71f9d4f 100644 --- a/src/machine/machine_atsamd21_usb.go +++ b/src/machine/machine_atsamd21_usb.go @@ -68,6 +68,20 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true } +// Attach connects the device to the USB bus, allowing the host to detect and +// enumerate it. It can be used together with Detach to delay enumeration +// until the USB configuration (device identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) +} + +// Detach disconnects the device from the USB bus. To the host this appears +// as if the device was unplugged. A subsequent Attach makes the host +// enumerate the device again. +func (dev *USBDevice) Detach() { + sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH) +} + func handlePadCalibration() { // Load Pad Calibration data from non-volatile memory // This requires registers that are not included in the SVD file. diff --git a/src/machine/machine_atsamd51_usb.go b/src/machine/machine_atsamd51_usb.go index 186ba4ce01..925cfef5ea 100644 --- a/src/machine/machine_atsamd51_usb.go +++ b/src/machine/machine_atsamd51_usb.go @@ -71,6 +71,20 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true } +// Attach connects the device to the USB bus, allowing the host to detect and +// enumerate it. It can be used together with Detach to delay enumeration +// until the USB configuration (device identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) +} + +// Detach disconnects the device from the USB bus. To the host this appears +// as if the device was unplugged. A subsequent Attach makes the host +// enumerate the device again. +func (dev *USBDevice) Detach() { + sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH) +} + func handlePadCalibration() { // Load Pad Calibration data from non-volatile memory // This requires registers that are not included in the SVD file. diff --git a/src/machine/machine_nrf52840_usb.go b/src/machine/machine_nrf52840_usb.go index 0dc222b372..263d120ff9 100644 --- a/src/machine/machine_nrf52840_usb.go +++ b/src/machine/machine_nrf52840_usb.go @@ -89,6 +89,21 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true } +// Attach connects the device to the USB bus by enabling the DP pull-up, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + nrf.USBD.USBPULLUP.Set(1) +} + +// Detach disconnects the device from the USB bus by disabling the DP pull-up. +// To the host this appears as if the device was unplugged. A subsequent +// Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + nrf.USBD.USBPULLUP.Set(0) +} + func handleUSBIRQ(interrupt.Interrupt) { if nrf.USBD.EVENTS_SOF.Get() == 1 { nrf.USBD.EVENTS_SOF.Set(0) diff --git a/src/machine/machine_rp2040_usb.go b/src/machine/machine_rp2040_usb.go index efa6440428..fb3c4c3a42 100644 --- a/src/machine/machine_rp2040_usb.go +++ b/src/machine/machine_rp2040_usb.go @@ -46,6 +46,21 @@ func (dev *USBDevice) Configure(config UARTConfig) { rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN) } +// Attach connects the device to the USB bus by enabling the DP pull-up, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN) +} + +// Detach disconnects the device from the USB bus by disabling the DP pull-up. +// To the host this appears as if the device was unplugged. A subsequent +// Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + rp.USBCTRL_REGS.SIE_CTRL.ClearBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN) +} + func handleUSBIRQ(intr interrupt.Interrupt) { status := rp.USBCTRL_REGS.INTS.Get() diff --git a/src/machine/machine_rp2350_usb.go b/src/machine/machine_rp2350_usb.go index ca565738a5..3d35f2d122 100644 --- a/src/machine/machine_rp2350_usb.go +++ b/src/machine/machine_rp2350_usb.go @@ -49,6 +49,21 @@ func (dev *USBDevice) Configure(config UARTConfig) { rp.USB.SetMAIN_CTRL_PHY_ISO(0x0) } +// Attach connects the device to the USB bus by enabling the DP pull-up, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN) +} + +// Detach disconnects the device from the USB bus by disabling the DP pull-up. +// To the host this appears as if the device was unplugged. A subsequent +// Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + rp.USB.SIE_CTRL.ClearBits(rp.USB_SIE_CTRL_PULLUP_EN) +} + func handleUSBIRQ(intr interrupt.Interrupt) { status := rp.USB.INTS.Get()