Skip to content

machine/rp2: expose usb endpoint stall handling #4850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/machine/machine_rp2040_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {

if !ok {
// Stall endpoint?
sendStallViaEPIn(0)
USBDev.SetStallEPIn(0)
}

}
Expand Down
2 changes: 1 addition & 1 deletion src/machine/machine_rp2350_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {

if !ok {
// Stall endpoint?
sendStallViaEPIn(0)
USBDev.SetStallEPIn(0)
}

}
Expand Down
55 changes: 50 additions & 5 deletions src/machine/machine_rp2_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,33 @@ func initEndpoint(ep, config uint32) {
offset := ep*2*usbBufferLen + 0x100
val |= offset

// Bulk and interrupt endpoints must have their Packet ID reset to DATA0 when un-stalled.
epXPIDReset[ep] = false // Default to false in case an endpoint is re-initialized.

switch config {
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
val |= usbEpControlEndpointTypeInterrupt
_usbDPSRAM.EPxControl[ep].In.Set(val)
epXPIDReset[ep] = true

case usb.ENDPOINT_TYPE_BULK | usb.EndpointOut:
val |= usbEpControlEndpointTypeBulk
_usbDPSRAM.EPxControl[ep].Out.Set(val)
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBufferLen & usbBuf0CtrlLenMask)
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
epXPIDReset[ep] = true

case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
val |= usbEpControlEndpointTypeInterrupt
_usbDPSRAM.EPxControl[ep].Out.Set(val)
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBufferLen & usbBuf0CtrlLenMask)
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
epXPIDReset[ep] = true

case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
val |= usbEpControlEndpointTypeBulk
_usbDPSRAM.EPxControl[ep].In.Set(val)
epXPIDReset[ep] = true

case usb.ENDPOINT_TYPE_CONTROL:
val |= usbEpControlEndpointTypeControl
Expand Down Expand Up @@ -109,7 +116,12 @@ func handleEndpointRx(ep uint32) []byte {
}

func handleEndpointRxComplete(ep uint32) {
epXdata0[ep] = !epXdata0[ep]
setEPDataPID(ep, !epXdata0[ep])
}

// Set the USB endpoint Packet ID to DATA0 or DATA1.
func setEPDataPID(ep uint32, dataOne bool) {
epXdata0[ep] = dataOne
if epXdata0[ep] || ep == 0 {
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlData1Pid)
}
Expand Down Expand Up @@ -138,7 +150,8 @@ func sendViaEPIn(ep uint32, data []byte, count int) {
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
}

func sendStallViaEPIn(ep uint32) {
// Set ENDPOINT_HALT/stall status on a USB IN endpoint.
func (dev *USBDevice) SetStallEPIn(ep uint32) {
// Prepare buffer control register value
if ep == 0 {
armEPZeroStall()
Expand All @@ -149,6 +162,37 @@ func sendStallViaEPIn(ep uint32) {
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
}

// Set ENDPOINT_HALT/stall status on a USB OUT endpoint.
func (dev *USBDevice) SetStallEPOut(ep uint32) {
if ep == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should ep be masked before comparing with 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The 0 endpoint is a special case. It's both an in and out endpoint in one so there's no direction bit applied for it, but couldn't hurt

panic("SetStallEPOut: EP0 OUT not valid")
}
val := uint32(usbBuf0CtrlStall)
_usbDPSRAM.EPxBufferControl[ep&0x7F].Out.Set(val)
}

// Clear the ENDPOINT_HALT/stall on a USB IN endpoint.
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
ep = ep & 0x7F
val := uint32(usbBuf0CtrlStall)
_usbDPSRAM.EPxBufferControl[ep].In.ClearBits(val)
if epXPIDReset[ep] {
// Reset the PID to DATA0
setEPDataPID(ep&0x7F, false)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: the masking is redundant here.

}
}

// Clear the ENDPOINT_HALT/stall on a USB OUT endpoint.
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
ep = ep & 0x7F
val := uint32(usbBuf0CtrlStall)
_usbDPSRAM.EPxBufferControl[ep].Out.ClearBits(val)
if epXPIDReset[ep] {
// Reset the PID to DATA0
setEPDataPID(ep, false)
}
}

type usbDPSRAM struct {
// Note that EPxControl[0] is not EP0Control but 8-byte setup data.
EPxControl [16]usbEndpointControlRegister
Expand All @@ -173,9 +217,10 @@ type usbBuffer struct {
}

var (
_usbDPSRAM = (*usbDPSRAM)(unsafe.Pointer(uintptr(0x50100000)))
epXdata0 [16]bool
setupBytes [8]byte
_usbDPSRAM = (*usbDPSRAM)(unsafe.Pointer(uintptr(0x50100000)))
epXdata0 [16]bool
epXPIDReset [16]bool
setupBytes [8]byte
)

func (d *usbDPSRAM) setupBytes() []byte {
Expand Down
16 changes: 16 additions & 0 deletions src/machine/usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ var (
usbTxHandler [usb.NumberOfEndpoints]func()
usbRxHandler [usb.NumberOfEndpoints]func([]byte)
usbSetupHandler [usb.NumberOfInterfaces]func(usb.Setup) bool
usbStallHandler [usb.NumberOfEndpoints]func(usb.Setup) bool

endPoints = []uint32{
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
Expand Down Expand Up @@ -212,6 +213,12 @@ func handleStandardSetup(setup usb.Setup) bool {
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
isRemoteWakeUpEnabled = false
} else if setup.WValueL == 0 { // ENDPOINTHALT
if idx := setup.WIndex & 0x7F; idx < usb.NumberOfEndpoints && usbStallHandler[idx] != nil {
// Host has requested to clear an endpoint stall. If the request is addressed to
// an endpoint with a configured StallHandler, forward the message on.
// The 0x7F mask is used to clear the direction bit from the endpoint number
return usbStallHandler[idx](setup)
}
isEndpointHalt = false
}
SendZlp()
Expand All @@ -221,6 +228,12 @@ func handleStandardSetup(setup usb.Setup) bool {
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
isRemoteWakeUpEnabled = true
} else if setup.WValueL == 0 { // ENDPOINTHALT
if idx := setup.WIndex & 0x7F; idx < usb.NumberOfEndpoints && usbStallHandler[idx] != nil {
// Host has requested to stall an endpoint. If the request is addressed to
// an endpoint with a configured StallHandler, forward the message on.
// The 0x7F mask is used to clear the direction bit from the endpoint number
return usbStallHandler[idx](setup)
}
isEndpointHalt = true
}
SendZlp()
Expand Down Expand Up @@ -320,6 +333,9 @@ func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointC
usbRxHandler[ep.Index] = ep.RxHandler
}
}
if ep.StallHandler != nil {
usbStallHandler[ep.Index] = ep.StallHandler
}
}

for _, s := range setup {
Expand Down
11 changes: 6 additions & 5 deletions src/machine/usb/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package usb

type EndpointConfig struct {
Index uint8
IsIn bool
TxHandler func()
RxHandler func([]byte)
Type uint8
Index uint8
IsIn bool
TxHandler func()
RxHandler func([]byte)
StallHandler func(Setup) bool
Type uint8
}

type SetupConfig struct {
Expand Down