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.
This commit is contained in:
Konstantin Sharlaimov
2026-05-17 09:08:39 +02:00
committed by Ron Evans
parent 2328d1e799
commit c3f1832d9a
8 changed files with 103 additions and 135 deletions
+3 -3
View File
@@ -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
+6 -3
View File
@@ -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(),
}),
}
+36 -96
View File
@@ -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 (
+10 -5
View File
@@ -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!
+10 -5
View File
@@ -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{},
}
+18 -13
View File
@@ -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(),
}),
}
+13 -3
View File
@@ -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(),
+7 -7
View File
@@ -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