Skip to content

Commit 93bc654

Browse files
authored
machine: add USBDevice.Attach and USBDevice.Detach (#5563)
* 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. * machine: make USB Detach sticky on nrf52840 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. * machine: add USB Attach and Detach to stm32 and esp32 targets - 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. * machine: use Attach and Detach in USB Configure 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.
1 parent 1958d6f commit 93bc654

10 files changed

Lines changed: 134 additions & 7 deletions

src/machine/machine_atsamd21_usb.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
5151
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
5252

5353
// attach
54-
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
54+
dev.Attach()
5555

5656
// enable interrupt for end of reset
5757
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
@@ -68,6 +68,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
6868
dev.initcomplete = true
6969
}
7070

71+
// Attach connects the device to the USB bus, allowing the host to detect and
72+
// enumerate it. It can be used together with Detach to delay enumeration
73+
// until the USB configuration (device identifiers, classes, ...) is complete.
74+
func (dev *USBDevice) Attach() {
75+
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
76+
}
77+
78+
// Detach disconnects the device from the USB bus. To the host this appears
79+
// as if the device was unplugged. A subsequent Attach makes the host
80+
// enumerate the device again.
81+
func (dev *USBDevice) Detach() {
82+
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH)
83+
}
84+
7185
func handlePadCalibration() {
7286
// Load Pad Calibration data from non-volatile memory
7387
// This requires registers that are not included in the SVD file.

src/machine/machine_atsamd51_usb.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
5151
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
5252

5353
// attach
54-
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
54+
dev.Attach()
5555

5656
// enable interrupt for end of reset
5757
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
@@ -71,6 +71,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
7171
dev.initcomplete = true
7272
}
7373

74+
// Attach connects the device to the USB bus, allowing the host to detect and
75+
// enumerate it. It can be used together with Detach to delay enumeration
76+
// until the USB configuration (device identifiers, classes, ...) is complete.
77+
func (dev *USBDevice) Attach() {
78+
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
79+
}
80+
81+
// Detach disconnects the device from the USB bus. To the host this appears
82+
// as if the device was unplugged. A subsequent Attach makes the host
83+
// enumerate the device again.
84+
func (dev *USBDevice) Detach() {
85+
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH)
86+
}
87+
7488
func handlePadCalibration() {
7589
// Load Pad Calibration data from non-volatile memory
7690
// This requires registers that are not included in the SVD file.

src/machine/machine_esp32c3_usb.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {}
6767
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
6868
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
6969

70+
// Attach and Detach are no-ops: the USB Serial/JTAG controller has no
71+
// software-controlled soft-connect, it is always attached to the bus.
72+
func (dev *USBDevice) Attach() {}
73+
func (dev *USBDevice) Detach() {}
74+
7075
// initUSB is intentionally empty — the interp phase evaluates init()
7176
// functions at compile time and cannot access hardware registers.
7277
// Actual hardware setup is deferred to the first Configure() call.

src/machine/machine_esp32c6_usb.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {}
6666
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
6767
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
6868

69+
// Attach and Detach are no-ops: the USB Serial/JTAG controller has no
70+
// software-controlled soft-connect, it is always attached to the bus.
71+
func (dev *USBDevice) Attach() {}
72+
func (dev *USBDevice) Detach() {}
73+
6974
// initUSB is intentionally empty — the interp phase evaluates init()
7075
// functions at compile time and cannot access hardware registers.
7176
// Actual hardware setup is deferred to the first Configure() call.

src/machine/machine_esp32xx_usb.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {}
6767
func (dev *USBDevice) ClearStallEPIn(ep uint32) {}
6868
func (dev *USBDevice) ClearStallEPOut(ep uint32) {}
6969

70+
// Attach and Detach are no-ops: the USB Serial/JTAG controller has no
71+
// software-controlled soft-connect, it is always attached to the bus.
72+
func (dev *USBDevice) Attach() {}
73+
func (dev *USBDevice) Detach() {}
74+
7075
// initUSB is intentionally empty — the interp phase evaluates init()
7176
// functions at compile time and cannot access hardware registers.
7277
// Actual hardware setup is deferred to the first Configure() call.

src/machine/machine_nrf52840_usb.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ var (
2222
epinen uint32
2323
epouten uint32
2424
easyDMABusy volatile.Register8
25+
26+
// usbDetached keeps the device detached from the bus after Detach: the
27+
// USB IRQ handler re-enables the DP pull-up on every power-ready event,
28+
// which would otherwise silently undo a Detach.
29+
usbDetached bool
2530
)
2631

2732
// enterCriticalSection is used to protect access to easyDMA - only one thing
@@ -89,6 +94,23 @@ func (dev *USBDevice) Configure(config UARTConfig) {
8994
dev.initcomplete = true
9095
}
9196

97+
// Attach connects the device to the USB bus by enabling the DP pull-up,
98+
// allowing the host to detect and enumerate it. It can be used together with
99+
// Detach to delay enumeration until the USB configuration (device
100+
// identifiers, classes, ...) is complete.
101+
func (dev *USBDevice) Attach() {
102+
usbDetached = false
103+
nrf.USBD.USBPULLUP.Set(1)
104+
}
105+
106+
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
107+
// To the host this appears as if the device was unplugged. A subsequent
108+
// Attach makes the host enumerate the device again.
109+
func (dev *USBDevice) Detach() {
110+
usbDetached = true
111+
nrf.USBD.USBPULLUP.Set(0)
112+
}
113+
92114
func handleUSBIRQ(interrupt.Interrupt) {
93115
if nrf.USBD.EVENTS_SOF.Get() == 1 {
94116
nrf.USBD.EVENTS_SOF.Set(0)
@@ -103,7 +125,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
103125

104126
// Configure control endpoint
105127
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
106-
nrf.USBD.USBPULLUP.Set(1)
128+
if !usbDetached {
129+
nrf.USBD.USBPULLUP.Set(1)
130+
}
107131

108132
usbConfiguration = 0
109133
}

src/machine/machine_rp2040_usb.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,24 @@ func (dev *USBDevice) Configure(config UARTConfig) {
4343
rp.USBCTRL_REGS_INTE_SETUP_REQ)
4444

4545
// Present full speed device by enabling pull up on DP
46+
dev.Attach()
47+
}
48+
49+
// Attach connects the device to the USB bus by enabling the DP pull-up,
50+
// allowing the host to detect and enumerate it. It can be used together with
51+
// Detach to delay enumeration until the USB configuration (device
52+
// identifiers, classes, ...) is complete.
53+
func (dev *USBDevice) Attach() {
4654
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
4755
}
4856

57+
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
58+
// To the host this appears as if the device was unplugged. A subsequent
59+
// Attach makes the host enumerate the device again.
60+
func (dev *USBDevice) Detach() {
61+
rp.USBCTRL_REGS.SIE_CTRL.ClearBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
62+
}
63+
4964
func handleUSBIRQ(intr interrupt.Interrupt) {
5065
status := rp.USBCTRL_REGS.INTS.Get()
5166

src/machine/machine_rp2350_usb.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,27 @@ func (dev *USBDevice) Configure(config UARTConfig) {
4343
rp.USB_INTE_SETUP_REQ)
4444

4545
// Present full speed device by enabling pull up on DP
46-
rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN)
46+
dev.Attach()
4747

4848
// 12.7.2 Disable phy isolation
4949
rp.USB.SetMAIN_CTRL_PHY_ISO(0x0)
5050
}
5151

52+
// Attach connects the device to the USB bus by enabling the DP pull-up,
53+
// allowing the host to detect and enumerate it. It can be used together with
54+
// Detach to delay enumeration until the USB configuration (device
55+
// identifiers, classes, ...) is complete.
56+
func (dev *USBDevice) Attach() {
57+
rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN)
58+
}
59+
60+
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
61+
// To the host this appears as if the device was unplugged. A subsequent
62+
// Attach makes the host enumerate the device again.
63+
func (dev *USBDevice) Detach() {
64+
rp.USB.SIE_CTRL.ClearBits(rp.USB_SIE_CTRL_PULLUP_EN)
65+
}
66+
5267
func handleUSBIRQ(intr interrupt.Interrupt) {
5368
status := rp.USB.INTS.Get()
5469

src/machine/machine_stm32_otgfs_usb.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
254254
otgPower.PCGCCTL.Set(0)
255255

256256
// Soft-disconnect now (after CSRST reset DCTL to its default connected state).
257-
otgDevice.DCTL.SetBits(dctlSDIS)
257+
dev.Detach()
258258

259259
// ---- 7. Configure data FIFOs --------------------------------------------
260260

@@ -311,11 +311,26 @@ func (dev *USBDevice) Configure(config UARTConfig) {
311311

312312
// ---- 12. Connect to host (clear soft-disconnect) -----------------------
313313

314-
otgDevice.DCTL.ClearBits(dctlSDIS)
314+
dev.Attach()
315315

316316
dev.initcomplete = true
317317
}
318318

319+
// Attach connects the device to the USB bus by releasing soft disconnect,
320+
// allowing the host to detect and enumerate it. It can be used together with
321+
// Detach to delay enumeration until the USB configuration (device
322+
// identifiers, classes, ...) is complete.
323+
func (dev *USBDevice) Attach() {
324+
otgDevice.DCTL.ClearBits(dctlSDIS)
325+
}
326+
327+
// Detach disconnects the device from the USB bus by asserting soft
328+
// disconnect. To the host this appears as if the device was unplugged. A
329+
// subsequent Attach makes the host enumerate the device again.
330+
func (dev *USBDevice) Detach() {
331+
otgDevice.DCTL.SetBits(dctlSDIS)
332+
}
333+
319334
// handleUSBIRQ is the OTG FS interrupt handler, dispatching on GINTSTS bits.
320335
func handleUSBIRQ(intr interrupt.Interrupt) {
321336
status := stm32.OTG_FS_GLOBAL.GINTSTS.Get() &

src/machine/machine_stm32h7_usb.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
206206

207207
// Stay soft-disconnected until configuration is complete; CSRST left
208208
// DCTL at its default "connected" state.
209-
usbOTG.DCTL.SetBits(DCTL_SDIS)
209+
dev.Detach()
210210

211211
// 5. Force device mode now that the core is out of reset. The mode
212212
// change takes effect only after up to 25 ms (RM0433); poll GINTSTS.CMOD
@@ -265,9 +265,24 @@ func (dev *USBDevice) Configure(config UARTConfig) {
265265
dev.initcomplete = true
266266

267267
// Release soft-disconnect: pulls D+ high, making device visible to host.
268+
dev.Attach()
269+
}
270+
271+
// Attach connects the device to the USB bus by releasing soft disconnect,
272+
// allowing the host to detect and enumerate it. It can be used together with
273+
// Detach to delay enumeration until the USB configuration (device
274+
// identifiers, classes, ...) is complete.
275+
func (dev *USBDevice) Attach() {
268276
usbOTG.DCTL.ClearBits(DCTL_SDIS)
269277
}
270278

279+
// Detach disconnects the device from the USB bus by asserting soft
280+
// disconnect. To the host this appears as if the device was unplugged. A
281+
// subsequent Attach makes the host enumerate the device again.
282+
func (dev *USBDevice) Detach() {
283+
usbOTG.DCTL.SetBits(DCTL_SDIS)
284+
}
285+
271286
func initEndpoint(ep, config uint32) {
272287
if ep == 0 {
273288
// Control endpoint

0 commit comments

Comments
 (0)