From c3f1832d9a60c0021e50d1b920817e2213801c7f Mon Sep 17 00:00:00 2001 From: Konstantin Sharlaimov Date: Sun, 17 May 2026 09:08:39 +0200 Subject: [PATCH] refactor(usb): dynamic endpoint descriptor generation Replace static endpoint variables with EndpointIN and EndpointOUT functions. Allows flexible endpoint remapping across USB configs. Map CDC, HID, MIDI and MSC USB devices to bidirectional endpoints consistently across different configurations. --- builder/sizes_test.go | 6 +- src/machine/usb/descriptor/cdc.go | 9 +- src/machine/usb/descriptor/endpoint.go | 132 +++++++------------------ src/machine/usb/descriptor/hid.go | 15 ++- src/machine/usb/descriptor/joystick.go | 15 ++- src/machine/usb/descriptor/midi.go | 31 +++--- src/machine/usb/descriptor/msc.go | 16 ++- src/machine/usb/usb.go | 14 +-- 8 files changed, 103 insertions(+), 135 deletions(-) diff --git a/builder/sizes_test.go b/builder/sizes_test.go index a51a371d8..e96577d9b 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) { // This is a small number of very diverse targets that we want to test. tests := []sizeTest{ // microcontrollers - {"hifive1b", "examples/echo", 3817, 299, 0, 2252}, - {"microbit", "examples/serial", 2820, 356, 8, 2248}, - {"wioterminal", "examples/pininterrupt", 8036, 1652, 132, 7480}, + {"hifive1b", "examples/echo", 3680, 280, 0, 2252}, + {"microbit", "examples/serial", 2694, 342, 8, 2248}, + {"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248}, // TODO: also check wasm. Right now this is difficult, because // wasm binaries are run through wasm-opt and therefore the diff --git a/src/machine/usb/descriptor/cdc.go b/src/machine/usb/descriptor/cdc.go index ec72186e3..d32611ca7 100644 --- a/src/machine/usb/descriptor/cdc.go +++ b/src/machine/usb/descriptor/cdc.go @@ -150,6 +150,9 @@ var InterfaceCDCData = InterfaceType{ data: interfaceCDCData[:], } +// EP1 IN : CDC Call Management +// EP2 OUT: CDC OUT +// EP2 IN : CDC IN var CDC = Descriptor{ Device: DeviceCDC.Bytes(), Configuration: Append([][]byte{ @@ -160,9 +163,9 @@ var CDC = Descriptor{ ClassSpecificCDCCallManagement.Bytes(), ClassSpecificCDCACM.Bytes(), ClassSpecificCDCUnion.Bytes(), - EndpointEP1IN.Bytes(), + EndpointIN(EndpointEP1, TransferTypeInterrupt, 0x10, 0x10).Bytes(), InterfaceCDCData.Bytes(), - EndpointEP2OUT.Bytes(), - EndpointEP3IN.Bytes(), + EndpointOUT(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), + EndpointIN(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), }), } diff --git a/src/machine/usb/descriptor/endpoint.go b/src/machine/usb/descriptor/endpoint.go index 57a17060c..c918caed1 100644 --- a/src/machine/usb/descriptor/endpoint.go +++ b/src/machine/usb/descriptor/endpoint.go @@ -15,104 +15,44 @@ const ( TransferTypeInterrupt ) -var endpointEP1IN = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x81, // EndpointAddress - 0x03, // Attributes - 0x10, // MaxPacketSizeL - 0x00, // MaxPacketSizeH - 0x10, // Interval +type EndpointNumber uint8 + +const ( + EndpointEP1 EndpointNumber = iota + EndpointEP2 + EndpointEP3 + EndpointEP4 +) + +const ( + maxEndpoints = 4 +) + +var ( + endpointEPIn = [maxEndpoints][endpointTypeLen]byte{} + endpointEPOut = [maxEndpoints][endpointTypeLen]byte{} +) + +func EndpointIN(ep EndpointNumber, transferType uint8, maxPacketSize uint16, interval uint8) EndpointType { + e := EndpointType{data: endpointEPIn[ep][:]} + e.Length(endpointTypeLen) + e.Type(TypeEndpoint) + e.EndpointAddress(uint8(ep+1) | 0x80) // EndpointNumber is 0-based, addresses are 1-based + e.Attributes(transferType) + e.MaxPacketSize(maxPacketSize) + e.Interval(interval) + return e } -var EndpointEP1IN = EndpointType{ - data: endpointEP1IN[:], -} - -var endpointEP2OUT = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x02, // EndpointAddress - 0x02, // Attributes - 0x40, // MaxPacketSizeL - 0x00, // MaxPacketSizeH - 0x00, // Interval -} - -var EndpointEP2OUT = EndpointType{ - data: endpointEP2OUT[:], -} - -var endpointEP3IN = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x83, // EndpointAddress - 0x02, // Attributes - 0x40, // MaxPacketSizeL - 0x00, // MaxPacketSizeH - 0x00, // Interval -} - -var EndpointEP3IN = EndpointType{ - data: endpointEP3IN[:], -} - -var endpointEP4IN = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x84, // EndpointAddress - 0x03, // Attributes - 0x40, // MaxPacketSizeL - 0x00, // MaxPacketSizeH - 0x01, // Interval -} - -var EndpointEP4IN = EndpointType{ - data: endpointEP4IN[:], -} - -var endpointEP5OUT = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x05, // EndpointAddress - 0x03, // Attributes - 0x40, // MaxPacketSizeL - 0x00, // MaxPacketSizeH - 0x01, // Interval -} - -var EndpointEP5OUT = EndpointType{ - data: endpointEP5OUT[:], -} - -// Mass Storage Class bulk in endpoint -var endpointMSCIN = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x86, // EndpointAddress - TransferTypeBulk, // Attributes - 0x40, // MaxPacketSizeL (64 bytes) - 0x00, // MaxPacketSizeH - 0x00, // Interval -} - -var EndpointMSCIN = EndpointType{ - data: endpointMSCIN[:], -} - -// Mass Storage Class bulk out endpoint -var endpointMSCOUT = [endpointTypeLen]byte{ - endpointTypeLen, - TypeEndpoint, - 0x07, // EndpointAddress - TransferTypeBulk, // Attributes - 0x40, // MaxPacketSizeL (64 bytes) - 0x00, // MaxPacketSizeH - 0x00, // Interval -} - -var EndpointMSCOUT = EndpointType{ - data: endpointMSCOUT[:], +func EndpointOUT(ep EndpointNumber, transferType uint8, maxPacketSize uint16, interval uint8) EndpointType { + e := EndpointType{data: endpointEPOut[ep][:]} + e.Length(endpointTypeLen) + e.Type(TypeEndpoint) + e.EndpointAddress(uint8(ep + 1)) // EndpointNumber is 0-based, addresses are 1-based + e.Attributes(transferType) + e.MaxPacketSize(maxPacketSize) + e.Interval(interval) + return e } const ( diff --git a/src/machine/usb/descriptor/hid.go b/src/machine/usb/descriptor/hid.go index 06b980153..6ee9683c0 100644 --- a/src/machine/usb/descriptor/hid.go +++ b/src/machine/usb/descriptor/hid.go @@ -111,6 +111,11 @@ var ClassHID = ClassHIDType{ data: classHID[:], } +// EP1 IN : CDC Call Management +// EP2 OUT: CDC OUT +// EP2 IN : CDC IN +// EP3 OUT: HID OUT +// EP3 IN : HID IN var CDCHID = Descriptor{ Device: DeviceCDC.Bytes(), Configuration: Append([][]byte{ @@ -121,14 +126,14 @@ var CDCHID = Descriptor{ ClassSpecificCDCACM.Bytes(), ClassSpecificCDCUnion.Bytes(), ClassSpecificCDCCallManagement.Bytes(), - EndpointEP1IN.Bytes(), + EndpointIN(EndpointEP1, TransferTypeInterrupt, 0x10, 0x10).Bytes(), InterfaceCDCData.Bytes(), - EndpointEP2OUT.Bytes(), - EndpointEP3IN.Bytes(), + EndpointOUT(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), + EndpointIN(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceHID.Bytes(), ClassHID.Bytes(), - EndpointEP4IN.Bytes(), - EndpointEP5OUT.Bytes(), + EndpointIN(EndpointEP3, TransferTypeInterrupt, 0x40, 0x01).Bytes(), + EndpointOUT(EndpointEP3, TransferTypeInterrupt, 0x40, 0x01).Bytes(), }), HID: map[uint16][]byte{ 2: Append([][]byte{ // Update ClassLength in classHID whenever the array length is modified! diff --git a/src/machine/usb/descriptor/joystick.go b/src/machine/usb/descriptor/joystick.go index 65756e0d6..725c3efa6 100644 --- a/src/machine/usb/descriptor/joystick.go +++ b/src/machine/usb/descriptor/joystick.go @@ -121,6 +121,11 @@ var JoystickDefaultHIDReport = Append([][]byte{ // CDCJoystick requires that you append the JoystickDescriptor // to the Configuration before using. This is in order to support // custom configurations. +// EP1 IN : CDC Call Management +// EP2 OUT: CDC OUT +// EP2 IN : CDC IN +// EP3 OUT: HID OUT +// EP3 IN : HID IN var CDCJoystick = Descriptor{ Device: DeviceJoystick.Bytes(), Configuration: Append([][]byte{ @@ -131,14 +136,14 @@ var CDCJoystick = Descriptor{ ClassSpecificCDCACM.Bytes(), ClassSpecificCDCUnion.Bytes(), ClassSpecificCDCCallManagement.Bytes(), - EndpointEP1IN.Bytes(), + EndpointIN(EndpointEP1, TransferTypeInterrupt, 0x10, 0x10).Bytes(), InterfaceCDCData.Bytes(), - EndpointEP2OUT.Bytes(), - EndpointEP3IN.Bytes(), + EndpointOUT(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), + EndpointIN(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceHIDJoystick.Bytes(), ClassHIDJoystick.Bytes(), - EndpointEP4IN.Bytes(), - EndpointEP5OUT.Bytes(), + EndpointIN(EndpointEP3, TransferTypeInterrupt, 0x40, 0x01).Bytes(), + EndpointOUT(EndpointEP3, TransferTypeInterrupt, 0x40, 0x01).Bytes(), }), HID: map[uint16][]byte{}, } diff --git a/src/machine/usb/descriptor/midi.go b/src/machine/usb/descriptor/midi.go index fad81f31d..6524e0578 100644 --- a/src/machine/usb/descriptor/midi.go +++ b/src/machine/usb/descriptor/midi.go @@ -171,10 +171,10 @@ var ClassSpecificMIDIInEndpoint = ClassSpecificType{ const endpointMIDITypeLen = 9 -var endpointEP6IN = [endpointMIDITypeLen]byte{ +var endpointMIDIIN = [endpointMIDITypeLen]byte{ endpointMIDITypeLen, TypeEndpoint, - 0x86, // EndpointAddress + 0x83, // EndpointAddress 0x02, // Attributes 0x40, // MaxPacketSizeL 0x00, // MaxPacketSizeH @@ -183,14 +183,14 @@ var endpointEP6IN = [endpointMIDITypeLen]byte{ 0x00, // sync address } -var EndpointEP6IN = EndpointType{ - data: endpointEP6IN[:], +var EndpointMIDIIN = EndpointType{ + data: endpointMIDIIN[:], } -var endpointEP7OUT = [endpointMIDITypeLen]byte{ +var endpointMIDIOUT = [endpointMIDITypeLen]byte{ endpointMIDITypeLen, TypeEndpoint, - 0x07, // EndpointAddress + 0x03, // EndpointAddress 0x02, // Attributes 0x40, // MaxPacketSizeL 0x00, // MaxPacketSizeH @@ -199,8 +199,8 @@ var endpointEP7OUT = [endpointMIDITypeLen]byte{ 0x00, // sync address } -var EndpointEP7OUT = EndpointType{ - data: endpointEP7OUT[:], +var EndpointMIDIOUT = EndpointType{ + data: endpointMIDIOUT[:], } var configurationCDCMIDI = [configurationTypeLen]byte{ @@ -218,6 +218,11 @@ var ConfigurationCDCMIDI = ConfigurationType{ data: configurationCDCMIDI[:], } +// EP1 IN : CDC Call Management +// EP2 OUT: CDC OUT +// EP2 IN : CDC IN +// EP3 OUT: MIDI OUT (custom endpoint descriptor) +// EP3 IN : MIDI IN (custom endpoint descriptor) var CDCMIDI = Descriptor{ Device: DeviceCDC.Bytes(), Configuration: Append([][]byte{ @@ -228,10 +233,10 @@ var CDCMIDI = Descriptor{ ClassSpecificCDCACM.Bytes(), ClassSpecificCDCUnion.Bytes(), ClassSpecificCDCCallManagement.Bytes(), - EndpointEP1IN.Bytes(), + EndpointIN(EndpointEP1, TransferTypeInterrupt, 0x10, 0x10).Bytes(), InterfaceCDCData.Bytes(), - EndpointEP2OUT.Bytes(), - EndpointEP3IN.Bytes(), + EndpointOUT(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), + EndpointIN(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceAssociationMIDI.Bytes(), InterfaceAudio.Bytes(), ClassSpecificAudioInterface.Bytes(), @@ -241,9 +246,9 @@ var CDCMIDI = Descriptor{ ClassSpecificMIDIInJack2.Bytes(), ClassSpecificMIDIOutJack1.Bytes(), ClassSpecificMIDIOutJack2.Bytes(), - EndpointEP7OUT.Bytes(), + EndpointMIDIOUT.Bytes(), ClassSpecificMIDIOutEndpoint.Bytes(), - EndpointEP6IN.Bytes(), + EndpointMIDIIN.Bytes(), ClassSpecificMIDIInEndpoint.Bytes(), }), } diff --git a/src/machine/usb/descriptor/msc.go b/src/machine/usb/descriptor/msc.go index 55c6ddd85..42e0088b9 100644 --- a/src/machine/usb/descriptor/msc.go +++ b/src/machine/usb/descriptor/msc.go @@ -52,7 +52,17 @@ var ConfigurationMSC = ConfigurationType{ data: configurationMSC[:], } +var ( + EndpointMSCIN = EndpointIN(EndpointEP3, TransferTypeBulk, 0x40, 0x00) + EndpointMSCOUT = EndpointOUT(EndpointEP3, TransferTypeBulk, 0x40, 0x00) +) + // Mass Storage Class +// EP1 IN : CDC Call Management +// EP2 OUT: CDC OUT +// EP2 IN : CDC IN +// EP3 OUT: MSC OUT +// EP3 IN : MSC IN var MSC = Descriptor{ Device: DeviceCDC.Bytes(), Configuration: Append([][]byte{ @@ -63,10 +73,10 @@ var MSC = Descriptor{ ClassSpecificCDCACM.Bytes(), ClassSpecificCDCUnion.Bytes(), ClassSpecificCDCCallManagement.Bytes(), - EndpointEP1IN.Bytes(), + EndpointIN(EndpointEP1, TransferTypeInterrupt, 0x10, 0x10).Bytes(), InterfaceCDCData.Bytes(), - EndpointEP2OUT.Bytes(), - EndpointEP3IN.Bytes(), + EndpointOUT(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), + EndpointIN(EndpointEP2, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceAssociationMSC.Bytes(), InterfaceMSC.Bytes(), EndpointMSCIN.Bytes(), diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go index 40983a9a3..1d2dad435 100644 --- a/src/machine/usb/usb.go +++ b/src/machine/usb/usb.go @@ -74,13 +74,13 @@ const ( CONTROL_ENDPOINT = 0 CDC_ENDPOINT_ACM = 1 CDC_ENDPOINT_OUT = 2 - CDC_ENDPOINT_IN = 3 - HID_ENDPOINT_IN = 4 // for Interrupt In - HID_ENDPOINT_OUT = 5 // for Interrupt Out - MIDI_ENDPOINT_IN = 6 // for Bulk In - MIDI_ENDPOINT_OUT = 7 // for Bulk Out - MSC_ENDPOINT_IN = 6 // for Bulk In - MSC_ENDPOINT_OUT = 7 // for Bulk Out + CDC_ENDPOINT_IN = 2 + HID_ENDPOINT_IN = 3 // for Interrupt In + HID_ENDPOINT_OUT = 3 // for Interrupt Out + MIDI_ENDPOINT_IN = 3 // for Bulk In + MIDI_ENDPOINT_OUT = 3 // for Bulk Out + MSC_ENDPOINT_IN = 3 // for Bulk In + MSC_ENDPOINT_OUT = 3 // for Bulk Out // bmRequestType REQUEST_HOSTTODEVICE = 0x00