From 7c20bfb2025db36c0d06fc36e37071d0af18fd29 Mon Sep 17 00:00:00 2001 From: Konstantin Sharlaimov Date: Sun, 17 May 2026 09:08:39 +0200 Subject: [PATCH 1/2] refactor(usb): dynamic endpoint descriptor generation Replace static endpoint variables with EndpointIN and EndpointOUT functions. Allows flexible endpoint remapping across USB configs. --- builder/sizes_test.go | 2 +- src/machine/usb/descriptor/cdc.go | 9 +- src/machine/usb/descriptor/endpoint.go | 128 +++++++------------------ src/machine/usb/descriptor/hid.go | 15 ++- src/machine/usb/descriptor/joystick.go | 15 ++- src/machine/usb/descriptor/midi.go | 11 ++- src/machine/usb/descriptor/msc.go | 17 +++- 7 files changed, 83 insertions(+), 114 deletions(-) diff --git a/builder/sizes_test.go b/builder/sizes_test.go index fd985a8977..7fd4f6f646 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -44,7 +44,7 @@ func TestBinarySize(t *testing.T) { // microcontrollers {"hifive1b", "examples/echo", 3680, 280, 0, 2252}, {"microbit", "examples/serial", 2694, 342, 8, 2248}, - {"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248}, + {"wioterminal", "examples/pininterrupt", 3541, 799, 88, 7008}, // 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 ec72186e3a..d38e06d966 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(EndpointEP3, TransferTypeBulk, 0x40, 0x00).Bytes(), }), } diff --git a/src/machine/usb/descriptor/endpoint.go b/src/machine/usb/descriptor/endpoint.go index 57a17060c4..c918caed1e 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 -} - -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[:], -} +type EndpointNumber uint8 -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 -} +const ( + EndpointEP1 EndpointNumber = iota + EndpointEP2 + EndpointEP3 + EndpointEP4 +) -var EndpointMSCIN = EndpointType{ - data: endpointMSCIN[:], -} +const ( + maxEndpoints = 4 +) -// 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 ( + endpointEPIn = [maxEndpoints][endpointTypeLen]byte{} + endpointEPOut = [maxEndpoints][endpointTypeLen]byte{} +) -var EndpointMSCOUT = EndpointType{ - data: endpointMSCOUT[:], +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 +} + +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 06b9801530..54f706ca83 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 +// EP1 OUT: HID OUT +// EP2 IN : HID IN +// EP2 OUT: CDC OUT +// EP3 IN : CDC 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(EndpointEP3, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceHID.Bytes(), ClassHID.Bytes(), - EndpointEP4IN.Bytes(), - EndpointEP5OUT.Bytes(), + EndpointIN(EndpointEP2, TransferTypeInterrupt, 0x40, 0x01).Bytes(), + EndpointOUT(EndpointEP1, 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 65756e0d63..1d07bf0a52 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 +// EP1 OUT: HID OUT +// EP2 IN : HID IN +// EP2 OUT: CDC OUT +// EP2 IN : CDC 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(EndpointEP3, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceHIDJoystick.Bytes(), ClassHIDJoystick.Bytes(), - EndpointEP4IN.Bytes(), - EndpointEP5OUT.Bytes(), + EndpointIN(EndpointEP2, TransferTypeInterrupt, 0x40, 0x01).Bytes(), + EndpointOUT(EndpointEP1, 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 fad81f31d5..a30b9df6eb 100644 --- a/src/machine/usb/descriptor/midi.go +++ b/src/machine/usb/descriptor/midi.go @@ -218,6 +218,11 @@ var ConfigurationCDCMIDI = ConfigurationType{ data: configurationCDCMIDI[:], } +// EP1 IN : CDC Call Management +// EP2 OUT: CDC OUT +// EP3 IN : CDC IN +// EP6 IN : MIDI IN (custom endpoint descriptor) +// EP7 OUT: MIDI OUT (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(EndpointEP3, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceAssociationMIDI.Bytes(), InterfaceAudio.Bytes(), ClassSpecificAudioInterface.Bytes(), diff --git a/src/machine/usb/descriptor/msc.go b/src/machine/usb/descriptor/msc.go index 55c6ddd857..6e2cad4f52 100644 --- a/src/machine/usb/descriptor/msc.go +++ b/src/machine/usb/descriptor/msc.go @@ -52,7 +52,18 @@ var ConfigurationMSC = ConfigurationType{ data: configurationMSC[:], } +var ( + EndpointMSCIN = EndpointIN(EndpointEP2, TransferTypeBulk, 0x40, 0x00) + EndpointMSCOUT = EndpointOUT(EndpointEP3, TransferTypeBulk, 0x40, 0x00) +) + // Mass Storage Class +// EP1 IN : CDC Call Management +// EP1 OUT: x +// EP2 IN : MSC IN +// EP2 OUT: CDC OUT +// EP3 IN : CDC IN +// EP3 OUT: MSC OUT var MSC = Descriptor{ Device: DeviceCDC.Bytes(), Configuration: Append([][]byte{ @@ -63,10 +74,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(EndpointEP3, TransferTypeBulk, 0x40, 0x00).Bytes(), InterfaceAssociationMSC.Bytes(), InterfaceMSC.Bytes(), EndpointMSCIN.Bytes(), From 3fa3779caa61c18fa6d340f6692b97f9e71a619e Mon Sep 17 00:00:00 2001 From: Konstantin Sharlaimov Date: Sun, 17 May 2026 09:59:11 +0200 Subject: [PATCH 2/2] Fix the size test. --- builder/sizes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/sizes_test.go b/builder/sizes_test.go index 7fd4f6f646..fd985a8977 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -44,7 +44,7 @@ func TestBinarySize(t *testing.T) { // microcontrollers {"hifive1b", "examples/echo", 3680, 280, 0, 2252}, {"microbit", "examples/serial", 2694, 342, 8, 2248}, - {"wioterminal", "examples/pininterrupt", 3541, 799, 88, 7008}, + {"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