diff --git a/src/examples/usb/demo/README.md b/src/examples/usb/demo/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/examples/usb/keyboard/keyboard.go b/src/examples/usb/keyboard/keyboard.go new file mode 100644 index 000000000..7e7530067 --- /dev/null +++ b/src/examples/usb/keyboard/keyboard.go @@ -0,0 +1,251 @@ +package main + +import ( + "machine" + "machine/usb" + "time" +) + +var keyboard = machine.HID0.Keyboard() + +func main() { + + println("USB HID keyboard demo") + + for { + time.Sleep(5 * time.Second) + + // Open a new text editor + keyboard.Down(usb.KeyModifierAlt) + keyboard.Press(usb.KeySpace) + keyboard.Up(usb.KeyModifierAlt) + time.Sleep(2 * time.Second) + keyboard.Write([]byte("kate")) + time.Sleep(time.Second) + keyboard.Press(usb.KeyEnter) + + time.Sleep(5 * time.Second) + + // Use the io.Writer interface + keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n")) + time.Sleep(2 * time.Second) + + // Or manually specify keycodes and Unicode codepoints + testKeys([]Key{ + // Print alphabet out-of-order + {Press: usb.KeyX}, + {Press: usb.KeyY}, + {Press: usb.KeyZ}, + {Press: usb.KeyG}, + {Press: usb.KeyH}, + {Press: usb.KeyI}, + {Press: usb.KeyJ}, + {Press: usb.KeyK}, + {Press: usb.KeyL}, + {Press: usb.KeyM}, + {Press: usb.KeyN}, + {Press: usb.KeyO}, + {Press: usb.KeyP}, + {Press: usb.KeyQ}, + {Press: usb.KeyR}, + {Press: usb.KeyS}, + {Press: usb.KeyT}, + {Press: usb.KeyA}, + {Press: usb.KeyB}, + {Press: usb.KeyC}, + {Press: usb.KeyD}, + {Press: usb.KeyE}, + {Press: usb.KeyF}, + {Press: usb.KeyU}, + {Press: usb.KeyV}, + {Press: usb.KeyW}, + // Pause 1 second + {Time: time.Second}, + // Move cursor left x3 + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + // Pause 1 second + {Time: time.Second}, + // Highlight 6 symbols to the left + {Down: usb.KeyModifierShift}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Up: usb.KeyModifierShift}, + // Pause 1 second + {Time: time.Second}, + // Use Ctrl-X to cut + {Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Move to beginning of line + {Press: usb.KeyHome}, + // Pause 1 second + {Time: time.Second}, + // Use Ctrl-V to paste + {Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Highlight 3 symbols to the right + {Down: usb.KeyModifierShift}, + {Press: usb.KeyRight}, + {Press: usb.KeyRight}, + {Press: usb.KeyRight}, + {Up: usb.KeyModifierShift}, + // Use Ctrl-X to cut + {Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Move to end of line + {Press: usb.KeyEnd}, + // Pause 1 second + {Time: time.Second}, + // Use Ctrl-V to paste + {Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Newline + {Press: usb.KeyEnter}, + {Press: usb.KeyEnter}, + }, 150*time.Millisecond) + + // Highlight all text and delete + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyA) + keyboard.Up(usb.KeyModifierCtrl) + time.Sleep(time.Second) + keyboard.Press(usb.KeyDelete) + time.Sleep(time.Second) + + // Close window + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyQ) + keyboard.Up(usb.KeyModifierCtrl) + time.Sleep(2 * time.Second) + // Confirm discard file + keyboard.Down(usb.KeyModifierAlt) + keyboard.Press(usb.KeyD) + keyboard.Up(usb.KeyModifierAlt) + + time.Sleep(5 * time.Second) + + // Open a new terminal + keyboard.Down(usb.KeyModifierAlt) + keyboard.Press(usb.KeySpace) + keyboard.Up(usb.KeyModifierAlt) + time.Sleep(2 * time.Second) + keyboard.Write([]byte("konsole")) + keyboard.Press(usb.KeyEnter) + + time.Sleep(5 * time.Second) + + // Open serial connection + keyboard.Write([]byte("screen /dev/ttyACM0 115200")) + time.Sleep(2 * time.Second) + keyboard.Press(usb.KeyEnter) + time.Sleep(4 * time.Second) + + // Write to UART + keyboard.Write([]byte("hello!")) + time.Sleep(time.Second) + keyboard.Press(usb.KeyEnter) + time.Sleep(2 * time.Second) + keyboard.Write([]byte("NO U")) + time.Sleep(time.Second) + keyboard.Press(usb.KeyEnter) + time.Sleep(2 * time.Second) + + // Close serial connection + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyX) + keyboard.Up(usb.KeyModifierCtrl) + time.Sleep(time.Second) + keyboard.Press(usb.KeyBackslash) + time.Sleep(time.Second) + keyboard.Press(usb.KeyY) + keyboard.Press(usb.KeyEnter) + time.Sleep(2 * time.Second) + + // Close terminal + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyD) + keyboard.Up(usb.KeyModifierCtrl) + + time.Sleep(25 * time.Second) + } +} + +type Key struct { + Press usb.Keycode + Down usb.Keycode + Up usb.Keycode + Time time.Duration +} + +func testKeys(key []Key, delay time.Duration) { + for _, k := range key { + if 0 != k.Down { + keyboard.Down(k.Down) + } + if 0 != k.Press { + keyboard.Press(k.Press) + } + if 0 != k.Up { + keyboard.Up(k.Up) + } + if 0 != k.Time { + time.Sleep(k.Time) + } else { + time.Sleep(delay) + } + } +} + +func testInternationalLayout() { + // International keyboard layouts also supported + keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Numbers: 0123456789\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬­®¯°±\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Cedilla: Çç\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Ring Above: Åå\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("AE: Ææ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Thorn: Þþ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Sharp S: ß\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("O-Stroke: Øø\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Eth: Ðð\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Euro: €\n")) +} diff --git a/src/examples/usb/serial/echo.go b/src/examples/usb/serial/echo.go new file mode 100644 index 000000000..b4a61fea7 --- /dev/null +++ b/src/examples/usb/serial/echo.go @@ -0,0 +1,37 @@ +// This is a echo console running on the device UART. +// Connect using default baudrate for this hardware, 8-N-1 with your terminal program. +package main + +import ( + "machine" + "time" +) + +func main() { + uart := machine.UART0 + uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n")) + + input := make([]byte, 4096) + i := 0 + for { + if uart.Buffered() > 0 { + data, _ := uart.ReadByte() + + switch data { + case 13: + // return key + uart.Write([]byte("\r\n")) + uart.Write([]byte("You typed: ")) + uart.Write(input[:i]) + uart.Write([]byte("\r\n")) + i = 0 + default: + // just echo the character + uart.WriteByte(data) + input[i] = data + i++ + } + } + time.Sleep(10 * time.Millisecond) + } +} diff --git a/src/machine/board_teensy40.go b/src/machine/board_teensy40.go index bf55c8b19..afa0dbc44 100644 --- a/src/machine/board_teensy40.go +++ b/src/machine/board_teensy40.go @@ -5,6 +5,7 @@ package machine import ( "device/nxp" + "machine/usb" "runtime/interrupt" ) @@ -109,11 +110,9 @@ func init() { // | USB | // #=====================================================# var ( - // USBCDC is a legacy class being retained here as temporary wrapper. - // See godoc comments on type USBCDC struct definition for details. - UART0 = USBCDC{ - port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0/4.1) - } + // UART0 = usb.UART{Port: 0} + HID0 = usb.HID{Port: 0} + UART0 = &UART1 ) // #=====================================================# diff --git a/src/machine/machine_common_usb.go b/src/machine/machine_common_usb.go deleted file mode 100644 index 22857351b..000000000 --- a/src/machine/machine_common_usb.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build mimxrt1062 - -package machine - -import ( - "machine/usb" -) - -// USBCDC is the legacy TinyGo type used to implement USB CDC-ACM device class -// emulation for serial UART communication. It is retained here as a temporary -// wrapper for type usb.UART from new package "machine/usb", which is still in -// active development. Once that package has stabilized a bit, this type should -// be removed and usb.UART should be used directly instead. -type USBCDC struct { - port uint8 - uart usb.UART -} - -// Configure the embedded usb.UART with our receiver's settings and the given -// UART configuration. This provides compatibility with machine.UART. -func (cdc *USBCDC) Configure(config UARTConfig) { - cdc.uart.Configure(usb.UARTConfig{BaudRate: config.BaudRate}) -} - -// Buffered returns the number of bytes currently stored in the RX buffer. -func (cdc USBCDC) Buffered() int { - return cdc.uart.Buffered() -} - -// ReadByte reads a single byte from the RX buffer. -// If there is no data in the buffer, returns an error. -func (cdc USBCDC) ReadByte() (byte, error) { - return cdc.uart.ReadByte() -} - -// Read from the RX buffer. -func (cdc USBCDC) Read(data []byte) (n int, err error) { - return cdc.uart.Read(data) -} - -// WriteByte writes a single byte of data to the UART interface. -func (cdc USBCDC) WriteByte(c byte) error { - return cdc.uart.WriteByte(c) -} - -// Write data to the UART. -func (cdc USBCDC) Write(data []byte) (n int, err error) { - return cdc.uart.Write(data) -} diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go index ba4393439..d4cbdfa73 100644 --- a/src/machine/usb/dcd.go +++ b/src/machine/usb/dcd.go @@ -12,7 +12,7 @@ func init() { // 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 // + ... +const dcdCount = descCDCACMCount + descHIDCount // dcdInstance provides statically-allocated instances of each USB device // controller configured on this platform. @@ -134,7 +134,6 @@ func (d *dcd) event(ev dcdEvent) { case dcdStageSetup: case dcdStageData: case dcdStageStatus: - d.controlStatus() case dcdStageStall: d.controlStall() } @@ -180,7 +179,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // SET ADDRESS (0x05): case descRequestStandardSetAddress: - d.controlDeviceAddress(sup.wValue) + d.setDeviceAddress(sup.wValue) d.controlReceive(uintptr(0), 0, false) return dcdStageSetup @@ -200,6 +199,14 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { 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 } @@ -225,8 +232,23 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET DESCRIPTOR (0x06): case descRequestStandardGetDescriptor: - d.controlDescriptor(sup) - return dcdStageSetup + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlDescriptorCDCACM(sup) + return dcdStageSetup + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } // GET CONFIGURATION (0x08): case descRequestStandardGetConfiguration: @@ -247,8 +269,38 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET DESCRIPTOR (0x06): case descRequestStandardGetDescriptor: - d.controlDescriptor(sup) - return dcdStageSetup + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlDescriptorCDCACM(sup) + return dcdStageSetup + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } + + // GET DESCRIPTOR (0x06): + case descHIDRequestGetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } default: // Unhandled request @@ -262,11 +314,15 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CLEAR FEATURE (0x01): case descRequestStandardClearFeature: - // TODO + d.endpointClearFeature(uint8(sup.wIndex)) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup // SET FEATURE (0x03): case descRequestStandardSetFeature: - // TODO + d.endpointSetFeature(uint8(sup.wIndex)) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup default: // Unhandled request @@ -322,6 +378,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { d.controlReceive( uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])), descCDCACMCodingSize, true) + // CDC Line Coding packet receipt handling occurs in method + // controlComplete(). return dcdStageSetup } @@ -338,11 +396,13 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CDC-ACM (single) case classDeviceCDCACM: - // Determine interface destination of the notification + // Determine interface destination of the request switch sup.wIndex { // Control/status interface: case descCDCACMInterfaceCtrl: + // DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02) + d.uartSetLineState(0 != sup.wValue&0x01, 0 != sup.wValue&0x02) d.controlReceive(uintptr(0), 0, false) return dcdStageSetup @@ -369,6 +429,79 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // 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 <= descHIDCxCount { + d.setup = sup + 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 dcdStageSetup + } + + 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 dcdStageSetup + + 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.controlReply[0] = 0 + d.controlReply[1] = 0 + d.controlTransmit( + uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) + return dcdStageSetup + + default: + // Unhandled device class + } + default: // Unhandled request } @@ -419,15 +552,14 @@ func (d *dcd) controlComplete(status uint32) { case classDeviceCDCACM: acm := &descCDCACM[d.cc.config-1] - // Determine interface destination of the notification + // Determine interface destination of the request switch d.setup.wIndex { - // Control/status interface: + // 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.controlLineCoding(descCDCACMLineCoding{ + d.uartSetLineCoding(descCDCACMLineCoding{ baud: packU32(acm.cx[:]), stopBits: acm.cx[4], parity: acm.cx[5], @@ -442,6 +574,60 @@ func (d *dcd) controlComplete(status uint32) { // 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 } @@ -455,129 +641,295 @@ func (d *dcd) controlComplete(status uint32) { } } -func (d *dcd) controlDescriptor(sup dcdSetup) { +func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) { - // Respond based on our device class configuration - switch d.cc.id { + acm := &descCDCACM[d.cc.config-1] + dxn := uint8(0) - // CDC-ACM (single) - case classDeviceCDCACM: - acm := &descCDCACM[d.cc.config-1] - dxn := uint8(0) + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { - // Determine the type of descriptor being requested - switch sup.wValue >> 8 { + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(acm.dx[:], acm.device[:dxn]) - // 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]) - // 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) { - // String descriptor - case descTypeString: - if 0 == len(acm.locale) { - break // No string descriptors defined! + // 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 } - var sd []uint8 - if 0 == uint8(sup.wValue) { + sd = acm.locale[code].descriptor[sup.wValue&0xFF][:] - // 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 { - } 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) { - // 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 - 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 + // 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 + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(acm.dx[:], sd[:dxn]) } - if dxn > 0 { - if dxn > uint8(sup.wLength) { - dxn = uint8(sup.wLength) + // 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 } - flushCache( - uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) - d.controlTransmit( - uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + 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 device class + // 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.go b/src/machine/usb/desc.go index 192e93d47..ae5c83d22 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -304,152 +304,75 @@ const ( descCDCUARTStateOverrun = 0x40 // UART state OVERRUN ) -// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., -// configuration index 1. -var descCDCACM0Device = [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) - descCDCTypeComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // 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 -} +// USB HID constants defined per specification +const ( + // HID class + descHIDType = 0x03 -// descCDCACM0Qualif holds the default device qualification descriptor for -// CDC-ACM[0], i.e., configuration index 1. -var descCDCACM0Qualif = [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) - descCDCTypeComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // 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 -} + // 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 - descLengthInterface + // communication/control interface - descCDCFuncLengthHeader + // CDC header - descCDCFuncLengthCallManagement + // CDC call management - descCDCFuncLengthAbstractControl + // CDC abstract control - descCDCFuncLengthUnion + // CDC union - descLengthEndpoint + // communication/control input endpoint - descLengthInterface + // data interface - descLengthEndpoint + // data input endpoint - descLengthEndpoint) // data output endpoint + 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 Endpoint Descriptor + descLengthEndpoint + + 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 ) -// descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0], -// i.e., configuration index 1. -var descCDCACM0Config = [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 - descCDCACMMaxPower, // 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 - descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines 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) - 16, // 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 -} - 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 @@ -479,28 +402,6 @@ const ( // for each string) seems a good compromise. ) -// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., -// configuration index 1. -var descCDCACM0String = [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 */ }, - }, - }, -} - // descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. const descCDCACMCodingSize = 7 @@ -514,23 +415,19 @@ type descCDCACMLineCoding struct { // Common configuration constants for the USB CDC-ACM (single) device class. const ( - // String descriptor languages available - descCDCACMLanguageCount = 1 + descCDCACMLanguageCount = 1 // String descriptor languages available - // Interfaces for all CDC-ACM configurations. - descCDCACMInterfaceCount = 2 - descCDCACMInterfaceCtrl = 0 - descCDCACMInterfaceData = 1 + descCDCACMInterfaceCount = 2 // Interfaces for all CDC-ACM configurations. + descCDCACMEndpointCount = 4 // Endpoints for all CDC-ACM configurations. - // Endpoints for all CDC-ACM configurations. - descCDCACMEndpointCount = 4 - descCDCACMEndpointStatus = 2 // Communication/control interrupt input - descCDCACMEndpointDataRx = 3 // Bulk data output - descCDCACMEndpointDataTx = 4 // Bulk data input - - // Endpoint configuration attributes for all CDC-ACM configurations. + descCDCACMInterfaceCtrl = 0 // CDC-ACM Control Interface + descCDCACMEndpointStatus = 2 // CDC-ACM Interrupt IN Endpoint descCDCACMConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt + + descCDCACMInterfaceData = 1 // CDC-ACM Data Interface + descCDCACMEndpointDataRx = 3 // CDC-ACM Bulk Data OUT (Rx) Endpoint descCDCACMConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused + descCDCACMEndpointDataTx = 4 // CDC-ACM Bulk Data IN (Tx) Endpoint descCDCACMConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk ) @@ -547,14 +444,600 @@ type descCDCACMClass struct { // descCDCACM holds statically-allocated instances for each of the CDC-ACM // (single) device class configurations, ordered by index (offset by -1). -var descCDCACM = [descCDCACMCount]descCDCACMClass{ +var descCDCACM = [dcdCount]descCDCACMClass{ { // CDC-ACM (single) class configuration index 1 descCDCACMClassData: &descCDCACMData[0], - locale: &descCDCACM0String, - device: &descCDCACM0Device, - qualif: &descCDCACM0Qualif, - config: &descCDCACM0Config, + 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 + descCDCACMMaxPower, // 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 + descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines 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. + + 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 + descHIDMaxPower, // 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_mimxrt1062.go b/src/machine/usb/desc_mimxrt1062.go index 4fa7b5527..fd8e3dbd9 100644 --- a/src/machine/usb/desc_mimxrt1062.go +++ b/src/machine/usb/desc_mimxrt1062.go @@ -5,6 +5,14 @@ package usb // descCPUFrequencyHz defines the target CPU frequency (Hz). const descCPUFrequencyHz = 600000000 +// descCDCACMCount defines the number of USB cores that may be configured as +// CDC-ACM (single) devices. +const descCDCACMCount = 0 + +// descHIDCount defines the number of USB cores that may be configured as a +// composite (keyboard + mouse + joystick) human interface device (HID). +const descHIDCount = 1 + // General USB device identification constants. const ( descCommonVendorID = 0x16C0 @@ -12,39 +20,166 @@ const ( descCommonReleaseID = 0x0101 // BCD (1.1) descCommonLanguage = descLanguageEnglish - descCommonManufacturer = "NXP Semiconductors" - descCommonProduct = "TinyGo USB" - descCommonSerialNumber = "1" + descCommonManufacturer = "TinyGo" + descCommonProduct = "USB" + descCommonSerialNumber = "00000" ) // Constants for USB CDC-ACM device classes. const ( - // descCDCACMCount defines the number of USB cores that will be configured as - // CDC-ACM (single) devices. - descCDCACMCount = 1 + descCDCACMMaxPower = 50 // 100 mA + + // CDC-ACM Control Buffers descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1) - descCDCACMRDCount = 2 * descCDCACMEndpointCount - descCDCACMTDCount = descCDCACMEndpointCount - descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount - descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount descCDCACMCxCount = 8 - descCDCACMMaxPower = 50 // 100 mA + // CDC-ACM Data Buffers + + descCDCACMRDCount = 2 * descCDCACMEndpointCount + descCDCACMRxSize = descCDCACMDataRxPacketSize + descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount + + descCDCACMTDCount = descCDCACMEndpointCount + descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize + descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount descCDCACMTxTimeoutMs = 120 // millisec descCDCACMTxSyncUs = 75 // microsec - descCDCACMStatusPacketSize = 16 - descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed - descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed - descCDCACMRxSize = descCDCACMDataRxPacketSize - descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize + // Default CDC-ACM Endpoint Configurations (High-Speed) - descCDCACMDataRxFSPacketSize = 64 // full-speed - descCDCACMDataTxFSPacketSize = 64 // full-speed - descCDCACMDataRxHSPacketSize = 512 // high-speed - descCDCACMDataTxHSPacketSize = 512 // high-speed + descCDCACMStatusInterval = descCDCACMStatusHSInterval // Status + descCDCACMStatusPacketSize = descCDCACMStatusHSPacketSize // + + descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // Data Rx + + descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // Data Tx + + // CDC-ACM Endpoint Configurations for Full-Speed Device + + descCDCACMStatusFSInterval = 5 // Status + descCDCACMStatusFSPacketSize = 16 // (full-speed) + + descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed) + + descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed) + + // CDC-ACM Endpoint Configurations for High-Speed Device + + descCDCACMStatusHSInterval = 5 // Status + descCDCACMStatusHSPacketSize = 16 // (high-speed) + + descCDCACMDataRxHSPacketSize = 512 // Data Rx (high-speed) + + descCDCACMDataTxHSPacketSize = 512 // Data Tx (high-speed) +) + +// Constants for USB HID (keyboard, mouse, joystick) device classes. +const ( + descHIDMaxPower = 50 // 100 mA + + // HID Control Buffers + + descHIDQHCount = 2 * (descHIDEndpointCount + 1) + descHIDCxCount = 8 + + // HID Serial Buffers + + descHIDSerialRDCount = 8 + descHIDSerialRxSize = descHIDSerialRxPacketSize + descHIDSerialRxCount = descHIDSerialRxSize * descHIDSerialRDCount + + descHIDSerialTDCount = 12 + descHIDSerialTxSize = descHIDSerialTxPacketSize + descHIDSerialTxCount = descHIDSerialTxSize * descHIDSerialTDCount + + descHIDSerialTxTimeoutMs = 50 // millisec + descHIDSerialTxSyncUs = 75 // microsec + + // HID Keyboard Buffers + + descHIDKeyboardTDCount = 12 + descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize + descHIDKeyboardTxCount = descHIDKeyboardTxSize * descHIDKeyboardTDCount + + descHIDKeyboardTxTimeoutMs = 50 // millisec + + // HID Mouse Buffers + + descHIDMouseTDCount = 4 + descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize + descHIDMouseTxCount = descHIDMouseTxSize * descHIDMouseTDCount + + descHIDMouseTxTimeoutMs = 30 // millisec + + // HID Joystick Buffers + + descHIDJoystickTDCount = 4 + descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize + descHIDJoystickTxCount = descHIDJoystickTxSize * descHIDJoystickTDCount + + descHIDJoystickTxTimeoutMs = 30 // millisec + + // Default HID Endpoint Configurations (High-Speed) + + descHIDSerialRxInterval = descHIDSerialRxHSInterval // Serial Rx + descHIDSerialRxPacketSize = descHIDSerialRxHSPacketSize // + + descHIDSerialTxInterval = descHIDSerialTxHSInterval // Serial Tx + descHIDSerialTxPacketSize = descHIDSerialTxHSPacketSize // + + descHIDKeyboardTxInterval = descHIDKeyboardTxHSInterval // Keyboard + descHIDKeyboardTxPacketSize = descHIDKeyboardTxHSPacketSize // + + descHIDMediaKeyTxInterval = descHIDMediaKeyTxHSInterval // Keyboard Media Keys + descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxHSPacketSize // + + descHIDMouseTxInterval = descHIDMouseTxHSInterval // Mouse + descHIDMouseTxPacketSize = descHIDMouseTxHSPacketSize // + + descHIDJoystickTxInterval = descHIDJoystickTxHSInterval // Joystick + descHIDJoystickTxPacketSize = descHIDJoystickTxHSPacketSize // + + // 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 + + descHIDSerialRxHSInterval = 2 // Serial + descHIDSerialRxHSPacketSize = 32 // (high-speed) + + descHIDSerialTxHSInterval = 1 // Serial Tx + descHIDSerialTxHSPacketSize = 64 // (high-speed) + + descHIDKeyboardTxHSInterval = 1 // Keyboard + descHIDKeyboardTxHSPacketSize = 8 // (high-speed) + + descHIDMediaKeyTxHSInterval = 4 // Keyboard Media Keys + descHIDMediaKeyTxHSPacketSize = 8 // (high-speed) + + descHIDMouseTxHSInterval = 1 // Mouse + descHIDMouseTxHSPacketSize = 8 // (high-speed) + + descHIDJoystickTxHSInterval = 2 // Joystick + descHIDJoystickTxHSPacketSize = 12 // (high-speed) ) // descCDCACM0QH is an array of endpoint queue heads, which is where all @@ -71,18 +206,28 @@ const ( //go:align 4096 var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint -// descCDCACM0CD is the transfer descriptor for data messages transmitted or -// received on the status/control endpoint 0 for the default CDC-ACM (single) -// device class configuration (index 1). +// descCDCACM0CD is the transfer descriptor for messages transmitted or received +// on the status/control endpoint 0 for the default CDC-ACM (single) device +// class configuration (index 1). //go:align 32 var descCDCACM0CD dhwTransfer +// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of +// the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Cx [descCDCACMCxCount]uint8 + // descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages // transmitted or received on the status/control endpoint 0 for the default // CDC-ACM (single) device class configuration (index 1). //go:align 32 var descCDCACM0AD dhwTransfer +// 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 + // descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers, // which describe to the device controller the location and quantity of data // being received for a given transfer, for the default CDC-ACM (single) device @@ -90,6 +235,11 @@ var descCDCACM0AD dhwTransfer //go:align 32 var descCDCACM0RD [descCDCACMRDCount]dhwTransfer +// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Rx [descCDCACMRxCount]uint8 + // descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers, // which describe to the device controller the location and quantity of data // being transmitted for a given transfer, for the default CDC-ACM (single) @@ -97,36 +247,16 @@ var descCDCACM0RD [descCDCACMRDCount]dhwTransfer //go:align 32 var descCDCACM0TD [descCDCACMTDCount]dhwTransfer -// descCDCACM0LineCoding holds the emulated UART line coding for the default -// CDC-ACM (single) device class configuration (index 1). -// i.e., configuration index 1. -//go:align 32 -var descCDCACM0LC descCDCACMLineCoding - -// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of -// the default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Cx [descCDCACMCxCount]uint8 - -// descCDCACM0Rx is the receive (Rx) buffer of data endpoints for the default -// CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Rx [descCDCACMRxCount]uint8 - -// descCDCACM0Tx is the transmit (Tx) buffer of data endpoints for the default -// CDC-ACM (single) device class configuration (index 1). +// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). //go:align 32 var descCDCACM0Tx [descCDCACMTxCount]uint8 -// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data for the default -// CDC-ACM (single) device class configuration (index 1). -var descCDCACM0Dx [descCDCACMConfigSize]uint8 - var descCDCACM0RDNum [descCDCACMRDCount]uint16 var descCDCACM0RDIdx [descCDCACMRDCount]uint16 -var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 +var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16 -// descCDCACM holds the buffers and control states for all of the CDC-ACM +// descCDCACMClassData holds the buffers and control states for all CDC-ACM // (single) device class configurations, ordered by index (offset by -1), for // iMXRT1062 targets only. // @@ -134,21 +264,30 @@ var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 // 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 an additional hardware abstraction layer. +// Thus, this type functions as a hardware abstraction layer (HAL). type descCDCACMClassData struct { + + // CDC-ACM Control Buffers + qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads - cd *dhwTransfer // control endpoint 0 Rx/Tx data transfer descriptor - ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor - rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors - td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors - + cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer - rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer - tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer - dx *[descCDCACMConfigSize]uint8 // descriptor data Tx (IN) transfer buffer + ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer - cxSize uint16 + // CDC-ACM Data Buffers + + rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors + rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer + td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors + tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer + + rxCount *[descCDCACMRDCount]uint16 + rxIndex *[descCDCACMRDCount]uint16 + rxQueue *[(descCDCACMRDCount + 1)]uint16 + + sxSize uint16 rxSize uint16 txSize uint16 @@ -159,40 +298,307 @@ type descCDCACMClassData struct { rxHead uint8 rxTail uint8 rxFree uint16 - - rxCount *[descCDCACMRDCount]uint16 - rxIndex *[descCDCACMRDCount]uint16 - rxQueue *[descCDCACMRDCount + 1]uint16 - - _ [2]uint8 } // descCDCACMData holds statically-allocated instances for each of the target- // specific (iMXRT1062) 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. -//go:align 32 -var descCDCACMData = [descCDCACMCount]descCDCACMClassData{ +//go:align 64 +var descCDCACMData = [dcdCount]descCDCACMClassData{ + + { // -- CDC-ACM (single) Class Configuration Index 1 -- + + // CDC-ACM Control Buffers - { // CDC-ACM (single) class configuration index 1 data qh: &descCDCACM0QH, cd: &descCDCACM0CD, - ad: &descCDCACM0AD, - rd: &descCDCACM0RD, - td: &descCDCACM0TD, - cx: &descCDCACM0Cx, - rx: &descCDCACM0Rx, - tx: &descCDCACM0Tx, + ad: &descCDCACM0AD, dx: &descCDCACM0Dx, - cxSize: descCDCACMStatusPacketSize, - rxSize: descCDCACMDataRxPacketSize, - txSize: descCDCACMDataTxPacketSize, + // CDC-ACM Data Buffers + + rd: &descCDCACM0RD, + rx: &descCDCACM0Rx, + td: &descCDCACM0TD, + tx: &descCDCACM0Tx, rxCount: &descCDCACM0RDNum, rxIndex: &descCDCACM0RDIdx, rxQueue: &descCDCACM0RDQue, + + sxSize: descCDCACMStatusPacketSize, + rxSize: descCDCACMDataRxPacketSize, + txSize: descCDCACMDataTxPacketSize, + }, +} + +// descHID0QH is an array of endpoint queue heads, which is where all transfers +// for a given endpoint are managed, for the default HID device class +// configuration (index 1). +// +// From the iMXRT1062 Reference Manual: +// +// Software must ensure that no interface data structure reachable +// by the Device Controller spans a 4K-page boundary. +// +// The [queue head] is a 48-byte data structure, but must be aligned on +// 64-byte boundaries. +// +// Endpoint queue heads are arranged in an array in a continuous area of +// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered +// device queue heads in the list support receive endpoints (OUT/SETUP) and +// the odd-numbered queue heads in the list are used for transmit endpoints +// (IN/INTERRUPT). The device controller will index into this array based upon +// the endpoint number received from the USB bus. All information necessary to +// respond to transactions for all primed transfers is contained in this list +// so the Device Controller can readily respond to incoming requests without +// having to traverse a linked list. +//go:align 4096 +var descHID0QH [descHIDQHCount]dhwEndpoint + +// descHID0CD is the transfer descriptor for messages transmitted or received on +// the status/control endpoint 0 for the default HID device class configuration +// (index 1). +//go:align 32 +var descHID0CD dhwTransfer + +// descHID0Cx is the buffer for control/status data received on endpoint 0 of +// the default HID device class configuration (index 1). +//go:align 32 +var descHID0Cx [descHIDCxCount]uint8 + +// descHID0AD is the transfer descriptor for ackowledgement (ACK) messages +// transmitted or received on the status/control endpoint 0 for the default HID +// device class configuration (index 1). +//go:align 32 +var descHID0AD dhwTransfer + +// 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 + +// descHID0SerialRD is an array of transfer descriptors for serial Rx (OUT) +// transfers, which describe to the device controller the location and quantity +// of data being received for a given transfer, for the default HID device class +// configuration (index 1). +//go:align 32 +var descHID0SerialRD [descHIDSerialRDCount]dhwTransfer + +// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +var descHID0SerialRx [descHIDSerialRxCount]uint8 + +// descHID0SerialTD is an array of transfer descriptors for serial Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0SerialTD [descHIDSerialTDCount]dhwTransfer + +// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +var descHID0SerialTx [descHIDSerialTxCount]uint8 + +var descHID0SerialRDIdx [descHIDSerialRDCount]uint16 +var descHID0SerialRDQue [(descHIDSerialRDCount + 1)]uint16 + +// descHID0KeyboardTD is an array of transfer descriptors for keyboard Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0KeyboardTD [descHIDKeyboardTDCount]dhwTransfer + +// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the +// default HID device class configuration (index 1). +//go:align 32 +var descHID0KeyboardTx [descHIDKeyboardTxCount]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 + +//go:align 32 +var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8 + +//go:align 32 +var descHID0KeyboardTxCon [hidKeyboardConCount]uint16 + +//go:align 32 +var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8 + +// descHID0MouseTD is an array of transfer descriptors for mouse Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0MouseTD [descHIDMouseTDCount]dhwTransfer + +// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +var descHID0MouseTx [descHIDMouseTxCount]uint8 + +// descHID0JoystickTD is an array of transfer descriptors for joystick Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0JoystickTD [descHIDJoystickTDCount]dhwTransfer + +// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the +// default HID device class configuration (index 1). +//go:align 32 +var descHID0JoystickTx [descHIDJoystickTxCount]uint8 + +// descHID0Keyboard is the Keyboard instance with which the user may interact +// when using the default HID device class configuration (index 1). +//go:align 64 +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 iMXRT1062 +// 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 + + qh *[descHIDQHCount]dhwEndpoint // endpoint queue heads + + cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor + cx *[descHIDCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer + ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer + + // HID Serial Buffers + + rdSerial *[descHIDSerialRDCount]dhwTransfer // interrupt endpoint serial Rx (OUT) transfer descriptors + rxSerial *[descHIDSerialRxCount]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer + tdSerial *[descHIDSerialTDCount]dhwTransfer // interrupt endpoint serial Tx (IN) transfer descriptors + txSerial *[descHIDSerialTxCount]uint8 // interrupt endpoint serial Tx (IN) transfer buffer + + rxSerialIndex *[descHIDSerialRDCount]uint16 + rxSerialQueue *[(descHIDSerialRDCount + 1)]uint16 + + rxSerialSize uint16 + txSerialSize uint16 + + txSerialHead uint8 + txSerialFree uint16 + txSerialPrev bool + + rxSerialHead uint8 + rxSerialTail uint8 + rxSerialFree uint16 + + // HID Keyboard Buffers + + tdKeyboard *[descHIDKeyboardTDCount]dhwTransfer // interrupt endpoint keyboard Tx (IN) transfer descriptors + txKeyboard *[descHIDKeyboardTxCount]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer + tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer + + txKeyboardSize uint16 + + txKeyboardHead uint8 + txKeyboardPrev bool + + // HID Mouse Buffers + + tdMouse *[descHIDMouseTDCount]dhwTransfer // interrupt endpoint mouse Tx (IN) transfer descriptors + txMouse *[descHIDMouseTxCount]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer + + txMouseSize uint16 + + txMouseHead uint8 + txMousePrev bool + + // HID Joystick Buffers + + tdJoystick *[descHIDJoystickTDCount]dhwTransfer // interrupt endpoint joystick Tx (IN) transfer descriptors + txJoystick *[descHIDJoystickTxCount]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer + + txJoystickSize uint16 + + txJoystickHead uint8 + txJoystickPrev bool + + // HID Device Instances + + keyboard *Keyboard +} + +// descHIDData holds statically-allocated instances for each of the target- +// specific (iMXRT1062) 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. +//go:align 64 +var descHIDData = [dcdCount]descHIDClassData{ + + { // -- HID Class Configuration Index 1 -- + + // HID Control Buffers + + qh: &descHID0QH, + + cd: &descHID0CD, + cx: &descHID0Cx, + ad: &descHID0AD, + dx: &descHID0Dx, + + // HID Serial Buffers + + rdSerial: &descHID0SerialRD, + rxSerial: &descHID0SerialRx, + tdSerial: &descHID0SerialTD, + txSerial: &descHID0SerialTx, + + rxSerialIndex: &descHID0SerialRDIdx, + rxSerialQueue: &descHID0SerialRDQue, + + rxSerialSize: descHIDSerialRxPacketSize, + txSerialSize: descHIDSerialTxPacketSize, + + // HID Keyboard Buffers + + tdKeyboard: &descHID0KeyboardTD, + txKeyboard: &descHID0KeyboardTx, + tpKeyboard: &descHID0KeyboardTp, + + txKeyboardSize: descHIDKeyboardTxPacketSize, + + // HID Mouse Buffers + + tdMouse: &descHID0MouseTD, + txMouse: &descHID0MouseTx, + + txMouseSize: descHIDMouseTxPacketSize, + + // HID Joystick Buffers + + tdJoystick: &descHID0JoystickTD, + txJoystick: &descHID0JoystickTx, + + txJoystickSize: descHIDJoystickTxPacketSize, + + // HID Device Instances + + keyboard: &descHID0Keyboard, }, } diff --git a/src/machine/usb/dhw_mimxrt1062.go b/src/machine/usb/dhw_mimxrt1062.go index e2d95da40..a2700c9a7 100644 --- a/src/machine/usb/dhw_mimxrt1062.go +++ b/src/machine/usb/dhw_mimxrt1062.go @@ -40,6 +40,8 @@ type dhw struct { sofUsage uint8 } +func runBootloader() { arm.Asm(`bkpt #251`) } + // cycleCount uses the ARM debug cycle counter available on iMXRT1062 (enabled // in runtime_mimxrt1062_time.go) to return the number of CPU cycles since boot. //go:inline @@ -184,7 +186,9 @@ func (d *dhw) enableSOF(enable bool, iface uint8) { d.bus.USBINTR.SetBits(nxp.USB_USBINTR_SRE) } arm.EnableInterrupts(ivm) + d.timerReboot = 80 } else { + d.timerReboot = 0 d.sofUsage &^= 1 << iface if 0 == d.sofUsage { d.bus.USBINTR.ClearBits(nxp.USB_USBINTR_SRE) @@ -354,33 +358,23 @@ func (d *dhw) interrupt() { d.timerReboot -= 1 if 0 == d.timerReboot { d.enableSOF(false, descCDCACMInterfaceCount) + runBootloader() } } } } -func (d *dhw) controlBusSpeed() uint8 { return d.busSpeed } +func (d *dhw) speed() uint8 { return d.busSpeed } -func (d *dhw) controlDeviceAddress(addr uint16) { +func (d *dhw) setDeviceAddress(addr uint16) { d.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | ((uint32(addr) << nxp.USB_DEVICEADDR_USBADR_Pos) & nxp.USB_DEVICEADDR_USBADR_Msk)) } -func (d *dhw) controlLineState(coding descCDCACMLineCoding, dtr, rts bool) { - // TBD: does the PHY need to handle on iMXRT1062 (e.g., Teensyduino Loader)? -} - -func (d *dhw) controlLineCoding(coding descCDCACMLineCoding) { - if 134 == coding.baud { - d.enableSOF(true, descCDCACMInterfaceCount) - } -} - -// controlStatus transitions transfers on control endpoint 0 into status stage. -func (d *dhw) controlStatus() { - // Not used on iMXRT1062 -} +// ============================================================================= +// Control Endpoint 0 +// ============================================================================= // controlStall stalls a transfer on control endpoint 0. To stall a transfer on // any other endpoint, use method endpointStall(). @@ -411,7 +405,7 @@ func (d *dhw) controlReceive( } // wait for endpoint finish priming } ad.next = dhwTransferEOL - ad.token = 1 << 7 + ad.token = 1 << 7 // Bit 7: active transfer if notify { ad.token |= 1 << 15 } @@ -424,6 +418,8 @@ func (d *dhw) controlReceive( if notify { d.controlMask = tm } + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming } // controlTransmit transmits (Tx, IN) data on control endpoint 0. @@ -449,7 +445,7 @@ func (d *dhw) controlTransmit( } // wait for endpoint finish priming } ad.next = dhwTransferEOL - ad.token = 1 << 7 + ad.token = 1 << 7 // Bit 7: active transfer if notify { ad.token |= 1 << 15 } @@ -462,11 +458,13 @@ func (d *dhw) controlTransmit( if notify { d.controlMask = rm } + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming } -// dhwEndpointSize defines the size (bytes) of a structure containing a USB -// standard endpoint. -const dhwEndpointSize = 64 // bytes +// ============================================================================= +// Endpoint Descriptor +// ============================================================================= // dhwEndpoint defines a USB standard endpoint, used as the general channel of // communication between host and device. @@ -488,6 +486,10 @@ type dhwEndpoint struct { callback func(transfer *dhwTransfer) } +// dhwEndpointSize defines the size (bytes) of a structure containing a USB +// standard endpoint. +const dhwEndpointSize = 64 // bytes + // endpointQueueHead returns the queue head for the given endpoint address, // encoded as direction D and endpoint number N with the 8-bit mask DxxxNNNN. //go:inline @@ -496,6 +498,8 @@ func (d *dhw) endpointQueueHead(endpoint uint8) *dhwEndpoint { switch d.cc.id { case classDeviceCDCACM: return &descCDCACM[d.cc.config-1].qh[endpointIndex(endpoint)] + case classDeviceHID: + return &descHID[d.cc.config-1].qh[endpointIndex(endpoint)] default: return nil } @@ -552,17 +556,23 @@ func (d *dhw) endpointStall(endpoint uint8) { ) } -// func (d *dhw) endpointPrime(mask uint32, transfer *dhwTransfer) { -// d.bus.ENDPTPRIME.Set(mask) -// } +func (d *dhw) endpointClearFeature(endpoint uint8) { + switch endpoint { + case rxEndpoint(endpoint): + d.endpointControlRegister(endpoint).ClearBits(nxp.USB_ENDPTCTRL0_RXS) + case txEndpoint(endpoint): + d.endpointControlRegister(endpoint).ClearBits(nxp.USB_ENDPTCTRL0_TXS) + } +} -// func (d *dhw) endpointPrimed() uint32 { -// return d.bus.ENDPTPRIME.Get() -// } - -// func (d *dhw) endpointUnprime(mask uint32) { -// d.bus.ENDPTCOMPLETE.Set(mask) -// } +func (d *dhw) endpointSetFeature(endpoint uint8) { + switch endpoint { + case rxEndpoint(endpoint): + d.endpointControlRegister(endpoint).SetBits(nxp.USB_ENDPTCTRL0_RXS) + case txEndpoint(endpoint): + d.endpointControlRegister(endpoint).SetBits(nxp.USB_ENDPTCTRL0_TXS) + } +} // endpointConfigure configures the given bulk data endpoint for transfer. func (d *dhw) endpointConfigure( @@ -603,8 +613,8 @@ func (d *dhw) endpointComplete(endpoint uint8) { ep.first = nil ep.last = nil } else { - if 0 != t.token&(1<<7) { - // active transfer, new list begins here + if 0 != t.token&(1<<7) { // Bit 7: active transfer + // new list begins here ep.first = t break } else { @@ -634,15 +644,23 @@ func (d *dhw) endpointConfigureRx( endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(rxEndpoint(endpoint)) - d.endpointConfigure(ep, packetSize, zlp, callback) - if nil != callback { - d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrRxPos + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return } default: // Unhandled device class } + + ep := d.endpointQueueHead(rxEndpoint(endpoint)) + d.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrRxPos + } } // endpointConfigureTx configures the given bulk data transmit (Tx, IN) endpoint @@ -659,15 +677,23 @@ func (d *dhw) endpointConfigureTx( endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(txEndpoint(endpoint)) - d.endpointConfigure(ep, packetSize, zlp, callback) - if nil != callback { - d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrTxPos + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return } default: // Unhandled device class } + + ep := d.endpointQueueHead(txEndpoint(endpoint)) + d.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrTxPos + } } // endpointReceive schedules a receive (Rx, OUT) transfer on the given endpoint. @@ -682,13 +708,21 @@ func (d *dhw) endpointReceive(endpoint uint8, transfer *dhwTransfer) { endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(rxEndpoint(endpoint)) - em := (uint32(1) << endpoint) << descEndptConfigAttrRxPos - d.transferSchedule(ep, em, transfer) + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return + } default: // Unhandled device class } + + ep := d.endpointQueueHead(rxEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descEndptConfigAttrRxPos + d.transferSchedule(ep, em, transfer) } // endpointTransmit schedules a transmit (Tx, IN) transfer on the given @@ -704,17 +738,26 @@ func (d *dhw) endpointTransmit(endpoint uint8, transfer *dhwTransfer) { endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(txEndpoint(endpoint)) - em := (uint32(1) << endpoint) << descEndptConfigAttrTxPos - d.transferSchedule(ep, em, transfer) + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return + } default: // Unhandled device class } + + ep := d.endpointQueueHead(txEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descEndptConfigAttrTxPos + d.transferSchedule(ep, em, transfer) } -// dhwTransferSize defines the size (bytes) of a USB standard transfer packet. -const dhwTransferSize = 32 // bytes +// ============================================================================= +// Transfer Descriptor +// ============================================================================= // dhwTransfer describes the size and location of data to be transferred to or // from a USB endpoint. @@ -725,6 +768,9 @@ type dhwTransfer struct { param uint32 } +// dhwTransferSize defines the size (bytes) of a USB standard transfer packet. +const dhwTransferSize = 32 // bytes + // dhwTransferEOL is a sentinel value used to indicate the final node in a // linked list of transfer descriptors. var dhwTransferEOL = (*dhwTransfer)(unsafe.Pointer(uintptr(1))) @@ -744,6 +790,8 @@ func (d *dhw) transferControl() (dat, ack *dhwTransfer) { switch d.cc.id { case classDeviceCDCACM: return descCDCACM[d.cc.config-1].cd, descCDCACM[d.cc.config-1].ad + case classDeviceHID: + return descHID[d.cc.config-1].cd, descHID[d.cc.config-1].ad default: return nil, nil } @@ -753,7 +801,9 @@ func (d *dhw) transferPrepare( transfer *dhwTransfer, data *uint8, size uint16, param uint32) { transfer.next = dhwTransferEOL - transfer.token = (uint32(size) << 16) | (1 << 7) + + // Set 15-bit packet size and transfer active bit 7. + transfer.token = (uint32(size&0x7FFF) << 16) | (1 << 7) addr := uintptr(unsafe.Pointer(data)) for i := range transfer.pointer { transfer.pointer[i] = addr + uintptr(i)*4096 @@ -794,6 +844,10 @@ endTransfer: arm.EnableInterrupts(ivm) } +// ============================================================================= +// General-Purpose (GP) Timer +// ============================================================================= + func (d *dhw) timerConfigure(timer int, usec uint32, fn func()) { if timer < 0 || timer >= len(d.timerInterrupt) { return @@ -831,9 +885,15 @@ func (d *dhw) timerStop(timer int) { } } +// ============================================================================= +// [CDC-ACM] Serial UART (Virtual COM Port) +// ============================================================================= + func (d *dhw) uartConfigure() { + acm := &descCDCACM[d.cc.config-1] - switch d.controlBusSpeed() { + + switch d.speed() { case descDeviceSpeedHigh: acm.rxSize = descCDCACMDataRxHSPacketSize acm.txSize = descCDCACMDataTxHSPacketSize @@ -843,6 +903,7 @@ func (d *dhw) uartConfigure() { } acm.txHead = 0 acm.txFree = 0 + acm.txPrev = false acm.rxHead = 0 acm.rxTail = 0 acm.rxFree = 0 @@ -855,7 +916,7 @@ func (d *dhw) uartConfigure() { false, descCDCACMConfigAttrDataTx) d.endpointConfigureTx(descCDCACMEndpointStatus, - acm.cxSize, false, nil) + acm.sxSize, false, nil) d.endpointConfigureRx(descCDCACMEndpointDataRx, acm.rxSize, false, d.uartNotify) d.endpointConfigureTx(descCDCACMEndpointDataTx, @@ -866,6 +927,16 @@ func (d *dhw) uartConfigure() { d.timerConfigure(0, descCDCACMTxSyncUs, d.uartSync) } +func (d *dhw) uartSetLineState(dtr, rts bool) { + // TBD: does the PHY need to handle on iMXRT1062 (e.g., Teensyduino Loader)? +} + +func (d *dhw) uartSetLineCoding(coding descCDCACMLineCoding) { + if 134 == coding.baud { + d.enableSOF(true, descCDCACMInterfaceCount) + } +} + func (d *dhw) uartReceive(endpoint uint8) { acm := &descCDCACM[d.cc.config-1] num := uint16(endpoint) & descEndptAddrNumberMsk @@ -1071,3 +1142,263 @@ func (d *dhw) uartSync() { } acm.txFree = 0 } + +// ============================================================================= +// [HID] Serial +// ============================================================================= + +func (d *dhw) serialConfigure() { + + hid := &descHID[d.cc.config-1] + + switch d.speed() { + case descDeviceSpeedHigh: + hid.rxSerialSize = descHIDSerialRxHSPacketSize + hid.txSerialSize = descHIDSerialTxHSPacketSize + default: + 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 + buf := &hid.rxSerial[num*descHIDSerialRxSize] + d.enableInterrupts(false) + d.transferPrepare(&hid.rdSerial[num], buf, hid.rxSerialSize, uint32(endpoint)) + deleteCache(uintptr(unsafe.Pointer(buf)), uintptr(hid.rxSerialSize)) + d.endpointReceive(descHIDEndpointSerialRx, &hid.rdSerial[num]) + d.enableInterrupts(true) +} + +func (d *dhw) serialTransmit() { + hid := &descHID[d.cc.config-1] + xfer := &hid.tdSerial[hid.txSerialHead] + buff := &hid.txSerial[uint16(hid.txSerialHead)*descHIDSerialTxSize] + d.transferPrepare(xfer, buff, hid.txSerialSize, 0) + flushCache(uintptr(unsafe.Pointer(buff)), uintptr(hid.txSerialSize)) + d.endpointTransmit(descHIDEndpointSerialTx, xfer) + hid.txSerialHead += 1 + if hid.txSerialHead >= descHIDSerialTDCount { + hid.txSerialHead = 0 + } +} + +func (d *dhw) serialNotify(transfer *dhwTransfer) { + hid := &descHID[d.cc.config-1] + len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF) + p := transfer.param + if len == hid.rxSerialSize && 0 != hid.rxSerial[p*uint32(hid.rxSerialSize)] { + // data packet + hid.rxSerialIndex[p] = 0 + h := hid.rxSerialHead + 1 + if h > descHIDSerialRDCount { // should be >= + h = 0 + } + hid.rxSerialQueue[h] = uint16(p) + hid.rxSerialHead = h + hid.rxSerialFree += len + } else { + // short packet + d.serialReceive(uint8(p)) + } +} + +// serialFlush discards all buffered input (Rx) data. +func (d *dhw) serialFlush() { + hid := &descHID[d.cc.config-1] + tail := hid.rxSerialTail + for tail != hid.rxSerialHead { + tail += 1 + if tail > descHIDSerialRDCount { + tail = 0 + } + i := hid.rxSerialQueue[tail] + d.serialReceive(uint8(i)) + hid.rxSerialTail = tail + } +} + +func (d *dhw) serialSync() { + const autoFlushTx = true + if !autoFlushTx { + return + } + hid := &descHID[d.cc.config-1] + if 0 == hid.txSerialFree { + return + } + xfer := &hid.tdSerial[hid.txSerialHead] + buff := &hid.txSerial[uint16(hid.txSerialHead)*descHIDSerialTxSize] + size := descHIDSerialTxSize - hid.txSerialFree + d.transferPrepare(xfer, buff, size, 0) + flushCache(uintptr(unsafe.Pointer(buff)), uintptr(size)) + d.endpointTransmit(descHIDEndpointSerialTx, xfer) + hid.txSerialHead += 1 + if hid.txSerialHead >= descHIDSerialTDCount { + hid.txSerialHead = 0 + } + hid.txSerialFree = 0 +} + +// ============================================================================= +// [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) + + switch d.speed() { + case descDeviceSpeedHigh: + hid.txKeyboardSize = descHIDKeyboardTxHSPacketSize + default: + 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] + + switch d.speed() { + case descDeviceSpeedHigh: + hid.txMouseSize = descHIDMouseTxHSPacketSize + default: + 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] + + switch d.speed() { + case descDeviceSpeedHigh: + hid.txJoystickSize = descHIDJoystickTxHSPacketSize + default: + hid.txJoystickSize = descHIDJoystickTxFSPacketSize + } + + d.endpointEnable(descHIDEndpointJoystick, + false, descHIDConfigAttrJoystick) + + d.endpointConfigureTx(descHIDEndpointJoystick, + hid.txJoystickSize, false, nil) +} diff --git a/src/machine/usb/hid.go b/src/machine/usb/hid.go new file mode 100644 index 000000000..6b2dcc9ab --- /dev/null +++ b/src/machine/usb/hid.go @@ -0,0 +1,42 @@ +package usb + +import ( + "errors" +) + +var ( + ErrHIDInvalidPort = errors.New("invalid USB port") + ErrHIDInvalidCore = errors.New("invalid USB core") + ErrHIDReportTransfer = errors.New("failed to transfer HID report") +) + +type HID struct { + Port int + core *core +} + +type HIDConfig struct { + // Port is the MCU's native USB core number. If in doubt, leave it + // uninitialized for default (0). + Port int +} + +func (hid *HID) Configure(config HIDConfig) error { + + if config.Port >= CoreCount || config.Port >= dcdCount { + return ErrHIDInvalidPort + } + hid.Port = config.Port + + // verify we have a free USB port and take ownership of it + var st status + hid.core, st = initCore(hid.Port, class{id: classDeviceHID, config: 1}) + if !st.ok() { + return ErrHIDInvalidPort + } + return nil +} + +func (hid *HID) Keyboard() *Keyboard { + return hid.core.dc.keyboard() +} diff --git a/src/machine/usb/keyboard.go b/src/machine/usb/keyboard.go new file mode 100644 index 000000000..b0988f78d --- /dev/null +++ b/src/machine/usb/keyboard.go @@ -0,0 +1,1055 @@ +package usb + +import "errors" + +var ( + ErrInvalidCodepoint = errors.New("invalid Unicode codepoint") + ErrInvalidKeycode = errors.New("invalid keyboard keycode") + ErrInvalidUTF8 = errors.New("invalid UTF-8 encoding") + ErrKeypressMaximum = errors.New("maximum keypresses exceeded") +) + +// Keyboard represents a USB HID keyboard device with support for international +// layouts and various control, system, multimedia, and consumer keycodes. +// +// Keyboard implements the io.Writer interface that translates UTF-8 encoded +// byte strings into sequences of keypress events. +type Keyboard struct { + dc *dcd + hc *descHIDClass + + // led holds the current state of all keyboard LEDs: + // 1=NumLock 2=CapsLock 4=ScrollLock 8=Compose 16=Kana + led uint8 + + // mod holds the current state of all keyboard modifier keys: + // 1=LeftCtrl 2=LeftShift 4=LeftAlt 8=LeftGUI + // 16=RightCtrl 32=RightShift 64=RightAlt 128=RightGUI + mod uint8 + + // key holds a list of all keyboard keys currently pressed. + key *[hidKeyboardKeyCount]uint8 + con *[hidKeyboardConCount]uint16 + sys *[hidKeyboardSysCount]uint8 + + // decode holds the current state of the UTF-8 decoder. + decode decodeState + + // wideChar holds high bits for the UTF-8 decoder. + wideChar uint16 +} + +// decodeState represents a state in the UTF-8 decode state machine. +type decodeState uint8 + +// Constant enumerated values of type decodeState. +const ( + decodeReset decodeState = iota + decodeByte1 + decodeByte2 + decodeByte3 +) + +// configure initializes the receiver Keyboard by associating it with the given +// USB device controller driver and HID class configuration. +func (kb *Keyboard) configure(dc *dcd, hc *descHIDClass) { + kb.dc = dc + kb.hc = hc +} + +// 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. +func (kb *Keyboard) Write(b []byte) (n int, err error) { + for _, c := range b { + if err = kb.WriteByte(c); nil != err { + break + } + n += 1 + } + return +} + +// WriteByte processes a single byte from a UTF-8 byte string. This method is a +// stateful method with respect to the receiver Keyboard, meaning that its exact +// behavior will depend on the current state of its UTF-8 decode state machine: +// +// (a) If the given byte is a valid ASCII encoding (0-127), then a keypress +// sequence is immediately transmitted for the respective Keycode. +// +// (b) If the given byte represents the final byte in a multi-byte codepoint, +// then a keypress sequence is immediately transmitted by translating the +// multi-byte codepoint to its respective Keycode. +// +// (c) If the given byte appears to represent high bits for a multi-byte +// codepoint, then the bits are copied to the receiver's internal state +// machine buffer for use by a subsequent call to WriteByte() (or Write()) +// that completes the codepoint. +// +// (d) If the given byte is out of range, or contains illegal bits for the +// current state of the UTF-8 decoder, then the UTF-8 decode state machine +// is reset to its initial state. +// +// In cases (c) and (d), a keypress sequence is not generated and no data is +// transmitted. In case (c), additional bytes must be received via WriteByte() +// (or Write()) to complete or discard the current codepoint. +func (kb *Keyboard) WriteByte(b byte) error { + switch { + case b < 0x80: + // 1-byte encoding (0x00-0x7F) + kb.decode = decodeByte1 + return kb.write(uint16(b)) + + case b < 0xC0: + // 2nd, 3rd, or 4th byte (0x80-0xBF) + b = Keycode(b).key() + switch kb.decode { + case decodeByte2: + kb.decode = decodeByte1 + return kb.write(kb.wideChar | uint16(b)) + case decodeByte3: + kb.decode = decodeByte2 + kb.wideChar |= uint16(b) << 6 + } + + case b < 0xE0: + // 2-byte encoding (0xC2-0xDF), or illegal byte 2 (0xC0-0xC1) + kb.decode = decodeByte2 + kb.wideChar = uint16(b&0x1F) << 6 + + case b < 0xF0: + // 3-byte encoding (0xE0-0xEF) + kb.decode = decodeByte3 + kb.wideChar = uint16(b&0x0F) << 12 + + default: + // 4-byte encoding unsupported (0xF0-0xF4), or illegal byte 4 (0xF5-0xFF) + kb.decode = decodeReset + return ErrInvalidUTF8 + } + return nil +} + +func (kb *Keyboard) write(p uint16) error { + c := keycode(p) + if 0 == c { + return ErrInvalidCodepoint + } + if d := deadkey(c); 0 != d { + if err := kb.writeKeycode(d); nil != err { + return err + } + } + return kb.writeKeycode(c) +} + +func (kb *Keyboard) writeKeycode(c Keycode) error { + kb.mod = c.mod() + kb.key[0] = c.key() + kb.key[1] = 0 + kb.key[2] = 0 + kb.key[3] = 0 + kb.key[4] = 0 + kb.key[5] = 0 + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + kb.mod = 0 + kb.key[0] = 0 + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + return nil +} + +// Press transmits a press-and-release sequence for the given Keycode, which +// simulates a discrete keypress event. +// +// The following values of Keycode are supported: +// +// 0x0020 - 0x007F ASCII (U+0020 to U+007F) [USES LAYOUT] +// 0x0080 - 0xC1FF Unicode (U+0080 to U+C1FF) [USES LAYOUT] +// 0xC200 - 0xDFFF UTF-8 packed (U+0080 to U+07FF) [USES LAYOUT] +// 0xE000 - 0xE0FF Modifier key (bitmap, 8 keys, Shift/Ctrl/Alt/GUI) +// 0xE200 - 0xE2FF System key (HID usage code, page 1) +// 0xE400 - 0xE7FF Media/Consumer key (HID usage code, page 12) +// 0xF000 - 0xFFFF Normal key (HID usage code, page 7) +func (kb *Keyboard) Press(c Keycode) error { + if err := kb.Down(c); nil != err { + return err + } + return kb.Up(c) +} + +// Down transmits a press (key-down) event for the given Keycode. +// +// The host will interpret the key as being held down continuously until a +// corresponding release (key-up) event is transmitted, e.g., via method Up(). +// +// See godoc comment on method Press() for details on what input is accepted and +// how it is interpreted. +func (kb *Keyboard) Down(c Keycode) error { + var res uint8 + msb := c >> 8 + if msb >= 0xC2 { + if msb < 0xE0 { + c = ((msb & 0x1F) << 6) | Keycode(c.key()) + } else { + switch msb { + case 0xF0: + return kb.down(uint8(c), 0) + + case 0xE0: + return kb.down(0, uint8(c)) + + case 0xE2: + return kb.downSys(uint8(c)) + + default: + if 0xE4 <= msb && msb <= 0xE7 { + return kb.downCon(uint16(c & 0x03FF)) + } + return ErrInvalidKeycode + } + } + } + c = keycode(uint16(c)) + if 0 == c { + return ErrInvalidCodepoint + } + if d := deadkey(c); 0 != d { + res = kb.mod + if 0 != res { + kb.mod = 0 + kb.dc.keyboardSendKeys(false) + } + kb.down(d.key(), d.mod()) + kb.up(d.key(), d.mod()) + } + return kb.down(c.key(), c.mod()|res) +} + +func (kb *Keyboard) down(key uint8, mod uint8) error { + send := false + if 0 != mod { + if kb.mod&mod != mod { + kb.mod |= mod + send = true + } + } + if 0 != key { + for _, k := range kb.key { + if k == key { + goto end + } + } + for i, k := range kb.key { + if 0 == k { + kb.key[i] = key + send = true + goto end + } + } + return ErrKeypressMaximum + } +end: + if send { + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + } + return nil +} + +func (kb *Keyboard) downCon(key uint16) error { + if 0 == key { + return ErrInvalidKeycode + } + for _, k := range kb.con { + if key == k { + return nil // already pressed + } + } + for i, k := range kb.con { + if 0 == k { + kb.con[i] = key + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return ErrKeypressMaximum +} + +func (kb *Keyboard) downSys(key uint8) error { + if 0 == key { + return ErrInvalidKeycode + } + for _, k := range kb.sys { + if key == k { + return nil // already pressed + } + } + for i, k := range kb.sys { + if 0 == k { + kb.sys[i] = key + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return ErrKeypressMaximum +} + +// Release transmits a release (key-up) event for the given Keycode. +// +// See godoc comment on method Press() for details on what input is accepted and +// how it is interpreted. +func (kb *Keyboard) Up(c Keycode) error { + msb := c >> 8 + if msb >= 0xC2 { + if msb < 0xE0 { + c = ((msb & 0x1F) << 6) | Keycode(c.key()) + } else { + switch msb { + case 0xF0: + return kb.up(uint8(c), 0) + + case 0xE0: + return kb.up(0, uint8(c)) + + case 0xE2: + return kb.upSys(uint8(c)) + + default: + if 0xE4 <= msb && msb <= 0xE7 { + return kb.upCon(uint16(c & 0x03FF)) + } + return ErrInvalidKeycode + } + } + } + c = keycode(uint16(c)) + if 0 == c { + return ErrInvalidCodepoint + } + return kb.up(c.key(), c.mod()) +} + +func (kb *Keyboard) Release() error { + + bits := uint16(kb.mod) + kb.mod = 0 + for i, k := range kb.key { + bits |= uint16(k) + kb.key[i] = 0 + } + if 0 != bits { + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + } + bits = 0 + for i, k := range kb.con { + bits |= k + kb.con[i] = 0 + } + for i, k := range kb.sys { + bits |= uint16(k) + kb.sys[i] = 0 + } + if 0 != bits { + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + } + return nil +} + +func (kb *Keyboard) up(key uint8, mod uint8) error { + send := false + if 0 != mod { + if kb.mod&mod != 0 { + kb.mod &^= mod + send = true + } + } + if 0 != key { + for i, k := range kb.key { + if key == k { + kb.key[i] = 0 + send = true + } + } + } + if send { + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + } + return nil +} + +func (kb *Keyboard) upCon(key uint16) error { + if 0 == key { + return ErrInvalidKeycode + } + for i, k := range kb.con { + if key == k { + kb.con[i] = 0 + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return nil +} + +func (kb *Keyboard) upSys(key uint8) error { + if 0 == key { + return ErrInvalidKeycode + } + for i, k := range kb.sys { + if key == k { + kb.sys[i] = 0 + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return nil +} + +// Keycode is a package-defined bitmap used to encode the value of a given key. +type Keycode uint16 + +// keycode returns the given Unicode codepoint translated to a Keycode sequence. +// Unicode codepoints greater than U+FFFF are unsupported. +//go:inline +func keycode(p uint16) Keycode { + if p < 0x80 { + return ascii[p] + } else if p >= 0xA0 && p < 0x0100 { + return iso88591[p-0xA0] + } else if uint16(UNICODEEXTRA00) == p { + return KEYCODEEXTRA00.mask() + } + return 0 +} + +//go:inline +func deadkey(c Keycode) Keycode { + switch c & deadkeysMask { + case acuteAccentBits: + return deadkeyAcuteAccent + case circumflexBits: + return deadkeyCircumflex + case diaeresisBits: + return deadkeyDiaeresis + case graveAccentBits: + return deadkeyGraveAccent + case tildeBits: + return deadkeyTilde + } + return 0 +} + +//go:inline +func (c Keycode) mask() Keycode { return c & keycodeMask } + +//go:inline +func (c Keycode) key() uint8 { return uint8(c & keyMask) } + +//go:inline +func (c Keycode) mod() uint8 { + var m Keycode + if 0 != c&shiftMask { + m |= KeyModifierShift + } + if 0 != c&altgrMask { + m |= KeyModifierRightAlt + } + return uint8(m) +} + +//go:inline +func (c Keycode) Shift() Keycode { return c | KeyModifierShift } + +const ( + hidKeyboardKeyCount = 6 // Max number of simultaneous keypresses + hidKeyboardSysCount = 3 + hidKeyboardConCount = 4 +) + +// Keycodes common to all Keyboard layouts +const ( + KeyModifierCtrl Keycode = (0x01 | 0xE000) + KeyModifierShift Keycode = (0x02 | 0xE000) + KeyModifierAlt Keycode = (0x04 | 0xE000) + KeyModifierGUI Keycode = (0x08 | 0xE000) + KeyModifierLeftCtrl Keycode = (0x01 | 0xE000) + KeyModifierLeftShift Keycode = (0x02 | 0xE000) + KeyModifierLeftAlt Keycode = (0x04 | 0xE000) + KeyModifierLeftGUI Keycode = (0x08 | 0xE000) + KeyModifierRightCtrl Keycode = (0x10 | 0xE000) + KeyModifierRightShift Keycode = (0x20 | 0xE000) + KeyModifierRightAlt Keycode = (0x40 | 0xE000) + KeyModifierRightGUI Keycode = (0x80 | 0xE000) + + KeySystemPowerDown Keycode = (0x81 | 0xE200) + KeySystemSleep Keycode = (0x82 | 0xE200) + KeySystemWakeUp Keycode = (0x83 | 0xE200) + + KeyMediaPlay Keycode = (0xB0 | 0xE400) + KeyMediaPause Keycode = (0xB1 | 0xE400) + KeyMediaRecord Keycode = (0xB2 | 0xE400) + KeyMediaFastForward Keycode = (0xB3 | 0xE400) + KeyMediaRewind Keycode = (0xB4 | 0xE400) + KeyMediaNextTrack Keycode = (0xB5 | 0xE400) + KeyMediaPrevTrack Keycode = (0xB6 | 0xE400) + KeyMediaStop Keycode = (0xB7 | 0xE400) + KeyMediaEject Keycode = (0xB8 | 0xE400) + KeyMediaRandomPlay Keycode = (0xB9 | 0xE400) + KeyMediaPlayPause Keycode = (0xCD | 0xE400) + KeyMediaPlaySkip Keycode = (0xCE | 0xE400) + KeyMediaMute Keycode = (0xE2 | 0xE400) + KeyMediaVolumeInc Keycode = (0xE9 | 0xE400) + KeyMediaVolumeDec Keycode = (0xEA | 0xE400) + + KeyA Keycode = (4 | 0xF000) + KeyB Keycode = (5 | 0xF000) + KeyC Keycode = (6 | 0xF000) + KeyD Keycode = (7 | 0xF000) + KeyE Keycode = (8 | 0xF000) + KeyF Keycode = (9 | 0xF000) + KeyG Keycode = (10 | 0xF000) + KeyH Keycode = (11 | 0xF000) + KeyI Keycode = (12 | 0xF000) + KeyJ Keycode = (13 | 0xF000) + KeyK Keycode = (14 | 0xF000) + KeyL Keycode = (15 | 0xF000) + KeyM Keycode = (16 | 0xF000) + KeyN Keycode = (17 | 0xF000) + KeyO Keycode = (18 | 0xF000) + KeyP Keycode = (19 | 0xF000) + KeyQ Keycode = (20 | 0xF000) + KeyR Keycode = (21 | 0xF000) + KeyS Keycode = (22 | 0xF000) + KeyT Keycode = (23 | 0xF000) + KeyU Keycode = (24 | 0xF000) + KeyV Keycode = (25 | 0xF000) + KeyW Keycode = (26 | 0xF000) + KeyX Keycode = (27 | 0xF000) + KeyY Keycode = (28 | 0xF000) + KeyZ Keycode = (29 | 0xF000) + Key1 Keycode = (30 | 0xF000) + Key2 Keycode = (31 | 0xF000) + Key3 Keycode = (32 | 0xF000) + Key4 Keycode = (33 | 0xF000) + Key5 Keycode = (34 | 0xF000) + Key6 Keycode = (35 | 0xF000) + Key7 Keycode = (36 | 0xF000) + Key8 Keycode = (37 | 0xF000) + Key9 Keycode = (38 | 0xF000) + Key0 Keycode = (39 | 0xF000) + KeyEnter Keycode = (40 | 0xF000) + KeyEsc Keycode = (41 | 0xF000) + KeyBackspace Keycode = (42 | 0xF000) + KeyTab Keycode = (43 | 0xF000) + KeySpace Keycode = (44 | 0xF000) + KeyMinus Keycode = (45 | 0xF000) + KeyEqual Keycode = (46 | 0xF000) + KeyLeftBrace Keycode = (47 | 0xF000) + KeyRightBrace Keycode = (48 | 0xF000) + KeyBackslash Keycode = (49 | 0xF000) + KeyNonUsNum Keycode = (50 | 0xF000) + KeySemicolon Keycode = (51 | 0xF000) + KeyQuote Keycode = (52 | 0xF000) + KeyTilde Keycode = (53 | 0xF000) + KeyComma Keycode = (54 | 0xF000) + KeyPeriod Keycode = (55 | 0xF000) + KeySlash Keycode = (56 | 0xF000) + KeyCapsLock Keycode = (57 | 0xF000) + KeyF1 Keycode = (58 | 0xF000) + KeyF2 Keycode = (59 | 0xF000) + KeyF3 Keycode = (60 | 0xF000) + KeyF4 Keycode = (61 | 0xF000) + KeyF5 Keycode = (62 | 0xF000) + KeyF6 Keycode = (63 | 0xF000) + KeyF7 Keycode = (64 | 0xF000) + KeyF8 Keycode = (65 | 0xF000) + KeyF9 Keycode = (66 | 0xF000) + KeyF10 Keycode = (67 | 0xF000) + KeyF11 Keycode = (68 | 0xF000) + KeyF12 Keycode = (69 | 0xF000) + KeyPrintscreen Keycode = (70 | 0xF000) + KeyScrollLock Keycode = (71 | 0xF000) + KeyPause Keycode = (72 | 0xF000) + KeyInsert Keycode = (73 | 0xF000) + KeyHome Keycode = (74 | 0xF000) + KeyPageUp Keycode = (75 | 0xF000) + KeyDelete Keycode = (76 | 0xF000) + KeyEnd Keycode = (77 | 0xF000) + KeyPageDown Keycode = (78 | 0xF000) + KeyRight Keycode = (79 | 0xF000) + KeyLeft Keycode = (80 | 0xF000) + KeyDown Keycode = (81 | 0xF000) + KeyUp Keycode = (82 | 0xF000) + KeyNumLock Keycode = (83 | 0xF000) + KeypadSlash Keycode = (84 | 0xF000) + KeypadAsterisk Keycode = (85 | 0xF000) + KeypadMinus Keycode = (86 | 0xF000) + KeypadPlus Keycode = (87 | 0xF000) + KeypadEnter Keycode = (88 | 0xF000) + Keypad1 Keycode = (89 | 0xF000) + Keypad2 Keycode = (90 | 0xF000) + Keypad3 Keycode = (91 | 0xF000) + Keypad4 Keycode = (92 | 0xF000) + Keypad5 Keycode = (93 | 0xF000) + Keypad6 Keycode = (94 | 0xF000) + Keypad7 Keycode = (95 | 0xF000) + Keypad8 Keycode = (96 | 0xF000) + Keypad9 Keycode = (97 | 0xF000) + Keypad0 Keycode = (98 | 0xF000) + KeypadPeriod Keycode = (99 | 0xF000) + KeyNonUSBS Keycode = (100 | 0xF000) + KeyMenu Keycode = (101 | 0xF000) + KeyF13 Keycode = (104 | 0xF000) + KeyF14 Keycode = (105 | 0xF000) + KeyF15 Keycode = (106 | 0xF000) + KeyF16 Keycode = (107 | 0xF000) + KeyF17 Keycode = (108 | 0xF000) + KeyF18 Keycode = (109 | 0xF000) + KeyF19 Keycode = (110 | 0xF000) + KeyF20 Keycode = (111 | 0xF000) + KeyF21 Keycode = (112 | 0xF000) + KeyF22 Keycode = (113 | 0xF000) + KeyF23 Keycode = (114 | 0xF000) + KeyF24 Keycode = (115 | 0xF000) + + KeyUpArrow Keycode = KeyUp + KeyDownArrow Keycode = KeyDown + KeyLeftArrow Keycode = KeyLeft + KeyRightArrow Keycode = KeyRight + KeyReturn Keycode = KeyEnter + KeyLeftCtrl Keycode = KeyModifierLeftCtrl + KeyLeftShift Keycode = KeyModifierLeftShift + KeyLeftAlt Keycode = KeyModifierLeftAlt + KeyLeftGUI Keycode = KeyModifierLeftGUI + KeyRightCtrl Keycode = KeyModifierRightCtrl + KeyRightShift Keycode = KeyModifierRightShift + KeyRightAlt Keycode = KeyModifierRightAlt + KeyRightGUI Keycode = KeyModifierRightGUI +) + +// Keycodes for layout US English (0x0904) +const ( + keycodeMask Keycode = 0x07FF + keyMask Keycode = 0x003F + + shiftMask Keycode = 0x0040 + altgrMask Keycode = 0x0080 + deadkeysMask Keycode = 0x0700 + circumflexBits Keycode = 0x0100 + acuteAccentBits Keycode = 0x0200 + graveAccentBits Keycode = 0x0300 + tildeBits Keycode = 0x0400 + diaeresisBits Keycode = 0x0500 + deadkeyCircumflex Keycode = Key6 + shiftMask + deadkeyAcuteAccent Keycode = KeyQuote + deadkeyGraveAccent Keycode = KeyTilde + deadkeyTilde Keycode = KeyTilde + shiftMask + deadkeyDiaeresis Keycode = KeyQuote + shiftMask + + ASCII00 Keycode = 0 // 0 NUL + ASCII01 Keycode = 0 // 1 SOH + ASCII02 Keycode = 0 // 2 STX + ASCII03 Keycode = 0 // 3 ETX + ASCII04 Keycode = 0 // 4 EOT + ASCII05 Keycode = 0 // 5 ENQ + ASCII06 Keycode = 0 // 6 ACK + ASCII07 Keycode = 0 // 7 BEL + ASCII08 Keycode = KeyBackspace // 8 BS + ASCII09 Keycode = KeyTab // 9 TAB + ASCII0A Keycode = KeyEnter // 10 LF + ASCII0B Keycode = 0 // 11 VT + ASCII0C Keycode = 0 // 12 FF + ASCII0D Keycode = 0 // 13 CR + ASCII0E Keycode = 0 // 14 SO + ASCII0F Keycode = 0 // 15 SI + ASCII10 Keycode = 0 // 16 DEL + ASCII11 Keycode = 0 // 17 DC1 + ASCII12 Keycode = 0 // 18 DC2 + ASCII13 Keycode = 0 // 19 DC3 + ASCII14 Keycode = 0 // 20 DC4 + ASCII15 Keycode = 0 // 21 NAK + ASCII16 Keycode = 0 // 22 SYN + ASCII17 Keycode = 0 // 23 ETB + ASCII18 Keycode = 0 // 24 CAN + ASCII19 Keycode = 0 // 25 EM + ASCII1A Keycode = 0 // 26 SUB + ASCII1B Keycode = 0 // 27 ESC + ASCII1C Keycode = 0 // 28 FS + ASCII1D Keycode = 0 // 29 GS + ASCII1E Keycode = 0 // 30 RS + ASCII1F Keycode = 0 // 31 US + + ASCII20 Keycode = KeySpace // 32 SPACE + ASCII21 Keycode = Key1 + shiftMask // 33 ! + ASCII22 Keycode = diaeresisBits + KeySpace // 34 " + ASCII23 Keycode = Key3 + shiftMask // 35 # + ASCII24 Keycode = Key4 + shiftMask // 36 $ + ASCII25 Keycode = Key5 + shiftMask // 37 % + ASCII26 Keycode = Key7 + shiftMask // 38 & + ASCII27 Keycode = acuteAccentBits + KeySpace // 39 ' + ASCII28 Keycode = Key9 + shiftMask // 40 ( + ASCII29 Keycode = Key0 + shiftMask // 41 ) + ASCII2A Keycode = Key8 + shiftMask // 42 * + ASCII2B Keycode = KeyEqual + shiftMask // 43 + + ASCII2C Keycode = KeyComma // 44 , + ASCII2D Keycode = KeyMinus // 45 - + ASCII2E Keycode = KeyPeriod // 46 . + ASCII2F Keycode = KeySlash // 47 / + ASCII30 Keycode = Key0 // 48 0 + ASCII31 Keycode = Key1 // 49 1 + ASCII32 Keycode = Key2 // 50 2 + ASCII33 Keycode = Key3 // 51 3 + ASCII34 Keycode = Key4 // 52 4 + ASCII35 Keycode = Key5 // 53 5 + ASCII36 Keycode = Key6 // 54 6 + ASCII37 Keycode = Key7 // 55 7 + ASCII38 Keycode = Key8 // 55 8 + ASCII39 Keycode = Key9 // 57 9 + ASCII3A Keycode = KeySemicolon + shiftMask // 58 : + ASCII3B Keycode = KeySemicolon // 59 ; + ASCII3C Keycode = KeyComma + shiftMask // 60 < + ASCII3D Keycode = KeyEqual // 61 = + ASCII3E Keycode = KeyPeriod + shiftMask // 62 > + ASCII3F Keycode = KeySlash + shiftMask // 63 ? + ASCII40 Keycode = Key2 + shiftMask // 64 @ + ASCII41 Keycode = KeyA + shiftMask // 65 A + ASCII42 Keycode = KeyB + shiftMask // 66 B + ASCII43 Keycode = KeyC + shiftMask // 67 C + ASCII44 Keycode = KeyD + shiftMask // 68 D + ASCII45 Keycode = KeyE + shiftMask // 69 E + ASCII46 Keycode = KeyF + shiftMask // 70 F + ASCII47 Keycode = KeyG + shiftMask // 71 G + ASCII48 Keycode = KeyH + shiftMask // 72 H + ASCII49 Keycode = KeyI + shiftMask // 73 I + ASCII4A Keycode = KeyJ + shiftMask // 74 J + ASCII4B Keycode = KeyK + shiftMask // 75 K + ASCII4C Keycode = KeyL + shiftMask // 76 L + ASCII4D Keycode = KeyM + shiftMask // 77 M + ASCII4E Keycode = KeyN + shiftMask // 78 N + ASCII4F Keycode = KeyO + shiftMask // 79 O + ASCII50 Keycode = KeyP + shiftMask // 80 P + ASCII51 Keycode = KeyQ + shiftMask // 81 Q + ASCII52 Keycode = KeyR + shiftMask // 82 R + ASCII53 Keycode = KeyS + shiftMask // 83 S + ASCII54 Keycode = KeyT + shiftMask // 84 T + ASCII55 Keycode = KeyU + shiftMask // 85 U + ASCII56 Keycode = KeyV + shiftMask // 86 V + ASCII57 Keycode = KeyW + shiftMask // 87 W + ASCII58 Keycode = KeyX + shiftMask // 88 X + ASCII59 Keycode = KeyY + shiftMask // 89 Y + ASCII5A Keycode = KeyZ + shiftMask // 90 Z + ASCII5B Keycode = KeyLeftBrace // 91 [ + ASCII5C Keycode = KeyBackslash // 92 \ + ASCII5D Keycode = KeyRightBrace // 93 ] + ASCII5E Keycode = circumflexBits + KeySpace // 94 ^ + ASCII5F Keycode = KeyMinus + shiftMask // 95 + ASCII60 Keycode = graveAccentBits + KeySpace // 96 ` + ASCII61 Keycode = KeyA // 97 a + ASCII62 Keycode = KeyB // 98 b + ASCII63 Keycode = KeyC // 99 c + ASCII64 Keycode = KeyD // 100 d + ASCII65 Keycode = KeyE // 101 e + ASCII66 Keycode = KeyF // 102 f + ASCII67 Keycode = KeyG // 103 g + ASCII68 Keycode = KeyH // 104 h + ASCII69 Keycode = KeyI // 105 i + ASCII6A Keycode = KeyJ // 106 j + ASCII6B Keycode = KeyK // 107 k + ASCII6C Keycode = KeyL // 108 l + ASCII6D Keycode = KeyM // 109 m + ASCII6E Keycode = KeyN // 110 n + ASCII6F Keycode = KeyO // 111 o + ASCII70 Keycode = KeyP // 112 p + ASCII71 Keycode = KeyQ // 113 q + ASCII72 Keycode = KeyR // 114 r + ASCII73 Keycode = KeyS // 115 s + ASCII74 Keycode = KeyT // 116 t + ASCII75 Keycode = KeyU // 117 u + ASCII76 Keycode = KeyV // 118 v + ASCII77 Keycode = KeyW // 119 w + ASCII78 Keycode = KeyX // 120 x + ASCII79 Keycode = KeyY // 121 y + ASCII7A Keycode = KeyZ // 122 z + ASCII7B Keycode = KeyLeftBrace + shiftMask // 123 { + ASCII7C Keycode = KeyBackslash + shiftMask // 124 | + ASCII7D Keycode = KeyRightBrace + shiftMask // 125 } + ASCII7E Keycode = tildeBits + KeySpace // 126 ~ + ASCII7F Keycode = KeyBackspace // 127 DEL + ISO88591A0 Keycode = KeySpace // 160 Nonbreakng Space + ISO88591A1 Keycode = Key1 + altgrMask // 161 ¡ Inverted Exclamation + ISO88591A2 Keycode = KeyC + altgrMask + shiftMask // 162 ¢ Cent SIGN + ISO88591A3 Keycode = Key4 + altgrMask + shiftMask // 163 £ Pound Sign + ISO88591A4 Keycode = Key4 + altgrMask // 164 ¤ Currency or Euro Sign + ISO88591A5 Keycode = KeyMinus + altgrMask // 165 ¥ YEN SIGN + ISO88591A6 Keycode = KeyBackslash + altgrMask + shiftMask // 166 ¦ BROKEN BAR ?? + ISO88591A7 Keycode = KeyS + altgrMask + shiftMask // 167 § SECTION SIGN + ISO88591A8 Keycode = KeyQuote + altgrMask + shiftMask // 168 ¨ DIAERESIS + ISO88591A9 Keycode = KeyC + altgrMask // 169 © COPYRIGHT SIGN + ISO88591AA Keycode = 0 // 170 ª FEMININE ORDINAL + ISO88591AB Keycode = KeyLeftBrace + altgrMask // 171 « LEFT DOUBLE ANGLE QUOTE + ISO88591AC Keycode = KeyBackslash + altgrMask // 172 ¬ NOT SIGN ?? + ISO88591AD Keycode = 0 // 173 SOFT HYPHEN + ISO88591AE Keycode = KeyR + altgrMask // 174 ® REGISTERED SIGN + ISO88591AF Keycode = 0 // 175 ¯ MACRON + ISO88591B0 Keycode = KeySemicolon + altgrMask + shiftMask // 176 ° DEGREE SIGN + ISO88591B1 Keycode = 0 // 177 ± PLUS-MINUS SIGN + ISO88591B2 Keycode = Key2 + altgrMask // 178 ² SUPERSCRIPT TWO + ISO88591B3 Keycode = Key3 + altgrMask // 179 ³ SUPERSCRIPT THREE + ISO88591B4 Keycode = KeyQuote + altgrMask // 180 ´ ACUTE ACCENT + ISO88591B5 Keycode = KeyM + altgrMask // 181 µ MICRO SIGN + ISO88591B6 Keycode = KeySemicolon + altgrMask // 182 ¶ PILCROW SIGN + ISO88591B7 Keycode = 0 // 183 · MIDDLE DOT + ISO88591B8 Keycode = 0 // 184 ¸ CEDILLA + ISO88591B9 Keycode = Key1 + altgrMask + shiftMask // 185 ¹ SUPERSCRIPT ONE + ISO88591BA Keycode = 0 // 186 º MASCULINE ORDINAL + ISO88591BB Keycode = KeyRightBrace + altgrMask // 187 » RIGHT DOUBLE ANGLE QUOTE + ISO88591BC Keycode = Key6 + altgrMask // 188 ¼ FRACTION ONE QUARTER + ISO88591BD Keycode = Key7 + altgrMask // 189 ½ FRACTION ONE HALF + ISO88591BE Keycode = Key8 + altgrMask // 190 ¾ FRACTION THREE QUARTERS + ISO88591BF Keycode = KeySlash + altgrMask // 191 ¿ INVERTED QUESTION MARK + ISO88591C0 Keycode = graveAccentBits + KeyA + shiftMask // 192 À A GRAVE + ISO88591C1 Keycode = KeyA + altgrMask + shiftMask // 193 Á A ACUTE + ISO88591C2 Keycode = circumflexBits + KeyA + shiftMask // 194  A CIRCUMFLEX + ISO88591C3 Keycode = tildeBits + KeyA + shiftMask // 195 à A TILDE + ISO88591C4 Keycode = KeyQ + altgrMask + shiftMask // 196 Ä A DIAERESIS + ISO88591C5 Keycode = KeyW + altgrMask + shiftMask // 197 Å A RING ABOVE + ISO88591C6 Keycode = KeyZ + altgrMask + shiftMask // 198 Æ AE + ISO88591C7 Keycode = KeyComma + altgrMask + shiftMask // 199 Ç C CEDILLA + ISO88591C8 Keycode = graveAccentBits + KeyE + shiftMask // 200 È E GRAVE + ISO88591C9 Keycode = KeyE + altgrMask + shiftMask // 201 É E ACUTE + ISO88591CA Keycode = circumflexBits + KeyE + shiftMask // 202 Ê E CIRCUMFLEX + ISO88591CB Keycode = diaeresisBits + KeyE + shiftMask // 203 Ë E DIAERESIS + ISO88591CC Keycode = graveAccentBits + KeyI + shiftMask // 204 Ì I GRAVE + ISO88591CD Keycode = KeyI + altgrMask + shiftMask // 205 Í I ACUTE + ISO88591CE Keycode = circumflexBits + KeyI + shiftMask // 206 Î I CIRCUMFLEX + ISO88591CF Keycode = diaeresisBits + KeyI + shiftMask // 207 Ï I DIAERESIS + ISO88591D0 Keycode = KeyD + altgrMask + shiftMask // 208 Ð ETH + ISO88591D1 Keycode = KeyN + altgrMask + shiftMask // 209 Ñ N TILDE + ISO88591D2 Keycode = graveAccentBits + KeyO + shiftMask // 210 Ò O GRAVE + ISO88591D3 Keycode = KeyO + altgrMask + shiftMask // 211 Ó O ACUTE + ISO88591D4 Keycode = circumflexBits + KeyO + shiftMask // 212 Ô O CIRCUMFLEX + ISO88591D5 Keycode = tildeBits + KeyO + shiftMask // 213 Õ O TILDE + ISO88591D6 Keycode = KeyP + altgrMask + shiftMask // 214 Ö O DIAERESIS + ISO88591D7 Keycode = KeyEqual + altgrMask // 215 × MULTIPLICATION + ISO88591D8 Keycode = KeyL + altgrMask + shiftMask // 216 Ø O STROKE + ISO88591D9 Keycode = graveAccentBits + KeyU + shiftMask // 217 Ù U GRAVE + ISO88591DA Keycode = KeyU + altgrMask + shiftMask // 218 Ú U ACUTE + ISO88591DB Keycode = circumflexBits + KeyU + shiftMask // 219 Û U CIRCUMFLEX + ISO88591DC Keycode = KeyY + altgrMask + shiftMask // 220 Ü U DIAERESIS + ISO88591DD Keycode = acuteAccentBits + KeyY + shiftMask // 221 Ý Y ACUTE + ISO88591DE Keycode = KeyT + altgrMask + shiftMask // 222 Þ THORN + ISO88591DF Keycode = KeyS + altgrMask // 223 ß SHARP S + ISO88591E0 Keycode = graveAccentBits + KeyA // 224 à a GRAVE + ISO88591E1 Keycode = KeyA + altgrMask // 225 á a ACUTE + ISO88591E2 Keycode = circumflexBits + KeyA // 226 â a CIRCUMFLEX + ISO88591E3 Keycode = tildeBits + KeyA // 227 ã a TILDE + ISO88591E4 Keycode = diaeresisBits + KeyA // 228 ä a DIAERESIS + ISO88591E5 Keycode = KeyW + altgrMask // 229 å a RING ABOVE + ISO88591E6 Keycode = KeyZ + altgrMask // 230 æ ae + ISO88591E7 Keycode = KeyComma + altgrMask // 231 ç c CEDILLA + ISO88591E8 Keycode = graveAccentBits + KeyE // 232 è e GRAVE + ISO88591E9 Keycode = acuteAccentBits + KeyE // 233 é e ACUTE + ISO88591EA Keycode = circumflexBits + KeyE // 234 ê e CIRCUMFLEX + ISO88591EB Keycode = diaeresisBits + KeyE // 235 ë e DIAERESIS + ISO88591EC Keycode = graveAccentBits + KeyI // 236 ì i GRAVE + ISO88591ED Keycode = KeyI + altgrMask // 237 í i ACUTE + ISO88591EE Keycode = circumflexBits + KeyI // 238 î i CIRCUMFLEX + ISO88591EF Keycode = diaeresisBits + KeyI // 239 ï i DIAERESIS + ISO88591F0 Keycode = KeyD + altgrMask // 240 ð ETH + ISO88591F1 Keycode = KeyN + altgrMask // 241 ñ n TILDE + ISO88591F2 Keycode = graveAccentBits + KeyO // 242 ò o GRAVE + ISO88591F3 Keycode = KeyO + altgrMask // 243 ó o ACUTE + ISO88591F4 Keycode = circumflexBits + KeyO // 244 ô o CIRCUMFLEX + ISO88591F5 Keycode = tildeBits + KeyO // 245 õ o TILDE + ISO88591F6 Keycode = KeyP + altgrMask // 246 ö o DIAERESIS + ISO88591F7 Keycode = KeyEqual + altgrMask + shiftMask // 247 ÷ DIVISION + ISO88591F8 Keycode = KeyL + altgrMask // 248 ø o STROKE + ISO88591F9 Keycode = graveAccentBits + KeyU // 249 ù u GRAVE + ISO88591FA Keycode = KeyU + altgrMask // 250 ú u ACUTE + ISO88591FB Keycode = circumflexBits + KeyU // 251 û u CIRCUMFLEX + ISO88591FC Keycode = KeyY + altgrMask // 252 ü u DIAERESIS + ISO88591FD Keycode = acuteAccentBits + KeyY // 253 ý y ACUTE + ISO88591FE Keycode = KeyT + altgrMask // 254 þ THORN + ISO88591FF Keycode = diaeresisBits + KeyY // 255 ÿ y DIAERESIS + UNICODE20AC Keycode = Key5 + altgrMask // € Euro Sign + UNICODEEXTRA00 Keycode = 0x20AC + KEYCODEEXTRA00 Keycode = Key5 + altgrMask // 20AC € Euro Sign + + // ASCII20 Keycode = KeySpace // 32 SPACE + // ASCII21 Keycode = Key1 + shiftMask // 33 ! + // ASCII22 Keycode = KeyQuote + shiftMask // 34 " + // ASCII23 Keycode = Key3 + shiftMask // 35 # + // ASCII24 Keycode = Key4 + shiftMask // 36 $ + // ASCII25 Keycode = Key5 + shiftMask // 37 % + // ASCII26 Keycode = Key7 + shiftMask // 38 & + // ASCII27 Keycode = KeyQuote // 39 ' + // ASCII28 Keycode = Key9 + shiftMask // 40 ( + // ASCII29 Keycode = Key0 + shiftMask // 41 ) + // ASCII2A Keycode = Key8 + shiftMask // 42 * + // ASCII2B Keycode = KeyEqual + shiftMask // 43 + + // ASCII2C Keycode = KeyComma // 44 , + // ASCII2D Keycode = KeyMinus // 45 - + // ASCII2E Keycode = KeyPeriod // 46 . + // ASCII2F Keycode = KeySlash // 47 / + // ASCII30 Keycode = Key0 // 48 0 + // ASCII31 Keycode = Key1 // 49 1 + // ASCII32 Keycode = Key2 // 50 2 + // ASCII33 Keycode = Key3 // 51 3 + // ASCII34 Keycode = Key4 // 52 4 + // ASCII35 Keycode = Key5 // 53 5 + // ASCII36 Keycode = Key6 // 54 6 + // ASCII37 Keycode = Key7 // 55 7 + // ASCII38 Keycode = Key8 // 55 8 + // ASCII39 Keycode = Key9 // 57 9 + // ASCII3A Keycode = KeySemicolon + shiftMask // 58 : + // ASCII3B Keycode = KeySemicolon // 59 ; + // ASCII3C Keycode = KeyComma + shiftMask // 60 < + // ASCII3D Keycode = KeyEqual // 61 = + // ASCII3E Keycode = KeyPeriod + shiftMask // 62 > + // ASCII3F Keycode = KeySlash + shiftMask // 63 ? + // ASCII40 Keycode = Key2 + shiftMask // 64 @ + // ASCII41 Keycode = KeyA + shiftMask // 65 A + // ASCII42 Keycode = KeyB + shiftMask // 66 B + // ASCII43 Keycode = KeyC + shiftMask // 67 C + // ASCII44 Keycode = KeyD + shiftMask // 68 D + // ASCII45 Keycode = KeyE + shiftMask // 69 E + // ASCII46 Keycode = KeyF + shiftMask // 70 F + // ASCII47 Keycode = KeyG + shiftMask // 71 G + // ASCII48 Keycode = KeyH + shiftMask // 72 H + // ASCII49 Keycode = KeyI + shiftMask // 73 I + // ASCII4A Keycode = KeyJ + shiftMask // 74 J + // ASCII4B Keycode = KeyK + shiftMask // 75 K + // ASCII4C Keycode = KeyL + shiftMask // 76 L + // ASCII4D Keycode = KeyM + shiftMask // 77 M + // ASCII4E Keycode = KeyN + shiftMask // 78 N + // ASCII4F Keycode = KeyO + shiftMask // 79 O + // ASCII50 Keycode = KeyP + shiftMask // 80 P + // ASCII51 Keycode = KeyQ + shiftMask // 81 Q + // ASCII52 Keycode = KeyR + shiftMask // 82 R + // ASCII53 Keycode = KeyS + shiftMask // 83 S + // ASCII54 Keycode = KeyT + shiftMask // 84 T + // ASCII55 Keycode = KeyU + shiftMask // 85 U + // ASCII56 Keycode = KeyV + shiftMask // 86 V + // ASCII57 Keycode = KeyW + shiftMask // 87 W + // ASCII58 Keycode = KeyX + shiftMask // 88 X + // ASCII59 Keycode = KeyY + shiftMask // 89 Y + // ASCII5A Keycode = KeyZ + shiftMask // 90 Z + // ASCII5B Keycode = KeyLeftBrace // 91 [ + // ASCII5C Keycode = KeyBackslash // 92 \ + // ASCII5D Keycode = KeyRightBrace // 93 ] + // ASCII5E Keycode = Key6 + shiftMask // 94 ^ + // ASCII5F Keycode = KeyMinus + shiftMask // 95 + // ASCII60 Keycode = KeyTilde // 96 ` + // ASCII61 Keycode = KeyA // 97 a + // ASCII62 Keycode = KeyB // 98 b + // ASCII63 Keycode = KeyC // 99 c + // ASCII64 Keycode = KeyD // 100 d + // ASCII65 Keycode = KeyE // 101 e + // ASCII66 Keycode = KeyF // 102 f + // ASCII67 Keycode = KeyG // 103 g + // ASCII68 Keycode = KeyH // 104 h + // ASCII69 Keycode = KeyI // 105 i + // ASCII6A Keycode = KeyJ // 106 j + // ASCII6B Keycode = KeyK // 107 k + // ASCII6C Keycode = KeyL // 108 l + // ASCII6D Keycode = KeyM // 109 m + // ASCII6E Keycode = KeyN // 110 n + // ASCII6F Keycode = KeyO // 111 o + // ASCII70 Keycode = KeyP // 112 p + // ASCII71 Keycode = KeyQ // 113 q + // ASCII72 Keycode = KeyR // 114 r + // ASCII73 Keycode = KeyS // 115 s + // ASCII74 Keycode = KeyT // 116 t + // ASCII75 Keycode = KeyU // 117 u + // ASCII76 Keycode = KeyV // 118 v + // ASCII77 Keycode = KeyW // 119 w + // ASCII78 Keycode = KeyX // 120 x + // ASCII79 Keycode = KeyY // 121 y + // ASCII7A Keycode = KeyZ // 122 z + // ASCII7B Keycode = KeyLeftBrace + shiftMask // 123 { + // ASCII7C Keycode = KeyBackslash + shiftMask // 124 | + // ASCII7D Keycode = KeyRightBrace + shiftMask // 125 } + // ASCII7E Keycode = KeyTilde + shiftMask // 126 ~ + // ASCII7F Keycode = KeyBackspace // 127 DEL +) + +var ascii = [...]Keycode{ + ASCII00.mask(), ASCII01.mask(), ASCII02.mask(), ASCII03.mask(), + ASCII04.mask(), ASCII05.mask(), ASCII06.mask(), ASCII07.mask(), + ASCII08.mask(), ASCII09.mask(), ASCII0A.mask(), ASCII0B.mask(), + ASCII0C.mask(), ASCII0D.mask(), ASCII0E.mask(), ASCII0F.mask(), + ASCII10.mask(), ASCII11.mask(), ASCII12.mask(), ASCII13.mask(), + ASCII14.mask(), ASCII15.mask(), ASCII16.mask(), ASCII17.mask(), + ASCII18.mask(), ASCII19.mask(), ASCII1A.mask(), ASCII1B.mask(), + ASCII1C.mask(), ASCII1D.mask(), ASCII1E.mask(), ASCII1F.mask(), + ASCII20.mask(), ASCII21.mask(), ASCII22.mask(), ASCII23.mask(), + ASCII24.mask(), ASCII25.mask(), ASCII26.mask(), ASCII27.mask(), + ASCII28.mask(), ASCII29.mask(), ASCII2A.mask(), ASCII2B.mask(), + ASCII2C.mask(), ASCII2D.mask(), ASCII2E.mask(), ASCII2F.mask(), + ASCII30.mask(), ASCII31.mask(), ASCII32.mask(), ASCII33.mask(), + ASCII34.mask(), ASCII35.mask(), ASCII36.mask(), ASCII37.mask(), + ASCII38.mask(), ASCII39.mask(), ASCII3A.mask(), ASCII3B.mask(), + ASCII3C.mask(), ASCII3D.mask(), ASCII3E.mask(), ASCII3F.mask(), + ASCII40.mask(), ASCII41.mask(), ASCII42.mask(), ASCII43.mask(), + ASCII44.mask(), ASCII45.mask(), ASCII46.mask(), ASCII47.mask(), + ASCII48.mask(), ASCII49.mask(), ASCII4A.mask(), ASCII4B.mask(), + ASCII4C.mask(), ASCII4D.mask(), ASCII4E.mask(), ASCII4F.mask(), + ASCII50.mask(), ASCII51.mask(), ASCII52.mask(), ASCII53.mask(), + ASCII54.mask(), ASCII55.mask(), ASCII56.mask(), ASCII57.mask(), + ASCII58.mask(), ASCII59.mask(), ASCII5A.mask(), ASCII5B.mask(), + ASCII5C.mask(), ASCII5D.mask(), ASCII5E.mask(), ASCII5F.mask(), + ASCII60.mask(), ASCII61.mask(), ASCII62.mask(), ASCII63.mask(), + ASCII64.mask(), ASCII65.mask(), ASCII66.mask(), ASCII67.mask(), + ASCII68.mask(), ASCII69.mask(), ASCII6A.mask(), ASCII6B.mask(), + ASCII6C.mask(), ASCII6D.mask(), ASCII6E.mask(), ASCII6F.mask(), + ASCII70.mask(), ASCII71.mask(), ASCII72.mask(), ASCII73.mask(), + ASCII74.mask(), ASCII75.mask(), ASCII76.mask(), ASCII77.mask(), + ASCII78.mask(), ASCII79.mask(), ASCII7A.mask(), ASCII7B.mask(), + ASCII7C.mask(), ASCII7D.mask(), ASCII7E.mask(), ASCII7F.mask(), +} + +var iso88591 = [...]Keycode{ + ISO88591A0.mask(), ISO88591A1.mask(), ISO88591A2.mask(), ISO88591A3.mask(), + ISO88591A4.mask(), ISO88591A5.mask(), ISO88591A6.mask(), ISO88591A7.mask(), + ISO88591A8.mask(), ISO88591A9.mask(), ISO88591AA.mask(), ISO88591AB.mask(), + ISO88591AC.mask(), ISO88591AD.mask(), ISO88591AE.mask(), ISO88591AF.mask(), + ISO88591B0.mask(), ISO88591B1.mask(), ISO88591B2.mask(), ISO88591B3.mask(), + ISO88591B4.mask(), ISO88591B5.mask(), ISO88591B6.mask(), ISO88591B7.mask(), + ISO88591B8.mask(), ISO88591B9.mask(), ISO88591BA.mask(), ISO88591BB.mask(), + ISO88591BC.mask(), ISO88591BD.mask(), ISO88591BE.mask(), ISO88591BF.mask(), + ISO88591C0.mask(), ISO88591C1.mask(), ISO88591C2.mask(), ISO88591C3.mask(), + ISO88591C4.mask(), ISO88591C5.mask(), ISO88591C6.mask(), ISO88591C7.mask(), + ISO88591C8.mask(), ISO88591C9.mask(), ISO88591CA.mask(), ISO88591CB.mask(), + ISO88591CC.mask(), ISO88591CD.mask(), ISO88591CE.mask(), ISO88591CF.mask(), + ISO88591D0.mask(), ISO88591D1.mask(), ISO88591D2.mask(), ISO88591D3.mask(), + ISO88591D4.mask(), ISO88591D5.mask(), ISO88591D6.mask(), ISO88591D7.mask(), + ISO88591D8.mask(), ISO88591D9.mask(), ISO88591DA.mask(), ISO88591DB.mask(), + ISO88591DC.mask(), ISO88591DD.mask(), ISO88591DE.mask(), ISO88591DF.mask(), + ISO88591E0.mask(), ISO88591E1.mask(), ISO88591E2.mask(), ISO88591E3.mask(), + ISO88591E4.mask(), ISO88591E5.mask(), ISO88591E6.mask(), ISO88591E7.mask(), + ISO88591E8.mask(), ISO88591E9.mask(), ISO88591EA.mask(), ISO88591EB.mask(), + ISO88591EC.mask(), ISO88591ED.mask(), ISO88591EE.mask(), ISO88591EF.mask(), + ISO88591F0.mask(), ISO88591F1.mask(), ISO88591F2.mask(), ISO88591F3.mask(), + ISO88591F4.mask(), ISO88591F5.mask(), ISO88591F6.mask(), ISO88591F7.mask(), + ISO88591F8.mask(), ISO88591F9.mask(), ISO88591FA.mask(), ISO88591FB.mask(), + ISO88591FC.mask(), ISO88591FD.mask(), ISO88591FE.mask(), ISO88591FF.mask(), +} diff --git a/src/machine/usb/uart.go b/src/machine/usb/uart.go index 7c1ae2e90..f7e8f3aef 100644 --- a/src/machine/usb/uart.go +++ b/src/machine/usb/uart.go @@ -11,28 +11,29 @@ var ( ErrUARTWriteFailed = errors.New("USB write failure") ) -type ( - UARTConfig struct { - BaudRate uint32 - } +// UART represents a virtual serial (UART) device emulation using the USB +// CDC-ACM device class driver. +type UART struct { + Port int + core *core +} - // UART represents a virtual serial (UART) device emulation using the USB - // CDC-ACM device class driver. - UART struct { - port int // USB port (core index, e.g., 0-1) - core *core - } -) +type UARTConfig struct { + // Port is the MCU's native USB core number. If in doubt, leave it + // uninitialized for default (0). + Port int +} func (uart *UART) Configure(config UARTConfig) error { - if uart.port >= CoreCount || uart.port >= dcdCount { + if config.Port >= CoreCount || config.Port >= dcdCount { return ErrUARTInvalidPort } + uart.Port = config.Port // verify we have a free USB port and take ownership of it var st status - uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1}) + uart.core, st = initCore(uart.Port, class{id: classDeviceCDCACM, config: 1}) if !st.ok() { return ErrUARTInvalidPort } diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go index 40d325ddb..5d4c4e4ca 100644 --- a/src/machine/usb/usb.go +++ b/src/machine/usb/usb.go @@ -106,14 +106,15 @@ type class struct { // Enumerated constants for all host/device class configurations. const ( - classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) + classDeviceCDCACM = 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: + case classDeviceCDCACM, classDeviceHID: return modeDevice default: return modeIdle diff --git a/src/machine/usb/util.go b/src/machine/usb/util.go index 94dfff99d..5b88f7a50 100644 --- a/src/machine/usb/util.go +++ b/src/machine/usb/util.go @@ -1,5 +1,8 @@ package usb +//go:linkname ticks runtime.ticks +func ticks() int64 + // leU64 returns a slice containing 8 bytes from the given uint64 u. // // The returned bytes have little-endian ordering; that is, the first element @@ -214,3 +217,55 @@ func endpointIndex(address uint8) uint8 { return ((address & descEndptAddrNumberMsk) << 1) | ((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos) } + +// The following buffLo and buffHi are helper methods for slice definitions from +// potentially zero-length arrays (depending on compile-time constants). +// +// For example, if we have an array containing a 5-element buffer for three +// instances of some device class (15 total elements), partitioned as follows, +// then we compute the indices for instance 2 as usual: +// +// Index: 01234 56789 ABCDE +// Array: [ 1 | 2 | 3 ] +// +// Lo: (n-1) * size => (2-1) * 5 => 5 +// Hi: (n) * size => (2) * 5 => 10 (0xA) +// +// However, if we have specified (via const definition) that 0 instances of some +// device class be allocated, then the associated device class buffer arrays +// will all be zero-length arrays, and the arithmetic to compute the slice +// indices used above will result in out-of-bounds indices: +// +// Index: +// Array: [] +// +// Lo: (n-1) * size => (2-1) * 5 => 5 [Error!] +// Hi: (n) * size => (2) * 5 => 10 (0xA) [Error!] +// +// +// I couldn't figure out a straight-forward way to resolve these slice indices +// using only arithmetic, so I've resorted to simple conditionals. If the number +// of instances for some given class is zero (count=0), defined via compile-time +// constant, then just use the empty slice range [0:0]. + +// buffLo returns the starting array slice index for the n'th region of size +// elements from an array containing count regions of size elements. +// Regions are specified using a 1-based index (n > 0). Returns 0 if any given +// argument equals 0. +func buffLo(n, count, size uint16) uint16 { + if 0 == n || 0 == count || 0 == size { + return 0 + } + return (n - 1) * size +} + +// buffHi returns the ending array slice index for the n'th region of size +// elements from an array containing count regions of size elements. +// Regions are specified using a 1-based index (n > 0). Returns 0 if any given +// argument equals 0. +func buffHi(n, count, size uint16) uint16 { + if 0 == n || 0 == count || 0 == size { + return 0 + } + return n * size +} diff --git a/src/machine/usb/util_arm.go b/src/machine/usb/util_arm.go index 750629357..f40112fea 100644 --- a/src/machine/usb/util_arm.go +++ b/src/machine/usb/util_arm.go @@ -4,9 +4,6 @@ package usb import "device/arm" -//go:linkname ticks runtime.ticks -func ticks() int64 - // udelay waits for the given number of microseconds before returning. // We cannot use the sleep timer from this context (import cycle), but we need // an approximate method to spin CPU cycles for short periods of time. diff --git a/src/runtime/runtime_mimxrt1062.go b/src/runtime/runtime_mimxrt1062.go index 2f201093a..994c355b9 100644 --- a/src/runtime/runtime_mimxrt1062.go +++ b/src/runtime/runtime_mimxrt1062.go @@ -7,6 +7,7 @@ import ( "device/arm" "device/nxp" "machine" + "machine/usb" "math/bits" "unsafe" ) @@ -124,7 +125,8 @@ func initUART() { } func initUSB() { - machine.UART0.Configure(machine.UARTConfig{}) + machine.HID0.Configure(usb.HIDConfig{}) + // machine.UART0.Configure(usb.UARTConfig{}) } func putchar(c byte) {