262 строки
7,2 КиБ
Go
262 строки
7,2 КиБ
Go
//go:build sam || nrf52840 || rp2040
|
|
|
|
package machine
|
|
|
|
import (
|
|
"errors"
|
|
"machine/usb"
|
|
)
|
|
|
|
type USBDevice struct {
|
|
initcomplete bool
|
|
}
|
|
|
|
var (
|
|
USBDev = &USBDevice{}
|
|
USBCDC Serialer
|
|
)
|
|
|
|
type Serialer interface {
|
|
WriteByte(c byte) error
|
|
Write(data []byte) (n int, err error)
|
|
Configure(config UARTConfig) error
|
|
Buffered() int
|
|
ReadByte() (byte, error)
|
|
DTR() bool
|
|
RTS() bool
|
|
}
|
|
|
|
var usbDescriptor = usb.DescriptorCDC
|
|
|
|
var usbDescriptorConfig uint8 = usb.DescriptorConfigCDC
|
|
|
|
// strToUTF16LEDescriptor converts a utf8 string into a string descriptor
|
|
// note: the following code only converts ascii characters to UTF16LE. In order
|
|
// to do a "proper" conversion, we would need to pull in the 'unicode/utf16'
|
|
// package, which at the time this was written added 512 bytes to the compiled
|
|
// binary.
|
|
func strToUTF16LEDescriptor(in string, out []byte) {
|
|
out[0] = byte(len(out))
|
|
out[1] = usb.STRING_DESCRIPTOR_TYPE
|
|
for i, rune := range in {
|
|
out[(i<<1)+2] = byte(rune)
|
|
out[(i<<1)+3] = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
const cdcLineInfoSize = 7
|
|
|
|
var (
|
|
ErrUSBReadTimeout = errors.New("USB read timeout")
|
|
ErrUSBBytesRead = errors.New("USB invalid number of bytes read")
|
|
)
|
|
|
|
var (
|
|
usbEndpointDescriptors [usb.NumberOfEndpoints]usb.DeviceDescriptor
|
|
|
|
isEndpointHalt = false
|
|
isRemoteWakeUpEnabled = false
|
|
|
|
usbConfiguration uint8
|
|
usbSetInterface uint8
|
|
)
|
|
|
|
//go:align 4
|
|
var udd_ep_control_cache_buffer [256]uint8
|
|
|
|
//go:align 4
|
|
var udd_ep_in_cache_buffer [usb.NumberOfEndpoints][64]uint8
|
|
|
|
//go:align 4
|
|
var udd_ep_out_cache_buffer [usb.NumberOfEndpoints][64]uint8
|
|
|
|
// usb_trans_buffer max size is 255 since that is max size
|
|
// for a descriptor (bLength is 1 byte), and the biggest use
|
|
// for this buffer is to transmit string descriptors. If
|
|
// this buffer is used for new purposes in future the length
|
|
// must be revisited.
|
|
var usb_trans_buffer [255]uint8
|
|
|
|
var (
|
|
usbTxHandler [usb.NumberOfEndpoints]func()
|
|
usbRxHandler [usb.NumberOfEndpoints]func([]byte)
|
|
usbSetupHandler [usb.NumberOfInterfaces]func(usb.Setup) bool
|
|
|
|
endPoints = []uint32{
|
|
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
|
|
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
|
|
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
|
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
|
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
|
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
|
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
|
}
|
|
)
|
|
|
|
// sendDescriptor creates and sends the various USB descriptor types that
|
|
// can be requested by the host.
|
|
func sendDescriptor(setup usb.Setup) {
|
|
switch setup.WValueH {
|
|
case usb.CONFIGURATION_DESCRIPTOR_TYPE:
|
|
sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength)
|
|
return
|
|
case usb.DEVICE_DESCRIPTOR_TYPE:
|
|
// composite descriptor
|
|
switch {
|
|
case (usbDescriptorConfig & usb.DescriptorConfigHID) > 0:
|
|
usbDescriptor = usb.DescriptorCDCHID
|
|
case (usbDescriptorConfig & usb.DescriptorConfigMIDI) > 0:
|
|
usbDescriptor = usb.DescriptorCDCMIDI
|
|
default:
|
|
usbDescriptor = usb.DescriptorCDC
|
|
}
|
|
|
|
usbDescriptor.Configure(usb_VID, usb_PID)
|
|
sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
|
|
return
|
|
|
|
case usb.STRING_DESCRIPTOR_TYPE:
|
|
switch setup.WValueL {
|
|
case 0:
|
|
usb_trans_buffer[0] = 0x04
|
|
usb_trans_buffer[1] = 0x03
|
|
usb_trans_buffer[2] = 0x09
|
|
usb_trans_buffer[3] = 0x04
|
|
sendUSBPacket(0, usb_trans_buffer[:4], setup.WLength)
|
|
|
|
case usb.IPRODUCT:
|
|
b := usb_trans_buffer[:(len(usb_STRING_PRODUCT)<<1)+2]
|
|
strToUTF16LEDescriptor(usb_STRING_PRODUCT, b)
|
|
sendUSBPacket(0, b, setup.WLength)
|
|
|
|
case usb.IMANUFACTURER:
|
|
b := usb_trans_buffer[:(len(usb_STRING_MANUFACTURER)<<1)+2]
|
|
strToUTF16LEDescriptor(usb_STRING_MANUFACTURER, b)
|
|
sendUSBPacket(0, b, setup.WLength)
|
|
|
|
case usb.ISERIAL:
|
|
// TODO: allow returning a product serial number
|
|
SendZlp()
|
|
}
|
|
return
|
|
case usb.HID_REPORT_TYPE:
|
|
if h, ok := usbDescriptor.HID[setup.WIndex]; ok {
|
|
sendUSBPacket(0, h, setup.WLength)
|
|
return
|
|
}
|
|
case usb.DEVICE_QUALIFIER:
|
|
// skip
|
|
default:
|
|
}
|
|
|
|
// do not know how to handle this message, so return zero
|
|
SendZlp()
|
|
return
|
|
}
|
|
|
|
func handleStandardSetup(setup usb.Setup) bool {
|
|
switch setup.BRequest {
|
|
case usb.GET_STATUS:
|
|
usb_trans_buffer[0] = 0
|
|
usb_trans_buffer[1] = 0
|
|
|
|
if setup.BmRequestType != 0 { // endpoint
|
|
if isEndpointHalt {
|
|
usb_trans_buffer[0] = 1
|
|
}
|
|
}
|
|
|
|
sendUSBPacket(0, usb_trans_buffer[:2], setup.WLength)
|
|
return true
|
|
|
|
case usb.CLEAR_FEATURE:
|
|
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
|
|
isRemoteWakeUpEnabled = false
|
|
} else if setup.WValueL == 0 { // ENDPOINTHALT
|
|
isEndpointHalt = false
|
|
}
|
|
SendZlp()
|
|
return true
|
|
|
|
case usb.SET_FEATURE:
|
|
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
|
|
isRemoteWakeUpEnabled = true
|
|
} else if setup.WValueL == 0 { // ENDPOINTHALT
|
|
isEndpointHalt = true
|
|
}
|
|
SendZlp()
|
|
return true
|
|
|
|
case usb.SET_ADDRESS:
|
|
return handleUSBSetAddress(setup)
|
|
|
|
case usb.GET_DESCRIPTOR:
|
|
sendDescriptor(setup)
|
|
return true
|
|
|
|
case usb.SET_DESCRIPTOR:
|
|
return false
|
|
|
|
case usb.GET_CONFIGURATION:
|
|
usb_trans_buffer[0] = usbConfiguration
|
|
sendUSBPacket(0, usb_trans_buffer[:1], setup.WLength)
|
|
return true
|
|
|
|
case usb.SET_CONFIGURATION:
|
|
if setup.BmRequestType&usb.REQUEST_RECIPIENT == usb.REQUEST_DEVICE {
|
|
for i := 1; i < len(endPoints); i++ {
|
|
initEndpoint(uint32(i), endPoints[i])
|
|
}
|
|
|
|
usbConfiguration = setup.WValueL
|
|
|
|
SendZlp()
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
|
|
case usb.GET_INTERFACE:
|
|
usb_trans_buffer[0] = usbSetInterface
|
|
sendUSBPacket(0, usb_trans_buffer[:1], setup.WLength)
|
|
return true
|
|
|
|
case usb.SET_INTERFACE:
|
|
usbSetInterface = setup.WValueL
|
|
|
|
SendZlp()
|
|
return true
|
|
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
|
|
usbDescriptorConfig |= usb.DescriptorConfigCDC
|
|
endPoints[usb.CDC_ENDPOINT_ACM] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn)
|
|
endPoints[usb.CDC_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut)
|
|
endPoints[usb.CDC_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn)
|
|
usbRxHandler[usb.CDC_ENDPOINT_OUT] = rxHandler
|
|
usbTxHandler[usb.CDC_ENDPOINT_IN] = txHandler
|
|
usbSetupHandler[usb.CDC_ACM_INTERFACE] = setupHandler // 0x02 (Communications and CDC Control)
|
|
usbSetupHandler[usb.CDC_DATA_INTERFACE] = nil // 0x0A (CDC-Data)
|
|
}
|
|
|
|
// EnableHID enables HID. This function must be executed from the init().
|
|
func EnableHID(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
|
|
usbDescriptorConfig |= usb.DescriptorConfigHID
|
|
endPoints[usb.HID_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn)
|
|
usbTxHandler[usb.HID_ENDPOINT_IN] = txHandler
|
|
usbSetupHandler[usb.HID_INTERFACE] = setupHandler // 0x03 (HID - Human Interface Device)
|
|
}
|
|
|
|
// EnableMIDI enables MIDI. This function must be executed from the init().
|
|
func EnableMIDI(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
|
|
usbDescriptorConfig |= usb.DescriptorConfigMIDI
|
|
endPoints[usb.MIDI_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut)
|
|
endPoints[usb.MIDI_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn)
|
|
usbRxHandler[usb.MIDI_ENDPOINT_OUT] = rxHandler
|
|
usbTxHandler[usb.MIDI_ENDPOINT_IN] = txHandler
|
|
}
|