diff --git a/src/device/nxp/mimxrt1062_clock.go b/src/device/nxp/mimxrt1062_clock.go index 38e6e5335..ae788b75a 100644 --- a/src/device/nxp/mimxrt1062_clock.go +++ b/src/device/nxp/mimxrt1062_clock.go @@ -7,6 +7,7 @@ package nxp import ( + "device/arm" "runtime/volatile" "unsafe" ) @@ -377,30 +378,6 @@ func (clk Clock) setCcm(value uint32) { } } -func setSysPfd(value ...uint32) { - for i, val := range value { - pfd528 := CCM_ANALOG.PFD_528.Get() & - ^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i))) - frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk - // disable the clock output first - CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i)))) - // set the new value and enable output - CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i)))) - } -} - -func setUsb1Pfd(value ...uint32) { - for i, val := range value { - pfd480 := CCM_ANALOG.PFD_480.Get() & - ^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i))) - frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk - // disable the clock output first - CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i)))) - // set the new value and enable output - CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i)))) - } -} - // PLL configuration for ARM type ClockConfigArmPll struct { LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2. @@ -471,59 +448,178 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) { setSysPfd(pfd...) } -// PLL configuration for USB -type ClockConfigUsbPll struct { - Instance uint8 // USB PLL number (1 or 2) - LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22 - Src uint8 // Pll clock source, reference _clock_pll_clk_src +func setSysPfd(value ...uint32) { + for i, val := range value { + pfd528 := CCM_ANALOG.PFD_528.Get() & + ^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i))) + frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk + // disable the clock output first + CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i)))) + // set the new value and enable output + CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i)))) + } } -func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) { +// PHY configuration for USB HS +type ClockConfigUsbPhy struct { + Instance uint8 // USB PHY number (1 or 2) + XtalFreq uint32 // External reference clock frequency (Hz) + DCal uint32 // Decode to trim nominal 17.78mA current source + TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+ + TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D- + PllConfig ClockConfigUsbPll +} +// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including +// the USB +3V regulator (PMU), for use as either USB host or device. +func (cfg ClockConfigUsbPhy) Configure() { + + var ( + usb *USB_Type + phy *USBPHY_Type + chrgDetectReg *volatile.Register32 + chrgDetectMsk uint32 + ) + + // Select appropriate peripherals based on receiver Instance switch cfg.Instance { case 1: - - // bypass PLL first - src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk - CCM_ANALOG.PLL_USB1.Set( - (CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) | - CCM_ANALOG_PLL_USB1_BYPASS_Msk | src) - - sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk) - CCM_ANALOG.PLL_USB1_SET.Set( - (CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) | - CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk | - CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel) - - for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) { - } - - // disable bypass - CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk) - - // update PFDs after update - setUsb1Pfd(pfd...) - + usb = USB1 // Select USB1 HS PHY/PLL + phy = USBPHY1 // + chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET + chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B | + USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B case 2: - // bypass PLL first - src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk - CCM_ANALOG.PLL_USB2.Set( - (CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) | - CCM_ANALOG_PLL_USB2_BYPASS_Msk | src) + usb = USB2 // Select USB2 HS PHY/PLL + phy = USBPHY2 // + chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET + chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B | + USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B + default: + panic("nxp: invalid USB PHY") + } - sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk) - CCM_ANALOG.PLL_USB2.Set( - (CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) | - CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk | - CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel) + // Configure and enable USB PLL clocks + cfg.PllConfig.Configure() - for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) { + // Release PHY from reset + phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST) + phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE) + + // Enable power to USB PHY + phy.PWD.Set(0) + phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE | + // enable support for low-speed device connection, direct and indirect (hub) + USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3) + + // Enable USB HS clocks gate + ClockIpUsbOh3.Enable(true) + // Reset USB peripheral + usb.USBCMD.SetBits(USB_USBCMD_RST) + + // Add a delay after RST to ensure there is a USB D+ pullup sequence + nopDelay(400000) + + // Enable USB LDO + PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) | + (0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG) + + // check whether we are connected to USB charger + chrgDetectReg.Set(chrgDetectMsk) + + // Decode to trim nominal 17.78mA source for HS TX on USB D+/D- + phy.TX.Set((phy.TX.Get() & + ^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) | + ((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) | + ((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) | + ((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk)) +} + +// PLL configuration for USB +type ClockConfigUsbPll struct { + Instance uint8 // USB PLL number (1 or 2) + LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22]) + Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N]) + Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default) +} + +func (cfg ClockConfigUsbPll) Configure() { + // select USB peripheral registers based on receiver's Instance + switch cfg.Instance { + case 1: // USB1 PLL + if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) { + // PLL already configured, enable USB clocks + CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS) + } else { + // bypass PLL first + src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & + CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk + CCM_ANALOG.PLL_USB1.Set( + (CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) | + CCM_ANALOG_PLL_USB1_BYPASS | src) + // reconfigure PLL + sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk + CCM_ANALOG.PLL_USB1.Set( + (CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) | + CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER | + CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel) + for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) { + } + // disable bypass + CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS) + + // update PFDs (if provided) + if nil != cfg.Pfd { + setUsb1Pfd(cfg.Pfd...) + } + } + case 2: // USB2 PLL + if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) { + // PLL already configured, enable USB clocks + CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS) + } else { + // bypass PLL first + src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & + CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk + CCM_ANALOG.PLL_USB2.Set( + (CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) | + CCM_ANALOG_PLL_USB2_BYPASS | src) + // reconfigure PLL + sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk + CCM_ANALOG.PLL_USB2.Set( + (CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) | + CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER | + CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel) + for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) { + } + // disable bypass + CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS) } - - // disable bypass - CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk) default: panic("nxp: invalid USB PLL") } } + +func setUsb1Pfd(value ...uint32) { + for i, val := range value { + pfd480 := CCM_ANALOG.PFD_480.Get() & + ^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i))) + frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk + // disable the clock output first + CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i)))) + // set the new value and enable output + CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i)))) + } +} + +// 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. +// go:inline +func nopDelay(cycles uint32) { + for i := uint32(0); i < cycles; i++ { + arm.Asm(`nop`) + } +} diff --git a/src/device/nxp/mimxrt1062_mpu.go b/src/device/nxp/mimxrt1062_mpu.go index f69dfc3fa..46f562af8 100644 --- a/src/device/nxp/mimxrt1062_mpu.go +++ b/src/device/nxp/mimxrt1062_mpu.go @@ -278,3 +278,83 @@ func enableDcache(enable bool) { } } } + +// FlushDcache flushes data from cache to memory +// +// Normally FlushDcache is used when metadata written to memory will be used by +// a DMA or a bus-controller peripheral. Any data in the cache is written to +// memory. A copy remains in the cache, so this is typically used with special +// fields you will want to quickly access in the future. For data transmission, +// use FlushDeleteDcache. +//go:inline +func FlushDcache(addr, size uintptr) { + location := addr & 0xFFFFFFE0 + endAddr := addr + size + arm.AsmFull(` + dsb 0xF + `, nil) + for { + SystemControl.DCCMVAC.Set(uint32(location)) + location += 32 + if location >= endAddr { + break + } + } + arm.AsmFull(` + dsb 0xF + isb 0xF + `, nil) +} + +// DeleteDcache deletes data from the cache, without touching memory. +// +// Normally DeleteDcache is used before receiving data via DMA or from +// bus-controller peripherals which write to memory. You want to delete anything +// the cache may have stored, so your next read is certain to access the +// physical memory. +//go:inline +func DeleteDcache(addr, size uintptr) { + location := addr & 0xFFFFFFE0 + endAddr := addr + size + arm.AsmFull(` + dsb 0xF + `, nil) + for { + SystemControl.DCIMVAC.Set(uint32(location)) + location += 32 + if location >= endAddr { + break + } + } + arm.AsmFull(` + dsb 0xF + isb 0xF + `, nil) +} + +// FlushDeleteDcache flushes data from cache to memory, and delete it from the +// cache +// +// Normally FlushDeleteDcache is used when transmitting data via DMA or +// bus-controller peripherals which read from memory. You want any cached data +// written to memory, and then removed from the cache, because you no longer +// need to access the data after transmission. +//go:inline +func FlushDeleteDcache(addr, size uintptr) { + location := addr & 0xFFFFFFE0 + endAddr := addr + size + arm.AsmFull(` + dsb 0xF + `, nil) + for { + SystemControl.DCCIMVAC.Set(uint32(location)) + location += 32 + if location >= endAddr { + break + } + } + arm.AsmFull(` + dsb 0xF + isb 0xF + `, nil) +} 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_matrixportal-m4_baremetal.go b/src/machine/board_matrixportal-m4_baremetal.go new file mode 100644 index 000000000..5c57fbd35 --- /dev/null +++ b/src/machine/board_matrixportal-m4_baremetal.go @@ -0,0 +1,53 @@ +// +build sam,atsamd51,matrixportal_m4 + +package machine + +import ( + "device/sam" + "runtime/interrupt" +) + +// UART on the MatrixPortal M4 +var ( + Serial = UART1 + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: sam.SERCOM1_USART_INT, + SERCOM: 1, + } + + UART2 = &_UART2 + _UART2 = UART{ + Buffer: NewRingBuffer(), + Bus: sam.SERCOM4_USART_INT, + SERCOM: 4, + } +) + +func init() { + UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, _UART1.handleInterrupt) + UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, _UART2.handleInterrupt) +} + +// I2C on the MatrixPortal M4 +var ( + I2C0 = &I2C{ + Bus: sam.SERCOM5_I2CM, + SERCOM: 5, + } +) + +// SPI on the MatrixPortal M4 +var ( + SPI0 = SPI{ + Bus: sam.SERCOM3_SPIM, + SERCOM: 3, // BUG: SDO on SERCOM1! + } + NINA_SPI = SPI0 + + SPI1 = SPI{ + Bus: sam.SERCOM0_SPIM, + SERCOM: 0, + } +) diff --git a/src/machine/board_teensy40.go b/src/machine/board_teensy40.go index f96f63de7..a28c37992 100644 --- a/src/machine/board_teensy40.go +++ b/src/machine/board_teensy40.go @@ -4,6 +4,7 @@ package machine import ( "device/nxp" + "machine/usb" "runtime/interrupt" ) @@ -55,21 +56,21 @@ const ( // Analog pins const ( - // = Pin // Dig | [Pad] {ADC1/ADC2} - A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 } - A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 } - A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 } - A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 } - A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 } - A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 } - A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 } - A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 } - A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 } - A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 } - A10 = PA12 // D24 | [AD_B0_12] { 1 / - } - A11 = PA13 // D25 | [AD_B0_13] { 2 / - } - A12 = PA30 // D26 | [AD_B1_14] { - / 3 } - A13 = PA31 // D27 | [AD_B1_15] { - / 4 } + // = Pin // Dig [Pad] {ADC1/ADC2} + A0 = PA18 // D14 [AD_B1_02] { 7 / 7 } + A1 = PA19 // D15 [AD_B1_03] { 8 / 8 } + A2 = PA23 // D16 [AD_B1_07] { 12 / 12 } + A3 = PA22 // D17 [AD_B1_06] { 11 / 11 } + A4 = PA17 // D18 [AD_B1_01] { 6 / 6 } + A5 = PA16 // D19 [AD_B1_00] { 5 / 5 } + A6 = PA26 // D20 [AD_B1_10] { 15 / 15 } + A7 = PA27 // D21 [AD_B1_11] { 0 / 0 } + A8 = PA24 // D22 [AD_B1_08] { 13 / 13 } + A9 = PA25 // D23 [AD_B1_09] { 14 / 14 } + A10 = PA12 // D24 [AD_B0_12] { 1 / - } + A11 = PA13 // D25 [AD_B0_13] { 2 / - } + A12 = PA30 // D26 [AD_B1_14] { - / 3 } + A13 = PA31 // D27 [AD_B1_15] { - / 4 } ) // Default peripheral pins @@ -104,6 +105,15 @@ func init() { _UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt) } +// #=====================================================# +// | USB | +// #=====================================================# +var ( + // UART0 = usb.UART{Port: 0} + HID0 = usb.HID{Port: 0} + UART0 = &UART1 +) + // #=====================================================# // | UART | // #===========#===========#=============#===============# diff --git a/src/machine/machine_mimxrt1062.go b/src/machine/machine_mimxrt1062.go index 38cb71f7e..d0dd8930b 100644 --- a/src/machine/machine_mimxrt1062.go +++ b/src/machine/machine_mimxrt1062.go @@ -11,6 +11,7 @@ import ( // Peripheral abstraction layer for the MIMXRT1062 +//go:inline func CPUFrequency() uint32 { return 600000000 } diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go new file mode 100644 index 000000000..d4cbdfa73 --- /dev/null +++ b/src/machine/usb/dcd.go @@ -0,0 +1,935 @@ +package usb + +// Implementation of 32-bit target-agnostic USB device controller driver (dcd). + +import "unsafe" + +func init() { + if unsafe.Sizeof(uintptr(0)) > 4 { + panic("USB device controller is only supported on 32-bit systems") + } +} + +// dcdCount defines the number of USB cores to configure for device mode. It is +// computed as the sum of all declared device configuration descriptors. +const dcdCount = descCDCACMCount + descHIDCount + +// dcdInstance provides statically-allocated instances of each USB device +// controller configured on this platform. +var dcdInstance [dcdCount]dcd + +// dhwInstance provides statically-allocated instances of each USB hardware +// abstraction for ports configured as device on this platform. +var dhwInstance [dcdCount]dhw + +// dcd implements a generic USB device controller driver (dcd) for 32-bit ARM +// targets. +type dcd struct { + *dhw // USB hardware abstraction layer + + core *core // Parent USB core this instance is attached to + port int // USB port index + cc class // USB device class + id int // USB device controller index +} + +// initDCD initializes and assigns a free device controller instance to the +// given USB port. Returns the initialized device controller or nil if no free +// device controller instances remain. +func initDCD(port int, class class) (*dcd, status) { + if 0 == dcdCount { + return nil, statusInvalid // Must have defined device controllers + } + switch class.id { + case classDeviceCDCACM: + if 0 == class.config || class.config > descCDCACMCount { + return nil, statusInvalid // Must have defined descriptors + } + default: + } + // Return the first instance whose assigned core is currently nil. + for i := range dcdInstance { + if nil == dcdInstance[i].core { + // Initialize device controller. + dcdInstance[i].dhw = allocDHW(port, i, &dcdInstance[i]) + dcdInstance[i].core = &coreInstance[port] + dcdInstance[i].port = port + dcdInstance[i].cc = class + dcdInstance[i].id = i + return &dcdInstance[i], statusOK + } + } + return nil, statusBusy // No free device controller instances available. +} + +// class returns the receiver's current device class configuration. +func (d *dcd) class() class { return d.cc } + +// dcdSetupSize defines the size (bytes) of a USB standard setup packet. +const dcdSetupSize = 8 // bytes + +// dcdSetup contains the USB standard setup packet used to configure a device. +type dcdSetup struct { + bmRequestType uint8 + bRequest uint8 + wValue uint16 + wIndex uint16 + wLength uint16 +} + +// pack returns the receiver setup packet encoded as uint64. +func (s dcdSetup) pack() uint64 { + return ((uint64(s.bmRequestType) & 0xFF) << 0) | + ((uint64(s.bRequest) & 0xFF) << 8) | + ((uint64(s.wValue) & 0xFFFF) << 16) | + ((uint64(s.wIndex) & 0xFFFF) << 32) | + ((uint64(s.wLength) & 0xFFFF) << 48) +} + +// dcdEvent is used to describe virtual interrupts on the USB bus to a device +// controller. +// +// Since the device controller software is intended for use with multiple TinyGo +// targets, all of which may not have exactly the same USB bus interrupts, a +// "virtual interrupt" is defined that is common to all targets. The target's +// hardware implementation (type dhw) is responsible for translating real system +// interrupts it receives into the appropriate virtual interrupt code, defined +// below, and notifying the device controller via method (*dcd).event(dcdEvent). +type dcdEvent struct { + id uint8 + setup dcdSetup + mask uint32 +} + +// Enumerated constants for all possible USB device controller interrupt codes. +const ( + dcdEventInvalid uint8 = iota // Invalid interrupt + dcdEventStatusReset // USB reset received + dcdEventStatusRun // USB controller entered run state + dcdEventStatusSuspend // USB suspend received + dcdEventStatusError // USB error condition detected on bus + dcdEventControlSetup // USB setup received + dcdEventPeripheralReady // USB PHY powered and ready to _go_ + dcdEventTransactComplete // USB transaction complete + dcdEventTimer // USB (system) timer +) + +func (d *dcd) event(ev dcdEvent) { + + switch ev.id { + case dcdEventInvalid: + case dcdEventStatusReset: + d.endpointMask = 0 + + case dcdEventPeripheralReady: + // Configure and enable control endpoint 0 + d.endpointEnable(0, true, 0) + + case dcdEventStatusRun: + case dcdEventStatusSuspend: + case dcdEventStatusError: + case dcdEventControlSetup: + // On control endpoint 0 setup events, the ev.setup field will be defined + switch d.controlSetup(ev.setup) { + case dcdStageSetup: + case dcdStageData: + case dcdStageStatus: + case dcdStageStall: + d.controlStall() + } + + case dcdEventTransactComplete: + case dcdEventTimer: + default: + } +} + +// dcdStage represents the USB transaction stage of a control request. +type dcdStage uint8 + +// Enumerated constants for all possible USB transaction stages. +const ( + dcdStageSetup dcdStage = iota // Indicates no stage transition required + dcdStageData // IN/OUT data transfer + dcdStageStatus // Setup request complete + dcdStageStall // Unhandled or invalid request +) + +// controlSetup handles setup messages on control endpoint 0. +func (d *dcd) controlSetup(sup dcdSetup) dcdStage { + + // Reset endpoint 0 notify mask + d.controlMask = 0 + + // First, switch on the type of request (standard, class, or vendor) + switch sup.bmRequestType & descRequestTypeTypeMsk { + + // === STANDARD REQUEST === + case descRequestTypeTypeStandard: + + // Switch on the recepient and direction of the request + switch sup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- DEVICE Rx (OUT) --- + case descRequestTypeRecipientDevice | descRequestTypeDirOut: + + // Identify which request was received + switch sup.bRequest { + + // SET ADDRESS (0x05): + case descRequestStandardSetAddress: + d.setDeviceAddress(sup.wValue) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup + + // SET CONFIGURATION (0x09): + case descRequestStandardSetConfiguration: + d.cc.config = int(sup.wValue) + if 0 == d.cc.config || d.cc.config > dcdCount { + // Use default if invalid index received + d.cc.config = 1 + } + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.uartConfigure() + d.controlReceive(uintptr(0), 0, false) + + // HID + case classDeviceHID: + d.serialConfigure() + d.keyboardConfigure() + d.mouseConfigure() + d.joystickConfigure() + d.controlReceive(uintptr(0), 0, false) + + default: + // Unhandled device class + } + return dcdStageSetup + + default: + // Unhandled request + } + + // --- DEVICE Tx (IN) --- + case descRequestTypeRecipientDevice | descRequestTypeDirIn: + + // Identify which request was received + switch sup.bRequest { + + // GET STATUS (0x00): + case descRequestStandardGetStatus: + d.controlReply[0] = 0 + d.controlReply[1] = 0 + d.controlTransmit( + uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) + return dcdStageSetup + + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlDescriptorCDCACM(sup) + return dcdStageSetup + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } + + // GET CONFIGURATION (0x08): + case descRequestStandardGetConfiguration: + d.controlReply[0] = uint8(d.cc.config) + d.controlTransmit( + uintptr(unsafe.Pointer(&d.controlReply[0])), 1, false) + return dcdStageSetup + + default: + // Unhandled request + } + + // --- INTERFACE Tx (IN) --- + case descRequestTypeRecipientInterface | descRequestTypeDirIn: + + // Identify which request was received + switch sup.bRequest { + + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlDescriptorCDCACM(sup) + return 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 + } + + // --- ENDPOINT Rx (OUT) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirOut: + + // Identify which request was received + switch sup.bRequest { + + // CLEAR FEATURE (0x01): + case descRequestStandardClearFeature: + d.endpointClearFeature(uint8(sup.wIndex)) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup + + // SET FEATURE (0x03): + case descRequestStandardSetFeature: + d.endpointSetFeature(uint8(sup.wIndex)) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup + + default: + // Unhandled request + } + + // --- ENDPOINT Tx (IN) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirIn: + + // Identify which request was received + switch sup.bRequest { + + // GET STATUS (0x00): + case descRequestStandardGetStatus: + status := d.endpointStatus(uint8(sup.wIndex)) + d.controlReply[0] = uint8(status) + d.controlReply[1] = uint8(status >> 8) + d.controlTransmit( + uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) + return dcdStageSetup + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch sup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch sup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + // line coding must contain exactly 7 bytes + if descCDCACMCodingSize == sup.wLength { + d.setup = sup + 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 + } + + default: + // Unhandled device class + } + + // CDC | SET CONTROL LINE STATE (0x22): + case descCDCRequestSetControlLineState: + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + + // Determine interface destination of the request + switch sup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + // 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 + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + // CDC | SEND BREAK (0x23): + case descCDCRequestSendBreak: + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup + + default: + // Unhandled device class + } + + // HID | SET REPORT (0x09) + case descHIDRequestSetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + if sup.wLength <= 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 + } + + default: + // Unhandled request recepient or direction + } + + case descRequestTypeTypeVendor: + default: + // Unhandled request type + } + + // All successful requests return early. If we reach this point, the request + // was invalid or unhandled. Stall the endpoint. + return dcdStageStall +} + +// controlComplete handles the setup completion of control endpoint 0. +func (d *dcd) controlComplete(status uint32) { + + // Reset endpoint 0 notify mask + d.controlMask = 0 + + // First, switch on the type of request (standard, class, or vendor) + switch d.setup.bmRequestType & descRequestTypeTypeMsk { + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch d.setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch d.setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + acm := &descCDCACM[d.cc.config-1] + + // Determine interface destination of the request + switch d.setup.wIndex { + + // CDC-ACM Control Interface: + case descCDCACMInterfaceCtrl: + // Notify PHY to handle triggers like special baud rates, which + // signal to reboot into bootloader or begin receiving OTA updates + d.uartSetLineCoding(descCDCACMLineCoding{ + baud: packU32(acm.cx[:]), + stopBits: acm.cx[4], + parity: acm.cx[5], + numBits: acm.cx[6], + }) + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + // HID | SET REPORT (0x09) + case descHIDRequestSetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + hid := &descHID[d.cc.config-1] + + // Determine interface destination of the request + switch d.setup.wIndex { + + // HID Keyboard Interface + case descHIDInterfaceKeyboard: + + // Determine the type of descriptor being requested + switch d.setup.wValue >> 8 { + + // Configuration descriptor + case descTypeConfigure: + if 1 == d.setup.wLength { + hid.keyboard.led = hid.cx[0] + d.controlTransmit(uintptr(0), 0, false) + } + + default: + // Unhandled descriptor type + } + + // HID Serial Interface + case descHIDInterfaceSerial: + + // Determine the type of descriptor being requested + switch d.setup.wValue >> 8 { + + // String descriptor + case descTypeString: + if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) { + d.enableSOF(true, descHIDInterfaceCount) + } + + default: + // Unhandled descriptor type + } + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + + default: + // Unhandled request type + } +} + +func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) { + + acm := &descCDCACM[d.cc.config-1] + dxn := uint8(0) + + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { + + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(acm.dx[:], acm.device[:dxn]) + + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descCDCACMConfigSize) + _ = copy(acm.dx[:], acm.config[:dxn]) + + // String descriptor + case descTypeString: + if 0 == len(acm.locale) { + break // No string descriptors defined! + } + var sd []uint8 + if 0 == uint8(sup.wValue) { + + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This case (setup.wValue = [0x03]00) + // is a string request from the host to determine what that language is. + // + // In subsequent string requests, the host will populate setup.wIndex + // with the language code we return here in this string descriptor. + // + // This way all strings returned to the host are in the same language, + // whatever language that may be. + code := int(sup.wIndex) + if code >= len(acm.locale) { + code = 0 + } + sd = acm.locale[code].descriptor[sup.wValue&0xFF][:] + + } else { + + // setup.wIndex now contains a language code, which we specified in a + // previous request (above: setup.wValue = [0x03]00). We need to locate + // the set of strings whose language matches the language code given in + // this new setup.wIndex. + for code := range acm.locale { + if sup.wIndex == acm.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) { + + // Found language with a string defined at the requested index. + // + // TODO: Add API methods to device controller that allows the user + // to provide these strings at/before driver initialization. + // + // For now, we just always use the descCommon* strings. + var s string + switch uint8(sup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + " CDC-ACM" + case 3: + s = descCommonSerialNumber + } + + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. + sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:] + // String descriptor format is 2-byte header + 2-bytes per rune + sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length + sd[1] = descTypeString // header[1] = descriptor type + // Copy UTF-8 string into string descriptor as UTF-16 + for n, c := range s { + if 2+2*n >= len(sd) { + break + } + sd[2+2*n] = uint8(c) + sd[3+2*n] = 0 + } + break // end search for matching language code + } + } + } + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(acm.dx[:], sd[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(acm.dx[:], acm.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + // TODO + + default: + // Unhandled descriptor type + } + + if dxn > 0 { + if dxn > uint8(sup.wLength) { + dxn = uint8(sup.wLength) + } + flushCache( + uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) + d.controlTransmit( + uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + } +} + +func (d *dcd) controlDescriptorHID(sup dcdSetup) { + + hid := &descHID[d.cc.config-1] + dxn := uint8(0) + pos := uint8(0) + + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { + + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(hid.dx[:], hid.device[:dxn]) + + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descHIDConfigSize) + _ = copy(hid.dx[:], hid.config[:dxn]) + + // String descriptor + case descTypeString: + if 0 == len(hid.locale) { + break // No string descriptors defined! + } + var sd []uint8 + if 0 == uint8(sup.wValue) { + + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This case (setup.wValue = [0x03]00) + // is a string request from the host to determine what that language is. + // + // In subsequent string requests, the host will populate setup.wIndex + // with the language code we return here in this string descriptor. + // + // This way all strings returned to the host are in the same language, + // whatever language that may be. + code := int(sup.wIndex) + if code >= len(hid.locale) { + code = 0 + } + sd = hid.locale[code].descriptor[sup.wValue&0xFF][:] + + } else { + + // setup.wIndex now contains a language code, which we specified in a + // previous request (above: setup.wValue = [0x03]00). We need to locate + // the set of strings whose language matches the language code given in + // this new setup.wIndex. + for code := range hid.locale { + if sup.wIndex == hid.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) { + + // Found language with a string defined at the requested index. + // + // TODO: Add API methods to device controller that allows the user + // to provide these strings at/before driver initialization. + // + // For now, we just always use the descCommon* strings. + var s string + switch uint8(sup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + " HID" + case 3: + s = descCommonSerialNumber + } + + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. + sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:] + // String descriptor format is 2-byte header + 2-bytes per rune + sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length + sd[1] = descTypeString // header[1] = descriptor type + // Copy UTF-8 string into string descriptor as UTF-16 + for n, c := range s { + if 2+2*n >= len(sd) { + break + } + sd[2+2*n] = uint8(c) + sd[3+2*n] = 0 + } + break // end search for matching language code + } + } + } + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(hid.dx[:], sd[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(hid.dx[:], hid.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + // TODO + + // HID descriptor + case descTypeHID: + + // Determine interface destination of the request + switch sup.wIndex { + case descHIDInterfaceKeyboard: + pos = descHIDConfigKeyboardPos + + case descHIDInterfaceMouse: + pos = descHIDConfigMousePos + + case descHIDInterfaceSerial: + pos = descHIDConfigSerialPos + + case descHIDInterfaceJoystick: + pos = descHIDConfigJoystickPos + + case descHIDInterfaceMediaKey: + pos = descHIDConfigMediaKeyPos + + default: + // Unhandled HID interface + } + + if 0 != pos { + dxn = descLengthInterface + _ = copy(hid.dx[:], hid.config[pos:pos+dxn]) + } + + // HID report descriptor + case descTypeHIDReport: + + // Determine interface destination of the request + switch sup.wIndex { + case descHIDInterfaceKeyboard: + dxn = uint8(len(descHIDReportKeyboard)) + _ = copy(hid.dx[:], descHIDReportKeyboard[:]) + + case descHIDInterfaceMouse: + dxn = uint8(len(descHIDReportMouse)) + _ = copy(hid.dx[:], descHIDReportMouse[:]) + + case descHIDInterfaceSerial: + dxn = uint8(len(descHIDReportSerial)) + _ = copy(hid.dx[:], descHIDReportSerial[:]) + + case descHIDInterfaceJoystick: + dxn = uint8(len(descHIDReportJoystick)) + _ = copy(hid.dx[:], descHIDReportJoystick[:]) + + case descHIDInterfaceMediaKey: + dxn = uint8(len(descHIDReportMediaKey)) + _ = copy(hid.dx[:], descHIDReportMediaKey[:]) + + default: + // Unhandled HID interface + } + + default: + // Unhandled descriptor type + } + + if dxn > 0 { + if dxn > uint8(sup.wLength) { + dxn = uint8(sup.wLength) + } + flushCache( + uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn)) + d.controlTransmit( + uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false) + } +} diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go new file mode 100644 index 000000000..ae5c83d22 --- /dev/null +++ b/src/machine/usb/desc.go @@ -0,0 +1,1043 @@ +package usb + +const descUSBSpecVersion = uint16(0x0200) // USB 2.0 + +const descLanguageEnglish = uint16(0x0409) + +// USB constants defined per specification. +const ( + + // Descriptor length + descLengthDevice = 18 + descLengthConfigure = 9 + descLengthInterface = 9 + descLengthEndpoint = 7 + descLengthQualification = 10 + descLengthOTG = 5 + descLengthBOS = 5 + descLengthEndpointCompanion = 6 + descLengthUSB20Extension = 7 + descLengthSuperspeed = 10 + + // Descriptor type + descTypeDevice = 0x01 + descTypeConfigure = 0x02 + descTypeString = 0x03 + descTypeInterface = 0x04 + descTypeEndpoint = 0x05 + descTypeQualification = 0x06 + descTypeOtherSpeedConfiguration = 0x07 + descTypeInterfacePower = 0x08 + descTypeOTG = 0x09 + descTypeInterfaceAssociation = 0x0B + descTypeBOS = 0x0F + descTypeDeviceCapability = 0x10 + descTypeHID = 0x21 + descTypeHIDReport = 0x22 + descTypeHIDPhysical = 0x23 + descTypeCDCInterface = 0x24 + descTypeCDCEndpoint = 0x25 + descTypeEndpointCompanion = 0x30 + + // Standard request type + descRequestTypeDirMsk = 0x80 + descRequestTypeDirPos = 7 + descRequestTypeDirOut = 0x00 + descRequestTypeDirIn = 0x80 + descRequestTypeTypeMsk = 0x60 + descRequestTypeTypePos = 5 + descRequestTypeTypeStandard = 0 + descRequestTypeTypeClass = 0x20 + descRequestTypeTypeVendor = 0x40 + descRequestTypeRecipientMsk = 0x1F + descRequestTypeRecipientPos = 0 + descRequestTypeRecipientDevice = 0x00 + descRequestTypeRecipientInterface = 0x01 + descRequestTypeRecipientEndpoint = 0x02 + descRequestTypeRecipientOther = 0x03 + + // Standard request + descRequestStandardGetStatus = 0x00 + descRequestStandardClearFeature = 0x01 + descRequestStandardSetFeature = 0x03 + descRequestStandardSetAddress = 0x05 + descRequestStandardGetDescriptor = 0x06 + descRequestStandardSetDescriptor = 0x07 + descRequestStandardGetConfiguration = 0x08 + descRequestStandardSetConfiguration = 0x09 + descRequestStandardGetInterface = 0x0A + descRequestStandardSetInterface = 0x0B + descRequestStandardSynchFrame = 0x0C + + // Configuration attributes + descConfigAttrD7Msk = 0x80 + descConfigAttrD7Pos = 7 + descConfigAttrSelfPoweredMsk = 0x40 + descConfigAttrSelfPoweredPos = 6 + descConfigAttrRemoteWakeupMsk = 0x20 + descConfigAttrRemoteWakeupPos = 5 + + // Endpoint type + descEndptTypeControl = 0x00 + descEndptTypeIsochronous = 0x01 + descEndptTypeBulk = 0x02 + descEndptTypeInterrupt = 0x03 + + // Endpoint address + descEndptAddrNumberMsk = 0x0F + descEndptAddrNumberPos = 0 + descEndptAddrDirectionMsk = 0x80 + descEndptAddrDirectionPos = 7 + descEndptAddrDirectionOut = 0 + descEndptAddrDirectionIn = 0x80 + + // Endpoint attributes + descEndptAttrTypeMsk = 0x03 + descEndptAttrNumberPos = 0 + descEndptAttrSyncTypeMsk = 0x0C + descEndptAttrSyncTypePos = 2 + descEndptAttrSyncTypeNoSync = 0x00 + descEndptAttrSyncTypeAsync = 0x04 + descEndptAttrSyncTypeAdaptive = 0x08 + descEndptAttrSyncTypeSync = 0x0C + descEndptAttrUsageTypeMsk = 0x30 + descEndptAttrUsageTypePos = 4 + descEndptAttrUsageTypeData = 0x00 + descEndptAttrUsageTypeFeed = 0x10 + descEndptAttrUsageTypeFeedData = 0x20 + + // Endpoint max packet size + descEndptMaxPktSizeMsk = 0x07FF + descEndptMaxPktSize = 64 + descEndptMaxPktSizeMultMsk = 0x1800 + descEndptMaxPktSizeMultPos = 11 + + // OTG attributes + descOTGAttrSRPMsk = 0x01 + descOTGAttrHNPMsk = 0x02 + descOTGAttrADPMsk = 0x04 + + // Device bus speed + descDeviceSpeedFull = 0x00 + descDeviceSpeedLow = 0x01 + descDeviceSpeedHigh = 0x02 + descDeviceSpeedSuper = 0x04 + + // Device capability type + descDeviceCapTypeWireless = 0x01 + descDeviceCapTypeUSB20Extension = 0x02 + descDeviceCapTypeSuperspeed = 0x03 + + // Device capability attributes (USB 2.0 extension) + descDeviceCapExtAttrLPMMsk = 0x02 + descDeviceCapExtAttrLPMPos = 1 + descDeviceCapExtAttrBESLMsk = 0x04 + descDeviceCapExtAttrBESLPos = 2 +) + +const ( + // Attributes of all endpoint descriptor configurations. + descEndptConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1) + (1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered + (0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup + 0 // Bits 0-4: reserved (0) + + descEndptConfigAttrRxPos = 0 + descEndptConfigAttrTxPos = 16 + descEndptConfigAttrRxMsk = (descEndptConfigAttr | descEndptAttrSyncTypeMsk) << descEndptConfigAttrRxPos + descEndptConfigAttrTxMsk = (descEndptConfigAttr | descEndptAttrSyncTypeMsk) << descEndptConfigAttrTxPos + + descEndptConfigAttrRxUnused = 0x02 << descEndptConfigAttrRxPos + descEndptConfigAttrTxUnused = 0x02 << descEndptConfigAttrTxPos + descEndptConfigAttrRxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrRxPos + descEndptConfigAttrTxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrTxPos + descEndptConfigAttrRxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrRxPos + descEndptConfigAttrTxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrTxPos + descEndptConfigAttrRxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrRxPos + descEndptConfigAttrTxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrTxPos +) + +// USB CDC constants defined per specification. +const ( + + // Device class + descCDCTypeComm = 0x02 // communication/control + descCDCTypeData = 0x0A // data + + // Communication/control subclass + descCDCSubNone = 0x00 + descCDCSubDirectLineControl = 0x01 + descCDCSubAbstractControl = 0x02 + descCDCSubTelephoneControl = 0x03 + descCDCSubMultiChannelControl = 0x04 + descCDCSubCAPIControl = 0x05 + descCDCSubEthernetNetworkingControl = 0x06 + descCDCSubATMNetworkingControl = 0x07 + descCDCSubWirelessHandsetControl = 0x08 + descCDCSubDeviceManagement = 0x09 + descCDCSubMobileDirectLine = 0x0A + descCDCSubOBEX = 0x0B + descCDCSubEthernetEmulation = 0x0C + + // Communication/control protocol + descCDCProtoNone = 0x00 // also for data class + descCDCProtoAT250 = 0x01 + descCDCProtoATPCCA101 = 0x02 + descCDCProtoATPCCA101AnnexO = 0x03 + descCDCProtoATGSM707 = 0x04 + descCDCProtoAT3GPP27007 = 0x05 + descCDCProtoATTIACDMA = 0x06 + descCDCProtoEthernetEmulation = 0x07 + descCDCProtoExternal = 0xFE + descCDCProtoVendorSpecific = 0xFF // also for data class + + // Data protocol + descCDCProtoPyhsicalInterface = 0x30 + descCDCProtoHDLC = 0x31 + descCDCProtoTransparent = 0x32 + descCDCProtoManagement = 0x50 + descCDCProtoDataLinkQ931 = 0x51 + descCDCProtoDataLinkQ921 = 0x52 + descCDCProtoDataCompressionV42BIS = 0x90 + descCDCProtoEuroISDN = 0x91 + descCDCProtoRateAdaptionISDNV24 = 0x92 + descCDCProtoCAPICommands = 0x93 + descCDCProtoHostBasedDriver = 0xFD + descCDCProtoUnitFunctional = 0xFE + + // Functional descriptor length + descCDCFuncLengthHeader = 5 + descCDCFuncLengthCallManagement = 5 + descCDCFuncLengthAbstractControl = 4 + descCDCFuncLengthUnion = 5 + + // Functional descriptor type + descCDCFuncTypeHeader = 0x00 + descCDCFuncTypeCallManagement = 0x01 + descCDCFuncTypeAbstractControl = 0x02 + descCDCFuncTypeDirectLine = 0x03 + descCDCFuncTypeTelephoneRinger = 0x04 + descCDCFuncTypeTelephoneReport = 0x05 + descCDCFuncTypeUnion = 0x06 + descCDCFuncTypeCountrySelect = 0x07 + descCDCFuncTypeTelephoneModes = 0x08 + descCDCFuncTypeTerminal = 0x09 + descCDCFuncTypeNetworkChannel = 0x0A + descCDCFuncTypeProtocolUnit = 0x0B + descCDCFuncTypeExtensionUnit = 0x0C + descCDCFuncTypeMultiChannel = 0x0D + descCDCFuncTypeCAPIControl = 0x0E + descCDCFuncTypeEthernetNetworking = 0x0F + descCDCFuncTypeATMNetworking = 0x10 + descCDCFuncTypeWirelessControl = 0x11 + descCDCFuncTypeMobileDirectLine = 0x12 + descCDCFuncTypeMDLMDetail = 0x13 + descCDCFuncTypeDeviceManagement = 0x14 + descCDCFuncTypeOBEX = 0x15 + descCDCFuncTypeCommandSet = 0x16 + descCDCFuncTypeCommandSetDetail = 0x17 + descCDCFuncTypeTelephoneControl = 0x18 + descCDCFuncTypeOBEXServiceID = 0x19 + + // Standard request + descCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND + descCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE + descCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE + descCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE + descCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE + descCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE + descCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE + descCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP + descCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE + descCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME + descCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK + descCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING + descCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING + descCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE + descCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK + descCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS + descCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS + descCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM + descCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM + descCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS + descCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS + descCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS + descCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER + descCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER + descCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER + descCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS + descCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER + descCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER + descCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER + descCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC + descCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT + descCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS + descCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC + descCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS + descCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK + + // Notification type + descCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION + descCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL + descCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE + descCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT + descCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE + descCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE + descCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE + descCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE + + // Feature select + descCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE + descCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING + + // Control signal + descCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION + descCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE + + // UART emulated state + descCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER + descCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER + descCDCUARTStateBreak = 0x04 // UART state BREAK + descCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL + descCDCUARTStateFraming = 0x10 // UART state FRAMING + descCDCUARTStateParity = 0x20 // UART state PARITY + descCDCUARTStateOverrun = 0x40 // UART state OVERRUN +) + +// USB HID constants defined per specification +const ( + // HID class + descHIDType = 0x03 + + // HID subclass + descHIDSubNone = 0x00 + descHIDSubBoot = 0x01 + + // HID protocol + descHIDProtoNone = 0x00 + descHIDProtoKeyboard = 0x01 + descHIDProtoMouse = 0x02 + + descHIDRequestGetReport = 0x01 // HID request GET_REPORT + descHIDRequestGetReportTypeInput = 0x01 // HID request GET_REPORT type INPUT + descHIDRequestGetReportTypeOutput = 0x02 // HID request GET_REPORT type OUTPUT + descHIDRequestGetReportTypeFeature = 0x03 // HID request GET_REPORT type FEATURE + descHIDRequestGetIdle = 0x02 // HID request GET_IDLE + descHIDRequestGetProtocol = 0x03 // HID request GET_PROTOCOL + descHIDRequestSetReport = 0x09 // HID request SET_REPORT + descHIDRequestSetIdle = 0x0A // HID request SET_IDLE + descHIDRequestSetProtocol = 0x0B // HID request SET_PROTOCOL +) + +const ( + // Size of all CDC-ACM configuration descriptors. + descCDCACMConfigSize = uint16( + descLengthConfigure + // Configuration Header + descLengthInterface + // CDC Interface Descriptor + descCDCFuncLengthHeader + // CDC Header + descCDCFuncLengthCallManagement + // CDC Call Management Func Descriptor + descCDCFuncLengthAbstractControl + // CDC Abstract Control Func Descriptor + descCDCFuncLengthUnion + // CDC Union Functional Descriptor + descLengthEndpoint + // CDC Status IN Endpoint Descriptor + descLengthInterface + // CDC Data Interface Descriptor + descLengthEndpoint + // CDC Data IN Endpoint Descriptor + descLengthEndpoint) // CDC Data OUT Endpoint Descriptor + + // Size of all HID configuration descriptors. + descHIDConfigSize = uint16( + descLengthConfigure + // Configuration Header + descLengthInterface + // Keyboard Interface Descriptor + descLengthInterface + // Keyboard HID Interface Descriptor + descLengthEndpoint + // Keyboard Endpoint Descriptor + descLengthInterface + // Mouse Interface Descriptor + descLengthInterface + // Mouse HID Interface Descriptor + descLengthEndpoint + // Mouse Endpoint Descriptor + descLengthInterface + // Serial Interface Descriptor + descLengthInterface + // Serial HID Interface Descriptor + descLengthEndpoint + // Serial 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 +) + +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 + // remaining bytes are UTF-16 code points, ordered low byte-first. If you just + // want to use UTF-8 (or even ASCII), you still need to reserve 2 bytes for + // each symbol, but you can set all of their high bytes 0. + descString [descStringSize]uint8 + // descStringIndex defines an indexed collection of string descriptors for a + // given language. + descStringIndex [descStringIndexCount]descString + // descStringLanguage contains a language code and an indexed collection of + // string descriptors encoded in that language. + descStringLanguage struct { + language uint16 + descriptor descStringIndex + } +) + +const ( + descStringIndexCount = 4 // Language, Manufacturer, Product, Serial Number + descStringSize = 64 // (64-2)/2 = 31 chars each (UTF-16 code points) + // The maximum allowable string descriptor size is 255, or (255-2)/2 = 126 + // available UTF-16 code points. Considering we are allocating this storage at + // compile-time, it seems like an awful waste of space (255*4 = ~1 KiB) just + // to store four strings, which, in all likelihood, will not be modified by + // anyone other than TinyGo devs; 64*4 = 256 B (i.e., 31 UTF-16 code points + // for each string) seems a good compromise. +) + +// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. +const descCDCACMCodingSize = 7 + +// descCDCACMLineCoding represents an emulated UART's line configuration. +type descCDCACMLineCoding struct { + baud uint32 + stopBits uint8 + parity uint8 + numBits uint8 +} + +// Common configuration constants for the USB CDC-ACM (single) device class. +const ( + descCDCACMLanguageCount = 1 // String descriptor languages available + + descCDCACMInterfaceCount = 2 // Interfaces for all CDC-ACM configurations. + descCDCACMEndpointCount = 4 // Endpoints for all CDC-ACM configurations. + + 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 +) + +// descCDCACMClass holds references to all descriptors, buffers, and control +// structures for the USB CDC-ACM (single) device class. +type descCDCACMClass struct { + *descCDCACMClassData // Target-defined, class-specific data + + locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descCDCACMConfigSize]uint8 // configuration descriptor +} + +// descCDCACM holds statically-allocated instances for each of the CDC-ACM +// (single) device class configurations, ordered by index (offset by -1). +var descCDCACM = [dcdCount]descCDCACMClass{ + + { // CDC-ACM (single) class configuration index 1 + descCDCACMClassData: &descCDCACMData[0], + + locale: &[descCDCACMLanguageCount]descStringLanguage{ + + { // [0x0409] US English + language: descLanguageEnglish, + descriptor: descStringIndex{ + { /* [0] Language */ + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + // Actual string descriptors (index > 0) are copied into here at runtime! + // This allows for application- or even user-defined string descriptors. + { /* [1] Manufacturer */ }, + { /* [2] Product */ }, + { /* [3] Serial Number */ }, + }, + }, + }, + device: &[descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descCommonReleaseID), // Device release number in BCD (low) + msU8(descCommonReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descCDCACMCount, // Number of possible configurations + }, + qualif: &[descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descCDCACMCount, // Number of possible configurations + 0, // Reserved + }, + config: &[descCDCACMConfigSize]uint8{ + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) + msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) + descCDCACMInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descEndptConfigAttr, // Configuration attributes + 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 new file mode 100644 index 000000000..fd8e3dbd9 --- /dev/null +++ b/src/machine/usb/desc_mimxrt1062.go @@ -0,0 +1,604 @@ +// +build mimxrt1062 + +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 + descCommonProductID = 0x0483 + descCommonReleaseID = 0x0101 // BCD (1.1) + + descCommonLanguage = descLanguageEnglish + descCommonManufacturer = "TinyGo" + descCommonProduct = "USB" + descCommonSerialNumber = "00000" +) + +// Constants for USB CDC-ACM device classes. +const ( + descCDCACMMaxPower = 50 // 100 mA + + // CDC-ACM Control Buffers + + descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1) + descCDCACMCxCount = 8 + + // 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 + + // Default CDC-ACM Endpoint Configurations (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 +// transfers for a given endpoint are managed, for the default CDC-ACM (single) +// 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 descCDCACM0QH [descCDCACMQHCount]dhwEndpoint + +// 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 +// class configuration (index 1). +//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) +// device class configuration (index 1). +//go:align 32 +var descCDCACM0TD [descCDCACMTDCount]dhwTransfer + +// 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 + +var descCDCACM0RDNum [descCDCACMRDCount]uint16 +var descCDCACM0RDIdx [descCDCACMRDCount]uint16 +var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16 + +// 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. +// +// Instances of this type (elements of descCDCACMData) are embedded in elements +// of the common/target-agnostic CDC-ACM class configurations (descCDCACM). +// Methods defined on this type implement target-specific functionality, and +// some of these methods are required by the common device controller driver. +// Thus, this type functions as a hardware abstraction layer (HAL). +type descCDCACMClassData struct { + + // CDC-ACM Control Buffers + + qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads + + cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor + cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer + ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer + + // 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 + + txHead uint8 + txFree uint16 + txPrev bool + + rxHead uint8 + rxTail uint8 + rxFree uint16 +} + +// 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 64 +var descCDCACMData = [dcdCount]descCDCACMClassData{ + + { // -- CDC-ACM (single) Class Configuration Index 1 -- + + // CDC-ACM Control Buffers + + qh: &descCDCACM0QH, + + cd: &descCDCACM0CD, + cx: &descCDCACM0Cx, + ad: &descCDCACM0AD, + dx: &descCDCACM0Dx, + + // 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 new file mode 100644 index 000000000..a2700c9a7 --- /dev/null +++ b/src/machine/usb/dhw_mimxrt1062.go @@ -0,0 +1,1404 @@ +// +build mimxrt1062 + +package usb + +// Implementation of USB device controller hardware abstraction (dhw) for NXP +// iMXRT1062. + +import ( + "device/arm" + "device/nxp" + "math/bits" + "runtime/interrupt" + "runtime/volatile" + "unsafe" +) + +// dhwInterruptPriority defines the priority for all USB device interrupts. +const dhwInterruptPriority = 3 + +// dhw implements USB device controller hardware abstraction for iMXRT1062. +type dhw struct { + *dcd // USB device controller driver + + bus *nxp.USB_Type // USB core register + phy *nxp.USBPHY_Type // USB PHY register + irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062 + + stat *dhwEndpoint // endpoint 0 Rx ("out" direction) + ctrl *dhwEndpoint // endpoint 0 Tx ("in" direction) + + busSpeed uint8 // 0 = full, 1 = low, 2 = high, 4 = super + + controlReply [8]uint8 + controlMask uint32 + endpointMask uint32 + setup dcdSetup + + timerInterrupt [2]func() + timerReboot uint8 + 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 +func cycleCount() uint32 { + return (*volatile.Register32)(unsafe.Pointer(uintptr(0xe0001004))).Get() +} + +// deleteCache deletes cached data without touching physical memory. Useful for +// receiving data via DMA, which writes directly to memory, as this will force +// subsequent reads to ignore cache and access physical memory. +func deleteCache(addr, size uintptr) { nxp.DeleteDcache(addr, size) } + +// flushCache immediately flushes cached data to physical memory. Useful for +// transmitting data via DMA, which reads directly from memory, as this will +// immediately flush data currently in cache to physical memory. This also +// purges the data from cache, since we no longer need to access it after +// priming DMA for transmission. +func flushCache(addr, size uintptr) { nxp.FlushDeleteDcache(addr, size) } + +// allocDHW returns a reference to the USB hardware abstraction for the given +// device controller driver. Should be called only one time and during device +// controller initialization. +func allocDHW(port, instance int, dc *dcd) *dhw { + switch port { + case 0: + dhwInstance[instance].dcd = dc + dhwInstance[instance].bus = nxp.USB1 + dhwInstance[instance].phy = nxp.USBPHY1 + dhwInstance[instance].irq = + interrupt.New(nxp.IRQ_USB_OTG1, + func(interrupt.Interrupt) { + coreInstance[0].dc.interrupt() + }) + + case 1: + dhwInstance[instance].dcd = dc + dhwInstance[instance].bus = nxp.USB2 + dhwInstance[instance].phy = nxp.USBPHY2 + dhwInstance[instance].irq = + interrupt.New(nxp.IRQ_USB_OTG2, + func(interrupt.Interrupt) { + //coreInstance[1].dc.interrupt() + }) + } + + return &dhwInstance[instance] +} + +// init configures the USB port for device mode operation by initializing all +// endpoint and transfer descriptor data structures, initializing core registers +// and interrupts, resetting the USB PHY, and enabling power on the bust. +func (d *dhw) init() status { + + // Reset the controller + d.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) + d.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) + for d.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { + } + + // Initialize USB interrupt priorities + d.irq.SetPriority(dhwInterruptPriority) + + // Clear interrupts + m := arm.DisableInterrupts() + switch d.port { + case 0: + m &^= uintptr(nxp.IRQ_USB_OTG1) + case 1: + m &^= uintptr(nxp.IRQ_USB_OTG2) + } + arm.EnableInterrupts(m) + + // Initiate reset, and enable PHY power supply + d.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) + d.phy.PWD.Set(0) // ["Power down"] 0 = Power enabled, 1 = Power disabled + + // Clear the controller mode field and set to device mode: + // Controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only + d.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, + nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) + + d.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // No interrupt threshold + d.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // Disable setup lockout + d.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // Use little-endianness + + // Initialize control endpoint 0 (stat = Rx/OUT, ctrl = Tx/IN) + d.stat = d.endpointQueueHead(rxEndpoint(0)) + d.ctrl = d.endpointQueueHead(txEndpoint(0)) + d.stat.config = (descEndptMaxPktSize << 16) | (1 << 15) + d.ctrl.config = (descEndptMaxPktSize << 16) + + // Install base address of endpoints + d.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer(d.stat)))) + + // Clear installed timer callbacks + d.timerInterrupt[0] = nil + d.timerInterrupt[1] = nil + + // Enable interrupts in USB core + d.bus.USBINTR.Set( + nxp.USB_USBINTR_UE_Msk | // bus enable + nxp.USB_USBINTR_UEE_Msk | // bus error + nxp.USB_USBINTR_PCE_Msk | // port change detect + nxp.USB_USBINTR_URE_Msk | // bus reset + nxp.USB_USBINTR_SLE) // sleep enable + + // Ensure D+ pulled down long enough for host to detect previous disconnect + udelay(5000) + + return statusOK +} + +// enable causes the USB core to enter (or exit) the normal run state and +// enables/disables all interrupts on the receiver's USB port. +func (d *dhw) enable(enable bool) { + if enable { + d.irq.Enable() // Enable USB interrupts + d.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) // Enable "run" state + } else { + d.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) // Disable "run" state + d.irq.Disable() // Disable USB interrupts + } +} + +// enableInterrupts enables/disables all interrupts on the receiver's USB port. +func (d *dhw) enableInterrupts(enable bool) { + if enable { + d.irq.Enable() + } else { + d.irq.Disable() + } +} + +// enableSOF enables or disables start-of-frame (SOF) interrupts on the given +// USB device interface. +func (d *dhw) enableSOF(enable bool, iface uint8) { + if enable { + ivm := arm.DisableInterrupts() + d.sofUsage |= 1 << iface + if !d.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) { + d.bus.USBSTS.Set(nxp.USB_USBSTS_SRI) + 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) + } + } +} + +// interrupt handles the USB hardware interrupt events and notifies the device +// controller driver using a common "virtual interrupt" code. +func (d *dhw) interrupt() { + + // read and clear the interrupts that fired + status := d.bus.USBSTS.Get() & d.bus.USBINTR.Get() + d.bus.USBSTS.Set(status) + + // USB Interrupt (USBINT) - R/WC + // This bit is set by the Host/Device Controller when the cause of an + // interrupt is a completion of a USB transaction where the Transfer + // Descriptor (TD) has an interrupt on complete (IOC) bit set. + // This bit is also set by the Host/Device Controller when a short packet is + // detected. A short packet is when the actual number of bytes received was + // less than the expected number of bytes. + if 0 != status&nxp.USB_USBSTS_UI { + + setupStatus := d.bus.ENDPTSETUPSTAT.Get() + for 0 != setupStatus { + d.bus.ENDPTSETUPSTAT.Set(setupStatus) + var setup dcdSetup + ready := false + for !ready { + d.bus.USBCMD.SetBits(nxp.USB_USBCMD_SUTW) + setup = d.stat.setup + ready = d.bus.USBCMD.HasBits(nxp.USB_USBCMD_SUTW) + } + d.bus.USBCMD.ClearBits(nxp.USB_USBCMD_SUTW) + // flush endpoint 0 (bit 0=Rx, 16=Tx) + d.bus.ENDPTFLUSH.Set(0x00010001) + // wait for flush to complete + for d.bus.ENDPTFLUSH.HasBits(0x00010001) { + } + // Notify device controller driver + d.event(dcdEvent{ + id: dcdEventControlSetup, + setup: setup, + }) + setupStatus = d.bus.ENDPTSETUPSTAT.Get() + } + + completeStatus := d.bus.ENDPTCOMPLETE.Get() + if 0 != completeStatus { + d.bus.ENDPTCOMPLETE.Set(completeStatus) + if 0 != completeStatus&d.controlMask { + d.controlComplete(completeStatus) + } + completeStatus &= d.endpointMask + if 0 != completeStatus { + tx := completeStatus >> 16 + for 0 != tx { + num := uint8(bits.TrailingZeros32(tx)) + d.endpointComplete(txEndpoint(num)) + tx &^= 1 << num + } + rx := completeStatus & 0xFFFF + for 0 != rx { + num := uint8(bits.TrailingZeros32(rx)) + d.endpointComplete(rxEndpoint(num)) + rx &^= 1 << num + } + } + // Notify device controller driver + d.event(dcdEvent{ + id: dcdEventTransactComplete, + mask: completeStatus, + }) + } + } + + // USB Reset Received - R/WC + // When the device controller detects a USB Reset and enters the default + // state, this bit will be set to a one. + // Software can write a 1 to this bit to clear the USB Reset Received status + // bit. + // Only used in device operation mode. + if 0 != status&nxp.USB_USBSTS_URI { + // clear all setup tokens + d.bus.ENDPTSETUPSTAT.Set(d.bus.ENDPTSETUPSTAT.Get()) + // clear all endpoint complete status + d.bus.ENDPTCOMPLETE.Set(d.bus.ENDPTCOMPLETE.Get()) + // wait on any endpoint priming + for 0 != d.bus.ENDPTPRIME.Get() { + } + d.bus.ENDPTFLUSH.Set(0xFFFFFFFF) + d.event(dcdEvent{id: dcdEventStatusReset}) + } + + // General Purpose Timer Interrupt 0(GPTINT0) - R/WC + // This bit is set when the counter in the GPTIMER0CTRL register transitions + // to zero, writing a one to this bit clears it. + if 0 != status&nxp.USB_USBSTS_TI0 { + if nil != d.timerInterrupt[0] { + d.timerInterrupt[0]() + } + d.event(dcdEvent{id: dcdEventTimer, mask: 0}) + } + + // General Purpose Timer Interrupt 1(GPTINT1) - R/WC + // This bit is set when the counter in the GPTIMER1CTRL register transitions + // to zero, writing a one to this bit will clear it. + if 0 != status&nxp.USB_USBSTS_TI1 { + if nil != d.timerInterrupt[1] { + d.timerInterrupt[1]() + } + d.event(dcdEvent{id: dcdEventTimer, mask: 1}) + } + + // Port Change Detect - R/WC + // The Host Controller sets this bit to a one when on any port a Connect + // Status occurs, a Port Enable/Disable Change occurs, or the Force Port + // Resume bit is set as the result of a J-K transition on the suspended port. + // The Device Controller sets this bit to a one when the port controller + // enters the full or high-speed operational state. When the port controller + // exits the full or high-speed operation states due to Reset or Suspend + // events, the notification mechanisms are the USB Reset Received bit and the + // DCSuspend bits respectively. + if 0 != status&nxp.USB_USBSTS_PCI { + if d.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) { + d.busSpeed = descDeviceSpeedHigh // 480 Mbit/sec + } else { + d.busSpeed = descDeviceSpeedFull // 12 Mbit/sec + } + d.event(dcdEvent{id: dcdEventStatusRun}) + } + + // DCSuspend - R/WC + // When a controller enters a suspend state from an active state, this bit + // will be set to a one. The device controller clears the bit upon exiting + // from a suspend state. Only used in device operation mode. + if 0 != status&nxp.USB_USBSTS_SLI { + d.event(dcdEvent{id: dcdEventStatusSuspend}) + } + + // USB Error Interrupt (USBERRINT) - R/WC + // When completion of a USB transaction results in an error condition, this + // bit is set by the Host/Device Controller. This bit is set along with the + // USBINT bit, if the TD on which the error interrupt occurred also had its + // interrupt on complete (IOC) bit set. + // The device controller detects resume signaling only. + if 0 != status&nxp.USB_USBSTS_UEI { + d.event(dcdEvent{id: dcdEventStatusError}) + } + + // SOF Received - R/WC + // When the device controller detects a Start Of (micro) Frame, this bit will + // be set to a one. When a SOF is extremely late, the device controller will + // automatically set this bit to indicate that an SOF was expected. + // Therefore, this bit will be set roughly every 1ms in device FS mode and + // every 125us in HS mode and will be synchronized to the actual SOF that is + // received. + // Because the device controller is initialized to FS before connect, this bit + // will be set at an interval of 1ms during the prelude to connect and chirp. + // In host mode, this bit will be set every 125us and can be used by host + // controller driver as a time base. Software writes a 1 to this bit to clear + // it. + if d.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) && + 0 != status&nxp.USB_USBSTS_SRI { + if 0 != d.timerReboot { + d.timerReboot -= 1 + if 0 == d.timerReboot { + d.enableSOF(false, descCDCACMInterfaceCount) + runBootloader() + } + } + } +} + +func (d *dhw) speed() uint8 { return d.busSpeed } + +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)) +} + +// ============================================================================= +// Control Endpoint 0 +// ============================================================================= + +// controlStall stalls a transfer on control endpoint 0. To stall a transfer on +// any other endpoint, use method endpointStall(). +func (d *dhw) controlStall() { + d.endpointStall(0) +} + +// controlReceive receives (Rx, OUT) data on control endpoint 0. +func (d *dhw) controlReceive( + data uintptr, size uint32, notify bool) { + const ( + rm = uint32(1 << descEndptConfigAttrRxPos) + tm = uint32(1 << descEndptConfigAttrTxPos) + ) + cd, ad := d.transferControl() + if size > 0 { + cd.next = dhwTransferEOL + cd.token = (size << 16) | (1 << 7) + for i := range cd.pointer { + cd.pointer[i] = data + uintptr(i)*4096 + } + // linked list is empty + qr := d.endpointQueueHead(rxEndpoint(0)) + qr.transfer.next = cd + qr.transfer.token = 0 + d.bus.ENDPTPRIME.SetBits(rm) + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming + } + ad.next = dhwTransferEOL + ad.token = 1 << 7 // Bit 7: active transfer + if notify { + ad.token |= 1 << 15 + } + ad.pointer[0] = 0 + qt := d.endpointQueueHead(txEndpoint(0)) + qt.transfer.next = ad + qt.transfer.token = 0 + d.bus.ENDPTCOMPLETE.Set(rm | tm) + d.bus.ENDPTPRIME.SetBits(tm) + 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. +func (d *dhw) controlTransmit( + data uintptr, size uint32, notify bool) { + const ( + rm = uint32(1 << descEndptConfigAttrRxPos) + tm = uint32(1 << descEndptConfigAttrTxPos) + ) + cd, ad := d.transferControl() + if size > 0 { + cd.next = dhwTransferEOL + cd.token = (size << 16) | (1 << 7) + for i := range cd.pointer { + cd.pointer[i] = data + uintptr(i)*4096 + } + // linked list is empty + qt := d.endpointQueueHead(txEndpoint(0)) + qt.transfer.next = cd + qt.transfer.token = 0 + d.bus.ENDPTPRIME.SetBits(tm) + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming + } + ad.next = dhwTransferEOL + ad.token = 1 << 7 // Bit 7: active transfer + if notify { + ad.token |= 1 << 15 + } + ad.pointer[0] = 0 + qr := d.endpointQueueHead(rxEndpoint(0)) + qr.transfer.next = ad + qr.transfer.token = 0 + d.bus.ENDPTCOMPLETE.Set(rm | tm) + d.bus.ENDPTPRIME.SetBits(rm) + if notify { + d.controlMask = rm + } + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming +} + +// ============================================================================= +// Endpoint Descriptor +// ============================================================================= + +// dhwEndpoint defines a USB standard endpoint, used as the general channel of +// communication between host and device. +type dhwEndpoint struct { + config uint32 + current *dhwTransfer + transfer dhwTransfer + setup dcdSetup + // Endpoints are 48-byte data structures. The remaining data extends this to + // 64-byte, and also makes it simpler to align contiguous endpoints on 64-byte + // boundaries. + first *dhwTransfer + last *dhwTransfer + // After some discussion, perhaps the simplest change to support 64-bit (or + // future TinyGo versions that don't use 8 bytes to refer to a function) is to + // allocate a separate buffer of callbacks. The device controller would assign + // callbacks to unused elements in that buffer, and only the index of that + // callback would be stored here in the endpoint. + 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 +func (d *dhw) endpointQueueHead(endpoint uint8) *dhwEndpoint { + // endpoint queue head is device class-specific + 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 + } +} + +func (d *dhw) endpointControlRegister(endpoint uint8) *volatile.Register32 { + num, _ := unpackEndpoint(endpoint) + switch num { + case 0: + return &d.bus.ENDPTCTRL0 + case 1: + return &d.bus.ENDPTCTRL1 + case 2: + return &d.bus.ENDPTCTRL2 + case 3: + return &d.bus.ENDPTCTRL3 + case 4: + return &d.bus.ENDPTCTRL4 + case 5: + return &d.bus.ENDPTCTRL5 + case 6: + return &d.bus.ENDPTCTRL6 + case 7: + return &d.bus.ENDPTCTRL7 + } + return nil +} + +func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) { + // control endpoint 0 configured in dhw init + if !control || 0 != endpoint&descEndptAddrNumberMsk { + d.endpointControlRegister(endpoint & descEndptAddrNumberMsk).Set(config) + } +} + +func (d *dhw) endpointStatus(endpoint uint8) uint16 { + status := uint16(0) + switch endpoint { + case rxEndpoint(endpoint): + status |= uint16((d.endpointControlRegister(endpoint).Get() & + nxp.USB_ENDPTCTRL0_RXS_Msk) >> nxp.USB_ENDPTCTRL0_RXS_Pos) + case txEndpoint(endpoint): + status |= uint16((d.endpointControlRegister(endpoint).Get() & + nxp.USB_ENDPTCTRL0_TXS_Msk) >> nxp.USB_ENDPTCTRL0_TXS_Pos) + } + return status +} + +// endpointStall stalls a transfer on the given endpoint. +func (d *dhw) endpointStall(endpoint uint8) { + // RXS and TXS bits at same position in all endpoint control registers. + d.endpointControlRegister(endpoint).SetBits( + nxp.USB_ENDPTCTRL0_RXS | nxp.USB_ENDPTCTRL0_TXS, + ) +} + +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) 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( + ep *dhwEndpoint, packetSize uint16, zlp bool, callback func(transfer *dhwTransfer)) { + + ep.config = uint32(packetSize) << 16 + if !zlp { + ep.config |= 1 << 29 + } + ep.current = nil + ep.transfer.next = dhwTransferEOL + ep.transfer.token = 0 + for i := range ep.transfer.pointer { + ep.transfer.pointer[i] = 0 + } + ep.transfer.param = 0 + ep.setup.bmRequestType = 0 + ep.setup.bRequest = 0 + ep.setup.wValue = 0 + ep.setup.wIndex = 0 + ep.setup.wLength = 0 + ep.first = nil + ep.last = nil + ep.callback = callback +} + +// endpointComplete handles transfer completion of a data endpoint. +func (d *dhw) endpointComplete(endpoint uint8) { + ep := d.endpointQueueHead(endpoint) + if nil == ep.first { + return + } + count := 0 + first := ep.first + for t, eol := first, false; !eol; t, eol = t.nextTransfer() { + if eol { + // reached end of list, new list empty + ep.first = nil + ep.last = nil + } else { + if 0 != t.token&(1<<7) { // Bit 7: active transfer + // new list begins here + ep.first = t + break + } else { + count += 1 + } + } + } + // invoke all callbacks + for i := 0; i < count; i++ { + next := first.next + ep.callback(first) + first = next + } +} + +// endpointConfigureRx configures the given bulk data receive (Rx, OUT) endpoint +// for transfer. +func (d *dhw) endpointConfigureRx( + endpoint uint8, packetSize uint16, zlp bool, callback func(transfer *dhwTransfer)) { + + // Configure based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + + // 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 +// for transfer. +func (d *dhw) endpointConfigureTx( + endpoint uint8, packetSize uint16, zlp bool, callback func(transfer *dhwTransfer)) { + + // Configure based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + + // 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. +func (d *dhw) endpointReceive(endpoint uint8, transfer *dhwTransfer) { + + // Configure based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + + // 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 +// endpoint. +func (d *dhw) endpointTransmit(endpoint uint8, transfer *dhwTransfer) { + + // Configure based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + + // 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) +} + +// ============================================================================= +// Transfer Descriptor +// ============================================================================= + +// dhwTransfer describes the size and location of data to be transferred to or +// from a USB endpoint. +type dhwTransfer struct { + next *dhwTransfer + token uint32 + pointer [5]uintptr + 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))) + +// nextTransfer returns the next transfer descriptor pointed to by the receiver +// transfer descriptor, and whether or not that next descriptor is the final +// descriptor in the list. +func (t dhwTransfer) nextTransfer() (*dhwTransfer, bool) { + return t.next, 1 == uintptr(unsafe.Pointer(t.next)) +} + +// transferControl returns the data and ackowledgement transfer descriptors for +// the control endpoint (i.e., endpoint 0). +//go:inline +func (d *dhw) transferControl() (dat, ack *dhwTransfer) { + // control endpoint is device class-specific + 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 + } +} + +func (d *dhw) transferPrepare( + transfer *dhwTransfer, data *uint8, size uint16, param uint32) { + + transfer.next = dhwTransferEOL + + // 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 + } + transfer.param = param +} + +func (d *dhw) transferSchedule( + endpoint *dhwEndpoint, mask uint32, transfer *dhwTransfer) { + + if nil != endpoint.callback { + transfer.token |= 1 << 15 + } + ivm := arm.DisableInterrupts() + last := endpoint.last + if nil != last { + last.next = transfer + if d.bus.ENDPTPRIME.HasBits(mask) { + goto endTransfer + } + start := cycleCount() + estat := uint32(0) + for !d.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) && + (cycleCount()-start < 2400) { + d.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW) + estat = d.bus.ENDPTSTAT.Get() + } + if 0 != estat&mask { + goto endTransfer + } + } + endpoint.transfer.next = transfer + endpoint.transfer.token = 0 + d.bus.ENDPTPRIME.SetBits(mask) + endpoint.first = transfer +endTransfer: + endpoint.last = transfer + 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 + } + d.timerInterrupt[timer] = fn + switch timer { + case 0: + d.bus.GPTIMER0CTRL.Set(0) + d.bus.GPTIMER0LD.Set(usec - 1) + d.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE0) + case 1: + d.bus.GPTIMER1CTRL.Set(0) + d.bus.GPTIMER1LD.Set(usec - 1) + d.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE1) + } +} + +func (d *dhw) timerOneShot(timer int) { + switch timer { + case 0: + d.bus.GPTIMER0CTRL.Set( + nxp.USB_GPTIMER0CTRL_GPTRUN | nxp.USB_GPTIMER0CTRL_GPTRST) + case 1: + d.bus.GPTIMER1CTRL.Set( + nxp.USB_GPTIMER1CTRL_GPTRUN | nxp.USB_GPTIMER1CTRL_GPTRST) + } +} + +func (d *dhw) timerStop(timer int) { + switch timer { + case 0: + d.bus.GPTIMER0CTRL.Set(0) + case 1: + d.bus.GPTIMER1CTRL.Set(0) + } +} + +// ============================================================================= +// [CDC-ACM] Serial UART (Virtual COM Port) +// ============================================================================= + +func (d *dhw) uartConfigure() { + + acm := &descCDCACM[d.cc.config-1] + + switch d.speed() { + case descDeviceSpeedHigh: + acm.rxSize = descCDCACMDataRxHSPacketSize + acm.txSize = descCDCACMDataTxHSPacketSize + default: + acm.rxSize = descCDCACMDataRxFSPacketSize + acm.txSize = descCDCACMDataTxFSPacketSize + } + acm.txHead = 0 + acm.txFree = 0 + acm.txPrev = false + acm.rxHead = 0 + acm.rxTail = 0 + acm.rxFree = 0 + + d.endpointEnable(descCDCACMEndpointStatus, + false, descCDCACMConfigAttrStatus) + d.endpointEnable(descCDCACMEndpointDataRx, + false, descCDCACMConfigAttrDataRx) + d.endpointEnable(descCDCACMEndpointDataTx, + false, descCDCACMConfigAttrDataTx) + + d.endpointConfigureTx(descCDCACMEndpointStatus, + acm.sxSize, false, nil) + d.endpointConfigureRx(descCDCACMEndpointDataRx, + acm.rxSize, false, d.uartNotify) + d.endpointConfigureTx(descCDCACMEndpointDataTx, + acm.txSize, true, nil) + for i := range acm.rd { + d.uartReceive(uint8(i)) + } + 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 + buf := &acm.rx[num*descCDCACMRxSize] + d.enableInterrupts(false) + d.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) + deleteCache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) + d.endpointReceive(descCDCACMEndpointDataRx, &acm.rd[num]) + d.enableInterrupts(true) +} + +func (d *dhw) uartNotify(transfer *dhwTransfer) { + acm := &descCDCACM[d.cc.config-1] + len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF) + p := transfer.param + if 0 == len { + // zero-length packet (ZLP) + d.uartReceive(uint8(p)) + } else { + // data packet + h := acm.rxHead + if h != acm.rxTail { + // previous packet is still buffered + q := acm.rxQueue[h] + n := acm.rxCount[q] + if len <= descCDCACMRxSize-n { + // previous buffer has enough free space for this packet's data + _ = copy(acm.rx[q*descCDCACMRxSize+n:], + acm.rx[p*descCDCACMRxSize:uint16(p)*descCDCACMRxSize+len]) + acm.rxCount[q] = n + len + acm.rxFree += len + d.uartReceive(uint8(p)) + return + } + } + // add this packet to Rx buffer + acm.rxCount[p] = len + acm.rxIndex[p] = 0 + h += 1 + if h > descCDCACMRDCount { // should be >= + h = 0 + } + acm.rxQueue[h] = uint16(p) + acm.rxHead = h + acm.rxFree += len + } +} + +// uartFlush discards all buffered input (Rx) data. +func (d *dhw) uartFlush() { + acm := &descCDCACM[d.cc.config-1] + tail := acm.rxTail + for tail != acm.rxHead { + tail += 1 + if tail > descCDCACMRDCount { + tail = 0 + } + i := acm.rxQueue[tail] + acm.rxFree -= acm.rxCount[i] - acm.rxIndex[i] + d.uartReceive(uint8(i)) + acm.rxTail = tail + } +} + +func (d *dhw) uartAvailable() int { + return int(descCDCACM[d.cc.config-1].rxFree) +} + +func (d *dhw) uartPeek() (uint8, bool) { + acm := &descCDCACM[d.cc.config-1] + tail := acm.rxTail + if tail == acm.rxHead { + return 0, false + } + tail += 1 + if tail > descCDCACMRDCount { + tail = 0 + } + i := acm.rxQueue[tail] + return acm.rx[i*descCDCACMRxSize+acm.rxIndex[i]], true +} + +func (d *dhw) uartReadByte() (uint8, bool) { + b := []uint8{0} + ok := d.uartRead(b) > 0 + return b[0], ok +} + +func (d *dhw) uartRead(data []uint8) int { + acm := &descCDCACM[d.cc.config-1] + read := uint16(0) + size := uint16(len(data)) + tail := acm.rxTail + dest := uint16(0) + d.enableInterrupts(false) + for read < size && tail != acm.rxHead { + tail += 1 + if tail > descCDCACMRDCount { + tail = 0 + } + i := acm.rxQueue[tail] + count := uint16(size - read) + avail := acm.rxCount[i] - acm.rxIndex[i] + start := i*descCDCACMRxSize + acm.rxIndex[i] + if avail > count { + // partially consume packet + _ = copy(data[dest:], acm.rx[start:start+count]) + acm.rxFree -= count + acm.rxIndex[i] += count + read += count + } else { + // fully consume packet + _ = copy(data[dest:], acm.rx[start:start+avail]) + dest += avail //* uint16(unsafe.Sizeof(&data[0])) + read += avail + acm.rxFree -= avail + acm.rxTail = tail + d.uartReceive(uint8(i)) + } + } + d.enableInterrupts(true) + return int(read) +} + +func (d *dhw) uartWriteByte(c uint8) bool { + return 1 == d.uartWrite([]uint8{c}) +} + +func (d *dhw) uartWrite(data []uint8) int { + acm := &descCDCACM[d.cc.config-1] + sent := 0 + size := len(data) + for size > 0 { + xfer := &acm.td[acm.txHead] + wait := false + when := int64(0) + for 0 == acm.txFree { + if 0 == xfer.token&0x80 { + if 0 != xfer.token&0x68 { + // TODO: token contains error, how to handle? + } + acm.txFree = descCDCACMTxSize + acm.txPrev = false + break + } + if !wait { + wait = true + when = ticks() + } + if acm.txPrev { + return sent + } + if ticks()-when > descCDCACMTxTimeoutMs { + acm.txPrev = true + return sent + } + } + buff := acm.tx[(int(acm.txHead)*descCDCACMTxSize)+ + (descCDCACMTxSize-int(acm.txFree)):] + if size > int(acm.txFree) { + _ = copy(buff, data[sent:sent+int(acm.txFree)]) + tx := &acm.tx[int(acm.txHead)*descCDCACMTxSize] + d.transferPrepare(xfer, tx, descCDCACMTxSize, 0) + flushCache(uintptr(unsafe.Pointer(tx)), descCDCACMTxSize) + d.endpointTransmit(descCDCACMEndpointDataTx, xfer) + acm.txHead += 1 + if acm.txHead >= descCDCACMTDCount { + acm.txHead = 0 + } + size -= int(acm.txFree) + sent += int(acm.txFree) + acm.txFree = 0 + d.timerStop(0) + } else { + _ = copy(buff, data[:size]) + acm.txFree -= uint16(size) + sent += size + size = 0 + d.timerOneShot(0) + } + } + return sent +} + +func (d *dhw) uartSync() { + const autoFlushTx = true + if !autoFlushTx { + return + } + acm := &descCDCACM[d.cc.config-1] + if 0 == acm.txFree { + return + } + xfer := &acm.td[acm.txHead] + buff := &acm.tx[uint16(acm.txHead)*descCDCACMTxSize] + size := descCDCACMTxSize - acm.txFree + d.transferPrepare(xfer, buff, size, 0) + flushCache(uintptr(unsafe.Pointer(buff)), uintptr(size)) + d.endpointTransmit(descCDCACMEndpointDataTx, xfer) + acm.txHead += 1 + if acm.txHead >= descCDCACMTDCount { + acm.txHead = 0 + } + 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/hcd.go b/src/machine/usb/hcd.go new file mode 100644 index 000000000..4e7f34d8d --- /dev/null +++ b/src/machine/usb/hcd.go @@ -0,0 +1,53 @@ +package usb + +// Implementation of 32-bit target-agnostic USB host controller driver (hcd). + +// hcdCount defines the number of USB cores to configure for host mode. It is +// computed as the sum of all declared host configuration descriptors. +const hcdCount = 0 // + ... + +// hcdInstance provides statically-allocated instances of each USB host +// controller configured on this platform. +var hcdInstance [hcdCount]hcd + +// hhwInstance provides statically-allocated instances of each USB hardware +// abstraction for ports configured as host on this platform. +var hhwInstance [hcdCount]hhw + +// hcd implements USB host controller driver (hcd) interface. +type hcd struct { + *hhw // USB hardware abstraction layer + + core *core // Parent USB core this instance is attached to + port int // USB port index + cc class // USB host class + id int // USB host controller index +} + +// initHCD initializes and assigns a free host controller instance to the given +// USB port. Returns the initialized host controller or nil if no free host +// controller instances remain. +func initHCD(port int, class class) (*hcd, status) { + if 0 == hcdCount { + return nil, statusInvalid // Must have defined host controllers + } + switch class.id { + default: + } + // Return the first instance whose assigned core is currently nil. + for i := range hcdInstance { + if nil == hcdInstance[i].core { + // Initialize host controller. + hcdInstance[i].hhw = allocHHW(port, i, &hcdInstance[i]) + hcdInstance[i].core = &coreInstance[port] + hcdInstance[i].port = port + hcdInstance[i].cc = class + hcdInstance[i].id = i + return &hcdInstance[i], statusOK + } + } + return nil, statusBusy // No free host controller instances available. +} + +// class returns the receiver's current host class configuration. +func (h *hcd) class() class { return h.cc } diff --git a/src/machine/usb/hhw_mimxrt1062.go b/src/machine/usb/hhw_mimxrt1062.go new file mode 100644 index 000000000..52c188437 --- /dev/null +++ b/src/machine/usb/hhw_mimxrt1062.go @@ -0,0 +1,59 @@ +// +build mimxrt1062 + +package usb + +// Implementation of USB host controller driver (hcd) for NXP iMXRT1062. + +import ( + "device/nxp" + "runtime/interrupt" +) + +// hcdInterruptPriority defines the priority for all USB host interrupts. +const hcdInterruptPriority = 3 + +// hcd implements USB host controller driver (hcd) interface. +type hhw struct { + *hcd // USB host controller driver + + bus *nxp.USB_Type // USB core register + phy *nxp.USBPHY_Type // USB PHY register + irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062 +} + +// allocHHW returns a reference to the USB hardware abstraction for the given +// host controller driver. Should be called only one time and during host +// controller initialization. +func allocHHW(port, instance int, hc *hcd) *hhw { + switch port { + case 0: + hhwInstance[instance].hcd = hc + hhwInstance[instance].bus = nxp.USB1 + hhwInstance[instance].phy = nxp.USBPHY1 + + case 1: + hhwInstance[instance].hcd = hc + hhwInstance[instance].bus = nxp.USB2 + hhwInstance[instance].phy = nxp.USBPHY2 + } + + return &hhwInstance[instance] +} + +// init configures the USB port for host mode operation by initializing all +// endpoint and transfer descriptor data structures, initializing core registers +// and interrupts, resetting the USB PHY, and enabling power on the bust. +func (h *hhw) init() status { + + return statusOK +} + +// enable causes the USB core to enter (or exit) the normal run state and +// enables/disables all interrupts on the receiver's USB port. +func (h *hhw) enable(enable bool) { + if enable { + h.irq.Enable() // Enable USB interrupts + } else { + h.irq.Disable() // Disable USB interrupts + } +} 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 new file mode 100644 index 000000000..f7e8f3aef --- /dev/null +++ b/src/machine/usb/uart.go @@ -0,0 +1,74 @@ +package usb + +import ( + "errors" +) + +var ( + ErrUARTInvalidPort = errors.New("invalid USB port") + ErrUARTInvalidCore = errors.New("invalid USB core") + ErrUARTEmptyBuffer = errors.New("USB receive buffer empty") + ErrUARTWriteFailed = errors.New("USB write failure") +) + +// UART represents a virtual serial (UART) device emulation using the USB +// CDC-ACM device class driver. +type UART struct { + Port int + 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 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}) + if !st.ok() { + return ErrUARTInvalidPort + } + return nil +} + +// Buffered returns the number of bytes currently stored in the RX buffer. +func (uart UART) Buffered() int { + return uart.core.dc.uartAvailable() +} + +// ReadByte reads a single byte from the RX buffer. +// If there is no data in the buffer, returns an error. +func (uart UART) ReadByte() (byte, error) { + n, ok := uart.core.dc.uartReadByte() + if !ok { + return 0, ErrUARTEmptyBuffer + } + return n, nil +} + +// Read from the RX buffer. +func (uart UART) Read(data []byte) (n int, err error) { + return uart.core.dc.uartRead(data), nil +} + +// WriteByte writes a single byte of data to the UART interface. +func (uart UART) WriteByte(c byte) error { + if !uart.core.dc.uartWriteByte(c) { + return ErrUARTWriteFailed + } + return nil +} + +// Write data to the UART. +func (uart UART) Write(data []byte) (n int, err error) { + return uart.core.dc.uartWrite(data), nil +} diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go new file mode 100644 index 000000000..5d4c4e4ca --- /dev/null +++ b/src/machine/usb/usb.go @@ -0,0 +1,143 @@ +package usb + +// Hardware abstraction for USB ports configured as either host or device. + +// CoreCount defines the total number of USB cores to configure in device or +// host mode. +const CoreCount = dcdCount + hcdCount + +// coreInstance provides statically-allocated instances of each USB core +// configured on this platform. +var coreInstance [CoreCount]core + +// core represents the core of a USB port configured as either host or device. +type core struct { + port int + mode int + dc *dcd + hc *hcd +} + +// Constant definitions for USB core operating modes. +const ( + modeIdle = 0 // USB port has not been configured + modeDevice = 1 + modeHost = 2 +) + +// initCore initializes a free USB core with given operating mode on the USB +// port at given index, if available. Returns a reference to the initialized +// core or nil if the core is unavailable. +func initCore(port int, class class) (*core, status) { + + iv := disableInterrupts() + defer enableInterrupts(iv) + + if port < 0 || port >= CoreCount || 0 == class.config { + return nil, statusInvalid + } + + if modeIdle != coreInstance[port].mode { + // Check if requested port is already configured as requested class. If so, + // just return a reference to the existing core instead of an error. + // + // This will allow, for instance, TinyGo examples that try to reconfigure + // the USB (CDC-ACM) UART port (which is already configured by the runtime) + // to continue without error. + if coreInstance[port].mode == class.mode() { + switch class.mode() { + case modeDevice: + if coreInstance[port].dc.class().equals(class) { + return &coreInstance[port], statusOK + } + case modeHost: + if coreInstance[port].hc.class().equals(class) { + return &coreInstance[port], statusOK + } + } + } + return nil, statusBusy + } + + switch class.mode() { + case modeDevice: + // Allocate a free device controller and install interrupts + dc, st := initDCD(port, class) + if !st.ok() { + return nil, st + } + // Initialize buffers and device descriptors + if st = dc.init(); !st.ok() { + return nil, st + } + coreInstance[port].port = port + coreInstance[port].mode = modeDevice + coreInstance[port].dc = dc + dc.enable(true) // Enable interrupts and enter runtime + + case modeHost: + // Allocate a free host controller and install interrupts + hc, st := initHCD(port, class) + if !st.ok() { + return nil, st + } + // Initialize buffers and device descriptors + if st = hc.init(); !st.ok() { + return nil, st + } + coreInstance[port].port = port + coreInstance[port].mode = modeHost + coreInstance[port].hc = hc + hc.enable(true) // Enable interrupts and enter runtime + + default: + return nil, statusInvalid + } + + return &coreInstance[port], statusOK +} + +// class represents the type of a host/device and its class configuration index. +// The first valid configuration index is 1. Index 0 is reserved and invalid. +type class struct { + id int + config int +} + +// Enumerated constants for all host/device class configurations. +const ( + 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, classDeviceHID: + return modeDevice + default: + return modeIdle + } +} + +// equals returns true if and only if all fields of the given class are equal to +// those of the receiver c. +//go:inline +func (c class) equals(class class) bool { + return c.id == class.id && c.config == class.config +} + +// status represents the return code of a subroutine. +type status uint8 + +// Constant definitions for all status codes used within the package. +const ( + statusOK status = iota // Success + statusBusy // Busy + statusInvalid // Invalid argument +) + +// ok returns true if and only if the receiver st equals statusOK. +//go:inline +func (s status) ok() bool { return statusOK == s } diff --git a/src/machine/usb/util.go b/src/machine/usb/util.go new file mode 100644 index 000000000..5b88f7a50 --- /dev/null +++ b/src/machine/usb/util.go @@ -0,0 +1,271 @@ +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 +// at index 0 is the least-significant byte in u and index 7 is the most- +// significant byte. +//go:inline +func leU64(u uint64) []uint8 { + if u == 0 { + // skip all processing for the common case (u = 0) + return []uint8{0, 0, 0, 0, 0, 0, 0, 0} + } + return []uint8{ + uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24), + uint8(u >> 32), uint8(u >> 40), uint8(u >> 48), uint8(u >> 56), + } +} + +// leU32 returns a slice containing 4 bytes from the given uint32 u. +// +// The returned bytes have little-endian ordering; that is, the first element +// at index 0 is the least-significant byte in u and index 3 is the most- +// significant byte. +//go:inline +func leU32(u uint32) []uint8 { + if u == 0 { + // skip all processing for the common case (u = 0) + return []uint8{0, 0, 0, 0} + } + return []uint8{ + uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24), + } +} + +// leU16 returns a slice containing 2 bytes from the given uint16 u. +// +// The returned bytes have little-endian ordering; that is, the first element +// at index 0 is the least-significant byte in u and index 1 is the most- +// significant byte. +//go:inline +func leU16(u uint16) []uint8 { + if u == 0 { + // skip all processing for the common case (u = 0) + return []uint8{0, 0} + } + return []uint8{ + uint8(u), uint8(u >> 8), + } +} + +// beU64 returns a slice containing 8 bytes from the given uint64 u. +// +// The returned bytes have big-endian ordering; that is, the first element at +// index 0 is the most-significant byte in u and index 7 is the least- +// significant byte. +//go:inline +func beU64(u uint64) []uint8 { + if u == 0 { + // skip all processing for the common case (u = 0) + return []uint8{0, 0, 0, 0, 0, 0, 0, 0} + } + return []uint8{ + uint8(u >> 56), uint8(u >> 48), uint8(u >> 40), uint8(u >> 32), + uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u), + } +} + +// beU32 returns a slice containing 4 bytes from the given uint32 u. +// +// The returned bytes have big-endian ordering; that is, the first element at +// index 0 is the most-significant byte in u and index 3 is the least- +// significant byte. +//go:inline +func beU32(u uint32) []uint8 { + if u == 0 { + // skip all processing for the common case (u = 0) + return []uint8{0, 0, 0, 0} + } + return []uint8{ + uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u), + } +} + +// beU16 returns a slice containing 2 bytes from the given uint16 u. +// +// The returned bytes have big-endian ordering; that is, the first element at +// index 0 is the most-significant byte in u and index 1 is the least- +// significant byte. +//go:inline +func beU16(u uint16) []uint8 { + if u == 0 { + // skip all processing for the common case (u = 0) + return []uint8{0, 0} + } + return []uint8{ + uint8(u >> 8), uint8(u), + } +} + +// revU64 returns the given uint64 u with bytes in the reverse order. +//go:inline +func revU64(u uint64) uint64 { + if u == 0 { + // skip all processing for the common case (u = 0) + return 0 + } + return ((u & 0x00000000000000FF) << 56) | + ((u & 0x000000000000FF00) << 40) | + ((u & 0x0000000000FF0000) << 24) | + ((u & 0x00000000FF000000) << 8) | + ((u & 0x000000FF00000000) >> 8) | + ((u & 0x0000FF0000000000) >> 24) | + ((u & 0x00FF000000000000) >> 40) | + ((u & 0xFF00000000000000) >> 56) +} + +// revU32 returns the given uint32 u with bytes in the reverse order. +//go:inline +func revU32(u uint32) uint32 { + if u == 0 { + // skip all processing for the common case (u = 0) + return 0 + } + return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) | + ((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24) +} + +// revU16 returns the given uint16 u with bytes in the reverse order. +//go:inline +func revU16(u uint16) uint16 { + if u == 0 { + // skip all processing for the common case (u = 0) + return 0 + } + return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8) +} + +// packU64 returns a uint64 constructed by concatenating the bytes in slice b. +// +// The least-significant byte in the returned value is the first element at +// index 0 in b and the most significant byte is index 7, if given. If fewer +// than 8 elements are given in b, the corresponding bytes in the returned value +// are all 0. +//go:inline +func packU64(b []uint8) (u uint64) { + for i := 0; i < 8 && i < len(b); i++ { + u |= uint64(b[i]) << (i * 8) + } + return +} + +// packU32 returns a uint32 constructed by concatenating the bytes in slice b. +// +// The least-significant byte in the returned value is the first element at +// index 0 in b and the most significant byte is index 3, if given. If fewer +// than 4 elements are given in b, the corresponding bytes in the returned value +// are all 0. +//go:inline +func packU32(b []uint8) (u uint32) { + for i := 0; i < 4 && i < len(b); i++ { + u |= uint32(b[i]) << (i * 8) + } + return +} + +// packU16 returns a uint16 constructed by concatenating the bytes in slice b. +// +// The least-significant byte in the returned value is the first element at +// index 0 in b and the most significant byte is index 1, if given. If fewer +// than 2 elements are given in b, the corresponding bytes in the returned value +// are all 0. +//go:inline +func packU16(b []uint8) (u uint16) { + for i := 0; i < 2 && i < len(b); i++ { + u |= uint16(b[i]) << (i * 8) + } + return +} + +// msU8 returns the most-significant byte of u. +//go:inline +func msU8(u uint16) uint8 { return uint8(u >> 8) } + +// lsU8 returns the least-significant byte of u. +//go:inline +func lsU8(u uint16) uint8 { return uint8(u) } + +// cycles converts the given number of microseconds to CPU cycles for a CPU with +// given frequency. +//go:inline +func cycles(microsec, cpuFreqHz uint32) uint32 { + return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000) +} + +//go:inline +func unpackEndpoint(address uint8) (number, direction uint8) { + return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos, + (address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos +} + +//go:inline +func rxEndpoint(number uint8) uint8 { + return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut +} + +//go:inline +func txEndpoint(number uint8) uint8 { + return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionIn +} + +//go:inline +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 new file mode 100644 index 000000000..f40112fea --- /dev/null +++ b/src/machine/usb/util_arm.go @@ -0,0 +1,24 @@ +// +build arm + +package usb + +import "device/arm" + +// 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. +//go:inline +func udelay(microsec uint32) { + n := cycles(microsec, descCPUFrequencyHz) + for i := uint32(0); i < n; i++ { + arm.Asm(`nop`) + } +} + +func disableInterrupts() uintptr { + return arm.DisableInterrupts() +} + +func enableInterrupts(mask uintptr) { + arm.EnableInterrupts(mask) +} diff --git a/src/runtime/runtime_cortexm_hardfault_debug.go b/src/runtime/runtime_cortexm_hardfault_debug.go index f136d7102..ac61c1f63 100644 --- a/src/runtime/runtime_cortexm_hardfault_debug.go +++ b/src/runtime/runtime_cortexm_hardfault_debug.go @@ -35,7 +35,7 @@ func handleHardFault(sp *interruptStack) { if fault.Mem().WhileUnstackingException() { print(" while unstacking exception") } - if fault.Mem().WileStackingException() { + if fault.Mem().WhileStackingException() { print(" while stacking exception") } if fault.Mem().DuringFPLazyStatePres() { @@ -161,13 +161,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool { return fs&arm.SCB_CFSR_MUNSTKERR != 0 } -// WileStackingException: stacking for an exception entry has caused one or more +// WhileStackingException: stacking for an exception entry has caused one or more // access violations // // "When this bit is 1, the SP is still adjusted but the values in the context // area on the stack might be incorrect. The processor has not written a fault // address to the MMAR." -func (fs MemFaultStatus) WileStackingException() bool { +func (fs MemFaultStatus) WhileStackingException() bool { return fs&arm.SCB_CFSR_MSTKERR != 0 } diff --git a/src/runtime/runtime_mimxrt1062.go b/src/runtime/runtime_mimxrt1062.go index 6c1212cba..064898eb6 100644 --- a/src/runtime/runtime_mimxrt1062.go +++ b/src/runtime/runtime_mimxrt1062.go @@ -6,6 +6,7 @@ import ( "device/arm" "device/nxp" "machine" + "machine/usb" "math/bits" "unsafe" ) @@ -36,7 +37,7 @@ func main() { // initialize cache and MPU initCache() - // enable SysTick, GPIO, and peripherals + // enable SysTick, GPIO, USB, and other peripherals initPeripherals() // reenable interrupts @@ -108,7 +109,8 @@ func initPeripherals() { initPins() // configure GPIO enablePeripheralClocks() // activate peripheral clock gates - initUART() // configure UART (initialized first for debugging) + initUSB() // configure USB CDC-ACM (UART0) + initUART() // configure hardware UART (UART1) } func initPins() { @@ -123,8 +125,14 @@ func initUART() { machine.UART1.Configure(machine.UARTConfig{}) } +func initUSB() { + machine.HID0.Configure(usb.HIDConfig{}) + // machine.UART0.Configure(usb.UARTConfig{}) +} + func putchar(c byte) { - machine.UART1.WriteByte(c) + // machine.UART0.WriteByte(c) // print to USB UART + machine.UART1.WriteByte(c) // print to hardware UART } func exit(code int) { diff --git a/src/runtime/runtime_mimxrt1062_clock.go b/src/runtime/runtime_mimxrt1062_clock.go index 3a5761986..94b0c3a6b 100644 --- a/src/runtime/runtime_mimxrt1062_clock.go +++ b/src/runtime/runtime_mimxrt1062_clock.go @@ -35,15 +35,31 @@ var ( Denominator: 1, // 30-bit DENOM of fractional loop divider Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N } - Usb1PllConfig = nxp.ClockConfigUsbPll{ - Instance: 1, // USB PLL instance - LoopDivider: 0, // PLL loop divider, Fout=Fin*20 - Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N + Usb1PhyConfig = nxp.ClockConfigUsbPhy{ + Instance: 1, // USB PHY number (1 or 2) + XtalFreq: OSC_FREQ, // External reference clock frequency (Hz) + DCal: 0xC, // Decode to trim nominal 17.78mA current source + TxCal45DP: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D+ + TxCal45DM: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D- + PllConfig: nxp.ClockConfigUsbPll{ + Instance: 1, // USB PLL number (1 or 2) + LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22]) + Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N]) + Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default) + }, } - Usb2PllConfig = nxp.ClockConfigUsbPll{ - Instance: 2, // USB PLL instance - LoopDivider: 0, // PLL loop divider, Fout=Fin*20 - Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N + Usb2PhyConfig = nxp.ClockConfigUsbPhy{ + Instance: 2, // USB PHY number (1 or 2) + XtalFreq: OSC_FREQ, // External reference clock frequency (Hz) + DCal: 0xC, // Decode to trim the nominal 17.78mA current source + TxCal45DP: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D+ + TxCal45DM: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D- + PllConfig: nxp.ClockConfigUsbPll{ + Instance: 2, // USB PLL number (1 or 2) + LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22]) + Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N]) + Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default) + }, } ) @@ -85,7 +101,7 @@ func initClocks() { // set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) | - ((13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk)) + ((0x13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk)) // wait until DCDC_STS_DC_OK bit is asserted for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) { @@ -104,6 +120,8 @@ func initClocks() { nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2) nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1) + nxp.ClockIpUsbOh3.Enable(false) // disable USB + nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT nxp.ClockIpGpt1S.Enable(false) // nxp.ClockIpGpt2.Enable(false) // @@ -112,6 +130,11 @@ func initClocks() { nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1) + nxp.ClockIpGpio1.Enable(false) // disable GPIO + nxp.ClockIpGpio2.Enable(false) // + nxp.ClockIpGpio3.Enable(false) // + nxp.ClockIpGpio4.Enable(false) // + nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1 nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2) nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0 @@ -120,9 +143,9 @@ func initClocks() { nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0 nxp.ClockIpSemc.Enable(false) // disable SEMC - nxp.DivIpSemc.Div(1) // divide SEMC_PODF (DIV2) + nxp.DivIpSemc.Div(7) // divide SEMC_PODF (DIV8) nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2 - nxp.MuxIpSemc.Mux(1) // SEMC select SEMC_ALT + nxp.MuxIpSemc.Mux(0) // SEMC select PERIPH_CLK if false { // TODO: external flash is on this bus, configured via DCD block @@ -191,7 +214,7 @@ func initClocks() { nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2) nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4) - nxp.MuxIpLcdifPre.Mux(5) // LCDIF_PRE select PLL3_PFD1 + nxp.MuxIpLcdifPre.Mux(4) // LCDIF_PRE select PLL2_PFD1 nxp.ClockIpSpdif.Enable(false) // disable SPDIF nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2) @@ -209,43 +232,43 @@ func initClocks() { nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN + // Disable Audio/Video/Ethernet PLLs + nxp.CCM_ANALOG.PLL_AUDIO.Set(nxp.CCM_ANALOG_PLL_AUDIO_POWERDOWN_Msk) + nxp.CCM_ANALOG.PLL_VIDEO.Set(nxp.CCM_ANALOG_PLL_VIDEO_POWERDOWN_Msk) + nxp.CCM_ANALOG.PLL_ENET.Set(nxp.CCM_ANALOG_PLL_ENET_POWERDOWN_Msk) + ArmPllConfig.Configure() // init ARM PLL // SYS PLL (PLL2) @ 528 MHz // PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz // PFD1 = 594 MHz -> (currently unused) - // PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz - // PFD3 = 454.73 MHz -> (currently unused) + // PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz + // PFD3 = 594 MHz -> (currently unused) SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs - // USB1 PLL (PLL3) @ 480 MHz - // PFD0 -> (currently unused) - // PFD1 -> (currently unused) - // PFD2 -> (currently unused) - // PFD3 -> (currently unused) - Usb1PllConfig.Configure() // init USB1 PLL and PFDs - Usb2PllConfig.Configure() // init USB2 PLL + Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL + Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC - // set LVDS1 clock source + // set LVDS1 clock source (ARM_PLL) nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) | ((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) - // set CLOCK_OUT1 divider + // set CLOCK_OUT1 divider (DIV1) nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) | ((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk)) - // set CLOCK_OUT1 source + // set CLOCK_OUT1 source (PLL2/DIV2) nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) | ((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk)) - // set CLOCK_OUT2 divider + // set CLOCK_OUT2 divider (DIV1) nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) | ((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk)) - // set CLOCK_OUT2 source + // set CLOCK_OUT2 source (SPDIF0) nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) | - ((18 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk)) + ((29 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk)) nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1 @@ -253,15 +276,10 @@ func initClocks() { nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC - // set GPT1 High frequency reference clock source + // set GPT1 High frequency reference clock source (PERCLK) nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk) - // set GPT2 High frequency reference clock source + // set GPT2 High frequency reference clock source (PERCLK) nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk) - - nxp.ClockIpGpio1.Enable(false) // disable GPIO - nxp.ClockIpGpio2.Enable(false) // - nxp.ClockIpGpio3.Enable(false) // - nxp.ClockIpGpio4.Enable(false) // } func enableTimerClocks() { diff --git a/src/runtime/runtime_mimxrt1062_time.go b/src/runtime/runtime_mimxrt1062_time.go index 22f29d179..645c98715 100644 --- a/src/runtime/runtime_mimxrt1062_time.go +++ b/src/runtime/runtime_mimxrt1062_time.go @@ -141,7 +141,7 @@ func timerSleep(cycles uint32) bool { nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer for { - //arm.Asm("wfi") // TODO: causes hardfault! why? + waitForEvents() if pitActive.Get() == 0 { return true } diff --git a/targets/mimxrt1062-teensy40.ld b/targets/mimxrt1062-teensy40.ld index 70a717b72..c18b5e361 100644 --- a/targets/mimxrt1062-teensy40.ld +++ b/targets/mimxrt1062-teensy40.ld @@ -9,6 +9,7 @@ MEMORY ENTRY(Reset_Handler); _stack_size = 4K; +_heap_size = 512K; SECTIONS {