From 4b81985d2b88515727c7edcc15bddfda521095a7 Mon Sep 17 00:00:00 2001 From: sago35 Date: Thu, 12 May 2022 20:35:17 +0900 Subject: [PATCH] refactor USB package with device class build tags added basic HID keyboard support for SAMx51 (not fully-functional) --- src/examples/usb/keyboard/keyboard.go | 5 +- src/machine/board_grandcentral-m4.go | 6 - src/machine/serial-usb.go | 8 - src/machine/usb-cdc.go | 14 + src/machine/usb-hid.go | 14 + src/machine/usb/dcd-cdc.go | 277 ++++++ src/machine/usb/dcd-hid.go | 375 +++++++ src/machine/usb/dcd.go | 675 +------------ src/machine/usb/desc-cdc.go | 399 ++++++++ src/machine/usb/desc-cdc_atsamd51.go | 241 +++++ src/machine/usb/desc-hid.go | 511 ++++++++++ src/machine/usb/desc-hid_atsamd51.go | 271 +++++ src/machine/usb/desc.go | 936 +----------------- src/machine/usb/desc_atsamd51.go | 446 +-------- src/machine/usb/dhw-cdc_atsamd51.go | 244 +++++ src/machine/usb/dhw-hid_atsamd51.go | 265 +++++ src/machine/usb/dhw_atsamd51.go | 541 +--------- src/machine/usb/uart.go | 80 -- src/machine/usb/usb-cdc.go | 83 ++ .../usb/{keyboard.go => usb-hid-keyboard.go} | 7 + src/machine/usb/{hid.go => usb-hid.go} | 7 + src/machine/usb/usb.go | 6 +- src/runtime/runtime_atsamd51.go | 13 +- src/runtime/usb-cdc.go | 14 + src/runtime/usb-hid.go | 14 + 25 files changed, 2768 insertions(+), 2684 deletions(-) create mode 100644 src/machine/usb-cdc.go create mode 100644 src/machine/usb-hid.go create mode 100644 src/machine/usb/dcd-cdc.go create mode 100644 src/machine/usb/dcd-hid.go create mode 100644 src/machine/usb/desc-cdc.go create mode 100644 src/machine/usb/desc-cdc_atsamd51.go create mode 100644 src/machine/usb/desc-hid.go create mode 100644 src/machine/usb/desc-hid_atsamd51.go create mode 100644 src/machine/usb/dhw-cdc_atsamd51.go create mode 100644 src/machine/usb/dhw-hid_atsamd51.go delete mode 100644 src/machine/usb/uart.go create mode 100644 src/machine/usb/usb-cdc.go rename src/machine/usb/{keyboard.go => usb-hid-keyboard.go} (99%) rename src/machine/usb/{hid.go => usb-hid.go} (87%) create mode 100644 src/runtime/usb-cdc.go create mode 100644 src/runtime/usb-hid.go diff --git a/src/examples/usb/keyboard/keyboard.go b/src/examples/usb/keyboard/keyboard.go index cf2c66253..899286d6c 100644 --- a/src/examples/usb/keyboard/keyboard.go +++ b/src/examples/usb/keyboard/keyboard.go @@ -6,10 +6,13 @@ import ( "time" ) -var keyboard = machine.HID0.Keyboard() +var keyboard = machine.USB.Keyboard() func main() { + for !machine.USB.Ready() { + } + println("USB HID keyboard demo") for { diff --git a/src/machine/board_grandcentral-m4.go b/src/machine/board_grandcentral-m4.go index 64b649a69..61201826f 100644 --- a/src/machine/board_grandcentral-m4.go +++ b/src/machine/board_grandcentral-m4.go @@ -3,10 +3,6 @@ package machine -import ( - "machine/usb" -) - // Digital pins const ( // = Pin Alt. Function SERCOM PWM Timer Interrupt @@ -252,8 +248,6 @@ const ( RESET_MAGIC_VALUE = 0xF01669EF // Used to reset into bootloader ) -var USB = usb.UART{Port: 0} - // USB CDC pins const ( USBCDC_HOSTEN_PIN = D77 // (PA27) host enable diff --git a/src/machine/serial-usb.go b/src/machine/serial-usb.go index a3dd4abe5..ac484f87a 100644 --- a/src/machine/serial-usb.go +++ b/src/machine/serial-usb.go @@ -5,11 +5,3 @@ package machine // Serial is implemented via USB (USB-CDC). var Serial = USB - -func init() { - - // configure pins - USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) - USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) - -} diff --git a/src/machine/usb-cdc.go b/src/machine/usb-cdc.go new file mode 100644 index 000000000..01f64c717 --- /dev/null +++ b/src/machine/usb-cdc.go @@ -0,0 +1,14 @@ +//go:build baremetal && usb.cdc +// +build baremetal,usb.cdc + +package machine + +import "machine/usb" + +var USB = &usb.CDC{Port: 0} + +func init() { + // Configure USB D+/D- pins. + USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) + USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) +} diff --git a/src/machine/usb-hid.go b/src/machine/usb-hid.go new file mode 100644 index 000000000..59fe4ff59 --- /dev/null +++ b/src/machine/usb-hid.go @@ -0,0 +1,14 @@ +//go:build baremetal && usb.hid +// +build baremetal,usb.hid + +package machine + +import "machine/usb" + +var USB = &usb.HID{Port: 0} + +func init() { + // Configure USB D+/D- pins. + USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) + USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) +} diff --git a/src/machine/usb/dcd-cdc.go b/src/machine/usb/dcd-cdc.go new file mode 100644 index 000000000..0b813bc18 --- /dev/null +++ b/src/machine/usb/dcd-cdc.go @@ -0,0 +1,277 @@ +//go:build baremetal && usb.cdc +// +build baremetal,usb.cdc + +package usb + +import "unsafe" + +//go:inline +func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 { + switch endpointNumber(endpoint) { + case descCDCEndpointCtrl: + return descControlPacketSize + case descCDCEndpointStatus: + return descCDCStatusPacketSize + case descCDCEndpointDataRx: + return descCDCDataRxPacketSize + case descCDCEndpointDataTx: + return descCDCDataTxPacketSize + } + return descControlPacketSize +} + +//go:inline +func (d *dcd) controlEndpoint() uint8 { + return descCDCEndpointCtrl +} + +func (d *dcd) controlSetConfiguration() { + d.cdcConfigure() +} + +func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage { + + // Switch on the recepient and direction of the request + switch sup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch sup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + // line coding must contain exactly 7 bytes + if uint16(descCDCLineCodingSize) == sup.wLength { + d.controlReceive( + uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].cx[0])), + uint32(descCDCLineCodingSize), true) + // CDC Line Coding packet receipt handling occurs in method + // controlComplete(). + return dcdStageDataOut + } + + // CDC | SET CONTROL LINE STATE (0x22): + case descCDCRequestSetControlLineState: + // Determine interface destination of the request + switch sup.wIndex { + // Control/status interface: + case descCDCInterfaceCtrl: + // CDC Control Line State packet receipt handling occurs in method + // controlComplete(). + d.controlReceive(uintptr(0), 0, false) + return dcdStageStatusOut + + default: + // Unhandled device interface + } + + // CDC | SEND BREAK (0x23): + case descCDCRequestSendBreak: + d.controlReceive(uintptr(0), 0, false) + return dcdStageStatusOut + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + return dcdStageStall +} + +func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool { + switch sup.bRequest { + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + d.controlGetDescriptor(sup) + return true + } + return false +} + +func (d *dcd) controlGetDescriptor(sup dcdSetup) { + + acm := &descCDC[d.cc.config-1] + dxn := uint8(0) + + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { + + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(acm.dx[:], acm.device[:dxn]) + + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descCDCConfigSize) + _ = copy(acm.dx[:], acm.config[:dxn]) + + // String descriptor + case descTypeString: + if 0 == len(acm.locale) { + break // No string descriptors defined! + } + var sd []uint8 + if 0 == uint8(sup.wValue) { + + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This case (setup.wValue = [0x03]00) + // is a string request from the host to determine what that language is. + // + // In subsequent string requests, the host will populate setup.wIndex + // with the language code we return here in this string descriptor. + // + // This way all strings returned to the host are in the same language, + // whatever language that may be. + code := int(sup.wIndex) + if code >= len(acm.locale) { + code = 0 + } + sd = acm.locale[code].descriptor[sup.wValue&0xFF][:] + + } else { + + // setup.wIndex now contains a language code, which we specified in a + // previous request (above: setup.wValue = [0x03]00). We need to locate + // the set of strings whose language matches the language code given in + // this new setup.wIndex. + for code := range acm.locale { + if sup.wIndex == acm.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) { + + // Found language with a string defined at the requested index. + // + // TODO: Add API methods to device controller that allows the user + // to provide these strings at/before driver initialization. + // + // For now, we just always use the descCommon* strings. + var s string + switch uint8(sup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + " CDC-ACM" + case 3: + s = descCommonSerialNumber + } + + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. + sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:] + // String descriptor format is 2-byte header + 2-bytes per rune + sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length + sd[1] = descTypeString // header[1] = descriptor type + // Copy UTF-8 string into string descriptor as UTF-16 + for n, c := range s { + if 2+2*n >= len(sd) { + break + } + sd[2+2*n] = uint8(c) + sd[3+2*n] = 0 + } + break // end search for matching language code + } + } + } + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(acm.dx[:], sd[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(acm.dx[:], acm.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + // TODO + + default: + // Unhandled descriptor type + } + + if dxn > 0 { + if dxn > uint8(sup.wLength) { + dxn = uint8(sup.wLength) + } + flushCache( + uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) + d.controlTransmit( + uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + } +} + +// controlComplete handles the setup completion of control endpoint 0. +func (d *dcd) controlComplete() { + + // First, switch on the type of request (standard, class, or vendor) + switch d.setup.bmRequestType & descRequestTypeTypeMsk { + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch d.setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch d.setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + acm := &descCDC[d.cc.config-1] + + // Determine interface destination of the request + switch d.setup.wIndex { + + // CDC-ACM Control Interface: + case descCDCInterfaceCtrl: + // Notify PHY to handle triggers like special baud rates, which + // signal to reboot into bootloader or begin receiving OTA updates + d.cdcSetLineCoding(acm.cx[:]) + + default: + // Unhandled device interface + } + + // CDC | SET CONTROL LINE STATE (0x22): + case descCDCRequestSetControlLineState: + + // Determine interface destination of the request + switch d.setup.wIndex { + + // Control/status interface: + case descCDCInterfaceCtrl: + // DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02) + d.cdcSetLineState(d.setup.wValue) + + default: + // Unhandled device interface + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + + default: + // Unhandled request type + } +} diff --git a/src/machine/usb/dcd-hid.go b/src/machine/usb/dcd-hid.go new file mode 100644 index 000000000..5623a962f --- /dev/null +++ b/src/machine/usb/dcd-hid.go @@ -0,0 +1,375 @@ +//go:build baremetal && usb.hid +// +build baremetal,usb.hid + +package usb + +import "unsafe" + +//go:inline +func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 { + switch endpointNumber(endpoint) { + case descHIDEndpointCtrl: + return descControlPacketSize + case descHIDEndpointKeyboard: + return descHIDKeyboardTxPacketSize + case descHIDEndpointMouse: + return descHIDMouseTxPacketSize + case descHIDEndpointSerialRx: // == descHIDEndpointSerialTx + switch endpoint { + case rxEndpoint(endpoint): + return descHIDSerialRxPacketSize + case txEndpoint(endpoint): + return descHIDSerialTxPacketSize + } + case descHIDEndpointJoystick: + return descHIDJoystickTxPacketSize + case descHIDEndpointMediaKey: + return descHIDMediaKeyTxPacketSize + } + return descControlPacketSize +} + +//go:inline +func (d *dcd) controlEndpoint() uint8 { + return descHIDEndpointCtrl +} + +//go:inline +func (d *dcd) controlSetConfiguration() { + d.serialConfigure() + d.keyboardConfigure() + d.mouseConfigure() + d.joystickConfigure() +} + +func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage { + + // Switch on the recepient and direction of the request + switch sup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch sup.bRequest { + + // HID | SET REPORT (0x09) + case descHIDRequestSetReport: + if sup.wLength <= descHIDSxSize { + descHID[d.cc.config-1].cx[0] = 0xE9 + d.controlReceive( + uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])), + uint32(sup.wLength), true) + return dcdStageDataOut + } + + // HID | SET IDLE (0x0A) + case descHIDRequestSetIdle: + idleRate := sup.wValue >> 8 + // TBD: do we need to handle this request? wIndex contains the target + // interface of the request. + _ = idleRate + d.controlReceive(uintptr(0), 0, false) + return dcdStageStatusOut + + default: + // Unhandled request + } + + // --- INTERFACE Tx (IN) --- + case descRequestTypeRecipientInterface | descRequestTypeDirIn: + + // Identify which request was received + switch sup.bRequest { + + // HID | GET REPORT (0x01) + case descHIDRequestGetReport: + + reportType := uint8(sup.wValue >> 8) + reportID := uint8(sup.wValue) + // TBD: do we need to handle this request? wIndex contains the target + // interface of the request. + _, _ = reportType, reportID + d.controlTransmit( + d.controlStatusBuffer([]uint8{0, 0}), + 2, false) + return dcdStageDataIn + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + return dcdStageStall +} + +func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool { + switch sup.bRequest { + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + d.controlGetDescriptor(sup) + return true + // GET HID REPORT (0x01): + case descHIDRequestGetReport: + d.controlGetDescriptor(sup) + return true + } + return false +} + +func (d *dcd) controlGetDescriptor(sup dcdSetup) { + + hid := &descHID[d.cc.config-1] + dxn := uint8(0) + pos := uint8(0) + + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { + + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(hid.dx[:], hid.device[:dxn]) + + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descHIDConfigSize) + _ = copy(hid.dx[:], hid.config[:dxn]) + + // String descriptor + case descTypeString: + if 0 == len(hid.locale) { + break // No string descriptors defined! + } + var sd []uint8 + if 0 == uint8(sup.wValue) { + + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This case (setup.wValue = [0x03]00) + // is a string request from the host to determine what that language is. + // + // In subsequent string requests, the host will populate setup.wIndex + // with the language code we return here in this string descriptor. + // + // This way all strings returned to the host are in the same language, + // whatever language that may be. + code := int(sup.wIndex) + if code >= len(hid.locale) { + code = 0 + } + sd = hid.locale[code].descriptor[sup.wValue&0xFF][:] + + } else { + + // setup.wIndex now contains a language code, which we specified in a + // previous request (above: setup.wValue = [0x03]00). We need to locate + // the set of strings whose language matches the language code given in + // this new setup.wIndex. + for code := range hid.locale { + if sup.wIndex == hid.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) { + + // Found language with a string defined at the requested index. + // + // TODO: Add API methods to device controller that allows the user + // to provide these strings at/before driver initialization. + // + // For now, we just always use the descCommon* strings. + var s string + switch uint8(sup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + " HID" + case 3: + s = descCommonSerialNumber + } + + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. + sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:] + // String descriptor format is 2-byte header + 2-bytes per rune + sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length + sd[1] = descTypeString // header[1] = descriptor type + // Copy UTF-8 string into string descriptor as UTF-16 + for n, c := range s { + if 2+2*n >= len(sd) { + break + } + sd[2+2*n] = uint8(c) + sd[3+2*n] = 0 + } + break // end search for matching language code + } + } + } + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(hid.dx[:], sd[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(hid.dx[:], hid.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + // TODO + + // HID descriptor + case descTypeHID: + + // Determine interface destination of the request + switch sup.wIndex { + case descHIDInterfaceKeyboard: + pos = descHIDConfigKeyboardPos + + case descHIDInterfaceMouse: + pos = descHIDConfigMousePos + + case descHIDInterfaceSerial: + pos = descHIDConfigSerialPos + + case descHIDInterfaceJoystick: + pos = descHIDConfigJoystickPos + + case descHIDInterfaceMediaKey: + pos = descHIDConfigMediaKeyPos + + default: + // Unhandled HID interface + } + + if 0 != pos { + dxn = descLengthInterface + _ = copy(hid.dx[:], hid.config[pos:pos+dxn]) + } + + // HID report descriptor + case descTypeHIDReport: + + // Determine interface destination of the request + switch sup.wIndex { + case descHIDInterfaceKeyboard: + dxn = uint8(len(descHIDReportKeyboard)) + _ = copy(hid.dx[:], descHIDReportKeyboard[:]) + + case descHIDInterfaceMouse: + dxn = uint8(len(descHIDReportMouse)) + _ = copy(hid.dx[:], descHIDReportMouse[:]) + + case descHIDInterfaceSerial: + dxn = uint8(len(descHIDReportSerial)) + _ = copy(hid.dx[:], descHIDReportSerial[:]) + + case descHIDInterfaceJoystick: + dxn = uint8(len(descHIDReportJoystick)) + _ = copy(hid.dx[:], descHIDReportJoystick[:]) + + case descHIDInterfaceMediaKey: + dxn = uint8(len(descHIDReportMediaKey)) + _ = copy(hid.dx[:], descHIDReportMediaKey[:]) + + default: + // Unhandled HID interface + } + + default: + // Unhandled descriptor type + } + + if dxn > 0 { + if dxn > uint8(sup.wLength) { + dxn = uint8(sup.wLength) + } + flushCache( + uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn)) + d.controlTransmit( + uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false) + } +} + +// controlComplete handles the setup completion of control endpoint 0. +func (d *dcd) controlComplete() { + + // First, switch on the type of request (standard, class, or vendor) + switch d.setup.bmRequestType & descRequestTypeTypeMsk { + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch d.setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch d.setup.bRequest { + + // HID | SET REPORT (0x09) + case descHIDRequestSetReport: + + hid := &descHID[d.cc.config-1] + + // Determine interface destination of the request + switch d.setup.wIndex { + + // HID Keyboard Interface + case descHIDInterfaceKeyboard: + + // Determine the type of descriptor being requested + switch d.setup.wValue >> 8 { + + // Configuration descriptor + case descTypeConfigure: + if 1 == d.setup.wLength { + hid.keyboard.led = hid.cx[0] + d.controlTransmit(uintptr(0), 0, false) + } + + default: + // Unhandled descriptor type + } + + // HID Serial Interface + case descHIDInterfaceSerial: + + // Determine the type of descriptor being requested + switch d.setup.wValue >> 8 { + + // String descriptor + case descTypeString: + if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) { + d.enableSOF(true, descHIDInterfaceCount) + } + + default: + // Unhandled descriptor type + } + + default: + // Unhandled device interface + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + + default: + // Unhandled request type + } +} diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go index 8936f3be2..18bf70138 100644 --- a/src/machine/usb/dcd.go +++ b/src/machine/usb/dcd.go @@ -14,7 +14,7 @@ import ( // dcdCount defines the number of USB cores to configure for device mode. It is // computed as the sum of all declared device configuration descriptors. -const dcdCount = descCDCACMCount + descHIDCount +const dcdCount = descCDCCount + descHIDCount // dcdInstance provides statically-allocated instances of each USB device // controller configured on this platform. @@ -44,8 +44,8 @@ func initDCD(port int, speed Speed, class class) (*dcd, status) { return nil, statusInvalid // Must have defined device controllers } switch class.id { - case classDeviceCDCACM: - if 0 == class.config || class.config > descCDCACMCount { + case classDeviceCDC: + if 0 == class.config || class.config > descCDCCount { return nil, statusInvalid // Must have defined descriptors } default: @@ -319,26 +319,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { d.cc.config = 1 } d.event(dcdEvent{id: dcdEventDeviceConfiguration}) - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - d.uartConfigure() - d.controlReceive(uintptr(0), 0, false) - - // HID - case classDeviceHID: - d.serialConfigure() - d.keyboardConfigure() - d.mouseConfigure() - d.joystickConfigure() - d.controlReceive(uintptr(0), 0, false) - - default: - // Unhandled device class - } + d.controlSetConfiguration() + d.controlReceive(uintptr(0), 0, false) return dcdStageStatusOut default: @@ -360,23 +342,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET DESCRIPTOR (0x06): case descRequestStandardGetDescriptor: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - d.controlDescriptorCDCACM(sup) - return dcdStageDataIn - - // HID - case classDeviceHID: - d.controlDescriptorHID(sup) - return dcdStageDataIn - - default: - // Unhandled device class - } + d.controlGetDescriptor(sup) + return dcdStageDataIn // GET CONFIGURATION (0x08): case descRequestStandardGetConfiguration: @@ -393,47 +360,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // --- INTERFACE Tx (IN) --- case descRequestTypeRecipientInterface | descRequestTypeDirIn: - - // Identify which request was received - switch sup.bRequest { - - // GET DESCRIPTOR (0x06): - case descRequestStandardGetDescriptor: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - d.controlDescriptorCDCACM(sup) - return dcdStageDataIn - - // HID - case classDeviceHID: - d.controlDescriptorHID(sup) - return dcdStageDataIn - - default: - // Unhandled device class - } - - // GET HID REPORT (0x01): - case descHIDRequestGetReport: - - // Respond based on our device class configuration - switch d.cc.id { - - // HID - case classDeviceHID: - d.controlDescriptorHID(sup) - return dcdStageDataIn - - default: - // Unhandled device class - } - - default: - // Unhandled request + if d.controlGetInterfaceDescriptor(sup) { + return dcdStageDataIn } // --- ENDPOINT Rx (OUT) --- @@ -486,158 +414,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // === CLASS REQUEST === case descRequestTypeTypeClass: - // Switch on the recepient and direction of the request - switch sup.bmRequestType & - (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { - - // --- INTERFACE Rx (OUT) --- - case descRequestTypeRecipientInterface | descRequestTypeDirOut: - - // Identify which request was received - switch sup.bRequest { - - // CDC | SET LINE CODING (0x20): - case descCDCRequestSetLineCoding: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - // line coding must contain exactly 7 bytes - if uint16(descCDCACMLineCodingSize) == sup.wLength { - d.controlReceive( - uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])), - uint32(descCDCACMLineCodingSize), true) - // CDC Line Coding packet receipt handling occurs in method - // controlComplete(). - return dcdStageDataOut - } - - default: - // Unhandled device class - } - - // CDC | SET CONTROL LINE STATE (0x22): - case descCDCRequestSetControlLineState: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - - // Determine interface destination of the request - switch sup.wIndex { - - // Control/status interface: - case descCDCACMInterfaceCtrl: - // CDC Control Line State packet receipt handling occurs in method - // controlComplete(). - d.controlReceive(uintptr(0), 0, false) - return dcdStageStatusOut - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - // CDC | SEND BREAK (0x23): - case descCDCRequestSendBreak: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - d.controlReceive(uintptr(0), 0, false) - return dcdStageStatusOut - - default: - // Unhandled device class - } - - // HID | SET REPORT (0x09) - case descHIDRequestSetReport: - - // Respond based on our device class configuration - switch d.cc.id { - - // HID - case classDeviceHID: - if sup.wLength <= descHIDSxSize { - descHID[d.cc.config-1].cx[0] = 0xE9 - d.controlReceive( - uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])), - uint32(sup.wLength), true) - return dcdStageDataOut - } - - default: - // Unhandled device class - } - - // HID | SET IDLE (0x0A) - case descHIDRequestSetIdle: - - // Respond based on our device class configuration - switch d.cc.id { - - // HID - case classDeviceHID: - idleRate := sup.wValue >> 8 - // TBD: do we need to handle this request? wIndex contains the target - // interface of the request. - _ = idleRate - d.controlReceive(uintptr(0), 0, false) - return dcdStageStatusOut - - default: - // Unhandled device class - } - - default: - // Unhandled request - } - - // --- INTERFACE Tx (IN) --- - case descRequestTypeRecipientInterface | descRequestTypeDirIn: - - // Identify which request was received - switch sup.bRequest { - - // HID | GET REPORT (0x01) - case descHIDRequestGetReport: - - // Respond based on our device class configuration - switch d.cc.id { - - // HID - case classDeviceHID: - reportType := uint8(sup.wValue >> 8) - reportID := uint8(sup.wValue) - // TBD: do we need to handle this request? wIndex contains the target - // interface of the request. - _, _ = reportType, reportID - d.controlTransmit( - d.controlStatusBuffer([]uint8{0, 0}), - 2, false) - return dcdStageDataIn - - default: - // Unhandled device class - } - - default: - // Unhandled request - } - - default: - // Unhandled request recepient or direction - } + // Forward all class requests to the device class implementation. + return d.controlClassRequest(sup) case descRequestTypeTypeVendor: default: @@ -648,434 +426,3 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // was invalid or unhandled. Stall the endpoint. return dcdStageStall } - -// controlComplete handles the setup completion of control endpoint 0. -func (d *dcd) controlComplete() { - - // First, switch on the type of request (standard, class, or vendor) - switch d.setup.bmRequestType & descRequestTypeTypeMsk { - - // === CLASS REQUEST === - case descRequestTypeTypeClass: - - // Switch on the recepient and direction of the request - switch d.setup.bmRequestType & - (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { - - // --- INTERFACE Rx (OUT) --- - case descRequestTypeRecipientInterface | descRequestTypeDirOut: - - // Identify which request was received - switch d.setup.bRequest { - - // CDC | SET LINE CODING (0x20): - case descCDCRequestSetLineCoding: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - acm := &descCDCACM[d.cc.config-1] - - // Determine interface destination of the request - switch d.setup.wIndex { - - // CDC-ACM Control Interface: - case descCDCACMInterfaceCtrl: - // Notify PHY to handle triggers like special baud rates, which - // signal to reboot into bootloader or begin receiving OTA updates - d.uartSetLineCoding(acm.cx[:]) - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - // CDC | SET CONTROL LINE STATE (0x22): - case descCDCRequestSetControlLineState: - - // Respond based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - - // Determine interface destination of the request - switch d.setup.wIndex { - - // Control/status interface: - case descCDCACMInterfaceCtrl: - // DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02) - d.uartSetLineState(d.setup.wValue) - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - // HID | SET REPORT (0x09) - case descHIDRequestSetReport: - - // Respond based on our device class configuration - switch d.cc.id { - - // HID - case classDeviceHID: - hid := &descHID[d.cc.config-1] - - // Determine interface destination of the request - switch d.setup.wIndex { - - // HID Keyboard Interface - case descHIDInterfaceKeyboard: - - // Determine the type of descriptor being requested - switch d.setup.wValue >> 8 { - - // Configuration descriptor - case descTypeConfigure: - if 1 == d.setup.wLength { - hid.keyboard.led = hid.cx[0] - d.controlTransmit(uintptr(0), 0, false) - } - - default: - // Unhandled descriptor type - } - - // HID Serial Interface - case descHIDInterfaceSerial: - - // Determine the type of descriptor being requested - switch d.setup.wValue >> 8 { - - // String descriptor - case descTypeString: - if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) { - d.enableSOF(true, descHIDInterfaceCount) - } - - default: - // Unhandled descriptor type - } - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - default: - // Unhandled request - } - - default: - // Unhandled recepient or direction - } - - default: - // Unhandled request type - } -} - -func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) { - - acm := &descCDCACM[d.cc.config-1] - dxn := uint8(0) - - // Determine the type of descriptor being requested - switch sup.wValue >> 8 { - - // Device descriptor - case descTypeDevice: - dxn = descLengthDevice - _ = copy(acm.dx[:], acm.device[:dxn]) - - // Configuration descriptor - case descTypeConfigure: - dxn = uint8(descCDCACMConfigSize) - _ = copy(acm.dx[:], acm.config[:dxn]) - - // String descriptor - case descTypeString: - if 0 == len(acm.locale) { - break // No string descriptors defined! - } - var sd []uint8 - if 0 == uint8(sup.wValue) { - - // setup.wIndex contains an arbitrary index referring to a collection of - // strings in some given language. This case (setup.wValue = [0x03]00) - // is a string request from the host to determine what that language is. - // - // In subsequent string requests, the host will populate setup.wIndex - // with the language code we return here in this string descriptor. - // - // This way all strings returned to the host are in the same language, - // whatever language that may be. - code := int(sup.wIndex) - if code >= len(acm.locale) { - code = 0 - } - sd = acm.locale[code].descriptor[sup.wValue&0xFF][:] - - } else { - - // setup.wIndex now contains a language code, which we specified in a - // previous request (above: setup.wValue = [0x03]00). We need to locate - // the set of strings whose language matches the language code given in - // this new setup.wIndex. - for code := range acm.locale { - if sup.wIndex == acm.locale[code].language { - // Found language, check if string descriptor at given index exists - if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) { - - // Found language with a string defined at the requested index. - // - // TODO: Add API methods to device controller that allows the user - // to provide these strings at/before driver initialization. - // - // For now, we just always use the descCommon* strings. - var s string - switch uint8(sup.wValue) { - case 1: - s = descCommonManufacturer - case 2: - s = descCommonProduct + " CDC-ACM" - case 3: - s = descCommonSerialNumber - } - - // Construct a string descriptor dynamically to be transmitted on - // the serial bus. - sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:] - // String descriptor format is 2-byte header + 2-bytes per rune - sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length - sd[1] = descTypeString // header[1] = descriptor type - // Copy UTF-8 string into string descriptor as UTF-16 - for n, c := range s { - if 2+2*n >= len(sd) { - break - } - sd[2+2*n] = uint8(c) - sd[3+2*n] = 0 - } - break // end search for matching language code - } - } - } - } - // Copy string descriptor into descriptor transmit buffer - if nil != sd && len(sd) >= 0 { - dxn = sd[0] - _ = copy(acm.dx[:], sd[:dxn]) - } - - // Device qualification descriptor - case descTypeQualification: - dxn = descLengthQualification - _ = copy(acm.dx[:], acm.qualif[:dxn]) - - // Alternate configuration descriptor - case descTypeOtherSpeedConfiguration: - // TODO - - default: - // Unhandled descriptor type - } - - if dxn > 0 { - if dxn > uint8(sup.wLength) { - dxn = uint8(sup.wLength) - } - flushCache( - uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) - d.controlTransmit( - uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) - } -} - -func (d *dcd) controlDescriptorHID(sup dcdSetup) { - - hid := &descHID[d.cc.config-1] - dxn := uint8(0) - pos := uint8(0) - - // Determine the type of descriptor being requested - switch sup.wValue >> 8 { - - // Device descriptor - case descTypeDevice: - dxn = descLengthDevice - _ = copy(hid.dx[:], hid.device[:dxn]) - - // Configuration descriptor - case descTypeConfigure: - dxn = uint8(descHIDConfigSize) - _ = copy(hid.dx[:], hid.config[:dxn]) - - // String descriptor - case descTypeString: - if 0 == len(hid.locale) { - break // No string descriptors defined! - } - var sd []uint8 - if 0 == uint8(sup.wValue) { - - // setup.wIndex contains an arbitrary index referring to a collection of - // strings in some given language. This case (setup.wValue = [0x03]00) - // is a string request from the host to determine what that language is. - // - // In subsequent string requests, the host will populate setup.wIndex - // with the language code we return here in this string descriptor. - // - // This way all strings returned to the host are in the same language, - // whatever language that may be. - code := int(sup.wIndex) - if code >= len(hid.locale) { - code = 0 - } - sd = hid.locale[code].descriptor[sup.wValue&0xFF][:] - - } else { - - // setup.wIndex now contains a language code, which we specified in a - // previous request (above: setup.wValue = [0x03]00). We need to locate - // the set of strings whose language matches the language code given in - // this new setup.wIndex. - for code := range hid.locale { - if sup.wIndex == hid.locale[code].language { - // Found language, check if string descriptor at given index exists - if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) { - - // Found language with a string defined at the requested index. - // - // TODO: Add API methods to device controller that allows the user - // to provide these strings at/before driver initialization. - // - // For now, we just always use the descCommon* strings. - var s string - switch uint8(sup.wValue) { - case 1: - s = descCommonManufacturer - case 2: - s = descCommonProduct + " HID" - case 3: - s = descCommonSerialNumber - } - - // Construct a string descriptor dynamically to be transmitted on - // the serial bus. - sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:] - // String descriptor format is 2-byte header + 2-bytes per rune - sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length - sd[1] = descTypeString // header[1] = descriptor type - // Copy UTF-8 string into string descriptor as UTF-16 - for n, c := range s { - if 2+2*n >= len(sd) { - break - } - sd[2+2*n] = uint8(c) - sd[3+2*n] = 0 - } - break // end search for matching language code - } - } - } - } - // Copy string descriptor into descriptor transmit buffer - if nil != sd && len(sd) >= 0 { - dxn = sd[0] - _ = copy(hid.dx[:], sd[:dxn]) - } - - // Device qualification descriptor - case descTypeQualification: - dxn = descLengthQualification - _ = copy(hid.dx[:], hid.qualif[:dxn]) - - // Alternate configuration descriptor - case descTypeOtherSpeedConfiguration: - // TODO - - // HID descriptor - case descTypeHID: - - // Determine interface destination of the request - switch sup.wIndex { - case descHIDInterfaceKeyboard: - pos = descHIDConfigKeyboardPos - - case descHIDInterfaceMouse: - pos = descHIDConfigMousePos - - case descHIDInterfaceSerial: - pos = descHIDConfigSerialPos - - case descHIDInterfaceJoystick: - pos = descHIDConfigJoystickPos - - case descHIDInterfaceMediaKey: - pos = descHIDConfigMediaKeyPos - - default: - // Unhandled HID interface - } - - if 0 != pos { - dxn = descLengthInterface - _ = copy(hid.dx[:], hid.config[pos:pos+dxn]) - } - - // HID report descriptor - case descTypeHIDReport: - - // Determine interface destination of the request - switch sup.wIndex { - case descHIDInterfaceKeyboard: - dxn = uint8(len(descHIDReportKeyboard)) - _ = copy(hid.dx[:], descHIDReportKeyboard[:]) - - case descHIDInterfaceMouse: - dxn = uint8(len(descHIDReportMouse)) - _ = copy(hid.dx[:], descHIDReportMouse[:]) - - case descHIDInterfaceSerial: - dxn = uint8(len(descHIDReportSerial)) - _ = copy(hid.dx[:], descHIDReportSerial[:]) - - case descHIDInterfaceJoystick: - dxn = uint8(len(descHIDReportJoystick)) - _ = copy(hid.dx[:], descHIDReportJoystick[:]) - - case descHIDInterfaceMediaKey: - dxn = uint8(len(descHIDReportMediaKey)) - _ = copy(hid.dx[:], descHIDReportMediaKey[:]) - - default: - // Unhandled HID interface - } - - default: - // Unhandled descriptor type - } - - if dxn > 0 { - if dxn > uint8(sup.wLength) { - dxn = uint8(sup.wLength) - } - flushCache( - uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn)) - d.controlTransmit( - uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false) - } -} diff --git a/src/machine/usb/desc-cdc.go b/src/machine/usb/desc-cdc.go new file mode 100644 index 000000000..27149b2f6 --- /dev/null +++ b/src/machine/usb/desc-cdc.go @@ -0,0 +1,399 @@ +//go:build baremetal && usb.cdc +// +build baremetal,usb.cdc + +package usb + +// USB CDC constants defined per specification. +const ( + + // Device class + descCDCTypeComm = 0x02 // communication/control + descCDCTypeData = 0x0A // data + + // Communication/control subclass + descCDCSubNone = 0x00 + descCDCSubDirectLineControl = 0x01 + descCDCSubAbstractControl = 0x02 + descCDCSubTelephoneControl = 0x03 + descCDCSubMultiChannelControl = 0x04 + descCDCSubCAPIControl = 0x05 + descCDCSubEthernetNetworkingControl = 0x06 + descCDCSubATMNetworkingControl = 0x07 + descCDCSubWirelessHandsetControl = 0x08 + descCDCSubDeviceManagement = 0x09 + descCDCSubMobileDirectLine = 0x0A + descCDCSubOBEX = 0x0B + descCDCSubEthernetEmulation = 0x0C + + // Communication/control protocol + descCDCProtoNone = 0x00 // also for data class + descCDCProtoAT250 = 0x01 + descCDCProtoATPCCA101 = 0x02 + descCDCProtoATPCCA101AnnexO = 0x03 + descCDCProtoATGSM707 = 0x04 + descCDCProtoAT3GPP27007 = 0x05 + descCDCProtoATTIACDMA = 0x06 + descCDCProtoEthernetEmulation = 0x07 + descCDCProtoExternal = 0xFE + descCDCProtoVendorSpecific = 0xFF // also for data class + + // Data protocol + descCDCProtoPyhsicalInterface = 0x30 + descCDCProtoHDLC = 0x31 + descCDCProtoTransparent = 0x32 + descCDCProtoManagement = 0x50 + descCDCProtoDataLinkQ931 = 0x51 + descCDCProtoDataLinkQ921 = 0x52 + descCDCProtoDataCompressionV42BIS = 0x90 + descCDCProtoEuroISDN = 0x91 + descCDCProtoRateAdaptionISDNV24 = 0x92 + descCDCProtoCAPICommands = 0x93 + descCDCProtoHostBasedDriver = 0xFD + descCDCProtoUnitFunctional = 0xFE + + // Functional descriptor length + descCDCFuncLengthHeader = 5 + descCDCFuncLengthCallManagement = 5 + descCDCFuncLengthAbstractControl = 4 + descCDCFuncLengthUnion = 5 + + // Functional descriptor type + descCDCFuncTypeHeader = 0x00 + descCDCFuncTypeCallManagement = 0x01 + descCDCFuncTypeAbstractControl = 0x02 + descCDCFuncTypeDirectLine = 0x03 + descCDCFuncTypeTelephoneRinger = 0x04 + descCDCFuncTypeTelephoneReport = 0x05 + descCDCFuncTypeUnion = 0x06 + descCDCFuncTypeCountrySelect = 0x07 + descCDCFuncTypeTelephoneModes = 0x08 + descCDCFuncTypeTerminal = 0x09 + descCDCFuncTypeNetworkChannel = 0x0A + descCDCFuncTypeProtocolUnit = 0x0B + descCDCFuncTypeExtensionUnit = 0x0C + descCDCFuncTypeMultiChannel = 0x0D + descCDCFuncTypeCAPIControl = 0x0E + descCDCFuncTypeEthernetNetworking = 0x0F + descCDCFuncTypeATMNetworking = 0x10 + descCDCFuncTypeWirelessControl = 0x11 + descCDCFuncTypeMobileDirectLine = 0x12 + descCDCFuncTypeMDLMDetail = 0x13 + descCDCFuncTypeDeviceManagement = 0x14 + descCDCFuncTypeOBEX = 0x15 + descCDCFuncTypeCommandSet = 0x16 + descCDCFuncTypeCommandSetDetail = 0x17 + descCDCFuncTypeTelephoneControl = 0x18 + descCDCFuncTypeOBEXServiceID = 0x19 + + // Standard request + descCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND + descCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE + descCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE + descCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE + descCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE + descCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE + descCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE + descCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP + descCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE + descCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME + descCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK + descCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING + descCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING + descCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE + descCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK + descCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS + descCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS + descCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM + descCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM + descCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS + descCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS + descCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS + descCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER + descCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER + descCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER + descCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS + descCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER + descCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER + descCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER + descCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC + descCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT + descCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS + descCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC + descCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS + descCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK + + // Notification type + descCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION + descCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL + descCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE + descCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT + descCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE + descCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE + descCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE + descCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE + + // Feature select + descCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE + descCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING + + // Control signal + descCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION + descCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE + + // UART emulated state + descCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER + descCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER + descCDCUARTStateBreak = 0x04 // UART state BREAK + descCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL + descCDCUARTStateFraming = 0x10 // UART state FRAMING + descCDCUARTStateParity = 0x20 // UART state PARITY + descCDCUARTStateOverrun = 0x40 // UART state OVERRUN +) + +const ( + // Size of all CDC-ACM configuration descriptors. + descCDCConfigSize = uint16( + descLengthConfigure + // Configuration Header + descLengthInterface + // CDC Interface Descriptor + descCDCFuncLengthHeader + // CDC Header + descCDCFuncLengthCallManagement + // CDC Call Management Func Descriptor + descCDCFuncLengthAbstractControl + // CDC Abstract Control Func Descriptor + descCDCFuncLengthUnion + // CDC Union Functional Descriptor + descLengthEndpoint + // CDC Status IN Endpoint Descriptor + descLengthInterface + // CDC Data Interface Descriptor + descLengthEndpoint + // CDC Data IN Endpoint Descriptor + descLengthEndpoint) // CDC Data OUT Endpoint Descriptor +) + +// descCDCLineCodingSize defines the length of a CDC-ACM UART line coding +// buffer. Note that the actual buffer may be padded for alignment; but for +// Rx/Tx transfer purposes, descCDCLineCodingSize defines the number of bytes +// that are transferred following a control SETUP request. +const descCDCLineCodingSize = 7 + +// descCDCLineCoding represents an emulated UART's line configuration. +// +// Use descCDCLineCodingSize instead of unsafe.Sizeof(descCDCLineCoding) +// in any transfer requests, because the actual struct is padded for alignment. +type descCDCLineCoding struct { + baud uint32 + stopBits uint8 + parity uint8 + numBits uint8 + _ uint8 +} + +// parse initializes the receiver descCDCLineCoding from the given []uint8 v. +// Argument v is a Rx transfer buffer, filled following the completion of a +// control transfer from a CDC SET_LINE_CODING (0x20) request +func (s *descCDCLineCoding) parse(v []uint8) bool { + if len(v) >= descCDCLineCodingSize { + s.baud = packU32(v[:]) + s.stopBits = v[4] + s.parity = v[5] + s.numBits = v[6] + return true + } + return false +} + +// descCDCLineState represents an emulated UART's line state. +type descCDCLineState struct { + // dataTerminalReady indicates if DTE is present or not. + // Corresponds to V.24 signal 108/2 and RS-232 signal DTR. + dataTerminalReady bool // DTR + // requestToSend is the carrier control for half-duplex modems. + // Corresponds to V.24 signal 105 and RS-232 signal RTS. + requestToSend bool // RTS +} + +// parse initializes the receiver descCDCLineState from the given uint16 v. +// Argument v corresponds to the wValue field in a control SETUP packet, which +// carries the line state from a CDC SET_CONTROL_LINE_STATE (0x22) request. +func (s *descCDCLineState) parse(v uint16) bool { + s.dataTerminalReady = 0 != v&0x1 + s.requestToSend = 0 != v&0x2 + return true +} + +// Common configuration constants for the USB CDC-ACM (single) device class. +const ( + descCDCLanguageCount = 1 // String descriptor languages available + + descCDCInterfaceCount = 2 // Interfaces for all CDC-ACM configurations. + descCDCEndpointCount = 4 // Endpoints for all CDC-ACM configurations. + + descCDCEndpointCtrl = 0 // CDC-ACM Control Endpoint 0 + + descCDCInterfaceCtrl = 0 // CDC-ACM Control Interface + descCDCEndpointStatus = 1 // CDC-ACM Interrupt IN Endpoint + descCDCConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt + + descCDCInterfaceData = 1 // CDC-ACM Data Interface + descCDCEndpointDataRx = 2 // CDC-ACM Bulk Data OUT (Rx) Endpoint + descCDCConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused + descCDCEndpointDataTx = 3 // CDC-ACM Bulk Data IN (Tx) Endpoint + descCDCConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk +) + +// descCDCClass holds references to all descriptors, buffers, and control +// structures for the USB CDC-ACM (single) device class. +type descCDCClass struct { + *descCDCClassData // Target-defined, class-specific data + + locale *[descCDCLanguageCount]descStringLanguage // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descCDCConfigSize]uint8 // configuration descriptor +} + +// descCDC holds statically-allocated instances for each of the CDC-ACM +// (single) device class configurations, ordered by index (offset by -1). +var descCDC = [dcdCount]descCDCClass{ + + { // CDC-ACM (single) class configuration index 1 + descCDCClassData: &descCDCData[0], + + locale: &[descCDCLanguageCount]descStringLanguage{ + + { // [0x0409] US English + language: descLanguageEnglish, + descriptor: descStringIndex{ + { /* [0] Language */ + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + // Actual string descriptors (index > 0) are copied into here at runtime! + // This allows for application- or even user-defined string descriptors. + { /* [1] Manufacturer */ }, + { /* [2] Product */ }, + { /* [3] Serial Number */ }, + }, + }, + }, + device: &[descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descCommonReleaseID), // Device release number in BCD (low) + msU8(descCommonReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descCDCACMCount, // Number of possible configurations + }, + qualif: &[descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descCDCACMCount, // Number of possible configurations + 0, // Reserved + }, + config: &[descCDCConfigSize]uint8{ + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descCDCConfigSize), // Total length of data returned for this configuration (low) + msU8(descCDCConfigSize), // Total length of data returned for this configuration (high) + descCDCInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descEndptConfigAttr, // Configuration attributes + descCDCMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units) + + // Communication/Control Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descCDCInterfaceCtrl, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descCDCTypeComm, // Class code + descCDCSubAbstractControl, // Subclass code + descCDCProtoAT250, // Protocol code (NOTE: Teensyduino & Arduino-Mbed define this as 1 [AT V.250]) + 0, // Interface Description String Index + + // CDC Header Functional Descriptor + descCDCFuncLengthHeader, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeHeader, // Descriptor Subtype + 0x10, // USB CDC specification version 1.10 (low) + 0x01, // USB CDC specification version 1.10 (high) + + // CDC Call Management Functional Descriptor + descCDCFuncLengthCallManagement, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeCallManagement, // Descriptor Subtype + 0x01, // Capabilities + descCDCInterfaceData, // Data Interface + + // CDC Abstract Control Management Functional Descriptor + descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeAbstractControl, // Descriptor Subtype + 0x06, // Capabilities + + // CDC Union Functional Descriptor + descCDCFuncLengthUnion, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeUnion, // Descriptor Subtype + descCDCInterfaceCtrl, // Controlling interface index + descCDCInterfaceData, // Controlled interface index + + // Communication/Control Notification Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCEndpointStatus | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descCDCStatusPacketSize), // Max packet size (low) + msU8(descCDCStatusPacketSize), // Max packet size (high) + descCDCStatusInterval, // Polling Interval + + // Data Interface Descriptor + descLengthInterface, // Interface length + descTypeInterface, // Interface type + descCDCInterfaceData, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descCDCTypeData, // Class code + descCDCSubNone, // Subclass code + descCDCProtoNone, // Protocol code + 0, // Interface Description String Index + + // Data Bulk Rx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCEndpointDataRx | // Endpoint address + descEndptAddrDirectionOut, + descEndptTypeBulk, // Attributes + lsU8(descCDCDataRxPacketSize), // Max packet size (low) + msU8(descCDCDataRxPacketSize), // Max packet size (high) + 0, // Polling Interval + + // Data Bulk Tx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCEndpointDataTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeBulk, // Attributes + lsU8(descCDCDataTxPacketSize), // Max packet size (low) + msU8(descCDCDataTxPacketSize), // Max packet size (high) + 0, // Polling Interval + }, + }, +} diff --git a/src/machine/usb/desc-cdc_atsamd51.go b/src/machine/usb/desc-cdc_atsamd51.go new file mode 100644 index 000000000..314143ed4 --- /dev/null +++ b/src/machine/usb/desc-cdc_atsamd51.go @@ -0,0 +1,241 @@ +//go:build baremetal && usb.cdc && (atsamd51 || atsame5x) +// +build baremetal +// +build usb.cdc +// +build atsamd51 atsame5x + +package usb + +import "runtime/volatile" + +// Constants for USB CDC-ACM device classes. +const ( + + // USB Bus Configuration Attributes + + descCDCMaxPowerMa = 100 // Maximum current (mA) requested from host + + // CDC-ACM Endpoint Descriptor Buffers + + descCDCEDCount = descMaxEndpoints + + // Setup packet is only 8 bytes in length. However, under certain scenarios, + // USB DMA controller may decide to overwrite/overflow the buffer with 2 extra + // bytes of CRC. From datasheet's "Management of SETUP Transactions" section: + // | If the number of received data bytes is the maximum data payload + // | specified by PCKSIZE.SIZE minus one, only the first CRC data is written + // | to the data buffer. If the number of received data is equal or less + // | than the data payload specified by PCKSIZE.SIZE minus two, both CRC + // | data bytes are written to the data buffer. + // Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT). + descCDCSxSize = 8 + 2 + descCDCCxSize = descControlPacketSize + + // CDC-ACM Data Buffers + + descCDCRxSize = descCDCDataRxPacketSize + descCDCTxSize = descCDCDataTxPacketSize + + descCDCTxTimeoutMs = 120 // millisec + descCDCTxSyncUs = 75 // microsec + + // Default CDC-ACM Endpoint Configurations (Full-Speed) + + descCDCStatusInterval = descCDCStatusFSInterval // Status + descCDCStatusPacketSize = descCDCStatusFSPacketSize // + + descCDCDataRxPacketSize = descCDCDataRxFSPacketSize // Data Rx + descCDCDataTxPacketSize = descCDCDataTxFSPacketSize // Data Tx + + // CDC-ACM Endpoint Configurations for Full-Speed Device + + descCDCStatusFSInterval = 5 // Status + descCDCStatusFSPacketSize = 64 // (full-speed) + + descCDCDataRxFSPacketSize = 64 // Data Rx (full-speed) + descCDCDataTxFSPacketSize = 64 // Data Tx (full-speed) + + // CDC-ACM Endpoint Configurations for High-Speed Device + + // - N/A, SAMx51 only has a full-speed PHY +) + +// descCDC0ED is an array of endpoint descriptors, which describes to the USB +// DMA controller the buffer and transfer properties for each endpoint, for the +// default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDC0ED [descCDCEDCount]dhwEPAddrDesc + +// descCDC0Sx is the receive (Rx) buffer for setup packets on control endpoint +// 0 of the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDC0Sx [descCDCSxSize]uint8 + +// descCDC0Cx is the transmit (Tx) buffer for control/status packets on control +// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDC0Cx [descCDCCxSize]uint8 + +// descCDC0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 +// for the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDC0Dx [descCDCConfigSize]uint8 + +// descCDC0Rx is the receive (Rx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDC0Rx [descCDCRxSize]uint8 + +// descCDC0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDC0Tx [descCDCTxSize]uint8 + +// descCDC0Rq is the receive (Rx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDC0Rq [descCDCRxSize]uint8 + +// descCDC0Tq is the transmit (Tx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDC0Tq [descCDCTxSize]uint8 + +// descCDC0LC is the emulated UART's line coding configuration for the +// default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDC0LC descCDCLineCoding + +// descCDC0LS is the emulated UART's line state for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDC0LS descCDCLineState + +// descCDCState defines the state of the CDC-ACM handshake initialization. +// +// Many USB hosts will send a default SET_LINE_CODING prior to SET_LINE_STATE, +// and then another SET_LINE_CODING containing the actual terminal settings. +// +// We do not want to start UART Rx/Tx transactions until after we have +// received the final SET_LINE_CODING with the intended terminal settings. +// Otherwise, the host may cancel any data transfers occurring during a change +// in line state or line coding. +// +// The "set" method on type descCDCState defines this incremental state +// machine, with the UART's current state stored in the volatile.Register8 +// field "st" of descCDCClassData. +type descCDCState uint8 + +// set implements the state transition logic described in the godoc comment on +// type descCDCState. Returns the value of the resulting state. +//go:inline +func (s *descCDCState) set(state descCDCState) descCDCState { + if state > *s { + // state must be incremented in-order. Otherwise, reset to initial state. + if state == *s+1 { + *s = state + } else { + var init descCDCState // Reset to zero-value of type. + *s = init + } + } + // Return a value for safely chaining the result. + // (Not a pointer to the object we just modified.) + return *s +} + +const ( + descCDCStateConfigured descCDCState = iota // Received SET_CONFIGURATION class request + descCDCStateLineState // Received SET_LINE_STATE after Configured state + descCDCStateLineCoding // Received SET_LINE_CODING after LineState state +) + +// descCDCClassData holds the buffers and control states for all CDC-ACM +// (single) device class configurations, ordered by index (offset by -1), for +// SAMx51 targets only. +// +// Instances of this type (elements of descCDCData) are embedded in elements +// of the common/target-agnostic CDC-ACM class configurations (descCDC). +// Methods defined on this type implement target-specific functionality, and +// some of these methods are required by the common device controller driver. +// Thus, this type functions as a hardware abstraction layer (HAL). +type descCDCClassData struct { + + // CDC-ACM Control Buffers + + ed *[descCDCEDCount]dhwEPAddrDesc // endpoint descriptors + + sx *[descCDCSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets + cx *[descCDCCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets + dx *[descCDCConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer + + // CDC-ACM Data Buffers + + rx *[descCDCRxSize]uint8 // bulk data endpoint Rx (OUT) transfer buffer + tx *[descCDCTxSize]uint8 // bulk data endpoint Tx (IN) transfer buffer + + rxq *[descCDCRxSize]uint8 // CDC-ACM UART Rx FIFO + txq *[descCDCTxSize]uint8 // CDC-ACM UART Tx FIFO + + rq *Queue // CDC-ACM UART Rx Queue (backed by FIFO rxq) + tq *Queue // CDC-ACM UART Tx Queue (backed by FIFO txq) + + lc *descCDCLineCoding // UART line coding + ls *descCDCLineState // UART line state + + st volatile.Register8 + + sxSize uint32 + rxSize uint32 + txSize uint32 +} + +// setState is a wrapper for converting and storing the given descCDCState +// value as a uint8 in the receiver's volatile.Register8 field st. +//go:inline +func (c *descCDCClassData) setState(state descCDCState) { + s := descCDCState(c.st.Get()) + c.st.Set(uint8(s.set(state))) +} + +// state is a wrapper for retrieving and converting the receiver's +// volatile.Register8 field st from uint8 to descCDCState. +//go:inline +func (c *descCDCClassData) state() descCDCState { + return descCDCState(c.st.Get()) +} + +// descCDCData holds statically-allocated instances for each of the target- +// specific (SAMx51) CDC-ACM (single) device class configurations' control and +// data structures, ordered by configuration index (offset by -1). Each element +// is embedded in a corresponding element of descCDC. +var descCDCData = [dcdCount]descCDCClassData{ + + { // -- CDC-ACM (single) Class Configuration Index 1 -- + + // CDC-ACM Control Buffers + + ed: &descCDC0ED, + + sx: &descCDC0Sx, + cx: &descCDC0Cx, + dx: &descCDC0Dx, + + // CDC-ACM Data Buffers + + rx: &descCDC0Rx, + tx: &descCDC0Tx, + + rxq: &descCDC0Rq, + txq: &descCDC0Tq, + + rq: &Queue{}, + tq: &Queue{}, + + lc: &descCDC0LC, + ls: &descCDC0LS, + + sxSize: descCDCStatusPacketSize, + rxSize: descCDCDataRxPacketSize, + txSize: descCDCDataTxPacketSize, + }, +} diff --git a/src/machine/usb/desc-hid.go b/src/machine/usb/desc-hid.go new file mode 100644 index 000000000..7dbe9511a --- /dev/null +++ b/src/machine/usb/desc-hid.go @@ -0,0 +1,511 @@ +//go:build baremetal && usb.hid +// +build baremetal,usb.hid + +package usb + +// USB HID constants defined per specification +const ( + // HID class + descHIDType = 0x03 + + // HID subclass + descHIDSubNone = 0x00 + descHIDSubBoot = 0x01 + + // HID protocol + descHIDProtoNone = 0x00 + descHIDProtoKeyboard = 0x01 + descHIDProtoMouse = 0x02 + + descHIDRequestGetReport = 0x01 // HID request GET_REPORT + descHIDRequestGetReportTypeInput = 0x01 // HID request GET_REPORT type INPUT + descHIDRequestGetReportTypeOutput = 0x02 // HID request GET_REPORT type OUTPUT + descHIDRequestGetReportTypeFeature = 0x03 // HID request GET_REPORT type FEATURE + descHIDRequestGetIdle = 0x02 // HID request GET_IDLE + descHIDRequestGetProtocol = 0x03 // HID request GET_PROTOCOL + descHIDRequestSetReport = 0x09 // HID request SET_REPORT + descHIDRequestSetIdle = 0x0A // HID request SET_IDLE + descHIDRequestSetProtocol = 0x0B // HID request SET_PROTOCOL +) + +const ( + // Size of all HID configuration descriptors. + descHIDConfigSize = uint16( + descLengthConfigure + // Configuration Header + descLengthInterface + // Keyboard Interface Descriptor + descLengthInterface + // Keyboard HID Interface Descriptor + descLengthEndpoint + // Keyboard Endpoint Descriptor + descLengthInterface + // Mouse Interface Descriptor + descLengthInterface + // Mouse HID Interface Descriptor + descLengthEndpoint + // Mouse Endpoint Descriptor + descLengthInterface + // Serial Interface Descriptor + descLengthInterface + // Serial HID Interface Descriptor + descLengthEndpoint + // Serial Tx Endpoint Descriptor + descLengthEndpoint + // Serial Rx Endpoint Descriptor + descLengthInterface + // Joystick Interface Descriptor + descLengthInterface + // Joystick HID Interface Descriptor + descLengthEndpoint + // Joystick Endpoint Descriptor + descLengthInterface + // Keyboard Media Keys Interface Descriptor + descLengthInterface + // Keyboard Media Keys HID Interface Descriptor + descLengthEndpoint) // Keyboard Media Keys Endpoint Descriptor + + // Position of each HID interface descriptor as offsets into the configuration + // descriptor. See comments in the configuration descriptor definition for the + // incremental tally that computes these. + descHIDConfigKeyboardPos = 18 + descHIDConfigMousePos = 43 + descHIDConfigSerialPos = 68 + descHIDConfigJoystickPos = 100 + descHIDConfigMediaKeyPos = 125 +) + +// Common configuration constants for the USB HID device class. +const ( + descHIDLanguageCount = 1 // String descriptor languages available + + descHIDInterfaceCount = 5 // Interfaces for all HID configurations. + descHIDEndpointCount = 6 // Endpoints for all HID configurations. + + descHIDEndpointCtrl = 0 // HID Control Endpoint 0 + + descHIDInterfaceKeyboard = 0 // HID Keyboard Interface + descHIDEndpointKeyboard = 3 // HID Keyboard IN Endpoint + descHIDConfigAttrKeyboard = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused + + descHIDInterfaceMouse = 1 // HID Mouse Interface + descHIDEndpointMouse = 5 // HID Mouse IN Endpoint + descHIDConfigAttrMouse = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused + + descHIDInterfaceSerial = 2 // HID Serial (UART emulation) Interface + descHIDEndpointSerialRx = 2 // HID Serial OUT (Rx) Endpoint + descHIDEndpointSerialTx = 2 // HID Serial IN (Tx) Endpoint + descHIDConfigAttrSerial = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxInterrupt + + descHIDInterfaceJoystick = 3 // HID Joystick Interface + descHIDEndpointJoystick = 6 // HID Joystick IN Endpoint + descHIDConfigAttrJoystick = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused + + descHIDInterfaceMediaKey = 4 // HID Keyboard Media Keys Interface + descHIDEndpointMediaKey = 4 // HID Keyboard Media Keys IN Endpoint + descHIDConfigAttrMediaKey = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused +) + +// descHIDClass holds references to all descriptors, buffers, and control +// structures for the USB HID device class. +type descHIDClass struct { + *descHIDClassData // Target-defined, class-specific data + + locale *[descHIDLanguageCount]descStringLanguage // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descHIDConfigSize]uint8 // configuration descriptor +} + +// descHID holds statically-allocated instances for each of the HID device class +// configurations, ordered by index (offset by -1). +var descHID = [dcdCount]descHIDClass{ + + { // HID class configuration index 1 + descHIDClassData: &descHIDData[0], + + locale: &[descHIDLanguageCount]descStringLanguage{ + + { // [0x0409] US English + language: descLanguageEnglish, + descriptor: descStringIndex{ + { /* [0] Language */ + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + // Actual string descriptors (index > 0) are copied into here at runtime! + // This allows for application- or even user-defined string descriptors. + { /* [1] Manufacturer */ }, + { /* [2] Product */ }, + { /* [3] Serial Number */ }, + }, + }, + }, + device: &[descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descCommonReleaseID), // Device release number in BCD (low) + msU8(descCommonReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descHIDCount, // Number of possible configurations + }, + qualif: &[descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descHIDCount, // Number of possible configurations + 0, // Reserved + }, + config: &[descHIDConfigSize]uint8{ + // [0+9] + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descHIDConfigSize), // Total length of data returned for this configuration (low) + msU8(descHIDConfigSize), // Total length of data returned for this configuration (high) + descHIDInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descEndptConfigAttr, // Configuration attributes + descHIDMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units) + + // [9+9] Keyboard Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceKeyboard, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubBoot, // Subclass code (Boot = 0x01) + descHIDProtoKeyboard, // Protocol code (Keyboard = 0x01) + 0, // Interface Description String Index + + // [18+9] Keyboard HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (low) + msU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (high) + + // [27+7] Keyboard Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointKeyboard | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDKeyboardTxPacketSize), // Max packet size (low) + msU8(descHIDKeyboardTxPacketSize), // Max packet size (high) + descHIDKeyboardTxInterval, // Polling Interval + + // [34+9] Mouse Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceMouse, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubBoot, // Subclass code (Boot = 0x01) + descHIDProtoMouse, // Protocol code (Mouse = 0x02) + 0, // Interface Description String Index + + // [43+9] Mouse HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportMouse))), // Descriptor length (low) + msU8(uint16(len(descHIDReportMouse))), // Descriptor length (high) + + // [52+7] Mouse Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointMouse | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDMouseTxPacketSize), // Max packet size (low) + msU8(descHIDMouseTxPacketSize), // Max packet size (high) + descHIDMouseTxInterval, // Polling Interval + + // [59+9] Serial Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceSerial, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubNone, // Subclass code + descHIDProtoNone, // Protocol code + 0, // Interface Description String Index + + // [68+9] Serial HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportSerial))), // Descriptor length (low) + msU8(uint16(len(descHIDReportSerial))), // Descriptor length (high) + + // [77+7] Serial Tx Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointSerialTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDSerialTxPacketSize), // Max packet size (low) + msU8(descHIDSerialTxPacketSize), // Max packet size (high) + descHIDSerialTxInterval, // Polling Interval + + // [84+7] Serial Rx Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointSerialRx | // Endpoint address + descEndptAddrDirectionOut, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDSerialRxPacketSize), // Max packet size (low) + msU8(descHIDSerialRxPacketSize), // Max packet size (high) + descHIDSerialRxInterval, // Polling Interval + + // [91+9] Joystick Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceJoystick, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubNone, // Subclass code + descHIDProtoNone, // Protocol code + 0, // Interface Description String Index + + // [100+9] Joystick HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportJoystick))), // Descriptor length (low) + msU8(uint16(len(descHIDReportJoystick))), // Descriptor length (high) + + // [109+7] Joystick Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointJoystick | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDJoystickTxPacketSize), // Max packet size (low) + msU8(descHIDJoystickTxPacketSize), // Max packet size (high) + descHIDJoystickTxInterval, // Polling Interval + + // [116+9] Keyboard Media Keys Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceMediaKey, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubNone, // Subclass code + descHIDProtoNone, // Protocol code + 0, // Interface Description String Index + + // [125+9] Keyboard Media Keys HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (low) + msU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (high) + + // [134+7] Keyboard Media Keys Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointMediaKey | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDMediaKeyTxPacketSize), // Max packet size (low) + msU8(descHIDMediaKeyTxPacketSize), // Max packet size (high) + descHIDMediaKeyTxInterval, // Polling Interval + }, + }, +} + +var descHIDReportSerial = [...]uint8{ + 0x06, 0xC9, 0xFF, // Usage Page 0xFFC9 (vendor defined) + 0x09, 0x04, // Usage 0x04 + 0xA1, 0x5C, // Collection 0x5C + 0x75, 0x08, // report size = 8 bits (global) + 0x15, 0x00, // logical minimum = 0 (global) + 0x26, 0xFF, 0x00, // logical maximum = 255 (global) + 0x95, descHIDSerialTxPacketSize, // report count (global) + 0x09, 0x75, // usage (local) + 0x81, 0x02, // Input + 0x95, descHIDSerialRxPacketSize, // report count (global) + 0x09, 0x76, // usage (local) + 0x91, 0x02, // Output + 0x95, 0x04, // report count (global) + 0x09, 0x76, // usage (local) + 0xB1, 0x02, // Feature + 0xC0, // end collection +} + +var descHIDReportKeyboard = [...]uint8{ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x05, 0x07, // Usage Page (Key Codes) + 0x19, 0xE0, // Usage Minimum (224) + 0x29, 0xE7, // Usage Maximum (231) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) [Modifier keys] + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) + 0x81, 0x03, // Input (Constant) [Reserved byte] + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x05, // Usage Maximum (5) + 0x91, 0x02, // Output (Data, Variable, Absolute) [LED report] + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x91, 0x03, // Output (Constant) [LED report padding] + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x7F, // Logical Maximum(104) + 0x05, 0x07, // Usage Page (Key Codes) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0x7F, // Usage Maximum (104) + 0x81, 0x00, // Input (Data, Array) [Normal keys] + 0xC0, // End Collection +} + +var descHIDReportMediaKey = [...]uint8{ + 0x05, 0x0C, // Usage Page (Consumer) + 0x09, 0x01, // Usage (Consumer Controls) + 0xA1, 0x01, // Collection (Application) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x04, // Report Count (4) + 0x19, 0x00, // Usage Minimum (0) + 0x2A, 0x9C, 0x02, // Usage Maximum (0x29C) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0x9C, 0x02, // Logical Maximum (0x29C) + 0x81, 0x00, // Input (Data, Array) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x75, 0x08, // Report Size (8) + 0x95, 0x03, // Report Count (3) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0xB7, // Usage Maximum (0xB7) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xB7, 0x00, // Logical Maximum (0xB7) + 0x81, 0x00, // Input (Data, Array) + 0xC0, // End Collection +} + +var descHIDReportMouse = [...]uint8{ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x01, // REPORT_ID (1) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x08, // Usage Maximum (Button #8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x08, // Report Count (8) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8), + 0x95, 0x03, // Report Count (3), + 0x81, 0x06, // Input (Data, Variable, Relative) + 0x05, 0x0C, // Usage Page (Consumer) + 0x0A, 0x38, 0x02, // Usage (AC Pan) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8), + 0x95, 0x01, // Report Count (1), + 0x81, 0x06, // Input (Data, Variable, Relative) + 0xC0, // End Collection + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x02, // REPORT_ID (2) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x75, 0x10, // Report Size (16), + 0x95, 0x02, // Report Count (2), + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0xC0, // End Collection +} + +var descHIDReportJoystick = [...]uint8{ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x20, // Report Count (32) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x20, // Usage Maximum (Button #32) + 0x81, 0x02, // Input (variable,absolute) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x65, 0x14, // Unit (20) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x39, // Usage (Hat switch) + 0x81, 0x42, // Input (variable,absolute,null_state) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection () + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x03, // Logical Maximum (1023) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x04, // Report Count (4) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x35, // Usage (Rz) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x03, // Logical Maximum (1023) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x02, // Report Count (2) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection +} diff --git a/src/machine/usb/desc-hid_atsamd51.go b/src/machine/usb/desc-hid_atsamd51.go new file mode 100644 index 000000000..65f6e956a --- /dev/null +++ b/src/machine/usb/desc-hid_atsamd51.go @@ -0,0 +1,271 @@ +//go:build baremetal && usb.hid && (atsamd51 || atsame5x) +// +build baremetal +// +build usb.hid +// +build atsamd51 atsame5x + +package usb + +// Constants for USB HID (keyboard, mouse, joystick) device classes. +const ( + + // USB Bus Configuration Attributes + + descHIDMaxPowerMa = 100 // Maximum current (mA) requested from host + + // HID Endpoint Descriptor Buffers + + descHIDEDCount = descMaxEndpoints + + // Setup packet is only 8 bytes in length. However, under certain scenarios, + // USB DMA controller may decide to overwrite/overflow the buffer with 2 extra + // bytes of CRC. From datasheet's "Management of SETUP Transactions" section: + // | If the number of received data bytes is the maximum data payload + // | specified by PCKSIZE.SIZE minus one, only the first CRC data is written + // | to the data buffer. If the number of received data is equal or less + // | than the data payload specified by PCKSIZE.SIZE minus two, both CRC + // | data bytes are written to the data buffer. + // Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT). + descHIDSxSize = 8 + 2 + descHIDCxSize = descControlPacketSize + + // HID Serial Buffers + + descHIDSerialRxSize = descHIDSerialRxPacketSize + descHIDSerialTxSize = descHIDSerialTxPacketSize + + descHIDSerialTxTimeoutMs = 50 // millisec + descHIDSerialTxSyncUs = 75 // microsec + + // HID Keyboard Buffers + + descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize + + descHIDKeyboardTxTimeoutMs = 50 // millisec + + // HID Mouse Buffers + + descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize + + descHIDMouseTxTimeoutMs = 30 // millisec + + // HID Joystick Buffers + + descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize + + descHIDJoystickTxTimeoutMs = 30 // millisec + + // Default HID Endpoint Configurations (Full-Speed) + + descHIDSerialRxInterval = descHIDSerialRxFSInterval // Serial Rx + descHIDSerialRxPacketSize = descHIDSerialRxFSPacketSize // + + descHIDSerialTxInterval = descHIDSerialTxFSInterval // Serial Tx + descHIDSerialTxPacketSize = descHIDSerialTxFSPacketSize // + + descHIDKeyboardTxInterval = descHIDKeyboardTxFSInterval // Keyboard + descHIDKeyboardTxPacketSize = descHIDKeyboardTxFSPacketSize // + + descHIDMediaKeyTxInterval = descHIDMediaKeyTxFSInterval // Keyboard Media Keys + descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxFSPacketSize // + + descHIDMouseTxInterval = descHIDMouseTxFSInterval // Mouse + descHIDMouseTxPacketSize = descHIDMouseTxFSPacketSize // + + descHIDJoystickTxInterval = descHIDJoystickTxFSInterval // Joystick + descHIDJoystickTxPacketSize = descHIDJoystickTxFSPacketSize // + + // HID Endpoint Configurations for Full-Speed Device + + descHIDSerialRxFSInterval = 2 // Serial Rx + descHIDSerialRxFSPacketSize = 8 // (full-speed) + + descHIDSerialTxFSInterval = 1 // Serial Tx + descHIDSerialTxFSPacketSize = 16 // (full-speed) + + descHIDKeyboardTxFSInterval = 4 // Keyboard + descHIDKeyboardTxFSPacketSize = 8 // (full-speed) + + descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys + descHIDMediaKeyTxFSPacketSize = 8 // (full-speed) + + descHIDMouseTxFSInterval = 4 // Mouse + descHIDMouseTxFSPacketSize = 8 // (full-speed) + + descHIDJoystickTxFSInterval = 4 // Joystick + descHIDJoystickTxFSPacketSize = 12 // (full-speed) + + // HID Endpoint Configurations for High-Speed Device + + // - N/A, SAMx51 only has a full-speed PHY +) + +// descHID0ED is an array of endpoint descriptors, which describes to the USB +// DMA controller the buffer and transfer properties for each endpoint, for the +// default HID device class configuration (index 1). +//go:align 32 +var descHID0ED [descHIDEDCount]dhwEPAddrDesc + +// descHID0Sx is the receive (Rx) buffer for setup packets on control endpoint 0 +// of the default HID device class configuration (index 1). +//go:align 32 +var descHID0Sx [descHIDSxSize]uint8 + +// descHID0Cx is the transmit (Tx) buffer for control/status packets on control +// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descHID0Cx [descHIDCxSize]uint8 + +// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for +// the default HID device class configuration (index 1). +//go:align 32 +var descHID0Dx [descHIDConfigSize]uint8 + +// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +// var descHID0SerialRx [descHIDSerialRxSize]uint8 + +// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +// var descHID0SerialTx [descHIDSerialTxSize]uint8 + +// descHID0KeyboardTx is the keyboard HID report transmit (Tx) transfer buffer +// for the default HID device class configuration (index 1). +//go:align 32 +var descHID0KeyboardTx [descHIDKeyboardTxPacketSize]uint8 + +// descHID0KeyboardTq is the keyboard transmit (Tx) transfer buffer for the +// default HID device class configuration (index 1). +//go:align 32 +// var descHID0KeyboardTq [descHIDKeyboardTxSize]uint8 + +// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +// var descHID0MouseTx [descHIDMouseTxSize]uint8 + +// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the +// default HID device class configuration (index 1). +//go:align 32 +// var descHID0JoystickTx [descHIDJoystickTxSize]uint8 + +var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8 +var descHID0KeyboardTxCon [hidKeyboardConCount]uint16 +var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8 + +// descHID0Keyboard is the Keyboard instance with which the user may interact +// when using the default HID device class configuration (index 1). +var descHID0Keyboard = Keyboard{ + key: &descHID0KeyboardTxKey, + con: &descHID0KeyboardTxCon, + sys: &descHID0KeyboardTxSys, +} + +// descHIDClassData holds the buffers and control states for all of the HID +// device class configurations, ordered by index (offset by -1), for SAMx51 +// targets only. +// +// Instances of this type (elements of descHIDData) are embedded in elements +// of the common/target-agnostic HID class configurations (descHID). +// Methods defined on this type implement target-specific functionality, and +// some of these methods are required by the common device controller driver. +// Thus, this type functions as a hardware abstraction layer (HAL). +type descHIDClassData struct { + + // HID Control Buffers + + ed *[descHIDEDCount]dhwEPAddrDesc // endpoint descriptors + + sx *[descHIDSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets + cx *[descHIDCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets + dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer + + // HID Serial Buffers + + // rxSerial *[descHIDSerialRxSize]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer + // txSerial *[descHIDSerialTxSize]uint8 // interrupt endpoint serial Tx (IN) transfer buffer + + // rxSerialSize uint16 + // txSerialSize uint16 + + // HID Keyboard Buffers + + txKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report buffer + // txqKeyboard *[descHIDKeyboardTxSize]uint8 // interrupt endpoint keyboard Tx (IN) transfer FIFO + // tqKeyboard *Queue + + txKeyboardSize uint16 + + // HID Mouse Buffers + + // txMouse *[descHIDMouseTxSize]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer + + // txMouseSize uint16 + + // HID Joystick Buffers + + // txJoystick *[descHIDJoystickTxSize]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer + + // txJoystickSize uint16 + + // HID Device Instances + + //serial *Serial + keyboard *Keyboard + //mouse *Mouse + //joystick *Joystick +} + +// descHIDData holds statically-allocated instances for each of the target- +// specific (SAMx51) HID device class configurations' control and data +// structures, ordered by configuration index (offset by -1). Each element is +// embedded in a corresponding element of descHID. +var descHIDData = [dcdCount]descHIDClassData{ + + { // -- HID Class Configuration Index 1 -- + + // HID Control Buffers + + ed: &descHID0ED, + + sx: &descHID0Sx, + cx: &descHID0Cx, + dx: &descHID0Dx, + + // HID Serial Buffers + + // rxSerial: &descHID0SerialRx, + // txSerial: &descHID0SerialTx, + + // rxSerialSize: descHIDSerialRxPacketSize, + // txSerialSize: descHIDSerialTxPacketSize, + + // HID Keyboard Buffers + + txKeyboard: &descHID0KeyboardTx, + // txqKeyboard: &descHID0KeyboardTq, + // tqKeyboard: &Queue{}, + + txKeyboardSize: descHIDKeyboardTxPacketSize, + + // HID Mouse Buffers + + // txMouse: &descHID0MouseTx, + + // txMouseSize: descHIDMouseTxPacketSize, + + // HID Joystick Buffers + + // txJoystick: &descHID0JoystickTx, + + // txJoystickSize: descHIDJoystickTxPacketSize, + + // HID Device Instances + + //serial: &descHID0Serial, + keyboard: &descHID0Keyboard, + //mouse: &descHID0Mouse, + //joystick: &descHID0Joystick, + }, +} diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go index 355a2d1b1..a75e8d143 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -1,7 +1,5 @@ package usb -import "runtime/volatile" - const descUSBSpecVersion = uint16(0x0200) // USB 2.0 const descLanguageEnglish = uint16(0x0409) // (US) English @@ -137,6 +135,9 @@ const ( descDeviceCapExtAttrBESLPos = 2 ) +// descEndpointInvalid represents an invalid endpoint address. +const descEndpointInvalid = ^uint8(descEndptAddrNumberMsk | descEndptAddrDirectionMsk) + const ( descDirOut = descRequestTypeDirOut >> descRequestTypeDirPos descDirIn = descRequestTypeDirIn >> descRequestTypeDirPos @@ -184,222 +185,6 @@ const ( descEndptConfigAttrTxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrTxPos ) -// USB CDC constants defined per specification. -const ( - - // Device class - descCDCTypeComm = 0x02 // communication/control - descCDCTypeData = 0x0A // data - - // Communication/control subclass - descCDCSubNone = 0x00 - descCDCSubDirectLineControl = 0x01 - descCDCSubAbstractControl = 0x02 - descCDCSubTelephoneControl = 0x03 - descCDCSubMultiChannelControl = 0x04 - descCDCSubCAPIControl = 0x05 - descCDCSubEthernetNetworkingControl = 0x06 - descCDCSubATMNetworkingControl = 0x07 - descCDCSubWirelessHandsetControl = 0x08 - descCDCSubDeviceManagement = 0x09 - descCDCSubMobileDirectLine = 0x0A - descCDCSubOBEX = 0x0B - descCDCSubEthernetEmulation = 0x0C - - // Communication/control protocol - descCDCProtoNone = 0x00 // also for data class - descCDCProtoAT250 = 0x01 - descCDCProtoATPCCA101 = 0x02 - descCDCProtoATPCCA101AnnexO = 0x03 - descCDCProtoATGSM707 = 0x04 - descCDCProtoAT3GPP27007 = 0x05 - descCDCProtoATTIACDMA = 0x06 - descCDCProtoEthernetEmulation = 0x07 - descCDCProtoExternal = 0xFE - descCDCProtoVendorSpecific = 0xFF // also for data class - - // Data protocol - descCDCProtoPyhsicalInterface = 0x30 - descCDCProtoHDLC = 0x31 - descCDCProtoTransparent = 0x32 - descCDCProtoManagement = 0x50 - descCDCProtoDataLinkQ931 = 0x51 - descCDCProtoDataLinkQ921 = 0x52 - descCDCProtoDataCompressionV42BIS = 0x90 - descCDCProtoEuroISDN = 0x91 - descCDCProtoRateAdaptionISDNV24 = 0x92 - descCDCProtoCAPICommands = 0x93 - descCDCProtoHostBasedDriver = 0xFD - descCDCProtoUnitFunctional = 0xFE - - // Functional descriptor length - descCDCFuncLengthHeader = 5 - descCDCFuncLengthCallManagement = 5 - descCDCFuncLengthAbstractControl = 4 - descCDCFuncLengthUnion = 5 - - // Functional descriptor type - descCDCFuncTypeHeader = 0x00 - descCDCFuncTypeCallManagement = 0x01 - descCDCFuncTypeAbstractControl = 0x02 - descCDCFuncTypeDirectLine = 0x03 - descCDCFuncTypeTelephoneRinger = 0x04 - descCDCFuncTypeTelephoneReport = 0x05 - descCDCFuncTypeUnion = 0x06 - descCDCFuncTypeCountrySelect = 0x07 - descCDCFuncTypeTelephoneModes = 0x08 - descCDCFuncTypeTerminal = 0x09 - descCDCFuncTypeNetworkChannel = 0x0A - descCDCFuncTypeProtocolUnit = 0x0B - descCDCFuncTypeExtensionUnit = 0x0C - descCDCFuncTypeMultiChannel = 0x0D - descCDCFuncTypeCAPIControl = 0x0E - descCDCFuncTypeEthernetNetworking = 0x0F - descCDCFuncTypeATMNetworking = 0x10 - descCDCFuncTypeWirelessControl = 0x11 - descCDCFuncTypeMobileDirectLine = 0x12 - descCDCFuncTypeMDLMDetail = 0x13 - descCDCFuncTypeDeviceManagement = 0x14 - descCDCFuncTypeOBEX = 0x15 - descCDCFuncTypeCommandSet = 0x16 - descCDCFuncTypeCommandSetDetail = 0x17 - descCDCFuncTypeTelephoneControl = 0x18 - descCDCFuncTypeOBEXServiceID = 0x19 - - // Standard request - descCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND - descCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE - descCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE - descCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE - descCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE - descCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE - descCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE - descCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP - descCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE - descCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME - descCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK - descCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING - descCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING - descCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE - descCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK - descCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS - descCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS - descCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM - descCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM - descCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS - descCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS - descCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS - descCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER - descCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER - descCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER - descCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS - descCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER - descCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER - descCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER - descCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC - descCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT - descCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS - descCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC - descCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS - descCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK - - // Notification type - descCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION - descCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL - descCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE - descCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT - descCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE - descCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE - descCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE - descCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE - - // Feature select - descCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE - descCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING - - // Control signal - descCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION - descCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE - - // UART emulated state - descCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER - descCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER - descCDCUARTStateBreak = 0x04 // UART state BREAK - descCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL - descCDCUARTStateFraming = 0x10 // UART state FRAMING - descCDCUARTStateParity = 0x20 // UART state PARITY - descCDCUARTStateOverrun = 0x40 // UART state OVERRUN -) - -// USB HID constants defined per specification -const ( - // HID class - descHIDType = 0x03 - - // HID subclass - descHIDSubNone = 0x00 - descHIDSubBoot = 0x01 - - // HID protocol - descHIDProtoNone = 0x00 - descHIDProtoKeyboard = 0x01 - descHIDProtoMouse = 0x02 - - descHIDRequestGetReport = 0x01 // HID request GET_REPORT - descHIDRequestGetReportTypeInput = 0x01 // HID request GET_REPORT type INPUT - descHIDRequestGetReportTypeOutput = 0x02 // HID request GET_REPORT type OUTPUT - descHIDRequestGetReportTypeFeature = 0x03 // HID request GET_REPORT type FEATURE - descHIDRequestGetIdle = 0x02 // HID request GET_IDLE - descHIDRequestGetProtocol = 0x03 // HID request GET_PROTOCOL - descHIDRequestSetReport = 0x09 // HID request SET_REPORT - descHIDRequestSetIdle = 0x0A // HID request SET_IDLE - descHIDRequestSetProtocol = 0x0B // HID request SET_PROTOCOL -) - -const ( - // Size of all CDC-ACM configuration descriptors. - descCDCACMConfigSize = uint16( - descLengthConfigure + // Configuration Header - descLengthInterface + // CDC Interface Descriptor - descCDCFuncLengthHeader + // CDC Header - descCDCFuncLengthCallManagement + // CDC Call Management Func Descriptor - descCDCFuncLengthAbstractControl + // CDC Abstract Control Func Descriptor - descCDCFuncLengthUnion + // CDC Union Functional Descriptor - descLengthEndpoint + // CDC Status IN Endpoint Descriptor - descLengthInterface + // CDC Data Interface Descriptor - descLengthEndpoint + // CDC Data IN Endpoint Descriptor - descLengthEndpoint) // CDC Data OUT Endpoint Descriptor - - // Size of all HID configuration descriptors. - descHIDConfigSize = uint16( - descLengthConfigure + // Configuration Header - descLengthInterface + // Keyboard Interface Descriptor - descLengthInterface + // Keyboard HID Interface Descriptor - descLengthEndpoint + // Keyboard Endpoint Descriptor - descLengthInterface + // Mouse Interface Descriptor - descLengthInterface + // Mouse HID Interface Descriptor - descLengthEndpoint + // Mouse Endpoint Descriptor - descLengthInterface + // Serial Interface Descriptor - descLengthInterface + // Serial HID Interface Descriptor - descLengthEndpoint + // Serial Tx Endpoint Descriptor - descLengthEndpoint + // Serial Rx Endpoint Descriptor - descLengthInterface + // Joystick Interface Descriptor - descLengthInterface + // Joystick HID Interface Descriptor - descLengthEndpoint + // Joystick Endpoint Descriptor - descLengthInterface + // Keyboard Media Keys Interface Descriptor - descLengthInterface + // Keyboard Media Keys HID Interface Descriptor - descLengthEndpoint) // Keyboard Media Keys Endpoint Descriptor - - // Position of each HID interface descriptor as offsets into the configuration - // descriptor. See comments in the configuration descriptor definition for the - // incremental tally that computes these. - descHIDConfigKeyboardPos = 18 - descHIDConfigMousePos = 43 - descHIDConfigSerialPos = 68 - descHIDConfigJoystickPos = 100 - descHIDConfigMediaKeyPos = 125 -) - type ( // descString is the actual byte array used to hold string descriptors. The // first two bytes are a USB-specified header (0=length, 1=type), and the @@ -428,718 +213,3 @@ const ( // anyone other than TinyGo devs; 64*4 = 256 B (i.e., 31 UTF-16 code points // for each string) seems a good compromise. ) - -// descEndpointInvalid represents an invalid endpoint address. -const descEndpointInvalid = ^uint8(descEndptAddrNumberMsk | descEndptAddrDirectionMsk) - -// descCDCACMLineCodingSize defines the length of a CDC-ACM UART line coding -// buffer. Note that the actual buffer may be padded for alignment; but for -// Rx/Tx transfer purposes, descCDCACMLineCodingSize defines the number of bytes -// that are transferred following a control SETUP request. -const descCDCACMLineCodingSize = 7 - -// descCDCACMLineCoding represents an emulated UART's line configuration. -// -// Use descCDCACMLineCodingSize instead of unsafe.Sizeof(descCDCACMLineCoding) -// in any transfer requests, because the actual struct is padded for alignment. -type descCDCACMLineCoding struct { - baud uint32 - stopBits uint8 - parity uint8 - numBits uint8 - _ uint8 -} - -// parse initializes the receiver descCDCACMLineCoding from the given []uint8 v. -// Argument v is a Rx transfer buffer, filled following the completion of a -// control transfer from a CDC SET_LINE_CODING (0x20) request -func (s *descCDCACMLineCoding) parse(v []uint8) bool { - if len(v) >= descCDCACMLineCodingSize { - s.baud = packU32(v[:]) - s.stopBits = v[4] - s.parity = v[5] - s.numBits = v[6] - return true - } - return false -} - -// descCDCACMLineState represents an emulated UART's line state. -type descCDCACMLineState struct { - // dataTerminalReady indicates if DTE is present or not. - // Corresponds to V.24 signal 108/2 and RS-232 signal DTR. - dataTerminalReady bool // DTR - // requestToSend is the carrier control for half-duplex modems. - // Corresponds to V.24 signal 105 and RS-232 signal RTS. - requestToSend bool // RTS -} - -// parse initializes the receiver descCDCACMLineState from the given uint16 v. -// Argument v corresponds to the wValue field in a control SETUP packet, which -// carries the line state from a CDC SET_CONTROL_LINE_STATE (0x22) request. -func (s *descCDCACMLineState) parse(v uint16) bool { - s.dataTerminalReady = 0 != v&0x1 - s.requestToSend = 0 != v&0x2 - return true -} - -type descCDCACMState uint8 - -const ( - descCDCACMStateConfigured descCDCACMState = iota // Received SET_CONFIGURATION class request - descCDCACMStateLineState // Received SET_LINE_STATE after Configured state - descCDCACMStateLineCoding // Received SET_LINE_CODING after LineState state -) - -func (s *descCDCACMState) set(state descCDCACMState) { - if state > *s { - // state must be incremented in-order. Otherwise, reset to initial state. - if state == *s+1 { - *s = state - } else { - var init descCDCACMState // Reset to zero-value of type. - *s = init - } - } -} - -// Common configuration constants for the USB CDC-ACM (single) device class. -const ( - descCDCACMLanguageCount = 1 // String descriptor languages available - - descCDCACMInterfaceCount = 2 // Interfaces for all CDC-ACM configurations. - descCDCACMEndpointCount = 4 // Endpoints for all CDC-ACM configurations. - - descCDCACMEndpointCtrl = 0 // CDC-ACM Control Endpoint 0 - - descCDCACMInterfaceCtrl = 0 // CDC-ACM Control Interface - descCDCACMEndpointStatus = 1 // CDC-ACM Interrupt IN Endpoint - descCDCACMConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt - - descCDCACMInterfaceData = 1 // CDC-ACM Data Interface - descCDCACMEndpointDataRx = 2 // CDC-ACM Bulk Data OUT (Rx) Endpoint - descCDCACMConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused - descCDCACMEndpointDataTx = 3 // CDC-ACM Bulk Data IN (Tx) Endpoint - descCDCACMConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk -) - -// descCDCACMClass holds references to all descriptors, buffers, and control -// structures for the USB CDC-ACM (single) device class. -type descCDCACMClass struct { - *descCDCACMClassData // Target-defined, class-specific data - - locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors - device *[descLengthDevice]uint8 // device descriptor - qualif *[descLengthQualification]uint8 // device qualification descriptor - config *[descCDCACMConfigSize]uint8 // configuration descriptor - - state volatile.Register8 -} - -func (c *descCDCACMClass) setState(state descCDCACMState) { - s := descCDCACMState(c.state.Get()) - s.set(state) - c.state.Set(uint8(s)) -} - -// descCDCACM holds statically-allocated instances for each of the CDC-ACM -// (single) device class configurations, ordered by index (offset by -1). -var descCDCACM = [dcdCount]descCDCACMClass{ - - { // CDC-ACM (single) class configuration index 1 - descCDCACMClassData: &descCDCACMData[0], - - locale: &[descCDCACMLanguageCount]descStringLanguage{ - - { // [0x0409] US English - language: descLanguageEnglish, - descriptor: descStringIndex{ - { /* [0] Language */ - 4, - descTypeString, - lsU8(descLanguageEnglish), - msU8(descLanguageEnglish), - }, - // Actual string descriptors (index > 0) are copied into here at runtime! - // This allows for application- or even user-defined string descriptors. - { /* [1] Manufacturer */ }, - { /* [2] Product */ }, - { /* [3] Serial Number */ }, - }, - }, - }, - device: &[descLengthDevice]uint8{ - descLengthDevice, // Size of this descriptor in bytes - descTypeDevice, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - 0, // Class code (assigned by the USB-IF). - 0, // Subclass code (assigned by the USB-IF). - 0, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) - msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) - lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) - msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) - lsU8(descCommonReleaseID), // Device release number in BCD (low) - msU8(descCommonReleaseID), // Device release number in BCD (high) - 1, // Index of string descriptor describing manufacturer - 2, // Index of string descriptor describing product - 3, // Index of string descriptor describing the device's serial number - descCDCACMCount, // Number of possible configurations - }, - qualif: &[descLengthQualification]uint8{ - descLengthQualification, // Size of this descriptor in bytes - descTypeQualification, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - 0, // Class code (assigned by the USB-IF). - 0, // Subclass code (assigned by the USB-IF). - 0, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - descCDCACMCount, // Number of possible configurations - 0, // Reserved - }, - config: &[descCDCACMConfigSize]uint8{ - descLengthConfigure, // Size of this descriptor in bytes - descTypeConfigure, // Descriptor Type - lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) - msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) - descCDCACMInterfaceCount, // Number of interfaces supported by this configuration - 1, // Value to use to select this configuration (1 = CDC-ACM[0]) - 0, // Index of string descriptor describing this configuration - descEndptConfigAttr, // Configuration attributes - descCDCACMMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units) - - // Communication/Control Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descCDCACMInterfaceCtrl, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descCDCTypeComm, // Class code - descCDCSubAbstractControl, // Subclass code - descCDCProtoAT250, // Protocol code (NOTE: Teensyduino & Arduino-Mbed define this as 1 [AT V.250]) - 0, // Interface Description String Index - - // CDC Header Functional Descriptor - descCDCFuncLengthHeader, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeHeader, // Descriptor Subtype - 0x10, // USB CDC specification version 1.10 (low) - 0x01, // USB CDC specification version 1.10 (high) - - // CDC Call Management Functional Descriptor - descCDCFuncLengthCallManagement, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeCallManagement, // Descriptor Subtype - 0x01, // Capabilities - descCDCACMInterfaceData, // Data Interface - - // CDC Abstract Control Management Functional Descriptor - descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeAbstractControl, // Descriptor Subtype - 0x06, // Capabilities - - // CDC Union Functional Descriptor - descCDCFuncLengthUnion, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeUnion, // Descriptor Subtype - descCDCACMInterfaceCtrl, // Controlling interface index - descCDCACMInterfaceData, // Controlled interface index - - // Communication/Control Notification Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACMEndpointStatus | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descCDCACMStatusPacketSize), // Max packet size (low) - msU8(descCDCACMStatusPacketSize), // Max packet size (high) - descCDCACMStatusInterval, // Polling Interval - - // Data Interface Descriptor - descLengthInterface, // Interface length - descTypeInterface, // Interface type - descCDCACMInterfaceData, // Interface index - 0, // Alternate setting - 2, // Number of endpoints - descCDCTypeData, // Class code - descCDCSubNone, // Subclass code - descCDCProtoNone, // Protocol code - 0, // Interface Description String Index - - // Data Bulk Rx Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACMEndpointDataRx | // Endpoint address - descEndptAddrDirectionOut, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataRxPacketSize), // Max packet size (low) - msU8(descCDCACMDataRxPacketSize), // Max packet size (high) - 0, // Polling Interval - - // Data Bulk Tx Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACMEndpointDataTx | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) - msU8(descCDCACMDataTxPacketSize), // Max packet size (high) - 0, // Polling Interval - }, - }, -} - -// Common configuration constants for the USB HID device class. -const ( - descHIDLanguageCount = 1 // String descriptor languages available - - descHIDInterfaceCount = 5 // Interfaces for all HID configurations. - descHIDEndpointCount = 6 // Endpoints for all HID configurations. - - descHIDEndpointCtrl = 0 // HID Control Endpoint 0 - - descHIDInterfaceKeyboard = 0 // HID Keyboard Interface - descHIDEndpointKeyboard = 3 // HID Keyboard IN Endpoint - descHIDConfigAttrKeyboard = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused - - descHIDInterfaceMouse = 1 // HID Mouse Interface - descHIDEndpointMouse = 5 // HID Mouse IN Endpoint - descHIDConfigAttrMouse = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused - - descHIDInterfaceSerial = 2 // HID Serial (UART emulation) Interface - descHIDEndpointSerialRx = 2 // HID Serial OUT (Rx) Endpoint - descHIDEndpointSerialTx = 2 // HID Serial IN (Tx) Endpoint - descHIDConfigAttrSerial = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxInterrupt - - descHIDInterfaceJoystick = 3 // HID Joystick Interface - descHIDEndpointJoystick = 6 // HID Joystick IN Endpoint - descHIDConfigAttrJoystick = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused - - descHIDInterfaceMediaKey = 4 // HID Keyboard Media Keys Interface - descHIDEndpointMediaKey = 4 // HID Keyboard Media Keys IN Endpoint - descHIDConfigAttrMediaKey = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused -) - -// descHIDClass holds references to all descriptors, buffers, and control -// structures for the USB HID device class. -type descHIDClass struct { - *descHIDClassData // Target-defined, class-specific data - - locale *[descHIDLanguageCount]descStringLanguage // string descriptors - device *[descLengthDevice]uint8 // device descriptor - qualif *[descLengthQualification]uint8 // device qualification descriptor - config *[descHIDConfigSize]uint8 // configuration descriptor -} - -// descHID holds statically-allocated instances for each of the HID device class -// configurations, ordered by index (offset by -1). -var descHID = [dcdCount]descHIDClass{ - - { // HID class configuration index 1 - descHIDClassData: &descHIDData[0], - - locale: &[descHIDLanguageCount]descStringLanguage{ - - { // [0x0409] US English - language: descLanguageEnglish, - descriptor: descStringIndex{ - { /* [0] Language */ - 4, - descTypeString, - lsU8(descLanguageEnglish), - msU8(descLanguageEnglish), - }, - // Actual string descriptors (index > 0) are copied into here at runtime! - // This allows for application- or even user-defined string descriptors. - { /* [1] Manufacturer */ }, - { /* [2] Product */ }, - { /* [3] Serial Number */ }, - }, - }, - }, - device: &[descLengthDevice]uint8{ - descLengthDevice, // Size of this descriptor in bytes - descTypeDevice, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - 0, // Class code (assigned by the USB-IF). - 0, // Subclass code (assigned by the USB-IF). - 0, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) - msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) - lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) - msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) - lsU8(descCommonReleaseID), // Device release number in BCD (low) - msU8(descCommonReleaseID), // Device release number in BCD (high) - 1, // Index of string descriptor describing manufacturer - 2, // Index of string descriptor describing product - 3, // Index of string descriptor describing the device's serial number - descHIDCount, // Number of possible configurations - }, - qualif: &[descLengthQualification]uint8{ - descLengthQualification, // Size of this descriptor in bytes - descTypeQualification, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - 0, // Class code (assigned by the USB-IF). - 0, // Subclass code (assigned by the USB-IF). - 0, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - descHIDCount, // Number of possible configurations - 0, // Reserved - }, - config: &[descHIDConfigSize]uint8{ - // [0+9] - descLengthConfigure, // Size of this descriptor in bytes - descTypeConfigure, // Descriptor Type - lsU8(descHIDConfigSize), // Total length of data returned for this configuration (low) - msU8(descHIDConfigSize), // Total length of data returned for this configuration (high) - descHIDInterfaceCount, // Number of interfaces supported by this configuration - 1, // Value to use to select this configuration (1 = CDC-ACM[0]) - 0, // Index of string descriptor describing this configuration - descEndptConfigAttr, // Configuration attributes - descHIDMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units) - - // [9+9] Keyboard Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descHIDInterfaceKeyboard, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descHIDType, // Class code (HID = 0x03) - descHIDSubBoot, // Subclass code (Boot = 0x01) - descHIDProtoKeyboard, // Protocol code (Keyboard = 0x01) - 0, // Interface Description String Index - - // [18+9] Keyboard HID Interface Descriptor - descLengthInterface, // Descriptor length - descTypeHID, // Descriptor type - 0x11, // HID BCD (low) - 0x01, // HID BCD (high) - 0, // Country code - 1, // Number of descriptors - descTypeHIDReport, // Descriptor type - lsU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (low) - msU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (high) - - // [27+7] Keyboard Endpoint Descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descHIDEndpointKeyboard | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descHIDKeyboardTxPacketSize), // Max packet size (low) - msU8(descHIDKeyboardTxPacketSize), // Max packet size (high) - descHIDKeyboardTxInterval, // Polling Interval - - // [34+9] Mouse Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descHIDInterfaceMouse, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descHIDType, // Class code (HID = 0x03) - descHIDSubBoot, // Subclass code (Boot = 0x01) - descHIDProtoMouse, // Protocol code (Mouse = 0x02) - 0, // Interface Description String Index - - // [43+9] Mouse HID Interface Descriptor - descLengthInterface, // Descriptor length - descTypeHID, // Descriptor type - 0x11, // HID BCD (low) - 0x01, // HID BCD (high) - 0, // Country code - 1, // Number of descriptors - descTypeHIDReport, // Descriptor type - lsU8(uint16(len(descHIDReportMouse))), // Descriptor length (low) - msU8(uint16(len(descHIDReportMouse))), // Descriptor length (high) - - // [52+7] Mouse Endpoint Descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descHIDEndpointMouse | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descHIDMouseTxPacketSize), // Max packet size (low) - msU8(descHIDMouseTxPacketSize), // Max packet size (high) - descHIDMouseTxInterval, // Polling Interval - - // [59+9] Serial Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descHIDInterfaceSerial, // Interface index - 0, // Alternate setting - 2, // Number of endpoints - descHIDType, // Class code (HID = 0x03) - descHIDSubNone, // Subclass code - descHIDProtoNone, // Protocol code - 0, // Interface Description String Index - - // [68+9] Serial HID Interface Descriptor - descLengthInterface, // Descriptor length - descTypeHID, // Descriptor type - 0x11, // HID BCD (low) - 0x01, // HID BCD (high) - 0, // Country code - 1, // Number of descriptors - descTypeHIDReport, // Descriptor type - lsU8(uint16(len(descHIDReportSerial))), // Descriptor length (low) - msU8(uint16(len(descHIDReportSerial))), // Descriptor length (high) - - // [77+7] Serial Tx Endpoint Descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descHIDEndpointSerialTx | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descHIDSerialTxPacketSize), // Max packet size (low) - msU8(descHIDSerialTxPacketSize), // Max packet size (high) - descHIDSerialTxInterval, // Polling Interval - - // [84+7] Serial Rx Endpoint Descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descHIDEndpointSerialRx | // Endpoint address - descEndptAddrDirectionOut, - descEndptTypeInterrupt, // Attributes - lsU8(descHIDSerialRxPacketSize), // Max packet size (low) - msU8(descHIDSerialRxPacketSize), // Max packet size (high) - descHIDSerialRxInterval, // Polling Interval - - // [91+9] Joystick Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descHIDInterfaceJoystick, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descHIDType, // Class code (HID = 0x03) - descHIDSubNone, // Subclass code - descHIDProtoNone, // Protocol code - 0, // Interface Description String Index - - // [100+9] Joystick HID Interface Descriptor - descLengthInterface, // Descriptor length - descTypeHID, // Descriptor type - 0x11, // HID BCD (low) - 0x01, // HID BCD (high) - 0, // Country code - 1, // Number of descriptors - descTypeHIDReport, // Descriptor type - lsU8(uint16(len(descHIDReportJoystick))), // Descriptor length (low) - msU8(uint16(len(descHIDReportJoystick))), // Descriptor length (high) - - // [109+7] Joystick Endpoint Descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descHIDEndpointJoystick | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descHIDJoystickTxPacketSize), // Max packet size (low) - msU8(descHIDJoystickTxPacketSize), // Max packet size (high) - descHIDJoystickTxInterval, // Polling Interval - - // [116+9] Keyboard Media Keys Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descHIDInterfaceMediaKey, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descHIDType, // Class code (HID = 0x03) - descHIDSubNone, // Subclass code - descHIDProtoNone, // Protocol code - 0, // Interface Description String Index - - // [125+9] Keyboard Media Keys HID Interface Descriptor - descLengthInterface, // Descriptor length - descTypeHID, // Descriptor type - 0x11, // HID BCD (low) - 0x01, // HID BCD (high) - 0, // Country code - 1, // Number of descriptors - descTypeHIDReport, // Descriptor type - lsU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (low) - msU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (high) - - // [134+7] Keyboard Media Keys Endpoint Descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descHIDEndpointMediaKey | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descHIDMediaKeyTxPacketSize), // Max packet size (low) - msU8(descHIDMediaKeyTxPacketSize), // Max packet size (high) - descHIDMediaKeyTxInterval, // Polling Interval - }, - }, -} - -var descHIDReportSerial = [...]uint8{ - 0x06, 0xC9, 0xFF, // Usage Page 0xFFC9 (vendor defined) - 0x09, 0x04, // Usage 0x04 - 0xA1, 0x5C, // Collection 0x5C - 0x75, 0x08, // report size = 8 bits (global) - 0x15, 0x00, // logical minimum = 0 (global) - 0x26, 0xFF, 0x00, // logical maximum = 255 (global) - 0x95, descHIDSerialTxPacketSize, // report count (global) - 0x09, 0x75, // usage (local) - 0x81, 0x02, // Input - 0x95, descHIDSerialRxPacketSize, // report count (global) - 0x09, 0x76, // usage (local) - 0x91, 0x02, // Output - 0x95, 0x04, // report count (global) - 0x09, 0x76, // usage (local) - 0xB1, 0x02, // Feature - 0xC0, // end collection -} - -var descHIDReportKeyboard = [...]uint8{ - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x06, // Usage (Keyboard) - 0xA1, 0x01, // Collection (Application) - 0x75, 0x01, // Report Size (1) - 0x95, 0x08, // Report Count (8) - 0x05, 0x07, // Usage Page (Key Codes) - 0x19, 0xE0, // Usage Minimum (224) - 0x29, 0xE7, // Usage Maximum (231) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x81, 0x02, // Input (Data, Variable, Absolute) [Modifier keys] - 0x95, 0x01, // Report Count (1) - 0x75, 0x08, // Report Size (8) - 0x81, 0x03, // Input (Constant) [Reserved byte] - 0x95, 0x05, // Report Count (5) - 0x75, 0x01, // Report Size (1) - 0x05, 0x08, // Usage Page (LEDs) - 0x19, 0x01, // Usage Minimum (1) - 0x29, 0x05, // Usage Maximum (5) - 0x91, 0x02, // Output (Data, Variable, Absolute) [LED report] - 0x95, 0x01, // Report Count (1) - 0x75, 0x03, // Report Size (3) - 0x91, 0x03, // Output (Constant) [LED report padding] - 0x95, 0x06, // Report Count (6) - 0x75, 0x08, // Report Size (8) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x7F, // Logical Maximum(104) - 0x05, 0x07, // Usage Page (Key Codes) - 0x19, 0x00, // Usage Minimum (0) - 0x29, 0x7F, // Usage Maximum (104) - 0x81, 0x00, // Input (Data, Array) [Normal keys] - 0xC0, // End Collection -} - -var descHIDReportMediaKey = [...]uint8{ - 0x05, 0x0C, // Usage Page (Consumer) - 0x09, 0x01, // Usage (Consumer Controls) - 0xA1, 0x01, // Collection (Application) - 0x75, 0x0A, // Report Size (10) - 0x95, 0x04, // Report Count (4) - 0x19, 0x00, // Usage Minimum (0) - 0x2A, 0x9C, 0x02, // Usage Maximum (0x29C) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0x9C, 0x02, // Logical Maximum (0x29C) - 0x81, 0x00, // Input (Data, Array) - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x75, 0x08, // Report Size (8) - 0x95, 0x03, // Report Count (3) - 0x19, 0x00, // Usage Minimum (0) - 0x29, 0xB7, // Usage Maximum (0xB7) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xB7, 0x00, // Logical Maximum (0xB7) - 0x81, 0x00, // Input (Data, Array) - 0xC0, // End Collection -} - -var descHIDReportMouse = [...]uint8{ - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x02, // Usage (Mouse) - 0xA1, 0x01, // Collection (Application) - 0x85, 0x01, // REPORT_ID (1) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (Button #1) - 0x29, 0x08, // Usage Maximum (Button #8) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x95, 0x08, // Report Count (8) - 0x75, 0x01, // Report Size (1) - 0x81, 0x02, // Input (Data, Variable, Absolute) - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x09, 0x38, // Usage (Wheel) - 0x15, 0x81, // Logical Minimum (-127) - 0x25, 0x7F, // Logical Maximum (127) - 0x75, 0x08, // Report Size (8), - 0x95, 0x03, // Report Count (3), - 0x81, 0x06, // Input (Data, Variable, Relative) - 0x05, 0x0C, // Usage Page (Consumer) - 0x0A, 0x38, 0x02, // Usage (AC Pan) - 0x15, 0x81, // Logical Minimum (-127) - 0x25, 0x7F, // Logical Maximum (127) - 0x75, 0x08, // Report Size (8), - 0x95, 0x01, // Report Count (1), - 0x81, 0x06, // Input (Data, Variable, Relative) - 0xC0, // End Collection - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x02, // Usage (Mouse) - 0xA1, 0x01, // Collection (Application) - 0x85, 0x02, // REPORT_ID (2) - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x7F, // Logical Maximum (32767) - 0x75, 0x10, // Report Size (16), - 0x95, 0x02, // Report Count (2), - 0x81, 0x02, // Input (Data, Variable, Absolute) - 0xC0, // End Collection -} - -var descHIDReportJoystick = [...]uint8{ - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x04, // Usage (Joystick) - 0xA1, 0x01, // Collection (Application) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x20, // Report Count (32) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (Button #1) - 0x29, 0x20, // Usage Maximum (Button #32) - 0x81, 0x02, // Input (variable,absolute) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x07, // Logical Maximum (7) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0x3B, 0x01, // Physical Maximum (315) - 0x75, 0x04, // Report Size (4) - 0x95, 0x01, // Report Count (1) - 0x65, 0x14, // Unit (20) - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x39, // Usage (Hat switch) - 0x81, 0x42, // Input (variable,absolute,null_state) - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x01, // Usage (Pointer) - 0xA1, 0x00, // Collection () - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x03, // Logical Maximum (1023) - 0x75, 0x0A, // Report Size (10) - 0x95, 0x04, // Report Count (4) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x09, 0x32, // Usage (Z) - 0x09, 0x35, // Usage (Rz) - 0x81, 0x02, // Input (variable,absolute) - 0xC0, // End Collection - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x03, // Logical Maximum (1023) - 0x75, 0x0A, // Report Size (10) - 0x95, 0x02, // Report Count (2) - 0x09, 0x36, // Usage (Slider) - 0x09, 0x36, // Usage (Slider) - 0x81, 0x02, // Input (variable,absolute) - 0xC0, // End Collection -} diff --git a/src/machine/usb/desc_atsamd51.go b/src/machine/usb/desc_atsamd51.go index d7c363ac1..ba271c201 100644 --- a/src/machine/usb/desc_atsamd51.go +++ b/src/machine/usb/desc_atsamd51.go @@ -11,13 +11,13 @@ const descCPUFrequencyHz = 120000000 // host/device controller instances. const descCoreCount = 1 // SAMx51 has a single, full-speed USB PHY -// descCDCACMCount defines the number of USB cores that may be configured as +// descCDCCount defines the number of USB cores that may be configured as // CDC-ACM (single) devices. -const descCDCACMCount = 1 +const descCDCCount = 0 // descHIDCount defines the number of USB cores that may be configured as a // composite (keyboard + mouse + joystick) human interface device (HID). -const descHIDCount = 0 +const descHIDCount = 1 // General USB device identification constants. const ( @@ -40,443 +40,3 @@ const ( descControlPacketSize = 64 ) - -// Constants for USB CDC-ACM device classes. -const ( - - // USB Bus Configuration Attributes - - descCDCACMMaxPowerMa = 100 // Maximum current (mA) requested from host - - // CDC-ACM Endpoint Descriptor Buffers - - descCDCACMEDCount = descMaxEndpoints - - // Setup packet is only 8 bytes in length. However, under certain scenarios, - // USB DMA controller may decide to overwrite/overflow the buffer with 2 extra - // bytes of CRC. From datasheet's "Management of SETUP Transactions" section: - // | If the number of received data bytes is the maximum data payload - // | specified by PCKSIZE.SIZE minus one, only the first CRC data is written - // | to the data buffer. If the number of received data is equal or less - // | than the data payload specified by PCKSIZE.SIZE minus two, both CRC - // | data bytes are written to the data buffer. - // Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT). - descCDCACMSxSize = 8 + 2 - descCDCACMCxSize = descControlPacketSize - - // CDC-ACM Data Buffers - - descCDCACMRxSize = descCDCACMDataRxPacketSize - descCDCACMTxSize = descCDCACMDataTxPacketSize - - descCDCACMTxTimeoutMs = 120 // millisec - descCDCACMTxSyncUs = 75 // microsec - - // Default CDC-ACM Endpoint Configurations (Full-Speed) - - descCDCACMStatusInterval = descCDCACMStatusFSInterval // Status - descCDCACMStatusPacketSize = descCDCACMStatusFSPacketSize // - - descCDCACMDataRxPacketSize = descCDCACMDataRxFSPacketSize // Data Rx - descCDCACMDataTxPacketSize = descCDCACMDataTxFSPacketSize // Data Tx - - // CDC-ACM Endpoint Configurations for Full-Speed Device - - descCDCACMStatusFSInterval = 5 // Status - descCDCACMStatusFSPacketSize = 64 // (full-speed) - - descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed) - descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed) - - // CDC-ACM Endpoint Configurations for High-Speed Device - - // - N/A, SAMx51 only has a full-speed PHY -) - -// Constants for USB HID (keyboard, mouse, joystick) device classes. -const ( - - // USB Bus Configuration Attributes - - descHIDMaxPowerMa = 100 // Maximum current (mA) requested from host - - // HID Endpoint Descriptor Buffers - - descHIDEDCount = descMaxEndpoints - - // Setup packet is only 8 bytes in length. However, under certain scenarios, - // USB DMA controller may decide to overwrite/overflow the buffer with 2 extra - // bytes of CRC. From datasheet's "Management of SETUP Transactions" section: - // | If the number of received data bytes is the maximum data payload - // | specified by PCKSIZE.SIZE minus one, only the first CRC data is written - // | to the data buffer. If the number of received data is equal or less - // | than the data payload specified by PCKSIZE.SIZE minus two, both CRC - // | data bytes are written to the data buffer. - // Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT). - descHIDSxSize = 8 + 2 - descHIDCxSize = descControlPacketSize - - // HID Serial Buffers - - descHIDSerialRxSize = descHIDSerialRxPacketSize - descHIDSerialTxSize = descHIDSerialTxPacketSize - - descHIDSerialTxTimeoutMs = 50 // millisec - descHIDSerialTxSyncUs = 75 // microsec - - // HID Keyboard Buffers - - descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize - - descHIDKeyboardTxTimeoutMs = 50 // millisec - - // HID Mouse Buffers - - descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize - - descHIDMouseTxTimeoutMs = 30 // millisec - - // HID Joystick Buffers - - descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize - - descHIDJoystickTxTimeoutMs = 30 // millisec - - // Default HID Endpoint Configurations (Full-Speed) - - descHIDSerialRxInterval = descHIDSerialRxFSInterval // Serial Rx - descHIDSerialRxPacketSize = descHIDSerialRxFSPacketSize // - - descHIDSerialTxInterval = descHIDSerialTxFSInterval // Serial Tx - descHIDSerialTxPacketSize = descHIDSerialTxFSPacketSize // - - descHIDKeyboardTxInterval = descHIDKeyboardTxFSInterval // Keyboard - descHIDKeyboardTxPacketSize = descHIDKeyboardTxFSPacketSize // - - descHIDMediaKeyTxInterval = descHIDMediaKeyTxFSInterval // Keyboard Media Keys - descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxFSPacketSize // - - descHIDMouseTxInterval = descHIDMouseTxFSInterval // Mouse - descHIDMouseTxPacketSize = descHIDMouseTxFSPacketSize // - - descHIDJoystickTxInterval = descHIDJoystickTxFSInterval // Joystick - descHIDJoystickTxPacketSize = descHIDJoystickTxFSPacketSize // - - // HID Endpoint Configurations for Full-Speed Device - - descHIDSerialRxFSInterval = 2 // Serial Rx - descHIDSerialRxFSPacketSize = 8 // (full-speed) - - descHIDSerialTxFSInterval = 1 // Serial Tx - descHIDSerialTxFSPacketSize = 16 // (full-speed) - - descHIDKeyboardTxFSInterval = 4 // Keyboard - descHIDKeyboardTxFSPacketSize = 8 // (full-speed) - - descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys - descHIDMediaKeyTxFSPacketSize = 8 // (full-speed) - - descHIDMouseTxFSInterval = 4 // Mouse - descHIDMouseTxFSPacketSize = 8 // (full-speed) - - descHIDJoystickTxFSInterval = 4 // Joystick - descHIDJoystickTxFSPacketSize = 12 // (full-speed) - - // HID Endpoint Configurations for High-Speed Device - - // - N/A, SAMx51 only has a full-speed PHY -) - -// descCDCACM0ED is an array of endpoint descriptors, which describes to the USB -// DMA controller the buffer and transfer properties for each endpoint, for the -// default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0ED [descCDCACMEDCount]dhwEPAddrDesc - -// descCDCACM0Sx is the receive (Rx) buffer for setup packets on control endpoint -// 0 of the default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Sx [descCDCACMSxSize]uint8 - -// descCDCACM0Cx is the transmit (Tx) buffer for control/status packets on control -// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Cx [descCDCACMCxSize]uint8 - -// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 -// for the default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Dx [descCDCACMConfigSize]uint8 - -// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM -// (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Rx [descCDCACMRxSize]uint8 - -// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM -// (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Tx [descCDCACMTxSize]uint8 - -// descCDCACM0Rq is the receive (Rx) transfer buffer for the default CDC-ACM -// (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Rq [descCDCACMRxSize]uint8 - -// descCDCACM0Tq is the transmit (Tx) transfer buffer for the default CDC-ACM -// (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Tq [descCDCACMTxSize]uint8 - -// descCDCACM0LC is the emulated UART's line coding configuration for the -// default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0LC descCDCACMLineCoding - -// descCDCACM0LS is the emulated UART's line state for the default CDC-ACM -// (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0LS descCDCACMLineState - -// descCDCACMClassData holds the buffers and control states for all CDC-ACM -// (single) device class configurations, ordered by index (offset by -1), for -// SAMx51 targets only. -// -// Instances of this type (elements of descCDCACMData) are embedded in elements -// of the common/target-agnostic CDC-ACM class configurations (descCDCACM). -// Methods defined on this type implement target-specific functionality, and -// some of these methods are required by the common device controller driver. -// Thus, this type functions as a hardware abstraction layer (HAL). -type descCDCACMClassData struct { - - // CDC-ACM Control Buffers - - ed *[descCDCACMEDCount]dhwEPAddrDesc // endpoint descriptors - - sx *[descCDCACMSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets - cx *[descCDCACMCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets - dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer - - // CDC-ACM Data Buffers - - rx *[descCDCACMRxSize]uint8 // bulk data endpoint Rx (OUT) transfer buffer - tx *[descCDCACMTxSize]uint8 // bulk data endpoint Tx (IN) transfer buffer - - rxq *[descCDCACMRxSize]uint8 - txq *[descCDCACMTxSize]uint8 - - lc *descCDCACMLineCoding // UART line coding - ls *descCDCACMLineState // UART line state - - rq *Queue - tq *Queue - - sxSize uint32 - rxSize uint32 - txSize uint32 -} - -// descCDCACMData holds statically-allocated instances for each of the target- -// specific (SAMx51) CDC-ACM (single) device class configurations' control and -// data structures, ordered by configuration index (offset by -1). Each element -// is embedded in a corresponding element of descCDCACM. -var descCDCACMData = [dcdCount]descCDCACMClassData{ - - { // -- CDC-ACM (single) Class Configuration Index 1 -- - - // CDC-ACM Control Buffers - - ed: &descCDCACM0ED, - - sx: &descCDCACM0Sx, - cx: &descCDCACM0Cx, - dx: &descCDCACM0Dx, - - // CDC-ACM Data Buffers - - rx: &descCDCACM0Rx, - tx: &descCDCACM0Tx, - - rxq: &descCDCACM0Rq, - txq: &descCDCACM0Tq, - - lc: &descCDCACM0LC, - ls: &descCDCACM0LS, - - rq: &Queue{}, - tq: &Queue{}, - - sxSize: descCDCACMStatusPacketSize, - rxSize: descCDCACMDataRxPacketSize, - txSize: descCDCACMDataTxPacketSize, - }, -} - -// descHID0ED is an array of endpoint descriptors, which describes to the USB -// DMA controller the buffer and transfer properties for each endpoint, for the -// default HID device class configuration (index 1). -//go:align 32 -var descHID0ED [descHIDEDCount]dhwEPAddrDesc - -// descHID0Sx is the receive (Rx) buffer for setup packets on control endpoint 0 -// of the default HID device class configuration (index 1). -//go:align 32 -var descHID0Sx [descHIDSxSize]uint8 - -// descHID0Cx is the transmit (Tx) buffer for control/status packets on control -// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descHID0Cx [descHIDCxSize]uint8 - -// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for -// the default HID device class configuration (index 1). -//go:align 32 -var descHID0Dx [descHIDConfigSize]uint8 - -// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default -// HID device class configuration (index 1). -//go:align 32 -var descHID0SerialRx [descHIDSerialRxSize]uint8 - -// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default -// HID device class configuration (index 1). -//go:align 32 -var descHID0SerialTx [descHIDSerialTxSize]uint8 - -// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the -// default HID device class configuration (index 1). -//go:align 32 -var descHID0KeyboardTx [descHIDKeyboardTxSize]uint8 - -// descHID0KeyboardTp is the keyboard HID report transmit (Tx) transfer buffer -// for the default HID device class configuration (index 1). -//go:align 32 -var descHID0KeyboardTp [descHIDKeyboardTxPacketSize]uint8 - -// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default -// HID device class configuration (index 1). -//go:align 32 -var descHID0MouseTx [descHIDMouseTxSize]uint8 - -// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the -// default HID device class configuration (index 1). -//go:align 32 -var descHID0JoystickTx [descHIDJoystickTxSize]uint8 - -var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8 -var descHID0KeyboardTxCon [hidKeyboardConCount]uint16 -var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8 - -// descHID0Keyboard is the Keyboard instance with which the user may interact -// when using the default HID device class configuration (index 1). -var descHID0Keyboard = Keyboard{ - key: &descHID0KeyboardTxKey, - con: &descHID0KeyboardTxCon, - sys: &descHID0KeyboardTxSys, -} - -// descHIDClassData holds the buffers and control states for all of the HID -// device class configurations, ordered by index (offset by -1), for SAMx51 -// targets only. -// -// Instances of this type (elements of descHIDData) are embedded in elements -// of the common/target-agnostic HID class configurations (descHID). -// Methods defined on this type implement target-specific functionality, and -// some of these methods are required by the common device controller driver. -// Thus, this type functions as a hardware abstraction layer (HAL). -type descHIDClassData struct { - - // HID Control Buffers - - ed *[descHIDEDCount]dhwEPAddrDesc // endpoint descriptors - - sx *[descHIDSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets - cx *[descHIDCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets - dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer - - // HID Serial Buffers - - rxSerial *[descHIDSerialRxSize]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer - txSerial *[descHIDSerialTxSize]uint8 // interrupt endpoint serial Tx (IN) transfer buffer - - rxSerialSize uint16 - txSerialSize uint16 - - // HID Keyboard Buffers - - txKeyboard *[descHIDKeyboardTxSize]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer - tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer - - txKeyboardSize uint16 - - // HID Mouse Buffers - - txMouse *[descHIDMouseTxSize]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer - - txMouseSize uint16 - - // HID Joystick Buffers - - txJoystick *[descHIDJoystickTxSize]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer - - txJoystickSize uint16 - - // HID Device Instances - - //serial *Serial - keyboard *Keyboard - //mouse *Mouse - //joystick *Joystick -} - -// descHIDData holds statically-allocated instances for each of the target- -// specific (SAMx51) HID device class configurations' control and data -// structures, ordered by configuration index (offset by -1). Each element is -// embedded in a corresponding element of descHID. -var descHIDData = [dcdCount]descHIDClassData{ - - { // -- HID Class Configuration Index 1 -- - - // HID Control Buffers - - ed: &descHID0ED, - - sx: &descHID0Sx, - cx: &descHID0Cx, - dx: &descHID0Dx, - - // HID Serial Buffers - - rxSerial: &descHID0SerialRx, - txSerial: &descHID0SerialTx, - - rxSerialSize: descHIDSerialRxPacketSize, - txSerialSize: descHIDSerialTxPacketSize, - - // HID Keyboard Buffers - - txKeyboard: &descHID0KeyboardTx, - tpKeyboard: &descHID0KeyboardTp, - - txKeyboardSize: descHIDKeyboardTxPacketSize, - - // HID Mouse Buffers - - txMouse: &descHID0MouseTx, - - txMouseSize: descHIDMouseTxPacketSize, - - // HID Joystick Buffers - - txJoystick: &descHID0JoystickTx, - - txJoystickSize: descHIDJoystickTxPacketSize, - - // HID Device Instances - - //serial: &descHID0Serial, - keyboard: &descHID0Keyboard, - //mouse: &descHID0Mouse, - //joystick: &descHID0Joystick, - }, -} diff --git a/src/machine/usb/dhw-cdc_atsamd51.go b/src/machine/usb/dhw-cdc_atsamd51.go new file mode 100644 index 000000000..b36e72168 --- /dev/null +++ b/src/machine/usb/dhw-cdc_atsamd51.go @@ -0,0 +1,244 @@ +//go:build baremetal && usb.cdc && (atsamd51 || atsame5x) +// +build baremetal +// +build usb.cdc +// +build atsamd51 atsame5x + +package usb + +import "unsafe" + +//go:inline +func (d *dhw) descriptorTable() uintptr { + return uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].ed[0])) +} + +// endpointDescriptor returns the endpoint descriptor for the given endpoint +// address, encoded as direction D and endpoint number N with the 8-bit mask +// D000NNNN. +//go:inline +func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc { + num, dir := unpackEndpoint(endpoint) + return &descCDC[d.cc.config-1].ed[num][dir] +} + +//go:inline +func (d *dhw) controlSetupBuffer() uintptr { + return uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].sx[0])) +} + +//go:inline +func (d *dhw) controlStatusBuffer(data []uint8) uintptr { + // reference to class configuration data + c := descCDC[d.cc.config-1] + for i := range c.cx { + c.cx[i] = 0 // zero out the control reply buffer + } + // copy the given data into control reply buffer + copy(c.cx[:], data) + return uintptr(unsafe.Pointer(&c.cx[0])) +} + +// ============================================================================= +// [CDC-ACM] Serial UART (Virtual COM Port) +// ============================================================================= + +func (d *dhw) cdcConfigure() { + + acm := &descCDC[d.cc.config-1] + + acm.setState(descCDCStateConfigured) + + // SAMx51 only supports USB full-speed (FS) operation + acm.sxSize = descCDCStatusFSPacketSize + acm.rxSize = descCDCDataRxFSPacketSize + acm.txSize = descCDCDataTxFSPacketSize + + rq := acm.rxq[:] + tq := acm.txq[:] + + // Rx gives priority to incoming data, Tx gives priority to outgoing data + acm.rq.Init(&rq, int(acm.rxSize), QueueFullDiscardFirst) + acm.tq.Init(&tq, int(acm.txSize), QueueFullDiscardLast) + + d.endpointEnable(txEndpoint(descCDCEndpointStatus), + false, descCDCConfigAttrStatus) + d.endpointEnable(rxEndpoint(descCDCEndpointDataRx), + false, descCDCConfigAttrDataRx) + d.endpointEnable(txEndpoint(descCDCEndpointDataTx), + false, descCDCConfigAttrDataTx) + + d.endpointConfigure(txEndpoint(descCDCEndpointStatus), + nil) + d.endpointConfigure(rxEndpoint(descCDCEndpointDataRx), + d.cdcReceiveComplete) + d.endpointConfigure(txEndpoint(descCDCEndpointDataTx), + d.cdcTransmitComplete) + + d.cdcReceiveStart(rxEndpoint(descCDCEndpointDataRx)) +} + +func (d *dhw) cdcSetLineState(state uint16) { + acm := &descCDC[d.cc.config-1] + acm.setState(descCDCStateLineState) + if acm.ls.parse(state) { + // TBD: respond to changes in line state? + } +} + +func (d *dhw) cdcSetLineCoding(coding []uint8) { + acm := &descCDC[d.cc.config-1] + acm.setState(descCDCStateLineCoding) + if acm.lc.parse(coding) { + switch acm.lc.baud { + case 1200: + if acm.ls.dataTerminalReady { + // reboot CPU + } + } + } +} + +func (d *dhw) cdcReady() bool { + acm := &descCDC[d.cc.config-1] + // Ensure we have received SET_CONFIGURATION class request, and then both + // SET_LINE_STATE and SET_LINE_CODING CDC requests (in that order). + return d.state() == dcdStateConfigured && + acm.st.Get() == uint8(descCDCStateLineCoding) +} + +func (d *dhw) cdcReceiveStart(endpoint uint8) { + acm := &descCDC[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + + ready, _ := d.ep[num][descDirRx].scheduleTransfer( + uintptr(unsafe.Pointer(&acm.rx[0])), acm.rxSize) + if ready { + if xfer, ok := d.ep[num][descDirRx].pendingTransfer(); ok { + // Update the active transfer descriptor on the corresponding endpoint. + d.ep[num][descDirRx].setActiveTransfer(xfer) + d.endpointTransfer(endpoint, xfer.data, xfer.size) + } + } +} + +func (d *dhw) cdcReceiveComplete(endpoint uint8, size uint32) { + acm := &descCDC[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + + if xfer, ok := d.ep[num][descDirRx].activeTransfer(); ok { + for ptr := xfer.data; ptr < xfer.data+uintptr(size); ptr++ { + acm.rq.Enq(*(*uint8)(unsafe.Pointer(ptr))) + } + } + d.ep[num][descDirRx].setActiveTransfer(nil) + d.cdcReceiveStart(endpoint) +} + +func (d *dhw) cdcTransmitStart(endpoint uint8) { + acm := &descCDC[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + + // BULK data endpoints can simply use a single time slot in the schedule, and + // repeatedly transfer from the same transmit buffer (acm.tx) as soon as the + // transaction complete callback has been called for a prior transaction. + + // Do not schedule another transfer if one is already active, or if our Tx + // FIFO is currently empty. + if d.ep[num][descDirTx].hasActiveTransfer() || acm.tq.Len() == 0 { + return + } + + if send, err := acm.tq.Read(acm.tx[:]); err == nil && send > 0 { + ready, _ := d.ep[num][descDirTx].scheduleTransfer( + uintptr(unsafe.Pointer(&acm.tx[0])), uint32(send)) + if ready { + if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok { + d.ep[num][descDirTx].setActiveTransfer(xfer) + d.endpointTransfer(endpoint, xfer.data, xfer.size) + } + } + } +} + +func (d *dhw) cdcTransmitComplete(endpoint uint8, size uint32) { + acm := &descCDC[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + if size > 0 && size%acm.txSize == 0 { + // Send ZLP if transfer length is a non-zero multiple of max packet size. + d.endpointTransfer(endpoint, 0, 0) + } + d.ep[num][descDirTx].setActiveTransfer(nil) + d.cdcTransmitStart(endpoint) +} + +// cdcFlush discards all buffered input (Rx) data. +func (d *dhw) cdcFlush() { + acm := &descCDC[d.cc.config-1] + acm.rq.Reset(int(acm.rxSize)) +} + +func (d *dhw) cdcAvailable() int { + acm := &descCDC[d.cc.config-1] + return acm.rq.Len() +} + +func (d *dhw) cdcPeek() (uint8, bool) { + acm := &descCDC[d.cc.config-1] + return acm.rq.Front() +} + +func (d *dhw) cdcReadByte() (uint8, bool) { + acm := &descCDC[d.cc.config-1] + return acm.rq.Deq() +} + +func (d *dhw) cdcRead(data []uint8) (int, error) { + acm := &descCDC[d.cc.config-1] + return acm.rq.Read(data) +} + +func (d *dhw) cdcWriteByte(c uint8) error { + _, err := d.cdcWrite([]uint8{c}) + return err +} + +func (d *dhw) cdcWrite(data []uint8) (int, error) { + + acm := &descCDC[d.cc.config-1] + num := uint16(descCDCEndpointDataTx) & descEndptAddrNumberMsk + + var sent int + var werr error + for off := 0; off < len(data); off += int(acm.txSize) { + + cnt := len(data[off:]) + if cnt > int(acm.txSize) { + cnt = int(acm.txSize) + } + + // Block until we have room in the Tx FIFO. Space will become available once + // the endpoint transaction complete interrupt is raised for the Tx BULK data + // endpoint, and then the uartTransmitComplete callback has dequeued data from + // the Tx FIFO (acm.tq) into the Tx transmit buffer (acm.tx). + for acm.tq.Rem() < cnt { + } + + // Add data to Tx FIFO + add, err := acm.tq.Write(data[off : off+cnt]) + if err != nil { + werr = err + break + } + sent += add + + if d.ep[num][descDirTx].hasActiveTransfer() { + // If there is already a transmit in-progress, wait for its callback to + // detect new data in the FIFO and continue the transfer automatically. + } else { + // Otherwise, initiate a new data transfer. + d.cdcTransmitStart(txEndpoint(descCDCEndpointDataTx)) + } + } + + return sent, werr +} diff --git a/src/machine/usb/dhw-hid_atsamd51.go b/src/machine/usb/dhw-hid_atsamd51.go new file mode 100644 index 000000000..4ecae3434 --- /dev/null +++ b/src/machine/usb/dhw-hid_atsamd51.go @@ -0,0 +1,265 @@ +//go:build baremetal && usb.hid && (atsamd51 || atsame5x) +// +build baremetal +// +build usb.hid +// +build atsamd51 atsame5x + +package usb + +import "unsafe" + +//go:inline +func (d *dhw) descriptorTable() uintptr { + return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].ed[0])) +} + +// endpointDescriptor returns the endpoint descriptor for the given endpoint +// address, encoded as direction D and endpoint number N with the 8-bit mask +// D000NNNN. +//go:inline +func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc { + num, dir := unpackEndpoint(endpoint) + return &descHID[d.cc.config-1].ed[num][dir] +} + +//go:inline +func (d *dhw) controlSetupBuffer() uintptr { + return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].sx[0])) +} + +//go:inline +func (d *dhw) controlStatusBuffer(data []uint8) uintptr { + // reference to class configuration data + c := descHID[d.cc.config-1] + for i := range c.cx { + c.cx[i] = 0 // zero out the control reply buffer + } + // copy the given data into control reply buffer + copy(c.cx[:], data) + return uintptr(unsafe.Pointer(&c.cx[0])) +} + +// ============================================================================= +// [HID] Serial +// ============================================================================= + +func (d *dhw) serialConfigure() { + + // hid := &descHID[d.cc.config-1] + + // SAMx51 only supports USB full-speed (FS) operation + // hid.rxSerialSize = descHIDSerialRxFSPacketSize + // hid.txSerialSize = descHIDSerialTxFSPacketSize + + // Rx and Tx are on same endpoint + d.endpointEnable(descHIDEndpointSerialRx, + false, descHIDConfigAttrSerial) + + // d.endpointConfigureRx(descHIDEndpointSerialRx, + // hid.rxSerialSize, false, d.serialNotify) + // d.endpointConfigureTx(descHIDEndpointSerialTx, + // hid.txSerialSize, false, nil) + + // for i := range hid.rdSerial { + // d.serialReceive(uint8(i)) + // } + + // d.timerConfigure(0, descHIDSerialTxSyncUs, d.serialSync) +} + +func (d *dhw) serialReceive(endpoint uint8) { + hid := &descHID[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + _, _ = hid, num // TODO(ardnew): elaborate stub +} + +func (d *dhw) serialTransmit() { + hid := &descHID[d.cc.config-1] + _ = hid // TODO(ardnew): elaborate stub +} + +func (d *dhw) serialNotify( /* transfer *dhwTransfer */ ) { + // hid := &descHID[d.cc.config-1] + // len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF) + // _ = len // TODO(ardnew): elaborate stub +} + +// serialFlush discards all buffered input (Rx) data. +func (d *dhw) serialFlush() { + hid := &descHID[d.cc.config-1] + _ = hid +} + +func (d *dhw) serialSync() { + +} + +// ============================================================================= +// [HID] Keyboard +// ============================================================================= + +func (d *dhw) keyboard() *Keyboard { return descHID[d.cc.config-1].keyboard } + +func (d *dhw) keyboardConfigure() { + + hid := &descHID[d.cc.config-1] + + // Initialize keyboard + hid.keyboard.configure(d.dcd, hid) + + // SAMx51 only supports USB full-speed (FS) operation + hid.txKeyboardSize = descHIDKeyboardTxPacketSize + + // tq := hid.txqKeyboard[:] + + // hid.tqKeyboard.Init(&tq, len(hid.txqKeyboard), QueueFullDiscardFirst) + + d.endpointEnable(txEndpoint(descHIDEndpointKeyboard), + false, descHIDConfigAttrKeyboard) + d.endpointEnable(txEndpoint(descHIDEndpointMediaKey), + false, descHIDConfigAttrMediaKey) + + d.endpointConfigure(txEndpoint(descHIDEndpointKeyboard), + d.keyboardWriteComplete) + d.endpointConfigure(txEndpoint(descHIDEndpointMediaKey), + d.keyboardWriteComplete) +} + +func (d *dhw) keyboardSendKeys(consumer bool) bool { + + hid := &descHID[d.cc.config-1] + + if !consumer { + + hid.txKeyboard[0] = hid.keyboard.mod + hid.txKeyboard[1] = 0 + hid.txKeyboard[2] = hid.keyboard.key[0] + hid.txKeyboard[3] = hid.keyboard.key[1] + hid.txKeyboard[4] = hid.keyboard.key[2] + hid.txKeyboard[5] = hid.keyboard.key[3] + hid.txKeyboard[6] = hid.keyboard.key[4] + hid.txKeyboard[7] = hid.keyboard.key[5] + + return d.keyboardWrite(txEndpoint(descHIDEndpointKeyboard), hid.txKeyboard[:]) + + } else { + + // 44444444 44333333 33332222 22222211 11111111 [ word ] + // 98765432 10987654 32109876 54321098 76543210 [ index ] (right-to-left) + + hid.txKeyboard[1] = uint8((hid.keyboard.con[1] << 2) | ((hid.keyboard.con[0] >> 8) & 0x03)) + hid.txKeyboard[2] = uint8((hid.keyboard.con[2] << 4) | ((hid.keyboard.con[1] >> 6) & 0x0F)) + hid.txKeyboard[3] = uint8((hid.keyboard.con[3] << 6) | ((hid.keyboard.con[2] >> 4) & 0x3F)) + hid.txKeyboard[4] = uint8(hid.keyboard.con[3] >> 2) + hid.txKeyboard[5] = hid.keyboard.sys[0] + hid.txKeyboard[6] = hid.keyboard.sys[1] + hid.txKeyboard[7] = hid.keyboard.sys[2] + + return d.keyboardWrite(txEndpoint(descHIDEndpointMediaKey), hid.txKeyboard[:]) + + } +} + +func (d *dhw) keyboardWriteComplete(endpoint uint8, size uint32) { + hid := &descHID[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + if size > 0 && size%uint32(hid.txKeyboardSize) == 0 { + // Send ZLP if transfer length is a non-zero multiple of max packet size. + d.endpointTransfer(endpoint, 0, 0) + } + d.ep[num][descDirTx].setActiveTransfer(nil) +} + +func (d *dhw) keyboardWrite(endpoint uint8, data []uint8) bool { + + hid := &descHID[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + + for off := 0; off < len(data); off += int(hid.txKeyboardSize) { + + cnt := len(data[off:]) + if cnt > int(hid.txKeyboardSize) { + cnt = int(hid.txKeyboardSize) + } + + for d.ep[num][descDirTx].hasActiveTransfer() { + } + + ready, _ := d.ep[num][descDirTx].scheduleTransfer( + uintptr(unsafe.Pointer(&hid.txKeyboard[0])), uint32(cnt)) + if ready { + if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok { + d.ep[num][descDirTx].setActiveTransfer(xfer) + d.endpointTransfer(endpoint, xfer.data, xfer.size) + } + } + } + + // size := uint16(len(data)) + // xfer := &hid.tdKeyboard[hid.txKeyboardHead] + // when := ticks() + // for { + // if 0 == xfer.token&0x80 { + // if 0 != xfer.token&0x68 { + // // TODO: token contains error, how to handle? + // } + // hid.txKeyboardPrev = false + // break + // } + // if hid.txKeyboardPrev { + // return false + // } + // if ticks()-when > descHIDKeyboardTxTimeoutMs { + // // Waited too long, assume host connection dropped + // hid.txKeyboardPrev = true + // return false + // } + // } + // // Without this delay, the order packets are transmitted is seriously screwy. + // udelay(60) + // buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:] + // _ = copy(buff, data) + // d.transferPrepare(xfer, &buff[0], size, 0) + // flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize) + // d.endpointTransmit(endpoint, xfer) + // hid.txKeyboardHead += 1 + // if hid.txKeyboardHead >= descHIDKeyboardTDCount { + // hid.txKeyboardHead = 0 + // } + return true +} + +// ============================================================================= +// [HID] Mouse +// ============================================================================= + +func (d *dhw) mouseConfigure() { + + // hid := &descHID[d.cc.config-1] + + // SAMx51 only supports USB full-speed (FS) operation + // hid.txMouseSize = descHIDMouseTxFSPacketSize + + d.endpointEnable(descHIDEndpointMouse, + false, descHIDConfigAttrMouse) + + // d.endpointConfigureTx(descHIDEndpointMouse, + // hid.txMouseSize, false, nil) +} + +// ============================================================================= +// [HID] Joystick +// ============================================================================= + +func (d *dhw) joystickConfigure() { + + // hid := &descHID[d.cc.config-1] + + // SAMx51 only supports USB full-speed (FS) operation + // hid.txJoystickSize = descHIDJoystickTxFSPacketSize + + d.endpointEnable(descHIDEndpointJoystick, + false, descHIDConfigAttrJoystick) + + // d.endpointConfigureTx(descHIDEndpointJoystick, + // hid.txJoystickSize, false, nil) +} diff --git a/src/machine/usb/dhw_atsamd51.go b/src/machine/usb/dhw_atsamd51.go index d518ad7fb..449eb2028 100644 --- a/src/machine/usb/dhw_atsamd51.go +++ b/src/machine/usb/dhw_atsamd51.go @@ -170,14 +170,7 @@ func (d *dhw) init() status { (3 << sam.USB_DEVICE_QOSCTRL_DQOS_Pos)) // Install USB endpoint descriptor table (USB_DEVICE.DESCADD) - var addr uintptr - switch d.cc.id { - case classDeviceCDCACM: - addr = uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].ed[0])) - case classDeviceHID: - addr = uintptr(unsafe.Pointer(&descHID[d.cc.config-1].ed[0])) - } - d.bus.DESCADD.Set(uint32(addr)) + d.bus.DESCADD.Set(uint32(d.descriptorTable())) // Configure bus speed (always full-speed (FS)), device mode, enable PHY, and // put finite-state machine (FSM) in standby. @@ -438,51 +431,6 @@ func (d *dhw) remoteWakeup() { // Control Endpoint 0 // ============================================================================= -func (d *dhw) controlEndpoint() uint8 { - switch d.cc.id { - case classDeviceCDCACM: - return descCDCACMEndpointCtrl - case classDeviceHID: - return descHIDEndpointCtrl - } - return descEndpointInvalid -} - -func (d *dhw) controlSetupBuffer() uintptr { - switch d.cc.id { - case classDeviceCDCACM: - return uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].sx[0])) - case classDeviceHID: - return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].sx[0])) - } - return 0 -} - -func (d *dhw) controlStatusBuffer(data []uint8) uintptr { - switch d.cc.id { - case classDeviceCDCACM: - // reference to class configuration data - c := descCDCACM[d.cc.config-1] - for i := range c.cx { - c.cx[i] = 0 // zero out the control reply buffer - } - // copy the given data into control reply buffer - copy(c.cx[:], data) - return uintptr(unsafe.Pointer(&c.cx[0])) - - case classDeviceHID: - // reference to class configuration data - c := descHID[d.cc.config-1] - for i := range c.cx { - c.cx[i] = 0 // zero out the control reply buffer - } - // copy the given data into control reply buffer - copy(c.cx[:], data) - return uintptr(unsafe.Pointer(&c.cx[0])) - } - return 0 -} - // controlStall stalls a transfer on control endpoint 0. To stall a transfer on // any other endpoint, use method endpointStall(). func (d *dhw) controlStall(stall bool, dir uint8) { @@ -1046,71 +994,6 @@ func (d *dhw) endpointDescriptors(endpoint uint8) (out, in *dhwEPDesc) { d.endpointDescriptor(txEndpoint(endpoint)) } -// endpointDescriptor returns the endpoint descriptor for the given endpoint -// address, encoded as direction D and endpoint number N with the 8-bit mask -// D000NNNN. -//go:inline -func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc { - // endpoint descriptor is device class-specific - num, dir := unpackEndpoint(endpoint) - switch d.cc.id { - case classDeviceCDCACM: - return &descCDCACM[d.cc.config-1].ed[num][dir] - case classDeviceHID: - return &descHID[d.cc.config-1].ed[num][dir] - default: - return nil - } -} - -func (d *dhw) endpointMaxPacketSize(endpoint uint8) uint32 { - - switch d.cc.id { - case classDeviceCDCACM: - - switch endpointNumber(endpoint) { - case descCDCACMEndpointCtrl: - return descControlPacketSize - - case descCDCACMEndpointStatus: - return descCDCACMStatusPacketSize - - case descCDCACMEndpointDataRx: - return descCDCACMDataRxPacketSize - - case descCDCACMEndpointDataTx: - return descCDCACMDataTxPacketSize - } - case classDeviceHID: - - switch endpointNumber(endpoint) { - case descHIDEndpointCtrl: - return descControlPacketSize - - case descHIDEndpointKeyboard: - return descHIDKeyboardTxPacketSize - - case descHIDEndpointMouse: - return descHIDMouseTxPacketSize - - case descHIDEndpointSerialRx: // == descHIDEndpointSerialTx - switch endpoint { - case rxEndpoint(endpoint): - return descHIDSerialRxPacketSize - case txEndpoint(endpoint): - return descHIDSerialTxPacketSize - } - - case descHIDEndpointJoystick: - return descHIDJoystickTxPacketSize - - case descHIDEndpointMediaKey: - return descHIDMediaKeyTxPacketSize - } - } - return descControlPacketSize -} - func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) { if control { @@ -1309,425 +1192,3 @@ func (d *dhw) endpointTransfer(endpoint uint8, data uintptr, size uint32) { func (d *dhw) endpointComplete(endpoint uint8, size uint32) { } - -// ============================================================================= -// General-Purpose (GP) Timer -// ============================================================================= - -func (d *dhw) timerConfigure(timer int, usec uint32, fn func()) { - -} - -func (d *dhw) timerOneShot(timer int) { - -} - -func (d *dhw) timerStop(timer int) { - -} - -// ============================================================================= -// [CDC-ACM] Serial UART (Virtual COM Port) -// ============================================================================= - -func (d *dhw) uartConfigure() { - - acm := &descCDCACM[d.cc.config-1] - - acm.setState(descCDCACMStateConfigured) - - // SAMx51 only supports USB full-speed (FS) operation - acm.sxSize = descCDCACMStatusFSPacketSize - acm.rxSize = descCDCACMDataRxFSPacketSize - acm.txSize = descCDCACMDataTxFSPacketSize - - rq := acm.rxq[:] - tq := acm.txq[:] - - // Rx gives priority to incoming data, Tx gives priority to outgoing data - acm.rq.Init(&rq, int(acm.rxSize), QueueFullDiscardFirst) - acm.tq.Init(&tq, int(acm.txSize), QueueFullDiscardLast) - - d.endpointEnable(txEndpoint(descCDCACMEndpointStatus), - false, descCDCACMConfigAttrStatus) - d.endpointEnable(rxEndpoint(descCDCACMEndpointDataRx), - false, descCDCACMConfigAttrDataRx) - d.endpointEnable(txEndpoint(descCDCACMEndpointDataTx), - false, descCDCACMConfigAttrDataTx) - - d.endpointConfigure(txEndpoint(descCDCACMEndpointStatus), - nil) - d.endpointConfigure(rxEndpoint(descCDCACMEndpointDataRx), - d.uartReceiveComplete) - d.endpointConfigure(txEndpoint(descCDCACMEndpointDataTx), - d.uartTransmitComplete) - - d.uartReceiveStart(rxEndpoint(descCDCACMEndpointDataRx)) -} - -func (d *dhw) uartSetLineState(state uint16) { - acm := &descCDCACM[d.cc.config-1] - acm.setState(descCDCACMStateLineState) - if acm.ls.parse(state) { - // TBD: respond to changes in line state? - } -} - -func (d *dhw) uartSetLineCoding(coding []uint8) { - acm := &descCDCACM[d.cc.config-1] - acm.setState(descCDCACMStateLineCoding) - if acm.lc.parse(coding) { - switch acm.lc.baud { - case 1200: - if acm.ls.dataTerminalReady { - // reboot CPU - } - } - } -} - -func (d *dhw) uartReady() bool { - acm := &descCDCACM[d.cc.config-1] - // Ensure we have received SET_CONFIGURATION class request, and then both - // SET_LINE_STATE and SET_LINE_CODING CDC requests (in that order). - // - // Many USB hosts will send a default SET_LINE_CODING prior to SET_LINE_STATE, - // and then another SET_LINE_CODING containing the actual terminal settings. - // - // We do not want to start UART Rx/Tx transactions until after we have - // received the final SET_LINE_CODING with the intended terminal settings. - // - // The "set" method on type descCDCACMState defines this incremental state - // machine, with the UART's current state stored in the volatile.Register8 - // field "state" of descCDCACMClass. - return d.state() == dcdStateConfigured && //acm.ls.dataTerminalReady && - acm.state.Get() == uint8(descCDCACMStateLineCoding) -} - -func (d *dhw) uartReceiveStart(endpoint uint8) { - acm := &descCDCACM[d.cc.config-1] - num := uint16(endpoint) & descEndptAddrNumberMsk - - ready, _ := d.ep[num][descDirRx].scheduleTransfer( - uintptr(unsafe.Pointer(&acm.rx[0])), acm.rxSize) - if ready { - if xfer, ok := d.ep[num][descDirRx].pendingTransfer(); ok { - // Update the active transfer descriptor on the corresponding endpoint. - d.ep[num][descDirRx].setActiveTransfer(xfer) - d.endpointTransfer(endpoint, xfer.data, xfer.size) - } - } -} - -func (d *dhw) uartReceiveComplete(endpoint uint8, size uint32) { - acm := &descCDCACM[d.cc.config-1] - num := uint16(endpoint) & descEndptAddrNumberMsk - - if xfer, ok := d.ep[num][descDirRx].activeTransfer(); ok { - for ptr := xfer.data; ptr < xfer.data+uintptr(size); ptr++ { - acm.rq.Enq(*(*uint8)(unsafe.Pointer(ptr))) - } - } - d.ep[num][descDirRx].setActiveTransfer(nil) - d.uartReceiveStart(endpoint) -} - -func (d *dhw) uartTransmitStart(endpoint uint8) { - acm := &descCDCACM[d.cc.config-1] - num := uint16(endpoint) & descEndptAddrNumberMsk - - // BULK data endpoints can simply use a single time slot in the schedule, and - // repeatedly transfer from the same transmit buffer (acm.tx) as soon as the - // transaction complete callback has been called for a prior transaction. - - // Do not schedule another transfer if one is already active, or if our Tx - // FIFO is currently empty. - if d.ep[num][descDirTx].hasActiveTransfer() || acm.tq.Len() == 0 { - return - } - - if send, err := acm.tq.Read(acm.tx[:]); err == nil && send > 0 { - ready, _ := d.ep[num][descDirTx].scheduleTransfer( - uintptr(unsafe.Pointer(&acm.tx[0])), uint32(send)) - if ready { - if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok { - d.ep[num][descDirTx].setActiveTransfer(xfer) - d.endpointTransfer(endpoint, xfer.data, xfer.size) - } - } - } -} - -func (d *dhw) uartTransmitComplete(endpoint uint8, size uint32) { - acm := &descCDCACM[d.cc.config-1] - num := uint16(endpoint) & descEndptAddrNumberMsk - if size > 0 && size%acm.txSize == 0 { - // Send ZLP if transfer length is a multiple of max packet size. - d.endpointTransfer(endpoint, 0, 0) - } - d.ep[num][descDirTx].setActiveTransfer(nil) - d.uartTransmitStart(endpoint) -} - -// uartFlush discards all buffered input (Rx) data. -func (d *dhw) uartFlush() { - acm := &descCDCACM[d.cc.config-1] - acm.rq.Reset(int(acm.rxSize)) -} - -func (d *dhw) uartAvailable() int { - acm := &descCDCACM[d.cc.config-1] - return acm.rq.Len() -} - -func (d *dhw) uartPeek() (uint8, bool) { - acm := &descCDCACM[d.cc.config-1] - return acm.rq.Front() -} - -func (d *dhw) uartReadByte() (uint8, bool) { - acm := &descCDCACM[d.cc.config-1] - return acm.rq.Deq() -} - -func (d *dhw) uartRead(data []uint8) (int, error) { - acm := &descCDCACM[d.cc.config-1] - return acm.rq.Read(data) -} - -func (d *dhw) uartWriteByte(c uint8) error { - _, err := d.uartWrite([]uint8{c}) - return err -} - -func (d *dhw) uartWrite(data []uint8) (int, error) { - - acm := &descCDCACM[d.cc.config-1] - num := uint16(descCDCACMEndpointDataTx) & descEndptAddrNumberMsk - - var sent int - var werr error - for off := 0; off < len(data); off += int(acm.txSize) { - - cnt := len(data[off:]) - if cnt > int(acm.txSize) { - cnt = int(acm.txSize) - } - - // Block until we have room in the Tx FIFO. Space will become available once - // the endpoint transaction complete interrupt is raised for the Tx BULK data - // endpoint, and then the uartTransmitComplete callback has dequeued data from - // the Tx FIFO (acm.tq) into the Tx transmit buffer (acm.tx). - for acm.tq.Rem() < cnt { - } - - // Add data to Tx FIFO - add, err := acm.tq.Write(data[off : off+cnt]) - if err != nil { - werr = err - break - } - sent += add - - if d.ep[num][descDirTx].hasActiveTransfer() { - // If there is already a transmit in-progress, wait for its callback to - // detect new data in the FIFO and continue the transfer automatically. - } else { - // Otherwise, initiate a new data transfer. - d.uartTransmitStart(txEndpoint(descCDCACMEndpointDataTx)) - } - } - - return sent, werr -} - -// ============================================================================= -// [HID] Serial -// ============================================================================= - -func (d *dhw) serialConfigure() { - - hid := &descHID[d.cc.config-1] - - // SAMx51 only supports USB full-speed (FS) operation - hid.rxSerialSize = descHIDSerialRxFSPacketSize - hid.txSerialSize = descHIDSerialTxFSPacketSize - - // Rx and Tx are on same endpoint - d.endpointEnable(descHIDEndpointSerialRx, - false, descHIDConfigAttrSerial) - - // d.endpointConfigureRx(descHIDEndpointSerialRx, - // hid.rxSerialSize, false, d.serialNotify) - // d.endpointConfigureTx(descHIDEndpointSerialTx, - // hid.txSerialSize, false, nil) - - // for i := range hid.rdSerial { - // d.serialReceive(uint8(i)) - // } - - d.timerConfigure(0, descHIDSerialTxSyncUs, d.serialSync) -} - -func (d *dhw) serialReceive(endpoint uint8) { - hid := &descHID[d.cc.config-1] - num := uint16(endpoint) & descEndptAddrNumberMsk - _, _ = hid, num // TODO(ardnew): elaborate stub -} - -func (d *dhw) serialTransmit() { - hid := &descHID[d.cc.config-1] - _ = hid // TODO(ardnew): elaborate stub -} - -func (d *dhw) serialNotify( /* transfer *dhwTransfer */ ) { - // hid := &descHID[d.cc.config-1] - // len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF) - // _ = len // TODO(ardnew): elaborate stub -} - -// serialFlush discards all buffered input (Rx) data. -func (d *dhw) serialFlush() { - hid := &descHID[d.cc.config-1] - _ = hid -} - -func (d *dhw) serialSync() { - -} - -// ============================================================================= -// [HID] Keyboard -// ============================================================================= - -func (d *dhw) keyboard() *Keyboard { return descHID[d.cc.config-1].keyboard } - -func (d *dhw) keyboardConfigure() { - - hid := &descHID[d.cc.config-1] - - // Initialize keyboard - hid.keyboard.configure(d.dcd, hid) - - // SAMx51 only supports USB full-speed (FS) operation - hid.txKeyboardSize = descHIDKeyboardTxFSPacketSize - - d.endpointEnable(descHIDEndpointKeyboard, - false, descHIDConfigAttrKeyboard) - d.endpointEnable(descHIDEndpointMediaKey, - false, descHIDConfigAttrMediaKey) - - // d.endpointConfigureTx(descHIDEndpointKeyboard, - // hid.txKeyboardSize, false, nil) - // d.endpointConfigureTx(descHIDEndpointMediaKey, - // hid.txKeyboardSize, false, nil) -} - -func (d *dhw) keyboardSendKeys(consumer bool) bool { - - hid := &descHID[d.cc.config-1] - - if !consumer { - - hid.tpKeyboard[0] = hid.keyboard.mod - hid.tpKeyboard[1] = 0 - hid.tpKeyboard[2] = hid.keyboard.key[0] - hid.tpKeyboard[3] = hid.keyboard.key[1] - hid.tpKeyboard[4] = hid.keyboard.key[2] - hid.tpKeyboard[5] = hid.keyboard.key[3] - hid.tpKeyboard[6] = hid.keyboard.key[4] - hid.tpKeyboard[7] = hid.keyboard.key[5] - - return d.keyboardWrite(descHIDEndpointKeyboard, hid.tpKeyboard[:]) - - } else { - - // 44444444 44333333 33332222 22222211 11111111 [ word ] - // 98765432 10987654 32109876 54321098 76543210 [ index ] (right-to-left) - - hid.tpKeyboard[1] = uint8((hid.keyboard.con[1] << 2) | ((hid.keyboard.con[0] >> 8) & 0x03)) - hid.tpKeyboard[2] = uint8((hid.keyboard.con[2] << 4) | ((hid.keyboard.con[1] >> 6) & 0x0F)) - hid.tpKeyboard[3] = uint8((hid.keyboard.con[3] << 6) | ((hid.keyboard.con[2] >> 4) & 0x3F)) - hid.tpKeyboard[4] = uint8(hid.keyboard.con[3] >> 2) - hid.tpKeyboard[5] = hid.keyboard.sys[0] - hid.tpKeyboard[6] = hid.keyboard.sys[1] - hid.tpKeyboard[7] = hid.keyboard.sys[2] - - return d.keyboardWrite(descHIDEndpointMediaKey, hid.tpKeyboard[:]) - - } -} - -func (d *dhw) keyboardWrite(endpoint uint8, data []uint8) bool { - - // hid := &descHID[d.cc.config-1] - - // size := uint16(len(data)) - // xfer := &hid.tdKeyboard[hid.txKeyboardHead] - // when := ticks() - // for { - // if 0 == xfer.token&0x80 { - // if 0 != xfer.token&0x68 { - // // TODO: token contains error, how to handle? - // } - // hid.txKeyboardPrev = false - // break - // } - // if hid.txKeyboardPrev { - // return false - // } - // if ticks()-when > descHIDKeyboardTxTimeoutMs { - // // Waited too long, assume host connection dropped - // hid.txKeyboardPrev = true - // return false - // } - // } - // // Without this delay, the order packets are transmitted is seriously screwy. - // udelay(60) - // buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:] - // _ = copy(buff, data) - // d.transferPrepare(xfer, &buff[0], size, 0) - // flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize) - // d.endpointTransmit(endpoint, xfer) - // hid.txKeyboardHead += 1 - // if hid.txKeyboardHead >= descHIDKeyboardTDCount { - // hid.txKeyboardHead = 0 - // } - return true -} - -// ============================================================================= -// [HID] Mouse -// ============================================================================= - -func (d *dhw) mouseConfigure() { - - hid := &descHID[d.cc.config-1] - - // SAMx51 only supports USB full-speed (FS) operation - hid.txMouseSize = descHIDMouseTxFSPacketSize - - d.endpointEnable(descHIDEndpointMouse, - false, descHIDConfigAttrMouse) - - // d.endpointConfigureTx(descHIDEndpointMouse, - // hid.txMouseSize, false, nil) -} - -// ============================================================================= -// [HID] Joystick -// ============================================================================= - -func (d *dhw) joystickConfigure() { - - hid := &descHID[d.cc.config-1] - - // SAMx51 only supports USB full-speed (FS) operation - hid.txJoystickSize = descHIDJoystickTxFSPacketSize - - d.endpointEnable(descHIDEndpointJoystick, - false, descHIDConfigAttrJoystick) - - // d.endpointConfigureTx(descHIDEndpointJoystick, - // hid.txJoystickSize, false, nil) -} diff --git a/src/machine/usb/uart.go b/src/machine/usb/uart.go deleted file mode 100644 index 3dfdc4ed0..000000000 --- a/src/machine/usb/uart.go +++ /dev/null @@ -1,80 +0,0 @@ -package usb - -import ( - "errors" -) - -var ( - ErrUARTInvalidPort = errors.New("invalid USB port") - ErrUARTEmptyBuffer = errors.New("USB receive buffer empty") -) - -// UART represents a virtual serial (UART) device emulation using the USB -// CDC-ACM device class driver. -type UART struct { - // Port is the MCU's native USB core number. If in doubt, leave it - // uninitialized for default (0). - Port int - core *core -} - -type UARTConfig struct { - BusSpeed Speed -} - -func (uart *UART) Configure(config UARTConfig) error { - - c := class{id: classDeviceCDCACM, config: 1} - - // verify we have a free USB port and take ownership of it - var st status - uart.core, st = initCore(uart.Port, config.BusSpeed, c) - if !st.ok() { - return ErrUARTInvalidPort - } - return nil -} - -func (uart *UART) Ready() bool { - return uart.core.dc.uartReady() -} - -// Buffered returns the number of bytes currently stored in the RX buffer. -func (uart *UART) Buffered() int { - for !uart.Ready() { - } - return uart.core.dc.uartAvailable() -} - -// ReadByte reads a single byte from the RX buffer. -// If there is no data in the buffer, returns an error. -func (uart *UART) ReadByte() (byte, error) { - for !uart.Ready() { - } - n, ok := uart.core.dc.uartReadByte() - if !ok { - return 0, ErrUARTEmptyBuffer - } - return n, nil -} - -// Read from the RX buffer. -func (uart *UART) Read(data []byte) (n int, err error) { - for !uart.Ready() { - } - return uart.core.dc.uartRead(data) -} - -// WriteByte writes a single byte of data to the UART interface. -func (uart *UART) WriteByte(c byte) error { - for !uart.Ready() { - } - return uart.core.dc.uartWriteByte(c) -} - -// Write data to the UART. -func (uart *UART) Write(data []byte) (n int, err error) { - for !uart.Ready() { - } - return uart.core.dc.uartWrite(data) -} diff --git a/src/machine/usb/usb-cdc.go b/src/machine/usb/usb-cdc.go new file mode 100644 index 000000000..518501be0 --- /dev/null +++ b/src/machine/usb/usb-cdc.go @@ -0,0 +1,83 @@ +//go:build baremetal && usb.cdc +// +build baremetal,usb.cdc + +package usb + +import ( + "errors" +) + +var ( + ErrCDCInvalidPort = errors.New("invalid port") + ErrCDCEmptyBuffer = errors.New("buffer empty") +) + +// CDC represents a virtual UART serial device emulation using the USB +// CDC-ACM device class driver. +type CDC struct { + // Port is the MCU's native USB core number. If in doubt, leave it + // uninitialized for default (0). + Port int + core *core +} + +type CDCConfig struct { + BusSpeed Speed +} + +func (cdc *CDC) Configure(config CDCConfig) error { + + c := class{id: classDeviceCDC, config: 1} + + // verify we have a free USB port and take ownership of it + var st status + cdc.core, st = initCore(cdc.Port, config.BusSpeed, c) + if !st.ok() { + return ErrCDCInvalidPort + } + return nil +} + +func (cdc *CDC) Ready() bool { + return cdc.core.dc.cdcReady() +} + +// Buffered returns the number of bytes currently stored in the Rx buffer. +func (cdc *CDC) Buffered() int { + for !cdc.Ready() { + } + return cdc.core.dc.cdcAvailable() +} + +// ReadByte reads a single byte from the Rx buffer. +// If there is no data in the buffer, returns an error. +func (cdc *CDC) ReadByte() (byte, error) { + for !cdc.Ready() { + } + n, ok := cdc.core.dc.cdcReadByte() + if !ok { + return 0, ErrCDCEmptyBuffer + } + return n, nil +} + +// Read from the Rx buffer. +func (cdc *CDC) Read(data []byte) (n int, err error) { + for !cdc.Ready() { + } + return cdc.core.dc.cdcRead(data) +} + +// WriteByte writes a single byte of data to the virtual UART interface. +func (cdc *CDC) WriteByte(c byte) error { + for !cdc.Ready() { + } + return cdc.core.dc.cdcWriteByte(c) +} + +// Write data to the virtual UART. +func (cdc *CDC) Write(data []byte) (n int, err error) { + for !cdc.Ready() { + } + return cdc.core.dc.cdcWrite(data) +} diff --git a/src/machine/usb/keyboard.go b/src/machine/usb/usb-hid-keyboard.go similarity index 99% rename from src/machine/usb/keyboard.go rename to src/machine/usb/usb-hid-keyboard.go index a67ae6c8d..952d9b267 100644 --- a/src/machine/usb/keyboard.go +++ b/src/machine/usb/usb-hid-keyboard.go @@ -1,3 +1,6 @@ +//go:build baremetal && usb.hid +// +build baremetal,usb.hid + package usb import "errors" @@ -57,6 +60,10 @@ func (kb *Keyboard) configure(dc *dcd, hc *descHIDClass) { kb.hc = hc } +func (kb *Keyboard) ready() bool { + return kb.dc != nil && kb.hc != nil +} + // Write transmits press-and-release key sequences for each Keycode translated // from the given UTF-8 byte string. Write implements the io.Writer interface // and conforms to all documented conventions for arguments and return values. diff --git a/src/machine/usb/hid.go b/src/machine/usb/usb-hid.go similarity index 87% rename from src/machine/usb/hid.go rename to src/machine/usb/usb-hid.go index fdc5c73c7..da25030a8 100644 --- a/src/machine/usb/hid.go +++ b/src/machine/usb/usb-hid.go @@ -1,3 +1,6 @@ +//go:build baremetal && usb.hid +// +build baremetal,usb.hid + package usb import ( @@ -36,6 +39,10 @@ func (hid *HID) Configure(config HIDConfig) error { return nil } +func (hid *HID) Ready() bool { + return hid.core.dc.keyboard().ready() +} + func (hid *HID) Keyboard() *Keyboard { return hid.core.dc.keyboard() } diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go index 208401337..16c2c7cb5 100644 --- a/src/machine/usb/usb.go +++ b/src/machine/usb/usb.go @@ -120,15 +120,15 @@ type class struct { // Enumerated constants for all supported host/device class configurations. const ( - classDeviceCDCACM = 0 - classDeviceHID = 1 + classDeviceCDC = 0 + classDeviceHID = 1 ) // mode returns the USB core operating mode of the receiver class c. //go:inline func (c class) mode() int { switch c.id { - case classDeviceCDCACM, classDeviceHID: + case classDeviceCDC, classDeviceHID: return modeDevice default: return modeIdle diff --git a/src/runtime/runtime_atsamd51.go b/src/runtime/runtime_atsamd51.go index 7af4ba214..e1bace511 100644 --- a/src/runtime/runtime_atsamd51.go +++ b/src/runtime/runtime_atsamd51.go @@ -7,7 +7,6 @@ import ( "device/arm" "device/sam" "machine" - "machine/usb" "runtime/interrupt" "runtime/volatile" ) @@ -28,11 +27,13 @@ func init() { initSERCOMClocks() initADCClock() - // connect to USB CDC interface - machine.Serial.Configure(usb.UARTConfig{}) - if !machine.USB.Configured() { - machine.USB.Configure(usb.UARTConfig{}) - } + //// connect to USB CDC interface + //machine.Serial.Configure(usb.UARTConfig{}) + //if !machine.USB.Configured() { + // machine.USB.Configure(usb.UARTConfig{}) + //} + + initUSB() } func putchar(c byte) { diff --git a/src/runtime/usb-cdc.go b/src/runtime/usb-cdc.go new file mode 100644 index 000000000..03b42ce59 --- /dev/null +++ b/src/runtime/usb-cdc.go @@ -0,0 +1,14 @@ +//go:build baremetal && usb.cdc +// +build baremetal,usb.cdc + +package runtime + +import ( + "machine" + "machine/usb" +) + +func initUSB() { + // Configure CDC interface. + machine.USB.Configure(usb.CDCConfig{}) +} diff --git a/src/runtime/usb-hid.go b/src/runtime/usb-hid.go new file mode 100644 index 000000000..3f000d70e --- /dev/null +++ b/src/runtime/usb-hid.go @@ -0,0 +1,14 @@ +//go:build baremetal && usb.hid +// +build baremetal,usb.hid + +package runtime + +import ( + "machine" + "machine/usb" +) + +func initUSB() { + // Configure HID interface. + machine.USB.Configure(usb.HIDConfig{}) +}