From df57c029a7987c719ab9ca21f9be004cf675d478 Mon Sep 17 00:00:00 2001 From: ardnew Date: Sun, 28 Mar 2021 15:46:03 -0500 Subject: [PATCH 1/9] begin USB refactor with package machine/usb2 --- src/device/nxp/mimxrt1062_clock.go | 228 +++- src/device/nxp/mimxrt1062_mpu.go | 29 +- src/machine/board_teensy40.go | 42 +- src/machine/machine_mimxrt1062.go | 1 + src/machine/machine_mimxrt1062_usb.go | 29 + src/machine/usb/config.go | 25 + src/machine/usb/config_mimxrt1062.go | 233 ++++ src/machine/usb/descriptor.go | 162 +++ src/machine/usb/device.go | 1066 +++++++++++++++++ src/machine/usb/device_cdc.go | 135 +++ src/machine/usb/device_cdc_acm.go | 690 +++++++++++ src/machine/usb/device_controller.go | 288 +++++ .../usb/device_controller_mimxrt1062.go | 709 +++++++++++ src/machine/usb/host.go | 25 + src/machine/usb/host_controller.go | 13 + src/machine/usb/spec.go | 167 +++ src/machine/usb/uart.go | 366 ++++++ src/machine/usb/usb.go | 416 +++++++ src/machine/usb2/dci.go | 9 + src/machine/usb2/dci_mimxrt1062.go | 194 +++ src/machine/usb2/desc.go | 334 ++++++ src/machine/usb2/desc_mimxrt1062.go | 32 + src/machine/usb2/hci.go | 9 + src/machine/usb2/hci_mimxrt1062.go | 126 ++ src/machine/usb2/uart.go | 53 + src/machine/usb2/usb.go | 99 ++ src/machine/usb2/util.go | 194 +++ .../runtime_cortexm_hardfault_debug.go | 6 +- src/runtime/runtime_mimxrt1062.go | 13 +- src/runtime/runtime_mimxrt1062_clock.go | 86 +- src/runtime/runtime_mimxrt1062_time.go | 2 +- targets/mimxrt1062-teensy40.ld | 2 +- 32 files changed, 5646 insertions(+), 137 deletions(-) create mode 100644 src/machine/machine_mimxrt1062_usb.go create mode 100644 src/machine/usb/config.go create mode 100644 src/machine/usb/config_mimxrt1062.go create mode 100644 src/machine/usb/descriptor.go create mode 100644 src/machine/usb/device.go create mode 100644 src/machine/usb/device_cdc.go create mode 100644 src/machine/usb/device_cdc_acm.go create mode 100644 src/machine/usb/device_controller.go create mode 100644 src/machine/usb/device_controller_mimxrt1062.go create mode 100644 src/machine/usb/host.go create mode 100644 src/machine/usb/host_controller.go create mode 100644 src/machine/usb/spec.go create mode 100644 src/machine/usb/uart.go create mode 100644 src/machine/usb/usb.go create mode 100644 src/machine/usb2/dci.go create mode 100644 src/machine/usb2/dci_mimxrt1062.go create mode 100644 src/machine/usb2/desc.go create mode 100644 src/machine/usb2/desc_mimxrt1062.go create mode 100644 src/machine/usb2/hci.go create mode 100644 src/machine/usb2/hci_mimxrt1062.go create mode 100644 src/machine/usb2/uart.go create mode 100644 src/machine/usb2/usb.go create mode 100644 src/machine/usb2/util.go 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 8483b7561..c085e9501 100644 --- a/src/device/nxp/mimxrt1062_mpu.go +++ b/src/device/nxp/mimxrt1062_mpu.go @@ -216,6 +216,12 @@ func enableIcache(enable bool) { } } +var ( + dcacheCcsidr volatile.Register32 + dcacheSets volatile.Register32 + dcacheWays volatile.Register32 +) + func enableDcache(enable bool) { if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) { if enable { @@ -244,11 +250,6 @@ func enableDcache(enable bool) { isb 0xF `, nil) } else { - var ( - ccsidr volatile.Register32 - sets volatile.Register32 - ways volatile.Register32 - ) SystemControl.CSSELR.Set(0) arm.AsmFull(` dsb 0xF @@ -257,17 +258,17 @@ func enableDcache(enable bool) { arm.AsmFull(` dsb 0xF `, nil) - ccsidr.Set(SystemControl.CCSIDR.Get()) - sets.Set((ccsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos) - for sets.Get() != 0 { - ways.Set((ccsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos) - for ways.Get() != 0 { + dcacheCcsidr.Set(SystemControl.CCSIDR.Get()) + dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos) + for dcacheSets.Get() != 0 { + dcacheWays.Set((dcacheCcsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos) + for dcacheWays.Get() != 0 { SystemControl.DCCISW.Set( - ((sets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) | - ((ways.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk)) - ways.Set(ways.Get() - 1) + ((dcacheSets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) | + ((dcacheWays.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk)) + dcacheWays.Set(dcacheWays.Get() - 1) } - sets.Set(sets.Get() - 1) + dcacheSets.Set(dcacheSets.Get() - 1) } arm.AsmFull(` dsb 0xF diff --git a/src/machine/board_teensy40.go b/src/machine/board_teensy40.go index 54ae4e6b4..0488e3b72 100644 --- a/src/machine/board_teensy40.go +++ b/src/machine/board_teensy40.go @@ -55,21 +55,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 @@ -99,6 +99,18 @@ func init() { UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt) } +// #=====================================================# +// | USB | +// #=====================================================# +var ( + // USBCDC is a legacy class being retained here as temporary wrapper. + // See godoc comments on type USBCDC struct definition for details. + USBCDC0 = USBCDC{ + port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0) + buff: NewRingBuffer(), + } +) + // #=====================================================# // | 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/machine_mimxrt1062_usb.go b/src/machine/machine_mimxrt1062_usb.go new file mode 100644 index 000000000..627cb2b0c --- /dev/null +++ b/src/machine/machine_mimxrt1062_usb.go @@ -0,0 +1,29 @@ +// +build mimxrt1062 + +// Compatibility wrapper for legacy type USBCDC, which provides USB CDC-ACM +// device class emulation for serial UART communication. +// This functionality is being replaced by a platform-agnostic type usb.UART in +// package "machine/usb". + +package machine + +import ( + "machine/usb2" +) + +// USBCDC is the legacy TinyGo type used to implement USB CDC-ACM device class +// emulation for serial UART communication. It is retained here as a temporary +// wrapper for type usb.UART from new package "machine/usb", which is still in +// active development. Once that package has stabilized a bit, this type should +// be removed and usb.UART should be used directly instead. +type USBCDC struct { + port uint8 + buff *RingBuffer + uart usb2.UART +} + +// Configure the embedded usb.UART with our receiver's settings and the given +// UART configuration. This provides compatibility with machine.UART. +func (cdc *USBCDC) Configure(config UARTConfig) { + cdc.uart.Configure(usb2.UARTConfig{BaudRate: config.BaudRate}) +} diff --git a/src/machine/usb/config.go b/src/machine/usb/config.go new file mode 100644 index 000000000..7663029a3 --- /dev/null +++ b/src/machine/usb/config.go @@ -0,0 +1,25 @@ +package usb + +// The following constants must be defined for USB 2.0 host/device support. +// +// However, some or all of these constants may be unused in the core driver +// code, depending on which components are allocated by the USB driver. +// +// These values are __PLATFORM-AGNOSTIC__, and they serve two primary roles: +// 1. Aggregate and export values derived from platform-specific constants. +// 2. Configure core attributes defined by the USB 2.0 specification. +// +const ( + // ConfigPortCount defines the number of USB cores supported on this platform. + ConfigPortCount = configDeviceCount + configHostCount +) + +// ConfigDeviceDescriptor defines the fields used to populate host-enumerated +// device descriptors. +type ConfigDeviceDescriptor struct { + Manufacturer string + Product string + VID uint16 + PID uint16 + BCD uint16 +} diff --git a/src/machine/usb/config_mimxrt1062.go b/src/machine/usb/config_mimxrt1062.go new file mode 100644 index 000000000..b14464e1b --- /dev/null +++ b/src/machine/usb/config_mimxrt1062.go @@ -0,0 +1,233 @@ +// +build mimxrt1062 + +package usb + +const configCPUFrequencyHz = 600000000 + +// The following constants must be defined for USB 2.0 host/device support. +// +// However, some or all of these constants may be unused in the core driver +// code, depending on which components are allocated by the USB driver. +// +// These values are __PLATFORM-SPECIFIC__, and they serve two primary roles: +// 1. Determine which components to allocate from the USB driver. +// 2. Configure driver attributes NOT defined by the USB 2.0 specification. +// +const ( + // configDeviceCount defines the number of USB device-mode ports available. + configDeviceCount = configDeviceCDCACMCount + + // configHostCount defines the number of USB host-mode ports available. + configHostCount = 0 + + // configInterruptQueueSize defines the number of interrupts to retain in the + // queue of runtime processes. + configInterruptQueueSize = 8 + + // configDeviceBufferSize defines the size of the send and receive data + // endpoint buffers. + configDeviceBufferSize = 512 + + // configDeviceMaxEndpoints defines the maximum number of endpoints supported. + configDeviceMaxEndpoints = 4 + + // configDeviceControllerMaxPacketSize defines the maximum packet size for + // communication with an endpoint. The maximum per USB 2.0 spec is 64 bytes, + // although a platform may restrict this to something lower if needed. + configDeviceControllerMaxPacketSize = 64 + + // configDeviceMaxPower defines the maximum power consumption of the USB + // device when fully-operational, expressed in 2 mA units. + configDeviceMaxPower = 50 // 100 mA + + // configDeviceSelfPowered defines whether the device is self-powered (1) or + // not (0). + configDeviceSelfPowered = 1 + + // configDeviceRemoteWakeup defines whether the device supports remote-wakeup + // (1) or not (0). + configDeviceRemoteWakeup = 0 // (NOT YET SUPPORTED) + + // configDeviceControllerMaxDTD defines the maximum number of DTD supported. + configDeviceControllerMaxDTD = 16 + + // configDeviceControllerQHAlign defines the memory alignment of QH buffer. + // Ensure this agrees with the go:align pragma on deviceControllerQHBuffer. + configDeviceControllerQHAlign = 2048 + + // configDeviceControllerDTDAlign defines the memory alignment of DTD buffer. + // Ensure this agrees with the go:align pragma on deviceControllerDTDBuffer. + configDeviceControllerDTDAlign = 32 +) + +// If USB CDC-ACM device support is required, the following constants must be +// defined. +const ( + // configDeviceCDCACMCount defines the number of USB CDC-ACM interfaces + // to initialize; must be less-than or equal to configDeviceCount. + configDeviceCDCACMCount = 1 + + // configDeviceCDCACMConfigurationCount defines the number of configurations + // required for each CDC-ACM device port. + configDeviceCDCACMConfigurationCount = 1 + + // configDeviceCDCACMConfigurationIndex defines the default configuration + // index used to initialize CDC-ACM device class configurations. This is a + // 1-based index into the list of CDC-ACM configurations (which are 0-based + // slices, and thus this index is offset by -1). A configuration index of 0 + // represents the invalid configuration index. + configDeviceCDCACMConfigurationIndex = 1 + + // configDeviceCDCACMInterfaceCount defines the number of interfaces required + // for each CDC-ACM configuration. + configDeviceCDCACMInterfaceCount = 2 + + // configDeviceCDCACMInterruptInPacketSize defines the packet size of CDC-ACM + // communication interface's interrupt input endpoint. + configDeviceCDCACMInterruptInPacketSize = configDeviceCDCACMFSInterruptInPacketSize // (full-speed) + + // configDeviceCDCACMBulkInPacketSize defines the packet size of CDC-ACM data + // interface's bulk input endpoint. + configDeviceCDCACMBulkInPacketSize = configDeviceCDCACMFSBulkInPacketSize // (full-speed) + + // configDeviceCDCACMBulkOutPacketSize defines the packet size of CDC-ACM data + // interface's bulk output endpoint. + configDeviceCDCACMBulkOutPacketSize = configDeviceCDCACMFSBulkOutPacketSize // (full-speed) + + // configDeviceCDCACMInterruptInInterval defines the interval of CDC-ACM + // communication interface's interrupt input endpoint. + configDeviceCDCACMInterruptInInterval = configDeviceCDCACMFSInterruptInInterval // (full-speed) +) + +// If USB CDC-ACM device support is required, the following arrays must be +// initialized with each device's CDC-ACM configuration. +var ( + // configDeviceCDCACM defines the configuration specific to CDC-ACM devices + // including the properties of endpoints required by the device class driver, + // as well as the serial UART line coding properties. + configDeviceCDCACM = [configDeviceCDCACMCount][configDeviceCDCACMConfigurationCount]deviceCDCACMConfig{ + {{ // USB CDC-ACM [0] + interfaceSpeed: specSpeedFull, // USB full-speed (12 Mbit/s) + // Serial line configuration + lineCodingSize: 7, // Size of line-coding message + lineCodingBaudRate: 115200, // Data terminal rate + lineCodingCharFormat: 0, // Character format + lineCodingParityType: 0, // Parity type + lineCodingDataBits: 8, // Data word size + // Communication/control interface + commInterfaceIndex: descriptorInterfaceCDCACMComm, // communication/control interface index + commInterruptInEndpoint: descriptorEndpointCDCACMCommInterruptIn, // interrupt input endpoint index (address) + commInterruptInPacketSize: configDeviceCDCACMInterruptInPacketSize, + commInterruptInInterval: configDeviceCDCACMInterruptInInterval, + // Data interface + dataInterfaceIndex: descriptorInterfaceCDCACMData, // data interface index + dataBulkInEndpoint: descriptorEndpointCDCACMDataBulkIn, // bulk input endpoint index (address) + dataBulkInPacketSize: configDeviceCDCACMBulkInPacketSize, + dataBulkOutEndpoint: descriptorEndpointCDCACMDataBulkOut, // bulk output endpoint index (address) + dataBulkOutPacketSize: configDeviceCDCACMBulkOutPacketSize, + }}, + } + + // configDeviceCDCACMDescriptor defines the generic USB 2.0 descriptors used + // to describe each CDC-ACM device enabled. + // + // Note that the actual descriptor byte slices need not (should not) be + // defined here. Instead, a reusable global slice should be declared as a + // standalone type and not a member of a composite type; this allows both + // reusability and better control over the memory layout/alignment for USB + // PHYs that may require constraints of this sort. The fields of struct + // deviceDescriptor are therefore all pointers to byte slices so that they + // can be initialized with references to the aforementioned global arrays. + configDeviceCDCACMDescriptor = [configDeviceCDCACMCount]deviceDescriptor{ + { // USB CDC-ACM [0] + pDevice: &descriptorDeviceCDCACM, + pConfig: &descriptorConfigurationCDCACM, + language: []deviceDescriptorLanguage{ + { + pString: []deviceDescriptorString{ + &descriptorStringCDCACMLanguage, + &configDescriptorStringCDCACMManufacturer, + &configDescriptorStringCDCACMProduct, + }, + ident: 0x0409, + }, + }, + }, + } + + // default device descriptor info (little-endian byte order). + configDescriptorDeviceCDCACMVID = []uint8{0xC9, 0x1F} // USB Vendor ID (0x1FC9) + configDescriptorDeviceCDCACMPID = []uint8{0x94, 0x00} // USB Product ID (0x0094) + configDescriptorDeviceCDCACMBCD = []uint8{0x01, 0x01} // USB Device Version (0x0101) + + configDescriptorStringCDCACMManufacturer = []uint8{ + 2 + 2*18, specDescriptorTypeString, + 'N', 0, + 'X', 0, + 'P', 0, + ' ', 0, + 'S', 0, + 'e', 0, + 'm', 0, + 'i', 0, + 'c', 0, + 'o', 0, + 'n', 0, + 'd', 0, + 'u', 0, + 'c', 0, + 't', 0, + 'o', 0, + 'r', 0, + 's', 0, + } + + configDescriptorStringCDCACMProduct = []uint8{ + 2 + 2*20, specDescriptorTypeString, + 'T', 0, + 'i', 0, + 'n', 0, + 'y', 0, + 'G', 0, + 'o', 0, + ' ', 0, + 'U', 0, + 'S', 0, + 'B', 0, + ' ', 0, + '(', 0, + 'C', 0, + 'D', 0, + 'C', 0, + '-', 0, + 'A', 0, + 'C', 0, + 'M', 0, + ')', 0, + } +) + +// The following additional constants are not required by the USB driver but are +// used by the usb package on this platform. +const ( + + // configInterruptPriority defines the priority number for USB interrupts. + configInterruptPriority = 3 + + // configDeviceControllerMaxPrimeAttempts defines the maximum number of + // attempts to prime an endpoint for transfer. If attempts exceeds this + // value, then the endpoint status has been reset. + configDeviceControllerMaxPrimeAttempts = 10000000 + + // USB CDC-ACM high-speed (480 Mbit/s) packet size + configDeviceCDCACMHSInterruptInPacketSize = 16 + configDeviceCDCACMHSInterruptInInterval = 7 // 2^(7-1)/8 = 8ms + configDeviceCDCACMHSBulkInPacketSize = 512 + configDeviceCDCACMHSBulkOutPacketSize = 512 + + // USB CDC-ACM full-speed (12 Mbit/s) packet size + configDeviceCDCACMFSInterruptInPacketSize = 16 + configDeviceCDCACMFSInterruptInInterval = 8 // 2^(8-1)/8 = 16ms + configDeviceCDCACMFSBulkInPacketSize = 64 + configDeviceCDCACMFSBulkOutPacketSize = 64 +) diff --git a/src/machine/usb/descriptor.go b/src/machine/usb/descriptor.go new file mode 100644 index 000000000..51f0ad82f --- /dev/null +++ b/src/machine/usb/descriptor.go @@ -0,0 +1,162 @@ +package usb + +// USB specification version number (BCD) +var descriptorUSBSpecification = []uint8{0x00, 0x02} // USB 2.0 + +// USB CDC-ACM descriptor constants +const ( + // interface indices + descriptorInterfaceCDCACMComm = 0 // communication/control interface + descriptorInterfaceCDCACMData = 1 // data interface + + descriptorEndpointCDCACMCommInterruptIn = 1 // interrupt endpoint (input) + descriptorEndpointCDCACMDataBulkIn = 2 // data endpoint (input) + descriptorEndpointCDCACMDataBulkOut = 3 // data endpoint (output) + + descriptorConfigurationCDCACMHeaderFuncSize = 5 + descriptorConfigurationCDCACMCallManageSize = 5 + descriptorConfigurationCDCACMAbstractSize = 4 + descriptorConfigurationCDCACMUnionFuncSize = 5 + + descriptorConfigurationCDCACMSize = uint16( + specDescriptorLengthConfigure + // configuration + specDescriptorLengthInterface + // communication/control interface + descriptorConfigurationCDCACMHeaderFuncSize + // CDC header + descriptorConfigurationCDCACMCallManageSize + // CDC call management + descriptorConfigurationCDCACMAbstractSize + // CDC abstract control + descriptorConfigurationCDCACMUnionFuncSize + // CDC union + specDescriptorLengthEndpoint + // communication/control input endpoint + specDescriptorLengthInterface + // data interface + specDescriptorLengthEndpoint + // data input endpoint + specDescriptorLengthEndpoint) // data output endpoint + + descriptorConfigurationCDCACMAttributes = (specDescriptorConfigureAttributeD7Msk) | // Bit 7: reserved (1) + (configDeviceSelfPowered << specDescriptorConfigureAttributeSelfPoweredPos) | // Bit 6: self-powered + (configDeviceRemoteWakeup << specDescriptorConfigureAttributeRemoteWakeupPos) | // Bit 5: remote wakeup + 0 // Bits 0-4: reserved (0) +) + +// USB 2.0 CDC-ACM descriptors +var ( + // descriptorDeviceCDCACM is the default device descriptor for CDC-ACM + // devices. + descriptorDeviceCDCACM = []uint8{ + specDescriptorLengthDevice, // Size of this descriptor in bytes + specDescriptorTypeDevice, // DEVICE Descriptor Type + descriptorUSBSpecification[0], // USB Specification Release Number in BCD (low) + descriptorUSBSpecification[1], // USB Specification Release Number in BCD (high) + uint8(deviceClassCDC), // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + deviceCDCNoClassSpecificProtocol, // Protocol code (assigned by the USB-IF). + configDeviceControllerMaxPacketSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + configDescriptorDeviceCDCACMVID[0], // Vendor ID (low) (assigned by the USB-IF) + configDescriptorDeviceCDCACMVID[1], // Vendor ID (high) (assigned by the USB-IF) + configDescriptorDeviceCDCACMPID[0], // Product ID (low) (assigned by the manufacturer) + configDescriptorDeviceCDCACMPID[1], // Product ID (high) (assigned by the manufacturer) + configDescriptorDeviceCDCACMBCD[0], // Device release number in BCD (low) + configDescriptorDeviceCDCACMBCD[1], // Device release number in BCD (high) + 0x01, // Index of string descriptor describing manufacturer + 0x02, // Index of string descriptor describing product + 0x00, // Index of string descriptor describing the device's serial number + configDeviceCDCACMConfigurationCount, // Number of possible configurations + } + + // descriptorConfigurationCDCACM is the default configuration descriptor for + // CDC-ACM devices. + descriptorConfigurationCDCACM = []uint8{ + specDescriptorLengthConfigure, // Size of this descriptor in bytes + specDescriptorTypeConfigure, // Descriptor Type + uint8(descriptorConfigurationCDCACMSize), // Total length of data returned for this configuration (low) + uint8(descriptorConfigurationCDCACMSize >> 8), // Total length of data returned for this configuration (high) + configDeviceCDCACMInterfaceCount, // Number of interfaces supported by this configuration + configDeviceCDCACMConfigurationIndex, // Value to use to select this configuration + 0, // Index of string descriptor describing this configuration + descriptorConfigurationCDCACMAttributes, // Configuration attributes + configDeviceMaxPower, // Max power consumption when fully-operational (2 mA units) + + // Communication/Control Interface Descriptor + specDescriptorLengthInterface, // Descriptor length + specDescriptorTypeInterface, // Descriptor type + descriptorInterfaceCDCACMComm, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + deviceCDCCommClass, // Class code + deviceCDCAbstractControlModel, // Subclass code + deviceCDCNoClassSpecificProtocol, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) + 0, // Interface Description String Index + + // CDC Header Functional Descriptor + descriptorConfigurationCDCACMHeaderFuncSize, // Size of this descriptor in bytes + specDescriptorTypeCDCInterface, // Descriptor Type + deviceCDCHeaderFuncDesc, // Descriptor Subtype + 0x10, // USB CDC specification version 1.10 (low) + 0x01, // USB CDC specification version 1.10 (high) + + // CDC Call Management Functional Descriptor + descriptorConfigurationCDCACMCallManageSize, // Size of this descriptor in bytes + specDescriptorTypeCDCInterface, // Descriptor Type + deviceCDCCallManagementFuncDesc, // Descriptor Subtype + 0x01, // Capabilities + 1, // Data Interface + + // CDC Abstract Control Management Functional Descriptor + descriptorConfigurationCDCACMAbstractSize, // Size of this descriptor in bytes + specDescriptorTypeCDCInterface, // Descriptor Type + deviceCDCAbstractControlFuncDesc, // Descriptor Subtype + 0x06, // Capabilities + + // CDC Union Functional Descriptor + descriptorConfigurationCDCACMUnionFuncSize, // Size of this descriptor in bytes + specDescriptorTypeCDCInterface, // Descriptor Type + deviceCDCUnionFuncDesc, // Descriptor Subtype + 0, // Controlling interface index + 1, // Controlled interface index + + // Communication/Control Notification Endpoint descriptor + specDescriptorLengthEndpoint, // Size of this descriptor in bytes + specDescriptorTypeEndpoint, // Descriptor Type + descriptorEndpointCDCACMCommInterruptIn | // Endpoint address + specDescriptorEndpointAddressDirectionIn, + uint8(specEndpointInterrupt), // Attributes + uint8(configDeviceCDCACMInterruptInPacketSize), // Max packet size (low) + uint8(configDeviceCDCACMInterruptInPacketSize >> 8), // Max packet size (high) + configDeviceCDCACMInterruptInInterval, // Polling Interval + + // Data Interface Descriptor + specDescriptorLengthInterface, // Interface length + specDescriptorTypeInterface, // Interface type + descriptorInterfaceCDCACMData, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + deviceCDCDataClass, // Class code + 0, // Subclass code + deviceCDCNoClassSpecificProtocol, // Protocol code + 0, // Interface Description String Index + + // Data Bulk Input Endpoint descriptor + specDescriptorLengthEndpoint, // Size of this descriptor in bytes + specDescriptorTypeEndpoint, // Descriptor Type + descriptorEndpointCDCACMDataBulkIn | // Endpoint address + specDescriptorEndpointAddressDirectionIn, + uint8(specEndpointBulk), // Attributes + uint8(configDeviceCDCACMBulkInPacketSize), // Max packet size (low) + uint8(configDeviceCDCACMBulkInPacketSize >> 8), // Max packet size (high) + 0, // Polling Interval + + // Data Bulk Output Endpoint descriptor + specDescriptorLengthEndpoint, // Size of this descriptor in bytes + specDescriptorTypeEndpoint, // Descriptor Type + descriptorEndpointCDCACMDataBulkOut | // Endpoint address + specDescriptorEndpointAddressDirectionOut, + uint8(specEndpointBulk), // Attributes + uint8(configDeviceCDCACMBulkOutPacketSize), // Max packet size (low) + uint8(configDeviceCDCACMBulkOutPacketSize >> 8), // Max packet size (high) + 0, // Polling Interval + } + + descriptorStringCDCACMLanguage = []uint8{ + 2 + 2*1, // Size of this descriptor in bytes + specDescriptorTypeString, + 0x09, 0x04, + } +) diff --git a/src/machine/usb/device.go b/src/machine/usb/device.go new file mode 100644 index 000000000..ce853190f --- /dev/null +++ b/src/machine/usb/device.go @@ -0,0 +1,1066 @@ +package usb + +// Unexported USB device type definitions. +type ( + // deviceClassDriver defines the class driver interface. + // + // This interface is used internally to abstract communication between a USB + // device and the device class (e.g., CDC, HID, MSC, etc.) in which it is + // configured. + deviceClassDriver interface { + init(device *device, config *deviceClassConfig, id uint8) status // Class driver initialization- entry of the class driver + deinit() status // Class driver de-initialization + event(event deviceClassEventID, param interface{}) status // Class driver event callback + send(ep uint8, buffer []uint8, length uint32) status // Class driver send to endpoint + receive(ep uint8, buffer []uint8, length uint32) status // Class driver receive from endpoint + } + + // deviceClassEventHandler defines the methods required to receive all device- + // and class-level event notifications on a given USB port. + // + // This interface is intended as the primary communication mechanism between + // a USB device (of any class) and the application layer using that device. + // Thus, it is meant to be implemented internally by one of the application's + // USB handlers (e.g., a serial UART driver using the CDC-ACM device class). + deviceClassEventHandler interface { + deviceEvent(ev deviceEventID, param interface{}) status + classEvent(ev uint32, param interface{}) status + } + + // deviceEndpointController defines the method(s) required + deviceEndpointController interface { + controlEndpoint(message deviceEndpointControlMessage, param interface{}) status + } + + deviceEventFunc func(ev deviceEventID, param interface{}) status + deviceRequestCallbackFunc func(setup *deviceSetup, buffer *[]uint8, length *uint32) status + + deviceNotificationID uint8 + deviceControlID uint8 + deviceStatusID uint8 + deviceStateID uint8 + deviceEndpointStatusID uint8 + deviceClassEventID uint8 + deviceEventID uint8 + deviceClassID uint8 + deviceControlRWSequence uint8 + + deviceDescriptorString *[]uint8 + + deviceDescriptorLanguage struct { + pString []deviceDescriptorString + ident uint16 + } + + deviceDescriptor struct { + pDevice *[]uint8 + pConfig *[]uint8 + language []deviceDescriptorLanguage + } + + deviceEndpointConfig struct { + maxPacketSize uint16 // Endpoint maximum packet size + address uint8 // Endpoint address + transferType uint8 // Endpoint transfer type + zlt uint8 // ZLT flag + interval uint8 // Endpoint interval + } + + deviceEndpointStatus struct { + address uint8 // Endpoint address + status uint16 // Endpoint status (idle or stalled) + } + + deviceNotification struct { + buffer []uint8 // Transferred buffer + length uint32 // Transferred data length + code deviceNotificationID // Notification code + isSetup bool // Is in a setup phase + } + + // deviceSetup contains the setup information for a USB device. + deviceSetupBitmap uint64 + deviceSetupBuffer [deviceSetupSize]uint8 + deviceSetup struct { + bmRequestType uint8 // 8, 1 (bits, bytes) + bRequest uint8 // 8, 1 + wValue uint16 // 16, 2 + wIndex uint16 // 16, 2 + wLength uint16 // 16, 2 (= 64 bits, 8 bytes) + } + + deviceEndpointControlMessage struct { + buffer []uint8 // Transferred buffer + length uint32 // Transferred data length + isSetup bool // Is in a setup phase + } + + deviceEndpointControlList [2 * configDeviceMaxEndpoints]deviceEndpointControl + deviceEndpointControl struct { + handler deviceEndpointController + param interface{} // Parameter for callback function + isBusy bool + } + + // deviceEndpoint contains the information for a USB device endpoint. + deviceEndpoint struct { + address uint8 // Endpoint address + transferType uint8 // Endpoint transfer type + maxPacketSize uint16 // Endpoint maximum packet size + interval uint8 // Endpoint interval + } + + // deviceInterface contains the endpoints and class-specific information for + // a USB device interface. + deviceInterface struct { + alternateSetting uint8 // Alternate setting number + endpoint []deviceEndpoint // Endpoints of the interface + classSpecific interface{} // Class specific structure handle + } + + // deviceClassInterface contains the USB device class details, including + // all of its device interfaces. + deviceClassInterface struct { + classCode uint8 // Class code of the interface + subclassCode uint8 // Subclass code of the interface + protocolCode uint8 // Protocol code of the interface + interfaceNumber uint8 // Interface number + deviceInterface []deviceInterface // Interface structure list + } + + // deviceClassInfo contains the USB device class ID and all of its class + // interfaces. + deviceClassInfo struct { + classID deviceClassID // Class type + interfaceList []deviceClassInterface // Interfaces of the class + } + + // deviceClassConfig contains the configuration for a USB device class. + deviceClassConfig struct { + driver deviceClassDriver // USB device class driver interface + info deviceClassInfo // Detailed information of the class + } + + // deviceClass contains common device class state information. + deviceClass struct { + device *device // USB device handle + config []deviceClassConfig // USB device class configuration list + handler deviceClassEventHandler // application callback + setupBuffer []uint8 // Setup packet data buffer + transcationBuffer uint16 // Get status/configuration, get/set interface, get sync frame + } + + device struct { + port uint8 // USB port (core index) + controller deviceController // Controller interface + class *deviceClass // USB device class + endpointControl deviceEndpointControlList // Endpoint callback function structure + deviceAddress uint8 // Current device address + state deviceStateID // Current device state + isResetting bool // Is doing device reset or not + hwTick int64 // (volatile) Current hw tick (ms) + } + + // // This structure is used to pass the control request information. + // // The structure is used in following two cases. + // // 1. Case one, the host wants to send data to the device in the control data stage: @n + // // a. If a setup packet is received, the structure is used to pass the setup packet data and wants to get the + // // buffer to receive data sent from the host. + // // The field isSetup is 1. + // // The length is the requested buffer length. + // // The buffer is filled by the class or application by using the valid buffer address. + // // The setup is the setup packet address. + // // b. If the data received is sent by the host, the structure is used to pass the data buffer address and the + // // data + // // length sent by the host. + // // In this way, the field isSetup is 0. + // // The buffer is the address of the data sent from the host. + // // The length is the received data length. + // // The setup is the setup packet address. @n + // // 2. Case two, the host wants to get data from the device in control data stage: @n + // // If the setup packet is received, the structure is used to pass the setup packet data and wants to get the + // // data buffer address to send data to the host. + // // The field isSetup is 1. + // // The length is the requested data length. + // // The buffer is filled by the class or application by using the valid buffer address. + // // The setup is the setup packet address. + deviceControlRequest struct { + setup deviceSetup // Setup data + buffer []uint8 // Buffer + length uint32 // Buffer length or requested length + isSetup bool // Indicates whether a setup packet is received + } + + // // deviceGetDescriptorCommon contains the result of a control request for: + // // get descriptor common + // deviceGetDescriptorCommon struct { + // buffer []uint8 // Buffer + // length uint32 // Buffer length + // } + + // deviceGetDeviceDescriptor contains the result of a control request for: + // get device descriptor + deviceGetDeviceDescriptor struct { + buffer []uint8 // Buffer + length uint32 // Buffer length + } + + // // deviceGetDeviceQualifierDescriptor contains the result of a control + // // request for: get device qualifier descriptor + // deviceGetDeviceQualifierDescriptor struct { + // buffer []uint8 // Buffer + // length uint32 // Buffer length + // } + + // deviceGetConfigurationDescriptor contains the result of a control request + // for: get configuration descriptor + deviceGetConfigurationDescriptor struct { + buffer []uint8 // Buffer + length uint32 // Buffer length + configuration uint8 // The configuration number + } + + // // deviceGetBOSDescriptor contains the result of a control request for: get + // // bos descriptor + // deviceGetBOSDescriptor struct { + // buffer []uint8 // Buffer + // length uint32 // Buffer length + // } + + // deviceGetStringDescriptor contains the result of a control request for: + // get string descriptor + deviceGetStringDescriptor struct { + buffer []uint8 // Buffer + length uint32 // Buffer length + languageID uint16 // Language ID + stringIndex uint8 // String index + } + + // // deviceGetHIDDescriptor contains the result of a control request for: get + // // HID descriptor + // deviceGetHIDDescriptor struct { + // buffer []uint8 // Buffer + // length uint32 // Buffer length + // interfaceNumber uint8 // The interface number + // } + + // // deviceGetHIDReportDescriptor contains the result of a control request for: + // // get HID report descriptor + // deviceGetHIDReportDescriptor struct { + // buffer []uint8 // Buffer + // length uint32 // Buffer length + // interfaceNumber uint8 // The interface number + // } + + // // deviceGetHIDPhysicalDescriptor contains the result of a control request + // // for: get HID physical descriptor + // deviceGetHIDPhysicalDescriptor struct { + // buffer []uint8 // Buffer + // length uint32 // Buffer length + // index uint8 // Physical index + // interfaceNumber uint8 // The interface number + // } + +) + +// Unexported enumerated constant values for USB device. +const ( + deviceNotifyBusReset deviceNotificationID = iota + 0x10 // Reset signal detected + deviceNotifySuspend // Suspend signal detected + deviceNotifyResume // Resume signal detected + deviceNotifyLPMSleep // LPM signal detected + deviceNotifyLPMResume // Resume signal detected + deviceNotifyError // Errors happened in bus + deviceNotifyDetach // Device disconnected from a host + deviceNotifyAttach // Device connected to a host + deviceNotifyDCDDetectFinished // Device charger detection finished +) + +const ( + deviceControlRun deviceControlID = iota // Enable the device functionality + deviceControlStop // Disable the device functionality + deviceControlEndpointInit // Initialize a specified endpoint + deviceControlEndpointDeinit // De-initialize a specified endpoint + deviceControlEndpointStall // Stall a specified endpoint + deviceControlEndpointUnstall // Un-stall a specified endpoint + deviceControlGetDeviceStatus // Get device status + deviceControlGetEndpointStatus // Get endpoint status + deviceControlSetDeviceAddress // Set device address + deviceControlGetSynchFrame // Get current frame + deviceControlResume // Drive controller to generate a resume signal in USB bus + deviceControlSleepResume // Drive controller to generate a LPM resume signal in USB bus + deviceControlSuspend // Drive controller to enter into suspend mode + deviceControlSleep // Drive controller to enter into sleep mode + deviceControlSetDefaultStatus // Set controller to default status + deviceControlGetSpeed // Get current speed + deviceControlGetOTGStatus // Get OTG status + deviceControlSetOTGStatus // Set OTG status + deviceControlSetTestMode // Drive xCHI into test mode + deviceControlGetRemoteWakeUp // Get flag of LPM Remote Wake-up Enabled by USB host. + deviceControlDCDDisable // disable dcd module function. + deviceControlDCDEnable // enable dcd module function. + deviceControlPreSetDeviceAddress // Pre set device address + deviceControlUpdateHwTick // update hardware tick +) + +const ( + deviceStatusTestMode deviceStatusID = iota + 1 // Test mode + deviceStatusSpeed // Current speed + deviceStatusOTG // OTG status + deviceStatusDevice // Device status + deviceStatusEndpoint // Endpoint state usb_device_endpoint_status_t + deviceStatusDeviceState // Device state + deviceStatusAddress // Device address + deviceStatusSynchFrame // Current frame + deviceStatusBus // Bus status + deviceStatusBusSuspend // Bus suspend + deviceStatusBusSleep // Bus suspend + deviceStatusBusResume // Bus resume + deviceStatusRemoteWakeup // Remote wakeup state + deviceStatusBusSleepResume // Bus resume +) + +const ( + deviceStateConfigured deviceStateID = iota // Device state, Configured + deviceStateAddress // Device state, Address + deviceStateDefault // Device state, Default + deviceStateAddressing // Device state, Address setting + deviceStateTestMode // Device state, Test mode + deviceStateInit // Device state, initializing +) + +const ( + deviceEndpointStateIdle deviceEndpointStatusID = iota // Endpoint state, idle + deviceEndpointStateStalled // Endpoint state, stalled +) + +const ( + deviceEventBusReset deviceEventID = iota + 1 // USB bus reset signal detected + deviceEventSuspend // USB bus suspend signal detected + deviceEventResume // USB bus resume signal detected. The resume signal is driven by itself or a host + deviceEventSleeped // USB bus LPM suspend signal detected + deviceEventLPMResume // USB bus LPM resume signal detected. The resume signal is driven by itself or a host + deviceEventError // An error is happened in the bus. + deviceEventDetach // USB device is disconnected from a host. + deviceEventAttach // USB device is connected to a host. + deviceEventSetConfiguration // Set configuration. + deviceEventSetInterface // Set interface. + deviceEventGetDeviceDescriptor // Get device descriptor. + deviceEventGetConfigurationDescriptor // Get configuration descriptor. + deviceEventGetStringDescriptor // Get string descriptor. + deviceEventGetHIDDescriptor // Get HID descriptor. + deviceEventGetHIDReportDescriptor // Get HID report descriptor. + deviceEventGetHIDPhysicalDescriptor // Get HID physical descriptor. + deviceEventGetBOSDescriptor // Get configuration descriptor. + deviceEventGetDeviceQualifierDescriptor // Get device qualifier descriptor. + deviceEventVendorRequest // Vendor request. + deviceEventSetRemoteWakeup // Enable or disable remote wakeup function. + deviceEventGetConfiguration // Get current configuration index + deviceEventGetInterface // Get current interface alternate setting value + deviceEventSetBHNPEnable // Enable or disable BHNP. + deviceEventDCDDetectionfinished // The DCD detection finished +) + +const ( + deviceClassInvalid deviceClassID = iota + deviceClassHID + deviceClassCDC + deviceClassMSC + deviceClassAudio + deviceClassPHDC + deviceClassVideo + deviceClassPrinter + deviceClassDFU + deviceClassCCID +) + +const ( + deviceClassEventInvalid deviceClassEventID = iota + deviceClassEventClassRequest + deviceClassEventDeviceReset + deviceClassEventSetConfiguration + deviceClassEventSetInterface + deviceClassEventSetEndpointHalt + deviceClassEventClearEndpointHalt +) + +const ( + deviceControlPipeSetupStage deviceControlRWSequence = iota // Setup stage + deviceControlPipeDataStage // Data stage + deviceControlPipeStatusStage // status stage +) + +// Sizes of various device structures and buffers. +const ( + deviceSetupSize = 8 // deviceSetup struct (bytes) +) + +var ( + deviceClassInstance [configDeviceCount]deviceClass + deviceSetupBufferInstance [configDeviceCount]deviceSetupBuffer + + deviceEndpointControlInConfig = deviceEndpointConfig{ + maxPacketSize: configDeviceControllerMaxPacketSize, + address: uint8(specEndpointControl | specDescriptorEndpointAddressDirectionIn), + transferType: specEndpointControl, + zlt: 1, + interval: 0, + } + deviceEndpointControlOutConfig = deviceEndpointConfig{ + maxPacketSize: configDeviceControllerMaxPacketSize, + address: uint8(specEndpointControl | specDescriptorEndpointAddressDirectionOut), + transferType: specEndpointControl, + zlt: 1, + interval: 0, + } +) + +func (d *device) init(port uint8) (s status) { + + // initialize device + d.port = port + d.controller = d.initController() // obtain a device controller handle + d.deviceAddress = 0 + d.state = deviceStateDefault + d.isResetting = false + d.hwTick = 0 + for i := range d.endpointControl { + d.endpointControl[i].handler = nil + d.endpointControl[i].param = nil + d.endpointControl[i].isBusy = false + } + + // initialize platform via device controller interface + return d.controller.init() +} + +func (d *device) deinit() status { + + // de=initialize device + s := d.controller.deinit() + d.controller = nil + return s +} + +func (d *device) initClass(id uint8, config []deviceClassConfig, + handler deviceClassEventHandler) *deviceClass { + + // verify a device configuration was provided + if nil == d || nil == config || len(config) == 0 { + return nil + } + + if 0 == id || int(id) > len(config) { + return nil + } + + c := &deviceClassInstance[d.port] + b := &deviceSetupBufferInstance[d.port] + + // initialize device class + c.device = d + c.config = config + c.handler = handler + c.setupBuffer = b[:] + c.transcationBuffer = 0 + + // add a class reference to the receiver + d.class = c + + // initialze each of the device class drivers + for i := range c.config { + if nil != c.config[i].driver { + if !c.config[i].driver.init(d, &c.config[i], id).OK() { + // remove the driver from configuration if it fails initialization + c.config[i].driver = nil + } + } + } + + return c +} + +func (d *device) transfer(address uint8, buffer []uint8, length uint32) status { + + if nil == d.controller { + return statusInvalidController + } + + endpoint, direction := unpackEndpoint(address) + index := (endpoint << 1) | direction + + if d.endpointControl[index].isBusy { + return statusBusy + } + d.endpointControl[index].isBusy = true + + var s status + if specDescriptorEndpointAddressDirectionIn == + address&specDescriptorEndpointAddressDirectionMsk { + s = d.controller.send(address, buffer, length) + } else { + s = d.controller.receive(address, buffer, length) + } + if !s.OK() { + d.endpointControl[index].isBusy = false + } + return s +} + +func (d *device) send(address uint8, buffer []uint8, length uint32) status { + return d.transfer((address&specDescriptorEndpointAddressNumberMsk)| + (specDescriptorEndpointAddressDirectionIn), buffer, length) +} + +func (d *device) receive(address uint8, buffer []uint8, length uint32) status { + return d.transfer((address&specDescriptorEndpointAddressNumberMsk)| + (specDescriptorEndpointAddressDirectionOut), buffer, length) +} + +func (d *device) cancel(address uint8) status { + if nil == d.controller { + return statusInvalidController + } + return d.controller.cancel(address) +} + +func (d *device) control(command deviceControlID, param interface{}) status { + if nil == d.controller { + return statusInvalidController + } + return d.controller.control(command, param) +} + +func (d *device) initControlPipes() status { + + if s := d.initEndpoint(&deviceEndpointControlInConfig, d, d.class); !s.OK() { + return s + } + + if s := d.initEndpoint(&deviceEndpointControlOutConfig, d, d.class); !s.OK() { + _ = d.deinitEndpoint(deviceEndpointControlInConfig.address) + return s + } + + return statusSuccess +} + +func (d *device) initEndpoint(config *deviceEndpointConfig, + handler deviceEndpointController, param interface{}) status { + + endpoint, direction := unpackEndpoint(config.address) + + if endpoint >= configDeviceMaxEndpoints { + return statusInvalidParameter + } + + d.endpointControl[(endpoint<<1)|direction].handler = handler + d.endpointControl[(endpoint<<1)|direction].param = param + d.endpointControl[(endpoint<<1)|direction].isBusy = false + + return d.control(deviceControlEndpointInit, config) +} + +func (d *device) deinitEndpoint(address uint8) status { + + s := d.control(deviceControlEndpointDeinit, address) + + endpoint, direction := unpackEndpoint(address) + + if endpoint >= configDeviceMaxEndpoints { + return statusInvalidParameter + } + + d.endpointControl[(endpoint<<1)|direction].handler = nil + d.endpointControl[(endpoint<<1)|direction].param = nil + d.endpointControl[(endpoint<<1)|direction].isBusy = false + + return s +} + +func (d *device) stallEndpoint(address uint8) status { + + endpoint, _ := unpackEndpoint(address) + + if endpoint >= configDeviceMaxEndpoints { + return statusInvalidParameter + } + return d.control(deviceControlEndpointStall, address) +} + +func (d *device) unstallEndpoint(address uint8) status { + + endpoint, _ := unpackEndpoint(address) + + if endpoint >= configDeviceMaxEndpoints { + return statusInvalidParameter + } + return d.control(deviceControlEndpointUnstall, address) +} + +func (d *device) feedback(setup *deviceSetup, status status, stage deviceControlRWSequence, + buffer *[]uint8, length *uint32) status { + direction := uint8(specIn) + if !status.OK() { + if (setup.bmRequestType&specRequestTypeTypeMsk) == specRequestTypeTypeStandard && + (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirOut && + 0 != setup.wLength && deviceControlPipeSetupStage == stage { + direction = specOut + } + return d.stallEndpoint(specEndpointControl | + (direction << specDescriptorEndpointAddressDirectionPos)) + } else { + if *length > uint32(setup.wLength) { + *length = uint32(setup.wLength) + } + s := d.send(specEndpointControl, *buffer, *length) + if s.OK() && (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirIn { + s = d.receive(specEndpointControl, nil, 0) + } + return s + } +} + +func (d *device) controlEndpoint(message deviceEndpointControlMessage, param interface{}) status { + + // verify request and parameters + if message.length == 0xFFFFFFFF || nil == param { + return statusInvalidRequest + } + class, ok := param.(*deviceClass) + if !ok { + return statusInvalidParameter + } + // read current setup buffer from given deviceClass in param + var setup deviceSetup + setupStatus := setup.parse(class.setupBuffer) + if !setupStatus.OK() { + return setupStatus + } + + // read current device state of receiver + var state deviceStateID + s := d.status(deviceStatusDeviceState, &state) + if !s.OK() { + return statusInvalidHandle + } + + // allocate buffer for request responses + buffer := []uint8{} + length := uint32(0) + + if message.isSetup { + // verify message received contains expected setup data + if nil == message.buffer || deviceSetupSize != message.length { + return statusInvalidRequest + } + // read setup data from given message buffer + s = setup.parse(message.buffer) + if !s.OK() { + return statusInvalidRequest + } + // process message as a received setup request + if (setup.bmRequestType & specRequestTypeTypeMsk) == specRequestTypeTypeStandard { + // handle standard request + if int(setup.bRequest) <= specRequestStandardSynchFrame { + _ = d.requestStandard(&setup, &buffer, &length) + } + } else { + if 0 != setup.wLength && + (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirOut { + if (setup.bmRequestType & specRequestTypeTypeClass) == specRequestTypeTypeClass { + req := deviceControlRequest{ + setup: setup, + buffer: nil, + length: uint32(setup.wLength), + isSetup: true, + } + d.classEvent(deviceClassEventClassRequest, &req) + buffer = req.buffer + length = req.length + } else if (setup.bmRequestType & specRequestTypeTypeVendor) == specRequestTypeTypeVendor { + req := deviceControlRequest{ + setup: setup, + buffer: nil, + length: uint32(setup.wLength), + isSetup: true, + } + d.event(deviceEventVendorRequest, &req) + buffer = req.buffer + length = req.length + } + if s.OK() { + return d.receive(specEndpointControl, buffer, uint32(setup.wLength)) + } + } else { + if (setup.bmRequestType & specRequestTypeTypeClass) == specRequestTypeTypeClass { + req := deviceControlRequest{ + setup: setup, + buffer: nil, + length: uint32(setup.wLength), + isSetup: true, + } + d.classEvent(deviceClassEventClassRequest, &req) + buffer = req.buffer + length = req.length + } else if (setup.bmRequestType & specRequestTypeTypeVendor) == specRequestTypeTypeVendor { + req := deviceControlRequest{ + setup: setup, + buffer: nil, + length: uint32(setup.wLength), + isSetup: true, + } + d.event(deviceEventVendorRequest, &req) + buffer = req.buffer + length = req.length + } + } + } + s = d.feedback(&setup, s, deviceControlPipeSetupStage, &buffer, &length) + } else if deviceStateAddressing == state { + // handle standard request + if int(setup.bRequest) <= specRequestStandardSynchFrame { + _ = d.requestStandard(&setup, &buffer, &length) + } + } else if 0 != message.length && 0 != setup.wLength && + (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirOut { + if setup.bmRequestType&specRequestTypeTypeClass == specRequestTypeTypeClass { + req := deviceControlRequest{ + setup: setup, + buffer: message.buffer, + length: message.length, + isSetup: false, + } + d.classEvent(deviceClassEventClassRequest, &req) + } else if setup.bmRequestType&specRequestTypeTypeVendor == specRequestTypeTypeVendor { + req := deviceControlRequest{ + setup: setup, + buffer: message.buffer, + length: message.length, + isSetup: false, + } + d.event(deviceEventVendorRequest, &req) + } + s = d.feedback(&setup, s, deviceControlPipeDataStage, &buffer, &length) + } + + return s +} + +func (d *device) requestStandard( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + switch setup.bRequest { + case specRequestStandardGetStatus: + return d.requestGetStatus(setup, buffer, length) + case specRequestStandardClearFeature, specRequestStandardSetFeature: + return d.requestSetClearFeature(setup, buffer, length) + case specRequestStandardSetAddress: + return d.requestSetAddress(setup, buffer, length) + case specRequestStandardGetDescriptor: + return d.requestGetDescriptor(setup, buffer, length) + case specRequestStandardGetConfiguration: + return d.requestGetConfiguration(setup, buffer, length) + case specRequestStandardSetConfiguration: + return d.requestSetConfiguration(setup, buffer, length) + case specRequestStandardGetInterface: + return d.requestGetInterface(setup, buffer, length) + case specRequestStandardSetInterface: + return d.requestSetInterface(setup, buffer, length) + case specRequestStandardSynchFrame: + return d.requestSynchFrame(setup, buffer, length) + default: + return statusInvalidRequest + } +} + +func (d *device) requestGetStatus( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestSetClearFeature( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestSetAddress( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestGetDescriptor( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestGetConfiguration( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestSetConfiguration( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestGetInterface( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestSetInterface( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) requestSynchFrame( + setup *deviceSetup, buffer *[]uint8, length *uint32) status { + return statusSuccess +} + +func (d *device) classEvent(ev deviceClassEventID, param interface{}) { + if nil != d.class && nil != d.class.config { + // route the event to all class configurations + for i := range d.class.config { + _ = d.class.config[i].driver.event(ev, param) + } + } +} + +func (d *device) event(ev deviceEventID, param interface{}) { + + switch ev { + case deviceEventBusReset: + // initialize control pipes + d.initControlPipes() + + // notify all classes of a bus reset signal + for i := range d.class.config { + _ = d.class.config[i].driver.event(deviceClassEventDeviceReset, d.class) + } + } + // notify the application driver of all events + if nil != d.class.handler { + _ = d.class.handler.deviceEvent(ev, param) + } +} + +func (d *device) notify(message deviceNotification) { + + switch message.code { + case deviceNotifyBusReset: + d.notifyReset(message) + + default: + endpoint, direction := unpackEndpoint(uint8(message.code)) + + if endpoint < configDeviceMaxEndpoints { + if nil != d.endpointControl[(endpoint<<1)|direction].handler { + if message.isSetup { + d.endpointControl[0].isBusy = false + d.endpointControl[1].isBusy = false + } else { + d.endpointControl[(endpoint<<1)|direction].isBusy = false + } + // call endpoint callback + _ = d.endpointControl[(endpoint<<1)|direction].handler.controlEndpoint( + deviceEndpointControlMessage{ + buffer: message.buffer, + length: message.length, + isSetup: message.isSetup, + }, + d.endpointControl[(endpoint<<1)|direction].param, + ) + } + } + } +} + +func (d *device) notifyReset(message deviceNotification) { + + d.isResetting = true + _ = d.control(deviceControlSetDefaultStatus, nil) + + d.state = deviceStateDefault + d.deviceAddress = 0 + + for count := 0; count < 2*configDeviceMaxEndpoints; count++ { + d.endpointControl[count].handler = nil + d.endpointControl[count].param = nil + d.endpointControl[count].isBusy = false + } + + d.event(deviceEventBusReset, nil) + d.isResetting = false +} + +func (d *device) status(deviceStatus deviceStatusID, param interface{}) status { + + if nil == param { + return statusInvalidParameter + } + + switch deviceStatus { + case deviceStatusSpeed: + return d.control(deviceControlGetSpeed, param) + + case deviceStatusOTG: + return d.control(deviceControlGetOTGStatus, param) + + case deviceStatusDeviceState: + if state, ok := param.(*deviceStateID); ok { + *state = d.state + return statusSuccess + } + return statusInvalidParameter + + case deviceStatusAddress: + if address, ok := param.(*uint8); ok { + *address = d.deviceAddress + return statusSuccess + } + return statusInvalidParameter + + case deviceStatusDevice: + return d.control(deviceControlGetDeviceStatus, param) + + case deviceStatusEndpoint: + return d.control(deviceControlGetEndpointStatus, param) + + case deviceStatusSynchFrame: + return d.control(deviceControlGetSynchFrame, param) + + default: + return statusInvalidParameter + } +} + +func (d *device) setStatus(deviceStatus deviceStatusID, param interface{}) status { + + switch deviceStatus { + case deviceStatusOTG: + return d.control(deviceControlSetOTGStatus, param) + + case deviceStatusDeviceState: + if state, ok := param.(deviceStateID); ok { + d.state = state + return statusSuccess + } + return statusInvalidParameter + + case deviceStatusAddress: + if d.state != deviceStateAddressing { + if address, ok := param.(uint8); ok { + d.deviceAddress = address + d.state = deviceStateAddressing + return d.control(deviceControlPreSetDeviceAddress, d.deviceAddress) + } + return statusInvalidParameter + } + return d.control(deviceControlSetDeviceAddress, d.deviceAddress) + + case deviceStatusBusResume: + return d.control(deviceControlResume, param) + + case deviceStatusBusSleepResume: + return d.control(deviceControlSleepResume, param) + + case deviceStatusBusSuspend: + return d.control(deviceControlSuspend, param) + + case deviceStatusBusSleep: + return d.control(deviceControlSleep, param) + + default: + return statusInvalidParameter + } +} + +func (d *device) busSpeed(speed *uint8) status { + return d.status(deviceStatusSpeed, speed) +} + +func (d *device) setBusSpeed(speed uint8) status { + // TODO + return statusSuccess +} + +func (d *device) deviceDescriptor(desc *deviceGetDeviceDescriptor) status { + if int(d.port) < len(configDeviceCDCACMDescriptor) { + if nil != configDeviceCDCACMDescriptor[d.port].pDevice { + desc.buffer = *configDeviceCDCACMDescriptor[d.port].pDevice + desc.length = specDescriptorLengthDevice + return statusSuccess + } + } + return statusInvalidHandle +} + +func (d *device) configurationDescriptor(desc *deviceGetConfigurationDescriptor) status { + if int(d.port) < len(configDeviceCDCACMDescriptor) { + if nil != configDeviceCDCACMDescriptor[d.port].pConfig { + desc.buffer = *configDeviceCDCACMDescriptor[d.port].pConfig + desc.length = uint32(descriptorConfigurationCDCACMSize) + return statusSuccess + } + } + return statusInvalidHandle +} + +func (d *device) stringDescriptor(desc *deviceGetStringDescriptor) status { + if 0 == desc.stringIndex { + desc.buffer = descriptorStringCDCACMLanguage + desc.length = uint32(len(descriptorStringCDCACMLanguage)) + return statusSuccess + } + if int(d.port) < len(configDeviceCDCACMDescriptor) { + for _, lang := range configDeviceCDCACMDescriptor[d.port].language { + if desc.languageID == lang.ident { + if int(desc.stringIndex) < len(lang.pString) { + desc.buffer = *lang.pString[desc.stringIndex] + desc.length = uint32(len(desc.buffer)) + } + } + } + } + return statusInvalidRequest +} + +func (s deviceSetup) pack() deviceSetupBitmap { + return deviceSetupBitmap( + ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8 // 8 (bits) + ((uint64(s.bRequest) & 0xFF) << 8) | // uint8 // 8 + ((uint64(s.wValue) & 0xFFFF) << 16) | // uint16 // 16 + ((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16 // 16 + ((uint64(s.wLength) & 0xFFFF) << 48)) // uint16 // 16 (= 64 bits) +} + +func (s deviceSetup) bytes() deviceSetupBuffer { + return [deviceSetupSize]uint8{ + s.bmRequestType, + s.bRequest, + // for 16-bit words: low-byte is at index N, high-byte is at N+1 + uint8(s.wValue), uint8(s.wValue >> 8), + uint8(s.wIndex), uint8(s.wIndex >> 8), + uint8(s.wLength), uint8(s.wLength >> 8), + } +} + +func (s deviceSetupBitmap) bytes() deviceSetupBuffer { + return [deviceSetupSize]uint8{ + uint8(s >> 0), uint8(s >> 8), uint8(s >> 16), uint8(s >> 24), + uint8(s >> 32), uint8(s >> 40), uint8(s >> 48), uint8(s >> 56), + } +} + +func (s *deviceSetup) parse(buffer []uint8) status { + if nil == buffer || len(buffer) < deviceSetupSize { + return statusInvalidParameter + } + s.bmRequestType = buffer[0] + s.bRequest = buffer[1] + s.wValue = (uint16(buffer[3]) << 8) | uint16(buffer[2]) + s.wIndex = (uint16(buffer[5]) << 8) | uint16(buffer[4]) + s.wLength = (uint16(buffer[7]) << 8) | uint16(buffer[6]) + return statusSuccess +} diff --git a/src/machine/usb/device_cdc.go b/src/machine/usb/device_cdc.go new file mode 100644 index 000000000..fadec6193 --- /dev/null +++ b/src/machine/usb/device_cdc.go @@ -0,0 +1,135 @@ +package usb + +const ( + // Communication Class + deviceCDCCommClass = 0x02 + // Data Class + deviceCDCDataClass = 0x0A + + // Communication Class SubClass Codes + deviceCDCDirectLineControlModel = 0x01 + deviceCDCAbstractControlModel = 0x02 + deviceCDCTelephoneControlModel = 0x03 + deviceCDCMultiChannelControlModel = 0x04 + deviceCDCCAPIControlMopdel = 0x05 + deviceCDCEthernetNetworkingControlModel = 0x06 + deviceCDCATMNetworkingControlModel = 0x07 + deviceCDCWirelessHandsetControlModel = 0x08 + deviceCDCDeviceManagement = 0x09 + deviceCDCMobileDirectLineModel = 0x0A + deviceCDCOBEX = 0x0B + deviceCDCEthernetEmulationModel = 0x0C + + // Communication Class Protocol Codes + deviceCDCNoClassSpecificProtocol = 0x00 // also for Data Class Protocol Code + deviceCDCAT250Protocol = 0x01 + deviceCDCATPCCA101Protocol = 0x02 + deviceCDCATPCCA101AnnexO = 0x03 + deviceCDCATGSM707 = 0x04 + deviceCDCAT3GPP27007 = 0x05 + deviceCDCATTIACDMA = 0x06 + deviceCDCEthernetEmulationProtocol = 0x07 + deviceCDCExternalProtocol = 0xFE + deviceCDCVendorSpecific = 0xFF // also for Data Class Protocol Code + + // Data Class Protocol Codes + deviceCDCPyhsicalInterfaceProtocol = 0x30 + deviceCDCHDLCProtocol = 0x31 + deviceCDCTransparentProtocol = 0x32 + deviceCDCManagementProtocol = 0x50 + deviceCDCDataLinkQ931Protocol = 0x51 + deviceCDCDataLinkQ921Protocol = 0x52 + deviceCDCDataCompressionV42BIS = 0x90 + deviceCDCEuroISDNProtocol = 0x91 + deviceCDCRateAdaptionISDNV24 = 0x92 + deviceCDCCAPICommands = 0x93 + deviceCDCHostBasedDriver = 0xFD + deviceCDCUnitFunctional = 0xFE + + // Descriptor SubType in Communications Class Functional Descriptors + deviceCDCHeaderFuncDesc = 0x00 + deviceCDCCallManagementFuncDesc = 0x01 + deviceCDCAbstractControlFuncDesc = 0x02 + deviceCDCDirectLineFuncDesc = 0x03 + deviceCDCTelephoneRingerFuncDesc = 0x04 + deviceCDCTelephoneReportFuncDesc = 0x05 + deviceCDCUnionFuncDesc = 0x06 + deviceCDCCountrySelectFuncDesc = 0x07 + deviceCDCTelephoneModesFuncDesc = 0x08 + deviceCDCTerminalFuncDesc = 0x09 + deviceCDCNetworkChannelFuncDesc = 0x0A + deviceCDCProtocolUnitFuncDesc = 0x0B + deviceCDCExtensionUnitFuncDesc = 0x0C + deviceCDCMultiChannelFuncDesc = 0x0D + deviceCDCCAPIControlFuncDesc = 0x0E + deviceCDCEthernetNetworkingFuncDesc = 0x0F + deviceCDCATMNetworkingFuncDesc = 0x10 + deviceCDCWirelessControlFuncDesc = 0x11 + deviceCDCMobileDirectLineFuncDesc = 0x12 + deviceCDCMDLMDetailFuncDesc = 0x13 + deviceCDCDeviceManagementFuncDesc = 0x14 + deviceCDCOBEXFuncDesc = 0x15 + deviceCDCCommandSetFuncDesc = 0x16 + deviceCDCCommandSetDetailFuncDesc = 0x17 + deviceCDCTelephoneControlFuncDesc = 0x18 + deviceCDCOBEXServiceIDFuncDesc = 0x19 + + deviceCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND + deviceCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE + deviceCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE + deviceCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE + deviceCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE + deviceCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE + deviceCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE + deviceCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP + deviceCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE + deviceCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME + deviceCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK + deviceCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING + deviceCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING + deviceCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE + deviceCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK + deviceCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS + deviceCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS + deviceCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM + deviceCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM + deviceCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS + deviceCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS + deviceCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS + deviceCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER + deviceCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER + deviceCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER + deviceCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS + deviceCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER + deviceCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER + deviceCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER + deviceCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC + deviceCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT + deviceCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS + deviceCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC + deviceCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS + deviceCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK + + deviceCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION + deviceCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL + deviceCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE + deviceCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT + deviceCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE + deviceCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE + deviceCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE + deviceCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE + + deviceCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE + deviceCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING + + deviceCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION + deviceCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE + deviceCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER + deviceCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER + deviceCDCUARTStateBreak = 0x04 // UART state BREAK + deviceCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL + deviceCDCUARTStateFraming = 0x10 // UART state FRAMING + deviceCDCUARTStateParity = 0x20 // UART state PARITY + deviceCDCUARTStateOverrun = 0x40 // UART state OVERRUN + +) diff --git a/src/machine/usb/device_cdc_acm.go b/src/machine/usb/device_cdc_acm.go new file mode 100644 index 000000000..0b0052e1c --- /dev/null +++ b/src/machine/usb/device_cdc_acm.go @@ -0,0 +1,690 @@ +package usb + +import "bytes" + +type ( + deviceCDCACMEventID uint8 + + // deviceCDCACMConfig defines the platform-specific configuration settings for + // a single CDC-ACM device class on a given USB port. + // + // To connect the USB CDC-ACM device driver to a particular platform, there + // should be one instance for each USB CDC-ACM port, defined in global array + // configDeviceCDCACM, indexed by USB port. + deviceCDCACMConfig struct { + // USB configuration + interfaceSpeed uint8 // Low/Full/High-speed + // CDC-ACM Serial line configuration + lineCodingSize uint32 // Size of line-coding message + lineCodingBaudRate uint32 // Data terminal rate + lineCodingCharFormat uint32 // Character format + lineCodingParityType uint32 // Parity type + lineCodingDataBits uint32 // Data word size + // CDC-ACM Communication/control interface + commInterfaceIndex uint8 // Communication/control interface index + commInterruptInEndpoint uint8 // Interrupt input endpoint index (address) + commInterruptInPacketSize uint16 // Interrupt input packet size + commInterruptInInterval uint8 // Interrupt input interval + // CDC-ACM Data interface + dataInterfaceIndex uint8 // Data interface index + dataBulkInEndpoint uint8 // Bulk input endpoint index (address) + dataBulkInPacketSize uint16 // Bulk input packet size + dataBulkOutEndpoint uint8 // Bulk output endpoint index (address) + dataBulkOutPacketSize uint16 // Bulk output packet size + } + + deviceCDCACM struct { + device *device // The handle of the USB device. + config *deviceClassConfig // The class configure structure. + info deviceCDCACMInfo // The ACM serial state. + comm *deviceInterface // The CDC communication interface handle. + data *deviceInterface // The CDC data interface handle. + sendBuffer *deviceCDCACMDataBuffer // Pointer to the global send buffer + recvBuffer *deviceCDCACMDataBuffer // Pointer to the global receive buffer + bulkIn deviceCDCACMPipe // The bulk in pipe for sending packet to host. + bulkOut deviceCDCACMPipe // The bulk out pipe for receiving packet from host. + interruptIn deviceCDCACMPipe // The interrupt in pipe for notifying the device state to host. + interfaceNumber uint8 // The current interface number. + alternate uint8 // The alternate setting value of the interface. + hasSentState bool // The device has primed the state in interrupt pipe + speed uint8 // Speed of USB device (Full/Low/High) + lineCodingSize uint32 // Size of line-coding message + baudRate uint32 // Data terminal rate + charFormat uint32 // Character format + parityType uint32 // Parity type + dataBits uint32 // Data word size + sendSize uint32 // Number of bytes scheduled in global send buffer + recvSize uint32 // Number of bytes scheduled in global receive buffer + } + + deviceCDCACMDataBuffer [configDeviceBufferSize]uint8 + deviceCDCACMAlternateList [configDeviceCDCACMInterfaceCount]uint16 + deviceCDCACMSerialState [deviceCDCACMSerialStateSize]uint8 + + deviceCDCACMRequestParam struct { + buffer *[]uint8 // The pointer to the address of the buffer for CDC class request. + length *uint32 // The pointer to the length of the buffer for CDC class request. + interfaceIndex uint16 // The interface index of the setup packet. + setupValue uint16 // The wValue field of the setup packet. + isSetup bool // The flag indicates if it is a setup packet + } + + deviceCDCACMPipe struct { + pipeDataBuffer []uint8 // pipe data buffer backup when stall + pipeDataLen uint32 // pipe data length backup when stall + pipeStall bool // pipe is stall + ep uint8 // The endpoint number of the pipe. + isBusy bool // The pipe is transferring packet + } + + deviceCDCACMInfo struct { + serialState deviceCDCACMSerialState // Serial state buffer of the CDC device to notify the serial state to host. + dtePresent bool // A flag to indicate whether DTE is present. + breakDuration uint16 // Length of time in milliseconds of the break signal + dteStatus uint8 // Status of data terminal equipment + currentInterface uint8 // Current interface index. + uartState uint16 // UART state of the CDC device. + } +) + +const ( + deviceCDCACMEventSendResponse deviceCDCACMEventID = iota + 1 // This event indicates the bulk send transfer is complete or cancelled etc. + deviceCDCACMEventRecvResponse // This event indicates the bulk receive transfer is complete or cancelled etc.. + deviceCDCACMEventSerialStateNotify // This event indicates the serial state has been sent to the host. + deviceCDCACMEventSendEncapsulatedCommand // This event indicates the device received the SEND_ENCAPSULATED_COMMAND request. + deviceCDCACMEventGetEncapsulatedResponse // This event indicates the device received the GET_ENCAPSULATED_RESPONSE request. + deviceCDCACMEventSetCommFeature // This event indicates the device received the SET_COMM_FEATURE request. + deviceCDCACMEventGetCommFeature // This event indicates the device received the GET_COMM_FEATURE request. + deviceCDCACMEventClearCommFeature // This event indicates the device received the CLEAR_COMM_FEATURE request. + deviceCDCACMEventGetLineCoding // This event indicates the device received the GET_LINE_CODING request. + deviceCDCACMEventSetLineCoding // This event indicates the device received the SET_LINE_CODING request. + deviceCDCACMEventSetControlLineState // This event indicates the device received the SET_CONTRL_LINE_STATE request. + deviceCDCACMEventSendBreak // This event indicates the device received the SEND_BREAK request. + + deviceCDCACMRequestNotify = 0xA1 + + deviceCDCACMInfoNotifyPacketSize = 8 // (bytes) + deviceCDCACMInfoUARTBitmapSize = 2 // + deviceCDCACMSerialStateSize = deviceCDCACMInfoNotifyPacketSize + deviceCDCACMInfoUARTBitmapSize +) + +var ( + deviceCDCACMDataSendBuffer = [configDeviceCDCACMCount]deviceCDCACMDataBuffer{} + deviceCDCACMDataRecvBuffer = [configDeviceCDCACMCount]deviceCDCACMDataBuffer{} + + deviceCDCACMLineCoding = [configDeviceCDCACMCount][][]uint8{ + { // USB CDC-ACM port 0 + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // configuration index 1 + }, + } + + deviceCDCACMConfigInstance = [configDeviceCDCACMCount][]deviceClassConfig{ + {{ + driver: &deviceCDCACM{}, + info: deviceClassInfo{ + classID: deviceClassCDC, + interfaceList: []deviceClassInterface{ + { // USB CDC-ACM communications/control interface + classCode: deviceCDCCommClass, + subclassCode: deviceCDCAbstractControlModel, + protocolCode: deviceCDCNoClassSpecificProtocol, + deviceInterface: []deviceInterface{{ + alternateSetting: 0, + endpoint: []deviceEndpoint{ + {transferType: specEndpointInterrupt}, + }, + }}, + }, + { // USB CDC-ACM serial data interface + classCode: deviceCDCDataClass, + subclassCode: 0x00, + protocolCode: deviceCDCNoClassSpecificProtocol, + deviceInterface: []deviceInterface{{ + alternateSetting: 0, + endpoint: []deviceEndpoint{ + {transferType: specEndpointBulk}, + {transferType: specEndpointBulk}, + }, + }}, + }, + }, + }, + }}, + } + + deviceCDCACMBufferInvalid32 = leU32(0xFFFFFFFF) +) + +func (acm *deviceCDCACM) init(device *device, config *deviceClassConfig, id uint8) status { + + // initialize our associated object references + acm.device = device + acm.config = config + + // grab references to the global data buffers + acm.sendBuffer = &deviceCDCACMDataSendBuffer[device.port] + acm.recvBuffer = &deviceCDCACMDataRecvBuffer[device.port] + + // initialize line-coding details from global configuration + acm.speed = configDeviceCDCACM[device.port][id-1].interfaceSpeed + acm.lineCodingSize = configDeviceCDCACM[device.port][id-1].lineCodingSize + acm.baudRate = configDeviceCDCACM[device.port][id-1].lineCodingBaudRate + acm.charFormat = configDeviceCDCACM[device.port][id-1].lineCodingCharFormat + acm.parityType = configDeviceCDCACM[device.port][id-1].lineCodingParityType + acm.dataBits = configDeviceCDCACM[device.port][id-1].lineCodingDataBits + + lineCoding := deviceCDCACMLineCoding[device.port][id-1] + lineCoding[0] = uint8(acm.baudRate >> 0) + lineCoding[1] = uint8(acm.baudRate >> 8) + lineCoding[2] = uint8(acm.baudRate >> 16) + lineCoding[3] = uint8(acm.baudRate >> 24) + lineCoding[4] = uint8(acm.charFormat) + lineCoding[5] = uint8(acm.parityType) + lineCoding[6] = uint8(acm.dataBits) + + // initialize remaining state data + acm.alternate = 0xFF + + return statusSuccess +} + +func (acm *deviceCDCACM) deinit() status { + return statusSuccess +} + +func (acm *deviceCDCACM) initEndpoints() status { + + // find the communication/control interface in our CDC-ACM configuration list + acm.interfaceNumber, acm.comm = acm.findInterface(deviceCDCCommClass) + + // verify we found a valid communication/control interface + if nil == acm.comm { + return statusInvalidHandle + } + + // initialize all of the communication/control input endpoints + if s := acm.initInterfaceEndpoints(specIn, specEndpointInterrupt); !s.OK() { + return s + } + + // find the data interface in our CDC-ACM configuration list + _, acm.data = acm.findInterface(deviceCDCDataClass) + + // verify we found a valid data interface + if nil == acm.data { + return statusInvalidHandle + } + + // initialize all of the data input endpoints + if s := acm.initInterfaceEndpoints(specIn, specEndpointBulk); !s.OK() { + return s + } + // initialize all of the data output endpoints + if s := acm.initInterfaceEndpoints(specOut, specEndpointBulk); !s.OK() { + return s + } + + return statusSuccess +} + +func (acm *deviceCDCACM) initInterfaceEndpoints(direction, transferType uint8) status { + + // select the relevant fields and callbacks for our given interface type + pipe, deviceInterface := acm.endpointInterface(direction, transferType) + if nil == deviceInterface { + return statusInvalidParameter + } + + // initialize each endpoint with given direction and transfer type + for _, ep := range deviceInterface.endpoint { + num, dir := unpackEndpoint(ep.address) + if dir == direction && ep.transferType == transferType { + pipe.pipeDataBuffer = deviceCDCACMBufferInvalid32 + pipe.pipeDataLen = 0 + pipe.pipeStall = false + pipe.ep = num + pipe.isBusy = false + + if s := acm.device.initEndpoint(&deviceEndpointConfig{ + maxPacketSize: ep.maxPacketSize, + address: ep.address, + transferType: ep.transferType, + zlt: 0, + interval: ep.interval, + }, acm, pipe); !s.OK() { + return s + } + } + } + + return statusSuccess +} + +func (acm *deviceCDCACM) deinitEndpoints() status { + + if nil == acm.device { + return statusInvalidHandle + } + + if nil != acm.comm { + for i := range acm.comm.endpoint { + _ = acm.device.deinitEndpoint(acm.comm.endpoint[i].address) + } + acm.comm = nil + } + if nil != acm.data { + for i := range acm.data.endpoint { + _ = acm.device.deinitEndpoint(acm.data.endpoint[i].address) + } + acm.data = nil + } + + return statusSuccess +} + +func (acm *deviceCDCACM) event(event deviceClassEventID, param interface{}) (s status) { + + // assume success unless error condition deliberately detected + s = statusSuccess + + switch event { + case deviceClassEventDeviceReset: + // bus reset, clear the selected configuration + acm.config = nil + + case deviceClassEventSetConfiguration: + if id, ok := param.(uint8); ok { + // configuration index is 1-based, meaning configuration 0 is invalid + if 0 == id || int(id) > len(acm.device.class.config) { + return statusInvalidParameter + } + if &acm.device.class.config[id-1] == acm.config { + break // configuration already selected + } + // de-initialize endpoints of current configuration + if s = acm.deinitEndpoints(); !s.OK() { + break + } + // select new configuration, reset alternate setting + acm.config = &acm.device.class.config[id-1] + acm.alternate = 0 + // initialize endpoints of new configuration + if s = acm.initEndpoints(); !s.OK() { + break + } + } else { + // unexpected parameter, should be uint8 (configuration index) + s = statusInvalidParameter + } + + case deviceClassEventSetInterface: + if interfaceAlternate, ok := param.(uint16); ok { + alternate := uint8(interfaceAlternate & 0xFF) + if acm.interfaceNumber != uint8(interfaceAlternate>>8) { + break // requested alternate from interface different than current + } + if alternate == acm.alternate { + break // requested alternate same as current + } + // de-initialize endpoints of current configuration + if s = acm.deinitEndpoints(); !s.OK() { + break + } + // select new alternate + acm.alternate = alternate + // initialize endpoints of new configuration + if s = acm.initEndpoints(); !s.OK() { + break + } + } else { + // unexpected parameter, should be uint16: interface (hi), alternate (lo) + s = statusInvalidParameter + } + + case deviceClassEventSetEndpointHalt: + // verify parameter and fields + if address, ok := param.(uint8); ok { + if nil != acm.config && nil != acm.comm && nil != acm.data { + // check if given endpoint is a communication/control endpoint + for _, e := range acm.comm.endpoint { + if address == e.address { + // found endpoint, set stall flag + acm.interruptIn.pipeStall = true + // notify device + c := acm.device.control(deviceControlEndpointStall, address) + if s.OK() && !c.OK() { + s = c + } + } + } + // check if given endpoint is a data endpoint + for _, e := range acm.data.endpoint { + if address == e.address { + _, direction := unpackEndpoint(address) + // found endpoint, set stall flag + if specIn == direction { + acm.bulkIn.pipeStall = true + } else { + acm.bulkOut.pipeStall = true + } + // notify device + c := acm.device.control(deviceControlEndpointStall, address) + if s.OK() && !c.OK() { + s = c + } + } + } + } else { + s = statusInvalidHandle + } + } else { + // unexpected parameter, should be uint8 (endpoint address) + s = statusInvalidParameter + } + + case deviceClassEventClearEndpointHalt: + // verify parameter and fields + if address, ok := param.(uint8); ok { + if nil != acm.config && nil != acm.comm && nil != acm.data { + // check if given endpoint is a communication/control endpoint + for _, e := range acm.comm.endpoint { + if address == e.address { + // found endpoint, notify device + c := acm.device.control(deviceControlEndpointUnstall, address) + if s.OK() && !c.OK() { + s = c + } + _, direction := unpackEndpoint(address) + if specIn == direction { + // flush any buffered data written to the stalled endpoint + if acm.interruptIn.pipeStall { + // clear stall flag + acm.interruptIn.pipeStall = false + // verify the buffer has valid data + if !bytes.Equal(acm.interruptIn.pipeDataBuffer, deviceCDCACMBufferInvalid32) { + // transmit + u := acm.device.send( + acm.interruptIn.ep, + acm.interruptIn.pipeDataBuffer, + acm.interruptIn.pipeDataLen) + if !u.OK() { + // notify upper layer driver of communication/control event + _ = acm.controlEndpoint( + deviceEndpointControlMessage{ + buffer: acm.interruptIn.pipeDataBuffer, + length: acm.interruptIn.pipeDataLen, + isSetup: false, + }, + &acm.interruptIn, + ) + if s.OK() { + s = u + } + } + // clear the stalled endpoint buffer + acm.interruptIn.pipeDataBuffer = deviceCDCACMBufferInvalid32 + acm.interruptIn.pipeDataLen = 0 + } + } + } + } + } + // check if given endpoint is a data endpoint + for _, e := range acm.data.endpoint { + if address == e.address { + // found endpoint, notify device + c := acm.device.control(deviceControlEndpointUnstall, address) + if s.OK() && !c.OK() { + s = c + } + // check if endpoint is an input or output + _, direction := unpackEndpoint(address) + if specIn == direction { + // flush any buffered data written to the stalled endpoint + if acm.bulkIn.pipeStall { + // clear stall flag + acm.bulkIn.pipeStall = false + // verify the buffer has valid data + if !bytes.Equal(acm.bulkIn.pipeDataBuffer, deviceCDCACMBufferInvalid32) { + // transmit + u := acm.device.send( + acm.bulkIn.ep, + acm.bulkIn.pipeDataBuffer, + acm.bulkIn.pipeDataLen) + if !u.OK() { + // notify upper layer driver of data input event + _ = acm.controlEndpoint( + deviceEndpointControlMessage{ + buffer: acm.bulkIn.pipeDataBuffer, + length: acm.bulkIn.pipeDataLen, + isSetup: false, + }, + &acm.bulkIn, + ) + if s.OK() { + s = u + } + } + // clear the stalled endpoint buffer + acm.bulkIn.pipeDataBuffer = deviceCDCACMBufferInvalid32 + acm.bulkIn.pipeDataLen = 0 + } + } + } else { + // flush any buffered data read from the stalled endpoint + if acm.bulkOut.pipeStall { + // clear stall flag + acm.bulkOut.pipeStall = false + // verify the buffer has valid data + if !bytes.Equal(acm.bulkOut.pipeDataBuffer, deviceCDCACMBufferInvalid32) { + // receive + u := acm.device.receive( + acm.bulkOut.ep, + acm.bulkOut.pipeDataBuffer, + acm.bulkOut.pipeDataLen) + if !u.OK() { + // notify upper layer driver of data output event + _ = acm.controlEndpoint( + deviceEndpointControlMessage{ + buffer: acm.bulkOut.pipeDataBuffer, + length: acm.bulkOut.pipeDataLen, + isSetup: false, + }, + &acm.bulkOut, + ) + if s.OK() { + s = u + } + } + // clear the stalled endpoint buffer + acm.bulkOut.pipeDataBuffer = deviceCDCACMBufferInvalid32 + acm.bulkOut.pipeDataLen = 0 + } + } + } + } + } + } else { + s = statusInvalidHandle + } + } else { + // unexpected parameter, should be uint8 (endpoint address) + s = statusInvalidParameter + } + case deviceClassEventClassRequest: + // verify parameter and fields + if request, ok := param.(*deviceControlRequest); ok { + // verify requested interface is receiver's interface and request is CDC + if (request.setup.wIndex&0xFF) != uint16(acm.interfaceNumber) || + (request.setup.bmRequestType&specRequestTypeTypeMsk) != specRequestTypeTypeClass { + // construct standard parameter to be passed on to upper layer drivevr + param := deviceCDCACMRequestParam{ + buffer: &request.buffer, + length: &request.length, + interfaceIndex: request.setup.wIndex, + setupValue: request.setup.wValue, + isSetup: request.isSetup, + } + // translate request code to event code + var event deviceCDCACMEventID + switch request.setup.bRequest { + case deviceCDCRequestSendEncapsulatedCommand: + event = deviceCDCACMEventSendEncapsulatedCommand + case deviceCDCRequestGetEncapsulatedResponse: + event = deviceCDCACMEventGetEncapsulatedResponse + case deviceCDCRequestSetCommFeature: + event = deviceCDCACMEventSetCommFeature + case deviceCDCRequestGetCommFeature: + event = deviceCDCACMEventGetCommFeature + case deviceCDCRequestClearCommFeature: + event = deviceCDCACMEventClearCommFeature + case deviceCDCRequestGetLineCoding: + event = deviceCDCACMEventGetLineCoding + case deviceCDCRequestSetLineCoding: + event = deviceCDCACMEventSetLineCoding + case deviceCDCRequestSetControlLineState: + event = deviceCDCACMEventSetControlLineState + case deviceCDCRequestSendBreak: + event = deviceCDCACMEventSendBreak + default: + s = statusInvalidRequest + } + // notify request to upper layer driver + if nil != acm.device && nil != acm.device.class && + nil != acm.device.class.handler { + acm.device.class.handler.classEvent(uint32(event), param) + } + } else { + s = statusInvalidRequest + } + } else { + s = statusInvalidParameter + } + } + + return s +} + +func (acm *deviceCDCACM) send(ep uint8, buffer []uint8, length uint32) status { + var pipe *deviceCDCACMPipe + // select which endpoint pipe to write into + switch ep { + case acm.interruptIn.ep: + pipe = &acm.interruptIn + case acm.bulkIn.ep: + pipe = &acm.bulkIn + default: + return statusInvalidParameter + } + if pipe.isBusy { + return statusBusy + } + // set pipe busy flag + pipe.isBusy = true + // check if pipe stall flag is set + if pipe.pipeStall { + // write data to pipe buffer + pipe.pipeDataBuffer = buffer + pipe.pipeDataLen = length + return statusSuccess + } + // pass data to device for transmission + if s := acm.device.send(ep, buffer, length); !s.OK() { + pipe.isBusy = false + return s + } + return statusSuccess +} + +func (acm *deviceCDCACM) receive(ep uint8, buffer []uint8, length uint32) status { + var pipe *deviceCDCACMPipe + // select which endpoint pipe to read from + switch ep { + case acm.bulkOut.ep: + pipe = &acm.bulkOut + default: + return statusInvalidParameter + } + if pipe.isBusy { + return statusBusy + } + // set pipe busy flag + pipe.isBusy = true + // check if pipe stall flag is set + if pipe.pipeStall { + // write data to pipe buffer + pipe.pipeDataBuffer = buffer + pipe.pipeDataLen = length + return statusSuccess + } + // pass data to device for reception + if s := acm.device.receive(ep, buffer, length); !s.OK() { + pipe.isBusy = false + return s + } + return statusSuccess +} + +// findInterface returns the interface number and deviceInterface from the +// receiver's CDC-ACM configuration list whose classCode and alternateSetting +// equals the given classCode and receiver's current alternateSetting; +// otherwise, no such interface is found, returns 0 and nil. +func (acm *deviceCDCACM) findInterface(classCode uint8) (uint8, *deviceInterface) { + if nil == acm.device || nil == acm.config { + return 0, nil + } + for c := range acm.config.info.interfaceList { + dci := &acm.config.info.interfaceList[c] + if classCode == dci.classCode { + for d := range dci.deviceInterface { + if acm.alternate == dci.deviceInterface[d].alternateSetting { + return dci.interfaceNumber, &dci.deviceInterface[d] + } + } + } + } + return 0, nil +} + +func (acm *deviceCDCACM) endpointInterface( + direction, transferType uint8) (*deviceCDCACMPipe, *deviceInterface) { + + switch transferType { + case specEndpointInterrupt: + switch direction { + case specIn: + return &acm.interruptIn, acm.comm + } + case specEndpointBulk: + switch direction { + case specIn: + return &acm.bulkIn, acm.data + case specOut: + return &acm.bulkOut, acm.data + } + } + return nil, nil +} + +func (acm *deviceCDCACM) controlEndpoint( + message deviceEndpointControlMessage, param interface{}) status { + + if pipe, ok := param.(*deviceCDCACMPipe); ok { + var event deviceCDCACMEventID + switch pipe { + case &acm.interruptIn: + event = deviceCDCACMEventSerialStateNotify + case &acm.bulkIn: + event = deviceCDCACMEventSendResponse + case &acm.bulkOut: + event = deviceCDCACMEventRecvResponse + } + pipe.isBusy = false + if nil != acm.device && nil != acm.device.class && + nil != acm.device.class.handler { + acm.device.class.handler.classEvent(uint32(event), message) + return statusSuccess + } + } + return statusInvalidParameter +} diff --git a/src/machine/usb/device_controller.go b/src/machine/usb/device_controller.go new file mode 100644 index 000000000..bf30719c2 --- /dev/null +++ b/src/machine/usb/device_controller.go @@ -0,0 +1,288 @@ +package usb + +import "unsafe" + +// deviceController provides hardware abstraction over a platform's physical +// USB device port controller (e.g., an EHCI-compliant peripheral). +// +// To connect the USB 2.0 device driver stack to a particular platform, the +// following method must be implemented: +// +// func (d *device) initController() deviceController +// +// This method shall return an implementation of deviceController, or nil if a +// controller cannot be allocated for the given port. +type ( + deviceController interface { + init() status // Controller initialization + deinit() status // Controller de-initialization + enable(enable bool) status // Controller enable runtime + interrupt() // Controller interrupt handler + process() status // Controller process interrupts + send(address uint8, buffer []uint8, length uint32) status // Controller send data + receive(address uint8, buffer []uint8, length uint32) status // Controller receive data + cancel(address uint8) status // Controller cancel transfer + control(command deviceControlID, param interface{}) status // Controller device control + critical(enter bool) status // Controller critical section + } + + // deviceControllerInterruptQueue represents a fixed-length, circular queue + // (FIFO) of interrupts. + // + // The enqueue and dequeue operations (methods enq and deq, respectively) are + // NOT interrupt-/thread-safe. The user must protect against race conditions + // using appropriate resources for their system. For example, the interface + // method (deviceController).critical(bool) implements the concept of critical + // sections, which could be used to prevent concurrent accesses. + // This restriction also applies to the higher-level methods fill and drain. + deviceControllerInterruptQueue struct { + queue [configInterruptQueueSize]uintptr // circular queue + head int + count int + } + + deviceControllerCapabilitiesBitmap uint32 + deviceControllerCapabilities struct { + reserved1 uint16 // 15 (bits) + ios uint8 // 1 + maxPacketSize uint16 // 11 + reserved2 uint8 // 2 + zlt uint8 // 1 + mult uint8 // 2 (= 32 bits) + } + + deviceControllerDTDTokenBitmap uint32 + deviceControllerDTDToken struct { + status uint8 // 8 (bits) + reserved1 uint8 // 2 + multiplierOverride uint8 // 2 + reserved2 uint8 // 3 + ioc uint8 // 1 + totalBytes uint16 // 15 + reserved3 uint8 // 1 (= 32 bits) + } + + deviceControllerEndpointStatusBitmap uint32 + deviceControllerEndpointStatus struct { + isOpened uint8 // 1 (bits) + zlt uint8 // 1 + _ uint32 // 30 (= 32 bits) + } + + deviceControllerOriginalBufferBitmap uint32 + deviceControllerOriginalBuffer struct { + originalBufferOffset uint16 // 12 (bits) + originalBufferLength uint32 // 19 + dtdInvalid uint8 // 1 (= 32 bits) + } + + deviceControllerQH struct { + capabilities deviceControllerCapabilitiesBitmap // 4 (bytes) + currentDTDPointer *deviceControllerDTD // 4 + nextDTDPointer *deviceControllerDTD // 4 + dtdToken deviceControllerDTDTokenBitmap // 4 + bufferPointerPage [5]uint32 // 20 + reserved1 uint32 // 4 + setupBuffer deviceSetupBuffer // 8 + setupBufferBack deviceSetupBuffer // 8 + endpointStatus deviceControllerEndpointStatusBitmap // 4 + reserved2 uint32 // 4 (= 64 bytes) + } + + deviceControllerDTDList [2 * configDeviceMaxEndpoints]*deviceControllerDTD + deviceControllerDTD struct { + nextDTDPointer *deviceControllerDTD // 4 (bytes) + dtdToken deviceControllerDTDTokenBitmap // 4 + bufferPointerPage [5]uint32 // 20 + originalBuffer deviceControllerOriginalBufferBitmap // 4 (= 32 bytes) + } +) + +const ( + + // device QH + deviceControllerQHSize = 64 // bytes + deviceControllerQHBufferSize = (configDeviceCount-1)*configDeviceControllerQHAlign + + 2*configDeviceMaxEndpoints*2*deviceControllerQHSize + + deviceControllerQHPointerMsk = 0xFFFFFFC0 + deviceControllerQHMultMsk = 0xC0000000 + deviceControllerQHZLTMsk = 0x20000000 + deviceControllerQHMaxPacketSizeMsk = 0x07FF0000 + deviceControllerQHMaxPacketSize = 0x00000800 + deviceControllerQHIOSMsk = 0x00008000 + + // device DTD + deviceControllerDTDSize = 32 // bytes + deviceControllerDTDBufferSize = (configDeviceCount-1)*configDeviceControllerDTDAlign + + configDeviceControllerMaxDTD*deviceControllerDTDSize + + deviceControllerDTDPointerMsk = 0xFFFFFFE0 + deviceControllerDTDTerminateMsk = 0x00000001 + deviceControllerDTDPageMsk = 0xFFFFF000 + deviceControllerDTDPageOffsetMsk = 0x00000FFF + deviceControllerDTDPageBlock = 0x00001000 + deviceControllerDTDTotalBytesMsk = 0x7FFF0000 + deviceControllerDTDTotalBytes = 0x00004000 + deviceControllerDTDIOCMsk = 0x00008000 + deviceControllerDTDMultIOMsk = 0x00000C00 + deviceControllerDTDStatusMsk = 0x000000FF + deviceControllerDTDStatusErrorMsk = 0x00000068 + deviceControllerDTDStatusActive = 0x00000080 + deviceControllerDTDStatusHalted = 0x00000040 + deviceControllerDTDStatusDataBufferError = 0x00000020 + deviceControllerDTDStatusTransactionError = 0x00000008 +) + +var ( + // special invalid pointer, indicating the end of a list of DTDs + deviceControllerDTDTerminate = (*deviceControllerDTD)(unsafe.Pointer(uintptr( + deviceControllerDTDTerminateMsk))) +) + +// enq enqueues the given mask into tail position of the receiver iq's queue, +// increasing queue length by 1. +// +// When the queue fills to capacity, any subsequent enqueue will overwrite the +// current head with the given value, positioning its following element at the +// front of the queue, and leaves queue length unaffected. +func (iq *deviceControllerInterruptQueue) enq(mask uintptr) { + if iq.count == configInterruptQueueSize { + // queue is full; overwrite oldest element (queue head) and increment head + iq.queue[iq.head] = mask + iq.head++ + iq.head %= configInterruptQueueSize + } else { + // queue is not full; place element at queue tail and increment count + iq.queue[(iq.head+iq.count)%configInterruptQueueSize] = mask + iq.count++ + } +} + +// deq dequeues the mask in tail position from the receiver iq's queue, reducing +// queue length by 1, and returns the dequeued value with true. +// +// When the queue is empty, queue length remains unaffected, and it returns 0 +// with false. +func (iq *deviceControllerInterruptQueue) deq() (uintptr, bool) { + if iq.count == 0 { + // queue is empty; reset head and return an invalid value + iq.head = 0 + return 0, false + } + // queue is not empty; decrement count, reset and return value at queue tail + iq.count-- + n := (iq.head + iq.count) % configInterruptQueueSize + m := iq.queue[n] + iq.queue[n] = 0 // ensure the queue contains no spurious data + return m, true +} + +// fill enqueues each given mask (in order) into tail position of the receiver +// iq's queue, increasing queue length up to capacity, if possible. +// +// If the number of values given is greater than available queue positions, each +// element in head position - at the time the value is enqueued - will be +// overwritten, so that queue length never exceeds capacity, and the element in +// tail position is always the final element given. +func (iq *deviceControllerInterruptQueue) fill(mask ...uintptr) { + for _, m := range mask { + iq.enq(m) + } +} + +// drain dequeues all elements in the receiver iq's queue, reducing queue length +// to 0, and returns the dequeued values (in order) and the number of number of +// values dequeued. +func (iq *deviceControllerInterruptQueue) drain() ( + q [configInterruptQueueSize]uintptr, n int, +) { + for { + m, ok := iq.deq() + if !ok { + return + } + q[n] = m + n++ + } +} + +func getQHBuffer(port uint8, qh int, ep int) *deviceControllerQH { + if port < configDeviceCount && qh < 2 && ep < 2*configDeviceMaxEndpoints { + return (*deviceControllerQH)(unsafe.Pointer( + &deviceControllerQHBuffer[int(port)*configDeviceControllerQHAlign+ + qh*2*configDeviceMaxEndpoints*deviceControllerQHSize+ + ep*deviceControllerQHSize])) + } + return nil +} + +func getDTDBuffer(port uint8, dtd int) *deviceControllerDTD { + if port < configDeviceCount && dtd < configDeviceControllerMaxDTD { + return (*deviceControllerDTD)(unsafe.Pointer( + &deviceControllerDTDBuffer[int(port)*configDeviceControllerDTDAlign+ + dtd*deviceControllerDTDSize])) + } + return nil +} + +func (s deviceControllerCapabilities) pack() deviceControllerCapabilitiesBitmap { + return deviceControllerCapabilitiesBitmap( + ((uint32(s.reserved1) & 0x7FFF) << 0) | // uint16 // 15 (bits) + ((uint32(s.ios) & 0x1) << 15) | // uint8 // 1 + ((uint32(s.maxPacketSize) & 0x7FF) << 16) | // uint16 // 11 + ((uint32(s.reserved2) & 0x3) << 27) | // uint8 // 2 + ((uint32(s.zlt) & 0x1) << 29) | // uint8 // 1 + ((uint32(s.mult) & 0x3) << 30)) // uint8 // 2 (= 32 bits) +} + +func (s deviceControllerDTDToken) pack() deviceControllerDTDTokenBitmap { + return deviceControllerDTDTokenBitmap( + ((uint32(s.status) & 0xFF) << 0) | // uint8 // 8 (bits) + ((uint32(s.reserved1) & 0x3) << 8) | // uint8 // 2 + ((uint32(s.multiplierOverride) & 0x3) << 10) | // uint8 // 2 + ((uint32(s.reserved2) & 0x7) << 12) | // uint8 // 3 + ((uint32(s.ioc) & 0x1) << 15) | // uint8 // 1 + ((uint32(s.totalBytes) & 0x7FFF) << 16) | // uint16 // 15 + ((uint32(s.reserved3) & 0x1) << 31)) // uint8 // 1 (= 32 bits) +} + +func (b deviceControllerDTDTokenBitmap) unpack() deviceControllerDTDToken { + return deviceControllerDTDToken{ + status: uint8(b>>0) & 0xFF, + reserved1: uint8(b>>8) & 0x3, + multiplierOverride: uint8(b>>10) & 0x3, + reserved2: uint8(b>>12) & 0x7, + ioc: uint8(b>>15) & 0x1, + totalBytes: uint16(b>>16) & 0x7FFF, + reserved3: uint8(b>>31) & 0x1, + } +} + +func (s deviceControllerEndpointStatus) pack() deviceControllerEndpointStatusBitmap { + return deviceControllerEndpointStatusBitmap( + ((uint32(s.isOpened) & 0x1) << 0) | // uint8 // 1 (bits) + ((uint32(s.zlt) & 0x1) << 1)) // uint8 // 1 +} + +func (s deviceControllerOriginalBuffer) pack() deviceControllerOriginalBufferBitmap { + return deviceControllerOriginalBufferBitmap( + ((uint32(s.originalBufferOffset) & 0xFFF) << 0) | // uint16 // 12 (bits) + ((uint32(s.originalBufferLength) & 0x7FFFF) << 12) | // uint32 // 19 + ((uint32(s.dtdInvalid) & 0x1) << 31)) // uint8 // 1 (= 32 bits) +} + +func (b deviceControllerOriginalBufferBitmap) unpack() deviceControllerOriginalBuffer { + return deviceControllerOriginalBuffer{ + originalBufferOffset: uint16(b>>0) & 0xFFF, + originalBufferLength: uint32(b>>12) & 0x7FFFF, + dtdInvalid: uint8(b>>31) & 0x1, + } +} + +// 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) +} diff --git a/src/machine/usb/device_controller_mimxrt1062.go b/src/machine/usb/device_controller_mimxrt1062.go new file mode 100644 index 000000000..90ad58f39 --- /dev/null +++ b/src/machine/usb/device_controller_mimxrt1062.go @@ -0,0 +1,709 @@ +// +build mimxrt1062 + +package usb + +// Implementation of a port controller for USB device mode on NXP iMXRT1062. + +import ( + "device/arm" + "device/nxp" + "runtime/interrupt" + "runtime/volatile" + "unsafe" +) + +// deviceControl represents a USB device controller for NXP iMXRT1062. +// It implements the USB API's deviceController interface. +type deviceControl struct { + port uint8 + device *device + irq interrupt.Interrupt + + messages deviceControllerInterruptQueue + + interruptMask uintptr // interrupt state upon entering critical section + criticalState volatile.Register8 // set to 1 if in critical section, else 0 + + bus *nxp.USB_Type + phy *nxp.USBPHY_Type + nc *nxp.USBNC_Type + qh *deviceControllerQH // The QH structure base address + dtd *deviceControllerDTD // The DTD structure base address + dtdFree *deviceControllerDTD // The idle DTD list head + dtdHead deviceControllerDTDList // The transferring DTD list head for each endpoint + dtdTail deviceControllerDTDList // The transferring DTD list tail for each endpoint + dtdCount uint8 // The idle DTD node count + endpointCount uint8 // The endpoint number of EHCI + isResetting bool // Whether a PORT reset is occurring or not + controllerId uint8 // Controller ID + speed uint8 // Current speed of EHCI + isSuspending bool // Is suspending of the PORT +} + +var ( + // deviceControlInstance holds instances for all USB device controllers + // available on the platform. + deviceControlInstance [configDeviceCount]deviceControl + + //go:align 2048 + deviceControllerQHBuffer [deviceControllerQHBufferSize]uint8 + //go:align 32 + deviceControllerDTDBuffer [deviceControllerDTDBufferSize]uint8 +) + +// 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 delayMicrosec(microsec uint32) { + n := cycles(microsec, configCPUFrequencyHz) + for i := uint32(0); i < n; i++ { + arm.Asm(`nop`) + } +} + +// initController returns a deviceController for the receiver USB device. +// It allocates the registers and installs/enables an interrupt handler for +// the receiver USB device. It also initializes the shared buffers used by the +// USB controller hardware. +func (d *device) initController() deviceController { + + dc := &deviceControlInstance[d.port] + + dc.port = d.port + dc.device = d + + // based on the selected port, install interrupt handler and initialize + // register references + switch d.port { + case 0: + dc.irq = interrupt.New(nxp.IRQ_USB_OTG1, func(interrupt.Interrupt) { + portInstance[0].device.controller.interrupt() + }) + dc.bus = nxp.USB1 + dc.phy = nxp.USBPHY1 + dc.nc = nxp.USBNC1 + case 1: + dc.irq = interrupt.New(nxp.IRQ_USB_OTG2, func(interrupt.Interrupt) { + //portInstance[1].device.controller.interrupt() + }) + dc.bus = nxp.USB2 + dc.phy = nxp.USBPHY2 + dc.nc = nxp.USBNC2 + } + + // get base address of QH and DTD buffers + dc.qh = (*deviceControllerQH)(unsafe.Pointer( + &deviceControllerQHBuffer[int(d.port)*configDeviceControllerQHAlign])) + dc.dtd = (*deviceControllerDTD)(unsafe.Pointer( + &deviceControllerDTDBuffer[int(d.port)*configDeviceControllerDTDAlign])) + + dc.irq.SetPriority(configInterruptPriority) + dc.irq.Enable() + + return dc +} + +// init initializes the USB device subsystem for the receiver deviceControl. +func (dc *deviceControl) init() status { + + // reset the controller + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) + for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { + } + + // get hardware's endpoint count + dc.endpointCount = uint8(dc.bus.DCCPARAMS.Get() & nxp.USB_DCCPARAMS_DEN_Msk) + if dc.endpointCount < configDeviceMaxEndpoints { + return statusError + } + + // clear the controller mode field and set to device mode: + // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only + dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, + nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) + + // reset the constroller state to default + return dc.resetState() +} + +func (dc *deviceControl) deinit() status { + return statusSuccess +} + +func (dc *deviceControl) enable(enable bool) status { + if enable { + // ensure D+ pulled down long enough for host to detect previous disconnect + delayMicrosec(5000) + return dc.control(deviceControlRun, nil) + } else { + return dc.control(deviceControlStop, nil) + } +} + +// interrupt is the base interrupt handler for all USB device interrupts. +func (dc *deviceControl) interrupt() { + + // protect access to message queue + for statusRetry == dc.critical(true) { + } + + // read and clear the interrupts that fired + status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() + dc.bus.USBSTS.Set(status) + + // enqueue interrupts for runtime processing + dc.messages.enq(uintptr(status)) + + // release message queue + _ = dc.critical(false) +} + +func (dc *deviceControl) process() status { + + // protect access to message queue + if dc.critical(true).OK() { + + // dequeue oldest interrupt in message queue + status, ok := dc.messages.deq() + + // release message queue + _ = dc.critical(false) + + // process message if queue was not empty + if ok { + + if 0 != (status & nxp.USB_USBSTS_URI_Msk) { // USB reset + dc.reset() + } + + if 0 != (status & nxp.USB_USBSTS_UI_Msk) { // USB token done + dc.tokenDone() + } + + if 0 != (status & nxp.USB_USBSTS_PCI_Msk) { // USB port status change + dc.portChange() + } + + if 0 != (status & nxp.USB_USBSTS_SRI_Msk) { // USB start of frame (SOF) + dc.frameStart() + } + } + + // message queue read and processed + return statusSuccess + } + + // could not acquire lock on message queue + return statusBusy +} + +func (dc *deviceControl) transfer(address uint8, buffer []uint8, length uint32) status { + + if dc.isResetting { + return statusError + } + + endpoint, direction := unpackEndpoint(address) + endpointIndex := int((endpoint << 1) | direction) + currentIndex := 0 + + primeBit := uint32(1) << ((address & specDescriptorEndpointAddressNumberMsk) + + ((address & specDescriptorEndpointAddressDirectionMsk) >> 3)) + epStatus, qhIdle := primeBit, false + + qh := getQHBuffer(dc.port, 0, endpointIndex) + if 0 == qh.endpointStatus&0x1 { // bit 0: isOpened + return statusError + } + + dtdRequestCount := (length + deviceControllerDTDTotalBytes - 1) / + deviceControllerDTDTotalBytes + if 0 == dtdRequestCount { + dtdRequestCount = 1 + } + + if dtdRequestCount > uint32(dc.dtdCount) { + return statusBusy + } + + var ( + dtdHead *deviceControllerDTD + sendLength uint32 + ) + + for { + + // limit transfer length to total DTD bytes + sendLength = length + if length > deviceControllerDTDTotalBytes { + sendLength = deviceControllerDTDTotalBytes + } + length -= sendLength + + // select a free DTD + dtd := dc.dtdFree + dc.dtdFree = dtd.nextDTDPointer + dc.dtdCount-- + + // save DTD head when current active buffer offset is 0 + if 0 == currentIndex { + dtdHead = dtd + } + + // set DTD field + dtd.nextDTDPointer = deviceControllerDTDTerminate + dtd.bufferPointerPage[0] = + uint32(uintptr(unsafe.Pointer(&buffer[0]))) + uint32(currentIndex) + dtd.bufferPointerPage[1] = + (dtd.bufferPointerPage[0] + deviceControllerDTDPageBlock) & deviceControllerDTDPageMsk + dtd.bufferPointerPage[2] = + dtd.bufferPointerPage[1] + deviceControllerDTDPageBlock + dtd.bufferPointerPage[3] = + dtd.bufferPointerPage[2] + deviceControllerDTDPageBlock + dtd.bufferPointerPage[4] = + dtd.bufferPointerPage[3] + deviceControllerDTDPageBlock + + // save original buffer and length to transfer + dtd.originalBuffer = deviceControllerOriginalBuffer{ + originalBufferOffset: uint16(dtd.bufferPointerPage[0]) & + deviceControllerDTDPageOffsetMsk, + originalBufferLength: sendLength, + dtdInvalid: 0, + }.pack() + + // set IOC field in final DTD + ioc := uint8(0) + if 0 == length { + ioc = 1 + } + // set DTD active flag + dtd.dtdToken = deviceControllerDTDToken{ + status: deviceControllerDTDStatusActive, + ioc: ioc, + totalBytes: uint16(sendLength), + }.pack() + + // update buffer offset + currentIndex += int(sendLength) + + // add DTD to in-use queue + if nil != dc.dtdTail[endpointIndex] { + dc.dtdTail[endpointIndex].nextDTDPointer = dtd + dc.dtdTail[endpointIndex] = dtd + } else { + dc.dtdHead[endpointIndex] = dtd + dc.dtdTail[endpointIndex] = dtd + qhIdle = true + } + + if 0 == length { + break + } + } + + if specEndpointControl == endpoint && specIn == direction { + // get last setup packet + setupIndex := int(endpoint << 1) + setupQH := getQHBuffer(dc.port, 0, setupIndex) + setupMaxSize := uint32(setupQH.capabilities&0x07FF0000) >> 16 // bits 15-26: maxPacketSize + var setup deviceSetup + setup.parse(setupQH.setupBufferBack[:]) + if 0 != qh.endpointStatus&0x2 { // bit 1: ZLT + if (0 != sendLength) && (sendLength < uint32(setup.wLength)) && + (0 == (sendLength % setupMaxSize)) { + // enable ZLT (zlt==0) + setupQH.capabilities &^= deviceControllerCapabilities{zlt: 1}.pack() + } + } + } + + // check if QH is empty + if !qhIdle { + // if prime bit is set, nothing left to do + if dc.bus.ENDPTPRIME.HasBits(primeBit) { + return statusSuccess + } + // wait to safely transmit DTD + for { + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW) + _ = dc.bus.ENDPTSTAT.Get() // read-clear endpoint status register + if dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) { + break + } + } + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ATDTW) + } + + // if QH is empty or the endpoint is not primed, need to link current DTD head + // to the QH. if endpoint is not primed and qhIdle is false, QH is empty. + if qhIdle || 0 == epStatus&primeBit { + qh.nextDTDPointer = dtdHead + qh.dtdToken = 0 + dc.bus.ENDPTPRIME.Set(primeBit) + primeAttempt := 0 + for !dc.bus.ENDPTSTAT.HasBits(primeBit) { + if primeAttempt++; primeAttempt >= configDeviceControllerMaxPrimeAttempts { + return statusError + } + if dc.bus.ENDPTCOMPLETE.HasBits(primeBit) { + break + } + dc.bus.ENDPTPRIME.Set(primeBit) + } + } + + return statusSuccess +} + +func (dc *deviceControl) send(address uint8, buffer []uint8, length uint32) status { + return dc.transfer((address&specDescriptorEndpointAddressNumberMsk)| + (specDescriptorEndpointAddressDirectionIn), buffer, length) +} + +func (dc *deviceControl) receive(address uint8, buffer []uint8, length uint32) status { + return dc.transfer((address&specDescriptorEndpointAddressNumberMsk)| + (specDescriptorEndpointAddressDirectionOut), buffer, length) +} + +func (dc *deviceControl) cancel(address uint8) status { + return statusSuccess +} + +func (dc *deviceControl) control(command deviceControlID, param interface{}) (s status) { + + // assume success unless error condition deliberately detected + s = statusSuccess + + switch command { + case deviceControlRun: + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) + + case deviceControlStop: + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) + + case deviceControlEndpointInit: + config, ok := param.(*deviceEndpointConfig) + if !ok { + return statusInvalidParameter + } + s = dc.initEndpoint(config) + + case deviceControlEndpointDeinit: + address, ok := param.(uint8) + if !ok { + return statusInvalidParameter + } + s = dc.deinitEndpoint(address) + + case deviceControlEndpointStall: + address, ok := param.(uint8) + if !ok { + return statusInvalidParameter + } + s = dc.stallEndpoint(address) + + case deviceControlEndpointUnstall: + address, ok := param.(uint8) + if !ok { + return statusInvalidParameter + } + s = dc.unstallEndpoint(address) + + case deviceControlGetDeviceStatus: + // param should be a pointer to uint16, acting as output parameter. + stat, ok := param.(*uint16) + if !ok { + return statusInvalidParameter + } + // configDeviceSelfPowered is a configuration constant on iMXRT1062 + *stat = configDeviceSelfPowered << + specRequestStandardGetStatusDeviceSelfPoweredPos + + case deviceControlGetEndpointStatus: + // param should be pointer to deviceEndpointStatus, acting as output + // parameter. + stat, ok := param.(*deviceEndpointStatus) + if !ok { + return statusInvalidParameter + } + endpoint, direction := unpackEndpoint(stat.address) + if endpoint >= configDeviceMaxEndpoints { + return statusInvalidParameter + } + mask := uint32(nxp.USB_ENDPTCTRL0_RXS) + if specOut != direction { + mask = nxp.USB_ENDPTCTRL0_TXS + } + ctrl := dc.endpointControlRegister(endpoint) + if nil == ctrl { + return statusInvalidParameter + } + if ctrl.HasBits(mask) { + stat.status = uint16(deviceEndpointStateStalled) + } else { + stat.status = uint16(deviceEndpointStateIdle) + } + + case deviceControlPreSetDeviceAddress: + address, ok := param.(uint8) + if !ok { + return statusInvalidParameter + } + dc.bus.DEVICEADDR.Set((uint32(address) << nxp.USB_DEVICEADDR_USBADR_Pos) | + nxp.USB_DEVICEADDR_USBADRA_Msk) + + case deviceControlSetDeviceAddress: + // TODO + + case deviceControlGetSynchFrame: + return statusNotSupported + + case deviceControlSetDefaultStatus: + for i := uint8(0); i < configDeviceMaxEndpoints; i++ { + _ = dc.deinitEndpoint(i | specDescriptorEndpointAddressDirectionIn) + _ = dc.deinitEndpoint(i | specDescriptorEndpointAddressDirectionOut) + } + s = dc.resetState() + + case deviceControlGetSpeed: + // param should be a pointer to uint8, acting as output parameter. + speed, ok := param.(*uint8) + if !ok { + return statusInvalidParameter + } + *speed = dc.speed + + case deviceControlGetOTGStatus: + return statusNotSupported + + case deviceControlSetOTGStatus: + return statusNotSupported + } + + return +} + +func (dc *deviceControl) critical(enter bool) status { + if enter { + // check if critical section already locked + if dc.criticalState.Get() != 0 { + return statusRetry + } + // lock critical section + dc.criticalState.Set(1) + // disable interrupts, storing state in receiver + dc.interruptMask = arm.DisableInterrupts() + } else { + // ensure critical section is locked + if dc.criticalState.Get() != 0 { + // re-enable interrupts, using state stored in receiver + arm.EnableInterrupts(dc.interruptMask) + // unlock critical section + dc.criticalState.Set(0) + } + } + return statusSuccess +} + +func (dc *deviceControl) resetState() status { + + dc.dtdFree = dc.dtd + p := dc.dtdFree + for i := 1; i < configDeviceControllerMaxDTD; i++ { + p.nextDTDPointer = getDTDBuffer(dc.port, i) + p = p.nextDTDPointer + } + p.nextDTDPointer = nil + dc.dtdCount = configDeviceControllerMaxDTD + + // no interrupt threshold + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) + + // disable setup lockout + dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) + + // use little-endianness + dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) + + for i := 0; i < 2*configDeviceMaxEndpoints; i++ { + qh := getQHBuffer(dc.port, 0, i) + qh.capabilities = + deviceControllerCapabilities{ + maxPacketSize: configDeviceControllerMaxPacketSize, + }.pack() + qh.endpointStatus = + deviceControllerEndpointStatus{ + isOpened: 0, + }.pack() + qh.nextDTDPointer = deviceControllerDTDTerminate + dc.dtdHead[i] = nil + dc.dtdTail[i] = nil + } + dc.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer( + getQHBuffer(dc.port, 0, 0))))) + + dc.bus.DEVICEADDR.Set(0) + + // enable interrupts: bus enable, bus error, port change detect, bus reset + dc.bus.USBINTR.Set(nxp.USB_USBINTR_UE_Msk | nxp.USB_USBINTR_UEE_Msk | + nxp.USB_USBINTR_PCE_Msk | nxp.USB_USBINTR_URE_Msk) + + dc.isResetting = false + + return statusSuccess +} + +func (dc *deviceControl) reset() { + + println("reset") + + // clear setup flag + dc.bus.ENDPTSETUPSTAT.Set(dc.bus.ENDPTSETUPSTAT.Get()) + // clear endpoint complete flag + dc.bus.ENDPTCOMPLETE.Set(dc.bus.ENDPTCOMPLETE.Get()) + + // flush any pending transfers + for dc.bus.ENDPTPRIME.HasBits(nxp.USB_ENDPTPRIME_PERB_Msk | nxp.USB_ENDPTPRIME_PETB_Msk) { + dc.bus.ENDPTFLUSH.Set(nxp.USB_ENDPTFLUSH_FERB_Msk | nxp.USB_ENDPTFLUSH_FETB_Msk) + } + + // set receiver flag if port reset bit is set; otherwise, notify device class. + if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR_Msk) { + dc.isResetting = true + } else { + // send reset notification to common device + dc.device.notify(deviceNotification{code: deviceNotifyBusReset}) + } +} + +func (dc *deviceControl) tokenDone() { + println("token done") +} + +func (dc *deviceControl) portChange() { + // check if port is resetting + if !dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { + // not resetting, update bus speed + if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) { + dc.speed = specSpeedHigh + } else { + dc.speed = specSpeedFull + } + // if reset flag is set, notify device layer reset has finished + if dc.isResetting { + dc.device.notify( + deviceNotification{ + buffer: nil, + length: 0, + code: deviceNotifyBusReset, + isSetup: false, + }) + dc.isResetting = false + } + } +} + +func (dc *deviceControl) frameStart() { + println("frame start") +} + +func (dc *deviceControl) endpointControlRegister(endpoint uint8) *volatile.Register32 { + endpoint &= specDescriptorEndpointAddressNumberMsk + if endpoint < configDeviceMaxEndpoints { + switch endpoint { + case 0: + return &dc.bus.ENDPTCTRL0 + case 1: + return &dc.bus.ENDPTCTRL1 + case 2: + return &dc.bus.ENDPTCTRL2 + case 3: + return &dc.bus.ENDPTCTRL3 + case 4: + return &dc.bus.ENDPTCTRL4 + case 5: + return &dc.bus.ENDPTCTRL5 + case 6: + return &dc.bus.ENDPTCTRL6 + case 7: + return &dc.bus.ENDPTCTRL7 + } + } + return nil +} + +func (dc *deviceControl) cancelControlPipe(endpoint, direction uint8) status { + index := (endpoint << 1) + direction + message := deviceNotification{ + buffer: nil, + length: 0, + } + + // get DTD of control pipe + currentDTD := (*deviceControllerDTD)(unsafe.Pointer( + uintptr(unsafe.Pointer(dc.dtdHead[index])) & deviceControllerDTDPointerMsk)) + + for nil != currentDTD { + originalBuffer := currentDTD.originalBuffer.unpack() + dtdToken := currentDTD.dtdToken.unpack() + + // pass transfer buffer address + if nil == message.buffer { + message.buffer = *(*[]uint8)(unsafe.Pointer( + uintptr((currentDTD.bufferPointerPage[0] & deviceControllerDTDPageMsk) | + uint32(originalBuffer.originalBufferOffset)))) + } + if 0 != dtdToken.status&deviceControllerDTDStatusActive { + message.length = packU32(deviceCDCACMBufferInvalid32) + } else { + message.length += originalBuffer.originalBufferLength - uint32(dtdToken.totalBytes) + } + + if dc.dtdHead[index] == dc.dtdTail[index] { + dc.dtdHead[index] = nil + dc.dtdTail[index] = nil + qh := getQHBuffer(dc.port, 0, int(index)) + qh.nextDTDPointer = deviceControllerDTDTerminate + qh.dtdToken = 0 + } else { + dc.dtdHead[index] = dc.dtdHead[index].nextDTDPointer + } + + if 0 != currentDTD.dtdToken.unpack().ioc || + 0 == uintptr(unsafe.Pointer(dc.dtdHead[index]))&deviceControllerDTDPointerMsk { + message.code = deviceNotificationID(endpoint | + (direction << specDescriptorEndpointAddressDirectionPos)) + message.isSetup = false + dc.device.notify(message) + message.buffer = nil + message.length = 0 + } + + currentDTD.dtdToken = 0 + currentDTD.nextDTDPointer = dc.dtdFree + dc.dtdFree = currentDTD + dc.dtdCount++ + + // get DTD of control pipe + currentDTD = (*deviceControllerDTD)(unsafe.Pointer( + uintptr(unsafe.Pointer(dc.dtdHead[index])) & deviceControllerDTDPointerMsk)) + + } + return statusSuccess +} + +func (dc *deviceControl) initEndpoint(config *deviceEndpointConfig) status { + return statusSuccess +} + +func (dc *deviceControl) deinitEndpoint(address uint8) status { + return statusSuccess +} + +func (dc *deviceControl) stallEndpoint(address uint8) status { + return statusSuccess +} + +func (dc *deviceControl) unstallEndpoint(address uint8) status { + return statusSuccess +} diff --git a/src/machine/usb/host.go b/src/machine/usb/host.go new file mode 100644 index 000000000..61a13886c --- /dev/null +++ b/src/machine/usb/host.go @@ -0,0 +1,25 @@ +package usb + +// Unexported USB device type definitions. +// In device mode, a USB device represents the whole USB port controller. +// In host mode, a USB device represents a peripheral device connected to the +// USB port controller. +type ( + host struct { + port uint8 + } +) + +func (h *host) init(port uint8) (s status) { + + // initialize host + h.port = port + + return statusSuccess +} + +func (h *host) deinit() status { + + // de-initialize host + return statusSuccess +} diff --git a/src/machine/usb/host_controller.go b/src/machine/usb/host_controller.go new file mode 100644 index 000000000..c4a6ff719 --- /dev/null +++ b/src/machine/usb/host_controller.go @@ -0,0 +1,13 @@ +package usb + +// hostController provides hardware abstraction over a platform's physical +// USB host port controller (e.g., an EHCI-compliant peripheral). +// +// To connect the USB 2.0 host driver stack to a particular platform, the +// following method must be implemented: +// +// func (h *host) initController() hostController +// +// This method shall return an implementation of hostController, or nil if a +// controller cannot be allocated for the given port. +type hostController interface{} diff --git a/src/machine/usb/spec.go b/src/machine/usb/spec.go new file mode 100644 index 000000000..856deb36c --- /dev/null +++ b/src/machine/usb/spec.go @@ -0,0 +1,167 @@ +package usb + +// Constants defined per USB 2.0 specification. +const ( + // USB speed + specSpeedFull = 0x00 + specSpeedLow = 0x01 + specSpeedHigh = 0x02 + specSpeedSuper = 0x04 + + // USB standard descriptor endpoint type + specEndpointControl = 0x00 + specEndpointIsochronous = 0x01 + specEndpointBulk = 0x02 + specEndpointInterrupt = 0x03 + + // USB standard descriptor transfer direction + specOut = 0 + specIn = 1 + + // USB standard descriptor length + specDescriptorLengthDevice = 18 + specDescriptorLengthConfigure = 9 + specDescriptorLengthInterface = 9 + specDescriptorLengthEndpoint = 7 + specDescriptorLengthEndpointCompanion = 6 + specDescriptorLengthDeviceQualitier = 10 + specDescriptorLengthOTGDescriptor = 5 + specDescriptorLengthBOSDescriptor = 5 + specDescriptorLengthDeviceCapabilityUSB20Extension = 7 + specDescriptorLengthDeviceCapabilitySuperspeed = 10 + + // USB device capability type codes + specDescriptorTypeDeviceCapabilityWireless = 0x01 + specDescriptorTypeDeviceCapabilityUSB20Extension = 0x02 + specDescriptorTypeDeviceCapabilitySuperspeed = 0x03 + + // USB standard descriptor type + specDescriptorTypeDevice = 0x01 + specDescriptorTypeConfigure = 0x02 + specDescriptorTypeString = 0x03 + specDescriptorTypeInterface = 0x04 + specDescriptorTypeEndpoint = 0x05 + specDescriptorTypeDeviceQualitier = 0x06 + specDescriptorTypeOtherSpeedConfiguration = 0x07 + specDescriptorTypeInterfaacePower = 0x08 + specDescriptorTypeOTG = 0x09 + specDescriptorTypeInterfaceAssociation = 0x0B + specDescriptorTypeBOS = 0x0F + specDescriptorTypeDeviceCapability = 0x10 + + specDescriptorTypeHID = 0x21 + specDescriptorTypeHIDReport = 0x22 + specDescriptorTypeHIDPhysical = 0x23 + + specDescriptorTypeCDCInterface = 0x24 + specDescriptorTypeCDCEndpoint = 0x25 + + specDescriptorTypeEndpointCompanion = 0x30 + + // USB standard request type + specRequestTypeDirMsk = 0x80 + specRequestTypeDirPos = 7 + specRequestTypeDirOut = 0x00 + specRequestTypeDirIn = 0x80 + + specRequestTypeTypeMsk = 0x60 + specRequestTypeTypePos = 5 + specRequestTypeTypeStandard = 0 + specRequestTypeTypeClass = 0x20 + specRequestTypeTypeVendor = 0x40 + + specRequestTypeRecipientMsk = 0x1F + specRequestTypeRecipientPos = 0 + specRequestTypeRecipientDevice = 0x00 + specRequestTypeRecipientInterface = 0x01 + specRequestTypeRecipientEndpoint = 0x02 + specRequestTypeRecipientOther = 0x03 + + // USB standard request + specRequestStandardGetStatus = 0x00 + specRequestStandardClearFeature = 0x01 + specRequestStandardSetFeature = 0x03 + specRequestStandardSetAddress = 0x05 + specRequestStandardGetDescriptor = 0x06 + specRequestStandardSetDescriptor = 0x07 + specRequestStandardGetConfiguration = 0x08 + specRequestStandardSetConfiguration = 0x09 + specRequestStandardGetInterface = 0x0A + specRequestStandardSetInterface = 0x0B + specRequestStandardSynchFrame = 0x0C + + // USB standard request: GET status + specRequestStandardGetStatusDeviceSelfPoweredPos = 0 + specRequestStandardGetStatusDeviceRemoteWakeupPos = 1 + + specRequestStandardGetStatusEndpointHaltMsk = 0x01 + specRequestStandardGetStatusEndpointHaltPos = 0 + + specRequestStandardGetStatusOTGStatusSelector = 0xF000 + + // USB standard request: CLEAR/SET feature + specRequestStandardFeatureSelectorEndpointHalt = 0 + specRequestStandardFeatureSelectorDeviceRemoteWakeup = 1 + specRequestStandardFeatureSelectorDeviceTestMode = 2 + specRequestStandardFeatureSelectorBHNPEnable = 3 + specRequestStandardFeatureSelectorAHNPSupport = 4 + specRequestStandardFeatureSelectorAAltHNPSupport = 5 + + // USB standard descriptor: configure attributes + specDescriptorConfigureAttributeD7Msk = 0x80 + specDescriptorConfigureAttributeD7Pos = 7 + + specDescriptorConfigureAttributeSelfPoweredMsk = 0x40 + specDescriptorConfigureAttributeSelfPoweredPos = 6 + + specDescriptorConfigureAttributeRemoteWakeupMsk = 0x20 + specDescriptorConfigureAttributeRemoteWakeupPos = 5 + + // USB standard descriptor: endpoint attributes + specDescriptorEndpointAddressDirectionMsk = 0x80 + specDescriptorEndpointAddressDirectionPos = 7 + specDescriptorEndpointAddressDirectionOut = 0 + specDescriptorEndpointAddressDirectionIn = 0x80 + + specDescriptorEndpointAddressNumberMsk = 0x0F + specDescriptorEndpointAddressNumberPos = 0 + + specDescriptorEndpointAttributeTypeMsk = 0x03 + specDescriptorEndpointAttributeNumberPos = 0 + + specDescriptorEndpointAttributeSyncTypeMsk = 0x0C + specDescriptorEndpointAttributeSyncTypePos = 2 + specDescriptorEndpointAttributeSyncTypeNoSync = 0x00 + specDescriptorEndpointAttributeSyncTypeAsync = 0x04 + specDescriptorEndpointAttributeSyncTypeAdaptive = 0x08 + specDescriptorEndpointAttributeSyncTypeSync = 0x0C + + specDescriptorEndpointAttributeUsageTypeMsk = 0x30 + specDescriptorEndpointAttributeUsageTypePos = 4 + specDescriptorEndpointAttributeUsageTypeDataEndpoint = 0x00 + specDescriptorEndpointAttributeUsageTypeFeedbackEndpoint = 0x10 + specDescriptorEndpointAttributeUsageTypeImplicitFeedbackDataEndpoint = 0x20 + + specDescriptorEndpointMaxpacketsizeSizeMsk = 0x07FF + specDescriptorEndpointMaxpacketsizeMultTransactionsMsk = 0x1800 + specDescriptorEndpointMaxpacketsizeMultTransactionsPos = 11 + + // USB standard descriptor: OTG attributes + specDescriptorOTGAttributesSRPMsk = 0x01 + specDescriptorOTGAttributesHNPMsk = 0x02 + specDescriptorOTGAttributesADPMsk = 0x04 + + // USB standard descriptor: device capability attributes (USB 2.0 extension) + specDescriptorDeviceCapabilityUSB20ExtensionLPMMsk = 0x02 + specDescriptorDeviceCapabilityUSB20ExtensionLPMPos = 1 + specDescriptorDeviceCapabilityUSB20ExtensionBESLMsk = 0x04 + specDescriptorDeviceCapabilityUSB20ExtensionBESLPos = 2 +) + +//go:inline +func unpackEndpoint(address uint8) (number, direction uint8) { + return (address & specDescriptorEndpointAddressNumberMsk) >> + specDescriptorEndpointAddressNumberPos, + (address & specDescriptorEndpointAddressDirectionMsk) >> + specDescriptorEndpointAddressDirectionPos +} diff --git a/src/machine/usb/uart.go b/src/machine/usb/uart.go new file mode 100644 index 000000000..1925aea59 --- /dev/null +++ b/src/machine/usb/uart.go @@ -0,0 +1,366 @@ +package usb + +import ( + "errors" +) + +var ( + ErrInvalidPort = errors.New("invalid USB port") +) + +type ( + UARTConfig struct { + BaudRate uint32 + } + + // UART represents a virtual serial (UART) device emulation using the USB + // CDC-ACM device class driver. + UART struct { + port uint8 // USB port (core index, e.g., 0-1) + desc *ConfigDeviceDescriptor // User-provided USB device descriptor information + class *deviceClass // USB device class handle + acm *deviceCDCACM // USB CDC-ACM device class handle + + attached bool + transact bool + config uint8 // selected configuration index (1-based, 0=invalid) + speed uint8 // enumerated bus speed (low, full, high) + alternate deviceCDCACMAlternateList + } +) + +var ( + uartAbstractState = []uint8{0x00, 0x00} + uartCountryCode = []uint8{0x00, 0x00} +) + +func (uart *UART) SetPort(port uint8) { + if port >= ConfigPortCount || port >= configDeviceCount || + port >= configDeviceCDCACMCount { + return // invalid port for USB CDC-ACM device class + } + // TODO: if changed, may need to reset port controller and tell host to + // re-enumerate devices + uart.port = port +} + +func (uart *UART) SetDeviceDescriptor(desc *ConfigDeviceDescriptor) { + if nil == desc { + return // invalid device descriptor information + } + // TODO: if changed, may need to reset port controller and tell host to + // re-enumerate devices + uart.desc = desc +} + +func (uart *UART) Configure(config UARTConfig) error { + + if uart.port >= ConfigPortCount || uart.port >= configDeviceCount || + uart.port >= configDeviceCDCACMCount { + return ErrInvalidPort + } + + // use default configuration index (1-based index; 0=invalid) + uart.config = configDeviceCDCACMConfigurationIndex + + // modify the global basic configuration struct configDeviceCDCACM for our USB + // port and configuration index. + // + // these settings are copied into the real CDC-ACM object, using interface + // deviceClassDriver, via initialization method (*deviceCDCACM).init(). + + // change baud rate from default, if provided + if config.BaudRate != 0 { + configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate = + config.BaudRate + } + + // verify we have a free USB port and take ownership of it + port, status := initPort(uart.port, modeDevice) + if !status.OK() { + return status + } + + // apply the CDC-ACM configuration to our port + uart.acm, uart.class = port.initCDCACM(uart.config, uart) + + // enable USB device mode functionality, which allows a host to enumerate us + status = port.device.controller.enable(true) + if !status.OK() { + return status + } + + return nil +} + +func (uart *UART) deviceEvent(ev deviceEventID, param interface{}) status { + + switch ev { + case deviceEventBusReset: + uart.attached = false + uart.config = 0 // clear selected configuration (1-based index) + s := uart.acm.device.busSpeed(&uart.speed) + if s.OK() { + s = uart.acm.device.setBusSpeed(uart.speed) + } + return s + + case deviceEventSetConfiguration: + if id, ok := param.(uint8); ok { + if 0 == id { + uart.attached = false + uart.config = 0 // clear selected configuration (1-based index) + return statusSuccess + } + // verify we have a config struct at given index + if nil != uart.class && nil != uart.class.config && + int(id) <= len(uart.class.config) && + int(id) <= len(configDeviceCDCACM[uart.port]) { + config := uart.class.config[id-1] // receiver configuration + system := configDeviceCDCACM[uart.port][id-1] // platform configuration + // verify the config is a CDC device and has a defined driver + if deviceClassCDC == config.info.classID && nil != config.driver { + // finally, verify the config driver is an ACM device driver + if _, ok := config.driver.(*deviceCDCACM); ok { + uart.attached = true + uart.config = id + return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:], + uint32(system.dataBulkOutPacketSize)) + } + } + } + } + return statusNotSupported // all non-error paths return early + + case deviceEventSetInterface: + if uart.attached { + if setting, ok := param.(uint16); ok { + inf := (setting & 0xFF00) >> 8 + if inf < configDeviceCDCACMInterfaceCount { + uart.alternate[inf] = setting & 0x00FF + } + } + } + + case deviceEventGetConfiguration: + case deviceEventGetInterface: + case deviceEventGetDeviceDescriptor: + case deviceEventGetConfigurationDescriptor: + case deviceEventGetStringDescriptor: + } + + return statusSuccess +} + +func (uart *UART) classEvent(ev uint32, param interface{}) status { + + if nil == uart.acm { + return statusInvalidHandle + } + + switch deviceCDCACMEventID(ev) { + case deviceCDCACMEventSendResponse: + if ctl, ok := param.(deviceEndpointControlMessage); ok { + + // verify receiver configuration + if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 || + int(uart.config) > len(configDeviceCDCACM[uart.port]) { + return statusNotSupported + } + // get platform configuration + system := configDeviceCDCACM[uart.port][uart.config-1] + + if ctl.length > 0 && 0 != ctl.length%uint32(system.dataBulkInPacketSize) { + // If the last packet is the size of endpoint, then send an additional + // zero-ended packet to notify the host it may flush output. + return uart.acm.send(system.dataBulkInEndpoint, nil, 0) + } + + if uart.attached && uart.transact && + (nil != ctl.buffer || (nil == ctl.buffer && 0 == ctl.length)) { + // send complete, schedule buffer for next receive event + return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:], + uint32(system.dataBulkOutPacketSize)) + } + } + return statusError // all non-error paths return early + + case deviceCDCACMEventRecvResponse: + if ctl, ok := param.(deviceEndpointControlMessage); ok { + if uart.attached && uart.transact { + uart.acm.recvSize = ctl.length + if 0 == ctl.length { + + // verify receiver configuration + if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 || + int(uart.config) > len(configDeviceCDCACM[uart.port]) { + return statusNotSupported + } + // get platform configuration + system := configDeviceCDCACM[uart.port][uart.config-1] + + // schedule buffer for next receive event + return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:], + uint32(system.dataBulkOutPacketSize)) + } + } + } + return statusError // all non-error paths return early + + case deviceCDCACMEventSerialStateNotify: + uart.acm.hasSentState = false + return statusSuccess + + case deviceCDCACMEventSendEncapsulatedCommand: + return statusNotImplemented + + case deviceCDCACMEventGetEncapsulatedResponse: + return statusNotImplemented + + case deviceCDCACMEventSetCommFeature: + if req, ok := param.(deviceCDCACMRequestParam); ok { + switch req.setupValue { + case deviceCDCFeatureAbstractState: + if req.isSetup { + *(req.buffer) = uartAbstractState + } else { + *(req.length) = 0 + } + return statusSuccess + + case deviceCDCFeatureCountrySetting: + if req.isSetup { + *(req.buffer) = uartCountryCode + } else { + *(req.length) = 0 + } + return statusSuccess + } + return statusInvalidParameter // unrecognized request code + } + return statusError // all non-error paths return early + + case deviceCDCACMEventGetCommFeature: + if req, ok := param.(deviceCDCACMRequestParam); ok { + switch req.setupValue { + case deviceCDCFeatureAbstractState: + *(req.buffer) = uartAbstractState + *(req.length) = uint32(len(uartAbstractState)) + return statusSuccess + + case deviceCDCFeatureCountrySetting: + *(req.buffer) = uartCountryCode + *(req.length) = uint32(len(uartCountryCode)) + return statusSuccess + } + return statusInvalidParameter // unrecognized request code + } + return statusError // all non-error paths return early + + case deviceCDCACMEventClearCommFeature: + return statusNotImplemented + + case deviceCDCACMEventGetLineCoding: + if req, ok := param.(deviceCDCACMRequestParam); ok { + // verify receiver configuration + if int(uart.port) >= len(deviceCDCACMLineCoding) || uart.config == 0 || + int(uart.config) > len(deviceCDCACMLineCoding[uart.port]) { + return statusNotSupported + } + lineCoding := deviceCDCACMLineCoding[uart.port][uart.config-1] + *(req.buffer) = lineCoding + *(req.length) = uint32(len(lineCoding)) + return statusSuccess + } + return statusError // all non-error paths return early + + case deviceCDCACMEventSetLineCoding: + if req, ok := param.(deviceCDCACMRequestParam); ok { + // verify receiver configuration + if int(uart.port) >= len(deviceCDCACMLineCoding) || uart.config == 0 || + int(uart.config) > len(deviceCDCACMLineCoding[uart.port]) { + return statusNotSupported + } + lineCoding := deviceCDCACMLineCoding[uart.port][uart.config-1] + if req.isSetup { + *(req.buffer) = lineCoding + } else { + *(req.length) = uint32(len(lineCoding)) + } + return statusSuccess + } + return statusError // all non-error paths return early + + case deviceCDCACMEventSetControlLineState: + if req, ok := param.(deviceCDCACMRequestParam); ok { + // verify receiver configuration + if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 || + int(uart.config) > len(configDeviceCDCACM[uart.port]) { + return statusNotSupported + } + // get platform configuration + system := configDeviceCDCACM[uart.port][uart.config-1] + + uart.acm.info.dteStatus = uint8(req.setupValue) + + // activate/deactivate Tx carrier + if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapCarrierActivation { + uart.acm.info.uartState |= deviceCDCUARTStateTxCarrier + } else { + uart.acm.info.uartState &^= deviceCDCUARTStateTxCarrier + } + + // activate/deactivate DTE and carrier + if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapDTEPresence { + uart.acm.info.uartState |= deviceCDCUARTStateRxCarrier + uart.acm.info.dtePresent = true // serial device is now open on host + } else { + uart.acm.info.uartState &^= deviceCDCUARTStateRxCarrier + uart.acm.info.dtePresent = false // serial device is now closed on host + } + + uart.acm.info.serialState[0] = deviceCDCACMRequestNotify // bmRequestType + uart.acm.info.serialState[1] = deviceCDCNotifySerialState // bNotification + uart.acm.info.serialState[2] = 0 // wValue (lo) + uart.acm.info.serialState[3] = 0 // wValue (hi) + uart.acm.info.serialState[4] = uint8(req.interfaceIndex) // wIndex (lo) + uart.acm.info.serialState[5] = uint8(req.interfaceIndex >> 8) // wIndex (hi) + uart.acm.info.serialState[6] = deviceCDCACMInfoUARTBitmapSize // wLength (lo) + uart.acm.info.serialState[7] = 0 // wLength (hi) + uart.acm.info.serialState[8] = uint8(uart.acm.info.uartState) // UART bitmap (lo) + uart.acm.info.serialState[9] = uint8(uart.acm.info.uartState >> 8) // UART bitmap (hi) + + s := statusSuccess + if !uart.acm.hasSentState { + s = uart.acm.send(system.commInterruptInEndpoint, + uart.acm.info.serialState[:], deviceCDCACMSerialStateSize) + uart.acm.hasSentState = true + } + + // update status + if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapCarrierActivation { + // carrier activated + } + if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapDTEPresence { + // DTE activated + if uart.attached { + uart.transact = true + } + } else { + // DTE deactivated + if uart.attached { + uart.transact = false + } + } + + return s + } + return statusError // all non-error paths return early + + case deviceCDCACMEventSendBreak: + return statusNotImplemented + + default: + return statusInvalidParameter + } +} diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go new file mode 100644 index 000000000..888d2cfcd --- /dev/null +++ b/src/machine/usb/usb.go @@ -0,0 +1,416 @@ +package usb + +type ( + // status is the common error code used internally for package operations. + status int8 + // mode defines the operating mode of a USB port. + mode int8 + + // port represents a physical USB port, which may be configured as either a + // device or as a host. + port struct { + mode mode + device device + host host + } +) + +// Constants for unexported types shared across entire package. +const ( + statusSuccess status = iota // Success + statusError // Failed + statusBusy // Busy + statusInvalidHandle // Invalid handle + statusInvalidParameter // Invalid parameter + statusInvalidRequest // Invalid request + statusControllerNotFound // Controller cannot be found + statusInvalidController // Invalid controller interface + statusNotSupported // Configuration is not supported + statusRetry // Enumeration get configuration retry + statusTransferStall // Transfer stalled + statusTransferFailed // Transfer failed + statusAllocFail // Allocation failed + statusLackSwapBuffer // Insufficient swap buffer for KHCI + statusTransferCancel // The transfer cancelled + statusBandwidthFail // Allocate bandwidth failed + statusMSDStatusFail // For MSD, the CSW status means fail + statusEHCIAttached // EHCI attached + statusEHCIDetached // EHCI detached + statusDataOverRun // Endpoint data (Rx) exceeds max size + statusNotImplemented // Supported feature not implemented + + modeIdle mode = iota // USB core idle (unallocated) + modeDevice // USB device mode + modeHost // USB host mode +) + +var ( + // portInstance holds instances for all available ports on the platform. + portInstance [ConfigPortCount]port +) + +func init() { + // ensure all ports are in idle state by default + for i := range portInstance { + portInstance[i].mode = modeIdle + } +} + +func ProcessMessages() (err error) { + for i := range portInstance { + s := portInstance[i].process() + if !s.OK() && nil == err { + err = s + } + } + return +} + +// initPort configures the mode for a given USB port and initializes the +// hardware's port controller. If the port is invalid or not idle (it has +// already been configured), it returns nil and a status code. +func initPort(port uint8, mode mode) (*port, status) { + if port >= ConfigPortCount || int(port) >= len(portInstance) { + return nil, statusInvalidController + } + if modeIdle != portInstance[port].mode { + return nil, statusBusy + } + portInstance[port].mode = mode + switch mode { + case modeDevice: + if s := portInstance[port].device.init(port); !s.OK() { + return nil, s + } + case modeHost: + if s := portInstance[port].host.init(port); !s.OK() { + return nil, s + } + } + return &portInstance[port], statusSuccess +} + +// deinit disables the receiver USB port, changing its mode to idle, freeing it +// for reuse or reconfiguration. +func (p *port) deinit() status { + switch p.mode { + case modeDevice: + return p.device.deinit() + case modeHost: + return p.host.deinit() + default: + return statusInvalidController + } +} + +func (p *port) process() status { + switch p.mode { + case modeDevice: + return p.device.controller.process() + case modeHost: + return statusSuccess // TODO: not implemented + default: + return statusInvalidController + } +} + +// initCDCACM applies a CDC-ACM configuration to the receiver port p and then +// returns the configured deviceClassDriver and deviceClass that were assigned +// to the receiver. +// +// The given deviceClassEventHandler is called for any USB device-level event +// notifications received, which allows an upper-layer CDC-ACM driver (such as +// a UART interface implementation) the opportunity to handle device events. +func (p *port) initCDCACM(id uint8, handler deviceClassEventHandler) (*deviceCDCACM, *deviceClass) { + + // verify a valid port was provided + if nil == p || nil == p.device.controller || p.mode != modeDevice { + return nil, nil + } + + if 0 == id || int(id) > len(configDeviceCDCACM[p.device.port]) { + return nil, nil + } + + // get a reference to each of the class interfaces + comm := &deviceCDCACMConfigInstance[p.device.port][id-1].info.interfaceList[0] + data := &deviceCDCACMConfigInstance[p.device.port][id-1].info.interfaceList[1] + + // configDeviceCDCACM must be defined per package API. these settings will be + // platform-specific, and will probably be implemented in a build tag- + // constrained source file. the length of this array corresponds to the number + // of USB CDC-ACM ports that are being created, and the index of each element + // corresponds to the physical USB port (core index). Each element is a slice + // of alternate device configurations that may be selected for a given port. + + // CDC-ACM Communication/control interface + comm.interfaceNumber = + configDeviceCDCACM[p.device.port][id-1].commInterfaceIndex + + comm.deviceInterface[0].endpoint[0].address = + configDeviceCDCACM[p.device.port][id-1].commInterruptInEndpoint | + specDescriptorEndpointAddressDirectionIn + + comm.deviceInterface[0].endpoint[0].maxPacketSize = + configDeviceCDCACM[p.device.port][id-1].commInterruptInPacketSize + + comm.deviceInterface[0].endpoint[0].interval = + configDeviceCDCACM[p.device.port][id-1].commInterruptInInterval + + // CDC-ACM Data interface + data.interfaceNumber = + configDeviceCDCACM[p.device.port][id-1].dataInterfaceIndex + + data.deviceInterface[0].endpoint[0].address = + configDeviceCDCACM[p.device.port][id-1].dataBulkInEndpoint | + specDescriptorEndpointAddressDirectionIn + + data.deviceInterface[0].endpoint[0].maxPacketSize = + configDeviceCDCACM[p.device.port][id-1].dataBulkInPacketSize + + data.deviceInterface[0].endpoint[1].address = + configDeviceCDCACM[p.device.port][id-1].dataBulkOutEndpoint | + specDescriptorEndpointAddressDirectionOut + + data.deviceInterface[0].endpoint[1].maxPacketSize = + configDeviceCDCACM[p.device.port][id-1].dataBulkOutPacketSize + + // assign our configured CDC-ACM class to the receiver's device and call its + // class initialization routine(s). + cls := p.device.initClass(id, deviceCDCACMConfigInstance[p.device.port], handler) + acm := cls.config[0].driver.(*deviceCDCACM) + + return acm, cls +} + +// OK returns true if and only if the receiver s is equal to statusSuccess. +//go:inline +func (s status) OK() bool { return statusSuccess == s } + +// Error returns a simple descriptive error string of the receiver s. +func (s status) Error() string { + switch s { + case statusSuccess: + return "" + case statusError: + return "failed" + case statusBusy: + return "busy" + case statusInvalidHandle: + return "invalid handle" + case statusInvalidParameter: + return "invalid parameter" + case statusInvalidRequest: + return "invalid request" + case statusControllerNotFound: + return "controller not found" + case statusInvalidController: + return "invalid controller interface" + case statusNotSupported: + return "configuration not supported" + case statusRetry: + return "retry enumeration" + case statusTransferStall: + return "transfer stalled" + case statusTransferFailed: + return "transfer failed" + case statusAllocFail: + return "allocation failed" + case statusLackSwapBuffer: + return "insufficient swap buffer" + case statusTransferCancel: + return "transfer cancelled" + case statusBandwidthFail: + return "bandwidth allocation failed" + case statusMSDStatusFail: + return "mass-storage device failed" + case statusEHCIAttached: + return "host attached" + case statusEHCIDetached: + return "host detached" + case statusDataOverRun: + return "data overrun" + case statusNotImplemented: + return "feature not implemented" + default: + return "unknown error" + } +} + +// 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 +} diff --git a/src/machine/usb2/dci.go b/src/machine/usb2/dci.go new file mode 100644 index 000000000..4ee63081d --- /dev/null +++ b/src/machine/usb2/dci.go @@ -0,0 +1,9 @@ +package usb2 + +type dci interface { + init() status + enable(enable bool) status + critical(enter bool) status + interrupt() + udelay(micros uint32) +} diff --git a/src/machine/usb2/dci_mimxrt1062.go b/src/machine/usb2/dci_mimxrt1062.go new file mode 100644 index 000000000..86e252f2c --- /dev/null +++ b/src/machine/usb2/dci_mimxrt1062.go @@ -0,0 +1,194 @@ +// +build mimxrt1062 + +package usb2 + +// Implementation of USB device controller interface (dci) for NXP iMXRT1062. + +import ( + "device/arm" + "device/nxp" + "runtime/interrupt" + "runtime/volatile" + "strconv" +) + +// dciCount defines the number of USB cores to configure for device mode. It is +// computed as the sum of all declared device configuration descriptors. +const dciCount = descCDCACMConfigCount + +// dciInterruptPriority defines the priority for all USB device interrupts. +const dciInterruptPriority = 3 + +// deviceController implements USB device controller interface (dci). +type deviceController struct { + core *core // Parent USB core this instance is attached to + port int // USB port index + id int // deviceControllerInstance index + + bus *nxp.USB_Type + phy *nxp.USBPHY_Type + irq interrupt.Interrupt + + cri volatile.Register8 // set to 1 if in critical section, else 0 + ivm uintptr // interrupt state when entering critical section +} + +// deviceControllerInstance provides statically-allocated instances of each USB +// device controller configured on this platform. +var deviceControllerInstance [dciCount]deviceController + +// initDCI 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 initDCI(port int) (dci, status) { + if 0 == dciCount { + return nil, statusInvalidArgument // must have defined device descriptors + } + // Return the first instance whose assigned core is currently nil. + for i := range deviceControllerInstance { + if nil == deviceControllerInstance[i].core { + // Initialize device controller. + deviceControllerInstance[i].core = &coreInstance[port] + deviceControllerInstance[i].port = port + deviceControllerInstance[i].id = i + switch port { + case 0: + deviceControllerInstance[i].bus = nxp.USB1 + deviceControllerInstance[i].phy = nxp.USBPHY1 + deviceControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG1, + func(interrupt.Interrupt) { + coreInstance[0].dc.interrupt() + }) + + case 1: + deviceControllerInstance[i].bus = nxp.USB2 + deviceControllerInstance[i].phy = nxp.USBPHY2 + deviceControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG2, + func(interrupt.Interrupt) { + //coreInstance[1].dc.interrupt() + }) + } + return &deviceControllerInstance[i], statusOK + } + } + return nil, statusBusy // No free device controller instances available. +} + +func (dc *deviceController) init() status { + + dc.bus.BURSTSIZE.Set(0x0404) + + // if dc.phy.PWD.HasBits((nxp.USBPHY_PWD_RXPWDRX | nxp.USBPHY_PWD_RXPWDDIFF | + // nxp.USBPHY_PWD_RXPWD1PT1 | nxp.USBPHY_PWD_RXPWDENV | + // nxp.USBPHY_PWD_TXPWDV2I | nxp.USBPHY_PWD_TXPWDIBIAS | + // nxp.USBPHY_PWD_TXPWDFS)) || + // dc.bus.USBMODE.HasBits(nxp.USB_USBMODE_CM_Msk) { + // // reset controller if it was already enabled + // dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) + // dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) + // for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { + // } + // // clear interrupts + // m := arm.DisableInterrupts() + // switch dc.port { + // case 0: + // arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) + // case 1: + // arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) + // } + // dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_SFTRST) + // } + + // reset the controller + dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) + for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { + } + // clear interrupts + m := arm.DisableInterrupts() + switch dc.port { + case 0: + arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) + case 1: + arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) + } + dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) + dc.phy.PWD.Set(0) + + // clear the controller mode field and set to device mode: + // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only + dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, + nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) + + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold + dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout + dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness + + // configure ENDPOINTLISTADDR + + // enable interrupts + dc.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 + dc.udelay(5000) + + return statusOK +} + +func (dc *deviceController) enable(enable bool) status { + + dc.irq.SetPriority(dciInterruptPriority) + dc.irq.Enable() + + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) + + return statusOK +} + +func (dc *deviceController) critical(enter bool) status { + if enter { + // check if critical section already locked + if dc.cri.Get() != 0 { + return statusRetry + } + // lock critical section + dc.cri.Set(1) + // disable interrupts, storing state in receiver + dc.ivm = arm.DisableInterrupts() + } else { + // ensure critical section is locked + if dc.cri.Get() != 0 { + // re-enable interrupts, using state stored in receiver + arm.EnableInterrupts(dc.ivm) + // unlock critical section + dc.cri.Set(0) + } + } + return statusOK +} + +func (dc *deviceController) interrupt() { + // read and clear the interrupts that fired + status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() + dc.bus.USBSTS.Set(status) + + println(strconv.FormatUint(uint64(status), 16)) +} + +// 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 (dc *deviceController) udelay(microsec uint32) { + n := cycles(microsec, descCPUFrequencyHz) + for i := uint32(0); i < n; i++ { + arm.Asm(`nop`) + } +} diff --git a/src/machine/usb2/desc.go b/src/machine/usb2/desc.go new file mode 100644 index 000000000..9a5b0e890 --- /dev/null +++ b/src/machine/usb2/desc.go @@ -0,0 +1,334 @@ +package usb2 + +const descUSBSpecVersion = 0x0200 // USB 2.0 + +// USB constants defined per specification. +const ( + + // Descriptor length + descLengthDevice = 18 + descLengthConfigure = 9 + descLengthInterface = 9 + descLengthEndpoint = 7 + descLengthDeviceQualitier = 10 + descLengthOTG = 5 + descLengthBOS = 5 + descLengthEndpointCompanion = 6 + descLengthDevCapTypeUSB20Extension = 7 + descLengthDevCapTypeSuperspeed = 10 + + // Descriptor type + descTypeDevice = 0x01 + descTypeConfigure = 0x02 + descTypeString = 0x03 + descTypeInterface = 0x04 + descTypeEndpoint = 0x05 + descTypeDeviceQualitier = 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 + + // 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 + descEndptAttrUsageTypeDataEndpoint = 0x00 + descEndptAttrUsageTypeFeedEndpoint = 0x10 + descEndptAttrUsageTypeFeedDataEndpoint = 0x20 + + // Endpoint max packet size + descEndptMaxPktSizeSizeMsk = 0x07FF + descEndptMaxPktSizeMultTransMsk = 0x1800 + descEndptMaxPktSizeMultTransPos = 11 + descEndptMaxPktSizeMaximum = 64 + + // OTG attributes + descOTGAttrSRPMsk = 0x01 + descOTGAttrHNPMsk = 0x02 + descOTGAttrADPMsk = 0x04 + + // Device capability type + descDevCapTypeWireless = 0x01 + descDevCapTypeUSB20Extension = 0x02 + descDevCapTypeSuperspeed = 0x03 + + // Device capability attributes (USB 2.0 extension) + descDevCapExtAttrLPMMsk = 0x02 + descDevCapExtAttrLPMPos = 1 + descDevCapExtAttrBESLMsk = 0x04 + descDevCapExtAttrBESLPos = 2 +) + +// USB CDC constants defined per specification. +const ( + + // Device class + descCDCComm = 0x02 // communication/control + descCDCData = 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 + descCDCLengthFuncHeader = 5 + descCDCLengthFuncCallManagement = 5 + descCDCLengthFuncAbstractControl = 4 + descCDCLengthFuncUnion = 5 + + // Functional descriptor type + descCDCTypeFuncHeader = 0x00 + descCDCTypeFuncCallManagement = 0x01 + descCDCTypeFuncAbstractControl = 0x02 + descCDCTypeFuncDirectLine = 0x03 + descCDCTypeFuncTelephoneRinger = 0x04 + descCDCTypeFuncTelephoneReport = 0x05 + descCDCTypeFuncUnion = 0x06 + descCDCTypeFuncCountrySelect = 0x07 + descCDCTypeFuncTelephoneModes = 0x08 + descCDCTypeFuncTerminal = 0x09 + descCDCTypeFuncNetworkChannel = 0x0A + descCDCTypeFuncProtocolUnit = 0x0B + descCDCTypeFuncExtensionUnit = 0x0C + descCDCTypeFuncMultiChannel = 0x0D + descCDCTypeFuncCAPIControl = 0x0E + descCDCTypeFuncEthernetNetworking = 0x0F + descCDCTypeFuncATMNetworking = 0x10 + descCDCTypeFuncWirelessControl = 0x11 + descCDCTypeFuncMobileDirectLine = 0x12 + descCDCTypeFuncMDLMDetail = 0x13 + descCDCTypeFuncDeviceManagement = 0x14 + descCDCTypeFuncOBEX = 0x15 + descCDCTypeFuncCommandSet = 0x16 + descCDCTypeFuncCommandSetDetail = 0x17 + descCDCTypeFuncTelephoneControl = 0x18 + descCDCTypeFuncOBEXServiceID = 0x19 +) + +// Common configuration constants for the USB CDC-ACM (single) device class. +const ( + // Interfaces for the first CDC-ACM device (index 1). + descCDCACM0InterfaceCount = 2 + descCDCACM0InterfaceCtrl = 0 + descCDCACM0InterfaceData = 1 + // Endpoints for the first CDC-ACM device (index 1). + descCDCACM0EndpointCount = 4 + descCDCACM0EndpointStatus = 2 // Communication/control interrupt input + descCDCACM0EndpointDataRx = 3 // Bulk data output + descCDCACM0EndpointDataTx = 4 // Bulk data input + + // Size of all CDC-ACM configuration descriptors. + descCDCACMConfigSize = uint16( + descLengthConfigure + // configuration + descLengthInterface + // communication/control interface + descCDCLengthFuncHeader + // CDC header + descCDCLengthFuncCallManagement + // CDC call management + descCDCLengthFuncAbstractControl + // CDC abstract control + descCDCLengthFuncUnion + // CDC union + descLengthEndpoint + // communication/control input endpoint + descLengthInterface + // data interface + descLengthEndpoint + // data input endpoint + descLengthEndpoint) // data output endpoint + // Attributes of all CDC-ACM configuration descriptors. + descCDCACMConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1) + (1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered + (0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup + 0 // Bits 0-4: reserved (0) +) + +// Common descriptors for the USB CDC-ACM (single) device class. +var ( + // descDeviceCDCACM holds the default device descriptors for CDC-ACM devices. + descDeviceCDCACM = [descCDCACMConfigCount][descLengthDevice]uint8{ + { + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // DEVICE Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + descCDCComm, // Class code (assigned by the USB-IF). + descCDCSubNone, // Subclass code (assigned by the USB-IF). + descCDCProtoNone, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSizeMaximum, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descReleaseID), // Device release number in BCD (low) + msU8(descReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 0, // Index of string descriptor describing the device's serial number + descCDCACMConfigCount, // Number of possible configurations + }, + } + + // descConfigCDCACM holds the default configuration descriptors for CDC-ACM + // devices. + descConfigCDCACM = [descCDCACMConfigCount][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) + descCDCACM0InterfaceCount, // 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 + descCDCACMConfigAttr, // Configuration attributes + descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) + + // Communication/Control Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descCDCACM0InterfaceCtrl, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descCDCComm, // 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 + descCDCLengthFuncHeader, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCTypeFuncHeader, // Descriptor Subtype + 0x10, // USB CDC specification version 1.10 (low) + 0x01, // USB CDC specification version 1.10 (high) + + // CDC Call Management Functional Descriptor + descCDCLengthFuncCallManagement, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCTypeFuncCallManagement, // Descriptor Subtype + 0x01, // Capabilities + descCDCACM0InterfaceData, // Data Interface + + // CDC Abstract Control Management Functional Descriptor + descCDCLengthFuncAbstractControl, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCTypeFuncAbstractControl, // Descriptor Subtype + 0x06, // Capabilities + + // CDC Union Functional Descriptor + descCDCLengthFuncUnion, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCTypeFuncUnion, // Descriptor Subtype + descCDCACM0InterfaceCtrl, // Controlling interface index + descCDCACM0InterfaceData, // Controlled interface index + + // Communication/Control Notification Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACM0EndpointStatus | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descCDCACMStatusPacketSize), // Max packet size (low) + msU8(descCDCACMStatusPacketSize), // Max packet size (high) + 8, // Polling Interval + + // Data Interface Descriptor + descLengthInterface, // Interface length + descTypeInterface, // Interface type + descCDCACM0InterfaceData, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descCDCData, // 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 + descCDCACM0EndpointDataRx | // 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 + descCDCACM0EndpointDataTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) + msU8(descCDCACMDataTxPacketSize), // Max packet size (high) + 0, // Polling Interval + }, + } +) diff --git a/src/machine/usb2/desc_mimxrt1062.go b/src/machine/usb2/desc_mimxrt1062.go new file mode 100644 index 000000000..ea4a40c99 --- /dev/null +++ b/src/machine/usb2/desc_mimxrt1062.go @@ -0,0 +1,32 @@ +package usb2 + +// descCPUFrequencyHz defines the target CPU frequency (Hz). +const descCPUFrequencyHz = 600000000 + +// General USB device identification constants. +const ( + descVendorID = 0xABCD + descProductID = 0x1234 + descReleaseID = 0x0101 + + descVendor = "NXP Semiconductors" + descProduct = "TinyGo USB" +) + +// Constants for USB CDC-ACM device classes. +const ( + // descCDCACMConfigCount defines the number of USB cores that will be + // configured as CDC-ACM (single) devices. + descCDCACMConfigCount = 1 + + descCDCACMMaxPower = 50 // 100 mA + + descCDCACMStatusPacketSize = 16 + descCDCACMDataRxPacketSize = descCDCACMDataRxFSPacketSize // full-speed + descCDCACMDataTxPacketSize = descCDCACMDataTxFSPacketSize // full-speed + + descCDCACMDataRxFSPacketSize = 64 // full-speed + descCDCACMDataTxFSPacketSize = 64 // full-speed + descCDCACMDataRxHSPacketSize = 512 // high-speed + descCDCACMDataTxHSPacketSize = 512 // high-speed +) diff --git a/src/machine/usb2/hci.go b/src/machine/usb2/hci.go new file mode 100644 index 000000000..7997c2351 --- /dev/null +++ b/src/machine/usb2/hci.go @@ -0,0 +1,9 @@ +package usb2 + +type hci interface { + init() status + enable(enable bool) status + critical(enter bool) status + interrupt() + udelay(micros uint32) +} diff --git a/src/machine/usb2/hci_mimxrt1062.go b/src/machine/usb2/hci_mimxrt1062.go new file mode 100644 index 000000000..85f66ecc2 --- /dev/null +++ b/src/machine/usb2/hci_mimxrt1062.go @@ -0,0 +1,126 @@ +// +build mimxrt1062 + +package usb2 + +// Implementation of USB host controller interface (hci) for NXP iMXRT1062. + +import ( + "device/arm" + "device/nxp" + "runtime/interrupt" + "runtime/volatile" +) + +// hciCount defines the number of USB cores to configure for host mode. It is +// computed as the sum of all declared host configuration descriptors. +const hciCount = 0 + +// hciInterruptPriority defines the priority for all USB host interrupts. +const hciInterruptPriority = 3 + +// hostController implements USB host controller interface (hci). +type hostController struct { + core *core // Parent USB core this instance is attached to + port int // USB port index + id int // hostControllerInstance index + + bus *nxp.USB_Type + phy *nxp.USBPHY_Type + irq interrupt.Interrupt + + cri volatile.Register8 // set to 1 if in critical section, else 0 + ivm uintptr // interrupt state when entering critical section +} + +// hostControllerInstance provides statically-allocated instances of each USB +// host controller configured on this platform. +var hostControllerInstance [hciCount]hostController + +// initHCI 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 initHCI(port int) (hci, status) { + if 0 == hciCount { + return nil, statusInvalidArgument // must have defined host descriptors + } + // Return the first instance whose assigned core is currently nil. + for i := range hostControllerInstance { + if nil == hostControllerInstance[i].core { + // Initialize host controller. + hostControllerInstance[i].core = &coreInstance[port] + hostControllerInstance[i].port = port + hostControllerInstance[i].id = i + switch port { + case 0: + hostControllerInstance[i].bus = nxp.USB1 + hostControllerInstance[i].phy = nxp.USBPHY1 + hostControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG1, + func(interrupt.Interrupt) { + coreInstance[0].hc.interrupt() + }) + + case 1: + hostControllerInstance[i].bus = nxp.USB2 + hostControllerInstance[i].phy = nxp.USBPHY2 + hostControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG2, + func(interrupt.Interrupt) { + //coreInstance[1].hc.interrupt() + }) + } + return &hostControllerInstance[i], statusOK + } + } + return nil, statusBusy // No free host controller instances available. +} + +func (hc *hostController) init() status { + + return statusOK +} + +func (hc *hostController) enable(enable bool) status { + + hc.irq.SetPriority(hciInterruptPriority) + hc.irq.Enable() + + return statusOK +} + +func (hc *hostController) critical(enter bool) status { + if enter { + // check if critical section already locked + if hc.cri.Get() != 0 { + return statusRetry + } + // lock critical section + hc.cri.Set(1) + // disable interrupts, storing state in receiver + hc.ivm = arm.DisableInterrupts() + } else { + // ensure critical section is locked + if hc.cri.Get() != 0 { + // re-enable interrupts, using state stored in receiver + arm.EnableInterrupts(hc.ivm) + // unlock critical section + hc.cri.Set(0) + } + } + return statusOK +} + +func (hc *hostController) interrupt() { + +} + +// 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 (hc *hostController) udelay(microsec uint32) { + n := cycles(microsec, descCPUFrequencyHz) + for i := uint32(0); i < n; i++ { + arm.Asm(`nop`) + } +} diff --git a/src/machine/usb2/uart.go b/src/machine/usb2/uart.go new file mode 100644 index 000000000..83bd9ffe0 --- /dev/null +++ b/src/machine/usb2/uart.go @@ -0,0 +1,53 @@ +package usb2 + +import ( + "errors" +) + +var ( + ErrInvalidPort = errors.New("invalid USB port") +) + +type ( + UARTConfig struct { + BaudRate uint32 + } + + // UART represents a virtual serial (UART) device emulation using the USB + // CDC-ACM device class driver. + UART struct { + port int // USB port (core index, e.g., 0-1) + core *core + } +) + +func (uart *UART) Configure(config UARTConfig) error { + + if uart.port >= CoreCount || uart.port >= dciCount || + uart.port >= descCDCACMConfigCount { + return ErrInvalidPort + } + + // use default configuration index (1-based index; 0=invalid) + // uart.config = configDeviceCDCACMConfigurationIndex + + // modify the global basic configuration struct configDeviceCDCACM for our USB + // port and configuration index. + // + // these settings are copied into the real CDC-ACM object, using interface + // deviceClassDriver, via initialization method (*deviceCDCACM).init(). + + // change baud rate from default, if provided + //if config.BaudRate != 0 { + // configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate = + // config.BaudRate + //} + + // verify we have a free USB port and take ownership of it + var st status + uart.core, st = initCore(uart.port, modeDevice) + if !st.ok() { + return ErrInvalidPort + } + return nil +} diff --git a/src/machine/usb2/usb.go b/src/machine/usb2/usb.go new file mode 100644 index 000000000..c450f86f8 --- /dev/null +++ b/src/machine/usb2/usb.go @@ -0,0 +1,99 @@ +package usb2 + +// Hardware abstraction for USB ports configured as either host or device. + +// core represents the core of a USB port configured as either host or device. +type core struct { + port int + mode int + dc dci + hc hci +} + +// Constant definitions for USB core operating modes. +const ( + modeIdle = 0 + modeDevice = 1 + modeHost = 2 +) + +// CoreCount defines the total number of USB cores to configure in device or +// host mode. +const CoreCount = dciCount + hciCount + +// coreInstance provides statically-allocated instances of each USB core +// configured on this platform. +var coreInstance [CoreCount]core + +// 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 + statusRetry // Retry + statusInvalidArgument // Invalid argument +) + +func (st status) ok() bool { return statusOK == st } + +// 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, mode int) (*core, status) { + + if port < 0 || port >= CoreCount { + return nil, statusInvalidArgument + } + if modeIdle != coreInstance[port].mode { + return nil, statusBusy + } + + switch mode { + case modeDevice: + // Allocate a free device controller and install interrupts + dc, st := initDCI(port) + 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 = mode + coreInstance[port].dc = dc + // Enable interrupts and enter runtime + if st = dc.enable(true); !st.ok() { + coreInstance[port].mode = modeIdle + coreInstance[port].dc = nil + return nil, st + } + + case modeHost: + // Allocate a free host controller and install interrupts + hc, st := initHCI(port) + 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 = mode + coreInstance[port].hc = hc + // Enable interrupts and enter runtime + if st = hc.enable(true); !st.ok() { + coreInstance[port].mode = modeIdle + coreInstance[port].hc = nil + return nil, st + } + + default: + return nil, statusInvalidArgument + } + + return &coreInstance[port], statusOK +} diff --git a/src/machine/usb2/util.go b/src/machine/usb2/util.go new file mode 100644 index 000000000..4b78c9fb5 --- /dev/null +++ b/src/machine/usb2/util.go @@ -0,0 +1,194 @@ +package usb2 + +// 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) +} 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 a9cc063df..3831ce146 100644 --- a/src/runtime/runtime_mimxrt1062.go +++ b/src/runtime/runtime_mimxrt1062.go @@ -36,7 +36,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 +108,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 +124,14 @@ func initUART() { machine.UART1.Configure(machine.UARTConfig{}) } +func initUSB() { + machine.USBCDC0.Configure(machine.UARTConfig{}) +} + func putchar(c byte) { - machine.UART1.WriteByte(c) + // ** TESTING: print byte to both serial UART interfaces ** + //machine.USBCDC0.WriteByte(c) // print to USB UART + machine.UART1.WriteByte(c) // print to hardware UART } func abort() { 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 312b1078c..c18b5e361 100644 --- a/targets/mimxrt1062-teensy40.ld +++ b/targets/mimxrt1062-teensy40.ld @@ -8,7 +8,7 @@ MEMORY ENTRY(Reset_Handler); -_stack_size = 128K; +_stack_size = 4K; _heap_size = 512K; SECTIONS From 1c2463f0b0c053259be823003a2bc10805134691 Mon Sep 17 00:00:00 2001 From: ardnew Date: Mon, 12 Apr 2021 23:03:01 -0500 Subject: [PATCH 2/9] cleanup unused code for baseline --- src/machine/usb2/dci_mimxrt1062.go | 38 +++++++----------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/machine/usb2/dci_mimxrt1062.go b/src/machine/usb2/dci_mimxrt1062.go index 86e252f2c..2af740f25 100644 --- a/src/machine/usb2/dci_mimxrt1062.go +++ b/src/machine/usb2/dci_mimxrt1062.go @@ -78,29 +78,6 @@ func initDCI(port int) (dci, status) { func (dc *deviceController) init() status { - dc.bus.BURSTSIZE.Set(0x0404) - - // if dc.phy.PWD.HasBits((nxp.USBPHY_PWD_RXPWDRX | nxp.USBPHY_PWD_RXPWDDIFF | - // nxp.USBPHY_PWD_RXPWD1PT1 | nxp.USBPHY_PWD_RXPWDENV | - // nxp.USBPHY_PWD_TXPWDV2I | nxp.USBPHY_PWD_TXPWDIBIAS | - // nxp.USBPHY_PWD_TXPWDFS)) || - // dc.bus.USBMODE.HasBits(nxp.USB_USBMODE_CM_Msk) { - // // reset controller if it was already enabled - // dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) - // dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) - // for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { - // } - // // clear interrupts - // m := arm.DisableInterrupts() - // switch dc.port { - // case 0: - // arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) - // case 1: - // arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) - // } - // dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_SFTRST) - // } - // reset the controller dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) @@ -126,7 +103,7 @@ func (dc *deviceController) init() status { dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness - // configure ENDPOINTLISTADDR + // TODO: configure ENDPOINTLISTADDR // enable interrupts dc.bus.USBINTR.Set( @@ -144,11 +121,14 @@ func (dc *deviceController) init() status { func (dc *deviceController) enable(enable bool) status { - dc.irq.SetPriority(dciInterruptPriority) - dc.irq.Enable() - - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) - + if enable { + dc.irq.SetPriority(dciInterruptPriority) + dc.irq.Enable() + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) + } else { + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) + dc.irq.Disable() + } return statusOK } From 84e61c183a620c07380c467d860547546ad8f7b6 Mon Sep 17 00:00:00 2001 From: ardnew Date: Mon, 19 Apr 2021 03:10:42 -0500 Subject: [PATCH 3/9] functioning ACM device registration with host --- src/device/nxp/mimxrt1062_mpu.go | 81 ++ src/machine/usb2/dcd.go | 81 ++ src/machine/usb2/dcd_mimxrt1062.go | 1242 +++++++++++++++++ src/machine/usb2/dci_mimxrt1062.go | 174 --- src/machine/usb2/desc.go | 589 +++++--- src/machine/usb2/desc_mimxrt1062.go | 175 ++- src/machine/usb2/{dci.go => hcd.go} | 2 +- .../{hci_mimxrt1062.go => hcd_mimxrt1062.go} | 31 +- src/machine/usb2/hci.go | 9 - src/machine/usb2/uart.go | 20 +- src/machine/usb2/usb.go | 65 +- src/machine/usb2/util.go | 22 + src/machine/usb2/util_arm.go | 16 + 13 files changed, 2066 insertions(+), 441 deletions(-) create mode 100644 src/machine/usb2/dcd.go create mode 100644 src/machine/usb2/dcd_mimxrt1062.go delete mode 100644 src/machine/usb2/dci_mimxrt1062.go rename src/machine/usb2/{dci.go => hcd.go} (85%) rename src/machine/usb2/{hci_mimxrt1062.go => hcd_mimxrt1062.go} (78%) delete mode 100644 src/machine/usb2/hci.go create mode 100644 src/machine/usb2/util_arm.go diff --git a/src/device/nxp/mimxrt1062_mpu.go b/src/device/nxp/mimxrt1062_mpu.go index c085e9501..adf5bb5c7 100644 --- a/src/device/nxp/mimxrt1062_mpu.go +++ b/src/device/nxp/mimxrt1062_mpu.go @@ -277,3 +277,84 @@ 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/machine/usb2/dcd.go b/src/machine/usb2/dcd.go new file mode 100644 index 000000000..510a9e95d --- /dev/null +++ b/src/machine/usb2/dcd.go @@ -0,0 +1,81 @@ +package usb2 + +import "unsafe" + +type dcd interface { + init() status + enable(enable bool) status + critical(enter bool) status + interrupt() + receive(endpoint uint8, transfer *dcdTransfer) + transmit(endpoint uint8, transfer *dcdTransfer) + control(setup dcdSetup) +} + +const ( + dcdEndpointSize = 64 // bytes + dcdTransferSize = 32 // + dcdSetupSize = 8 // +) + +type dcdEndpoint struct { + config uint32 // 4 + current *dcdTransfer // 4 *dcdTransfer + transfer dcdTransfer // 32 + setup dcdSetup // 8 + // Endpoints are 48-byte data structures. The remaining data extends this to + // 64-byte, and also makes it simpler for implementations to align endpoints + // allocated contiguously on 64-byte boundaries. + first *dcdTransfer // 4 *dcdTransfer + last *dcdTransfer // 4 *dcdTransfer + // 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 descriptor. + callback dcdTransferCallback // 8 +} + +type dcdTransferCallback func(transfer *dcdTransfer) +type dcdTransfer struct { + next *dcdTransfer // 4 *dcdTransfer + token uint32 // 4 + pointer [5]uintptr // 20 + param uint32 // 4 +} + +type dcdSetup struct { + bmRequestType uint8 + bRequest uint8 + wValue uint16 + wIndex uint16 + wLength uint16 +} + +func (s dcdSetup) pack() uint64 { + return ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8 + ((uint64(s.bRequest) & 0xFF) << 8) | // uint8 + ((uint64(s.wValue) & 0xFFFF) << 16) | // uint16 + ((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16 + ((uint64(s.wLength) & 0xFFFF) << 48) // uint16 +} + +var ( + // dcdPointerNil is a sentinel value used to indicate a pointer to an invalid + // value. Do not attempt to dereference! + dcdPointerNil = uintptr(0) + // dcdTransferEOL is a sentinel value used to indicate the final node in a + // linked list of transfer descriptors. + dcdTransferEOL = (*dcdTransfer)(unsafe.Pointer(dcdTransferPointerEOL)) + // dcdTransferPointerEOL is the notional memory address of dcdTransferEOL. + // The address does not refer to an actual dcdTransfer, so no attempt should + // be made to dereference it. + dcdTransferPointerEOL = 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 *dcdTransfer) nextTransfer() (*dcdTransfer, bool) { + return t.next, uintptr(unsafe.Pointer(t.next)) == dcdTransferPointerEOL +} diff --git a/src/machine/usb2/dcd_mimxrt1062.go b/src/machine/usb2/dcd_mimxrt1062.go new file mode 100644 index 000000000..372952a47 --- /dev/null +++ b/src/machine/usb2/dcd_mimxrt1062.go @@ -0,0 +1,1242 @@ +// +build mimxrt1062 + +package usb2 + +// Implementation of USB device controller interface (dcd) for NXP iMXRT1062. + +import ( + "device/arm" + "device/nxp" + "math/bits" + "runtime/interrupt" + "runtime/volatile" + "unsafe" +) + +// 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 + +// dcdInterruptPriority defines the priority for all USB device interrupts. +const dcdInterruptPriority = 3 + +// deviceController implements USB device controller driver (dcd) interface. +type deviceController struct { + core *core // Parent USB core this instance is attached to + port int // USB port index + class class // USB device class + id int // deviceControllerInstance index + + bus *nxp.USB_Type + phy *nxp.USBPHY_Type + irq interrupt.Interrupt + + cri volatile.Register8 // set to 1 if in critical section, else 0 + ivm uintptr // interrupt state when entering critical section + + stat *dcdEndpoint // endpoint 0 Rx ("out" direction) + ctrl *dcdEndpoint // endpoint 0 Tx ("in" direction) + + acm *descCDCACMClass + + timerInterrupt [2]func() + controlNotify uint32 + endpointNotify uint32 + sofUsage uint8 + rebootTimer uint8 + setup dcdSetup + controlReply [8]uint8 + + speed uint8 // bus speed (0=full, 1=low, 2=high, 4=super) +} + +// deviceControllerInstance provides statically-allocated instances of each USB +// device controller configured on this platform. +var deviceControllerInstance [dcdCount]deviceController + +// 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() +} + +//go:linkname ticks runtime.ticks +func ticks() int64 + +// 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 deviceControllerInstance { + if nil == deviceControllerInstance[i].core { + // Initialize device controller. + deviceControllerInstance[i].core = &coreInstance[port] + deviceControllerInstance[i].port = port + deviceControllerInstance[i].class = class + deviceControllerInstance[i].id = i + switch port { + case 0: + deviceControllerInstance[i].bus = nxp.USB1 + deviceControllerInstance[i].phy = nxp.USBPHY1 + deviceControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG1, + func(interrupt.Interrupt) { + coreInstance[0].dc.interrupt() + }) + + case 1: + deviceControllerInstance[i].bus = nxp.USB2 + deviceControllerInstance[i].phy = nxp.USBPHY2 + deviceControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG2, + func(interrupt.Interrupt) { + //coreInstance[1].dc.interrupt() + }) + } + switch class.id { + case classDeviceCDCACM: + deviceControllerInstance[i].acm = &descCDCACM[class.config-1] + default: + } + return &deviceControllerInstance[i], statusOK + } + } + return nil, statusBusy // No free device controller instances available. +} + +func (dc *deviceController) init() status { + // reset the controller + dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) + for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { + } + // clear interrupts + m := arm.DisableInterrupts() + switch dc.port { + case 0: + arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) + case 1: + arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) + } + dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) + dc.phy.PWD.Set(0) + + // clear the controller mode field and set to device mode: + // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only + dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, + nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) + + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold + dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout + dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness + + dc.stat = dc.endpointQueueHead(rxEndpoint(0)) + dc.ctrl = dc.endpointQueueHead(txEndpoint(0)) + dc.stat.config = (descEndptMaxPktSize << 16) | (1 << 15) + dc.ctrl.config = (descEndptMaxPktSize << 16) + + dc.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer(dc.stat)))) + + // clear installed timer callbacks + dc.timerInterrupt[0] = nil + dc.timerInterrupt[1] = nil + + // enable interrupts + dc.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 +} + +func (dc *deviceController) enable(enable bool) status { + if enable { + dc.irq.SetPriority(dcdInterruptPriority) + dc.irq.Enable() + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) + } else { + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) + dc.irq.Disable() + } + return statusOK +} + +func (dc *deviceController) critical(enter bool) status { + if enter { + // check if critical section already locked + if dc.cri.Get() != 0 { + return statusRetry + } + // lock critical section + dc.cri.Set(1) + // disable interrupts, storing state in receiver + dc.ivm = arm.DisableInterrupts() + } else { + // ensure critical section is locked + if dc.cri.Get() != 0 { + // re-enable interrupts, using state stored in receiver + arm.EnableInterrupts(dc.ivm) + // unlock critical section + dc.cri.Set(0) + } + } + return statusOK +} + +func (dc *deviceController) interrupt() { + // read and clear the interrupts that fired + status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() + dc.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 := dc.bus.ENDPTSETUPSTAT.Get() + for 0 != setupStatus { + dc.bus.ENDPTSETUPSTAT.Set(setupStatus) + var setup dcdSetup + ready := false + for !ready { + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_SUTW) + setup = dc.stat.setup + ready = dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_SUTW) + } + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_SUTW) + // flush endpoint 0 (bit 0=Rx, 16=Tx) + dc.bus.ENDPTFLUSH.Set(0x00010001) + for dc.bus.ENDPTFLUSH.HasBits(0x00010001) { + } // wait for flush to complete + dc.controlNotify = 0 + dc.control(setup) + setupStatus = dc.bus.ENDPTSETUPSTAT.Get() + } + + completeStatus := dc.bus.ENDPTCOMPLETE.Get() + if 0 != completeStatus { + dc.bus.ENDPTCOMPLETE.Set(completeStatus) + if 0 != completeStatus&dc.controlNotify { + dc.controlNotify = 0 + dc.controlComplete() + } + completeStatus &= dc.endpointNotify + if 0 != completeStatus { + tx := completeStatus >> 16 + for 0 != tx { + num := uint8(bits.TrailingZeros32(tx)) + dc.endpointComplete(txEndpoint(num)) + tx &^= 1 << num + } + rx := completeStatus & 0xFFFF + for 0 != rx { + num := uint8(bits.TrailingZeros32(rx)) + dc.endpointComplete(rxEndpoint(num)) + rx &^= 1 << num + } + } + } + } + + // 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 + dc.bus.ENDPTSETUPSTAT.Set(dc.bus.ENDPTSETUPSTAT.Get()) + // clear all endpoint complete status + dc.bus.ENDPTCOMPLETE.Set(dc.bus.ENDPTCOMPLETE.Get()) + // wait on any endpoint priming + for 0 != dc.bus.ENDPTPRIME.Get() { + } + dc.bus.ENDPTFLUSH.Set(0xFFFFFFFF) + // if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { + // } + switch dc.class.id { + case classDeviceCDCACM: + // TBD: reset CDC-ACM UART? + default: + } + dc.endpointNotify = 0 + } + + // 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 != dc.timerInterrupt[0] { + dc.timerInterrupt[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 != dc.timerInterrupt[1] { + dc.timerInterrupt[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 dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) { + dc.speed = descDeviceSpeedHigh // 480 Mbit/sec + } else { + dc.speed = descDeviceSpeedFull // 12 Mbit/sec + } + } + + // 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 { + //println("suspend") + } + + // 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 { + //println("error") + } + + // 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 125ms 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 dc.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) && + 0 != status&nxp.USB_USBSTS_SRI { + if 0 != dc.rebootTimer { + dc.rebootTimer -= 1 + if 0 == dc.rebootTimer { + dc.enableSofInterrupts(false, descCDCACMInterfaceCount) + } + } + } +} + +func (dc *deviceController) enableSofInterrupts(enable bool, iface uint8) { + if enable { + ivm := arm.DisableInterrupts() + dc.sofUsage |= 1 << iface + if !dc.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) { + dc.bus.USBSTS.Set(nxp.USB_USBSTS_SRI) + dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_SRE) + } + arm.EnableInterrupts(ivm) + } else { + dc.sofUsage &^= 1 << iface + if 0 == dc.sofUsage { + dc.bus.USBINTR.ClearBits(nxp.USB_USBINTR_SRE) + } + } +} + +// receive schedules a receive (Rx, OUT) transfer on the given endpoint. +func (dc *deviceController) receive(endpoint uint8, transfer *dcdTransfer) { + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(rxEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descCDCACMConfigAttrRxPos + dc.transferSchedule(ep, em, transfer) +} + +// transmit schedules a transmit (Tx, IN) transfer on the given endpoint. +func (dc *deviceController) transmit(endpoint uint8, transfer *dcdTransfer) { + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(txEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descCDCACMConfigAttrTxPos + dc.transferSchedule(ep, em, transfer) +} + +// control handles setup messages on control endpoint 0. +func (dc *deviceController) control(setup dcdSetup) { + + // println(strconv.FormatUint(setup.pack(), 16)) + + // First, switch on the type of request (standard, class, or vendor) + switch setup.bmRequestType & descRequestTypeTypeMsk { + + // === STANDARD REQUEST === + case descRequestTypeTypeStandard: + + // Switch on the recepient and direction of the request + switch setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- DEVICE Rx (OUT) --- + case descRequestTypeRecipientDevice | descRequestTypeDirOut: + + // Identify which request was received + switch setup.bRequest { + + // SET ADDRESS (0x05): + case descRequestStandardSetAddress: + dc.controlReceive(dcdPointerNil, 0, false) + dc.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | + ((uint32(setup.wValue) << nxp.USB_DEVICEADDR_USBADR_Pos) & + nxp.USB_DEVICEADDR_USBADR_Msk)) + return + + // SET CONFIGURATION (0x09): + case descRequestStandardSetConfiguration: + dc.class.config = int(setup.wValue) + if 0 == dc.class.config || dc.class.config > dcdCount { + // Use default if invalid index received + dc.class.config = 1 + } + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + dc.bus.ENDPTCTRL2.Set(descCDCACMConfigAttrStatus) // Status Tx + dc.bus.ENDPTCTRL3.Set(descCDCACMConfigAttrDataRx) // Bulk data Rx + dc.bus.ENDPTCTRL4.Set(descCDCACMConfigAttrDataTx) // Bulk data Tx + dc.serialConfigure() + dc.controlReceive(dcdPointerNil, 0, false) + + default: + // Unhandled device class + } + return + + default: + // Unhandled request + } + + // --- DEVICE Tx (IN) --- + case descRequestTypeRecipientDevice | descRequestTypeDirIn: + + // Identify which request was received + switch setup.bRequest { + + // GET STATUS (0x00): + case descRequestStandardGetStatus: + dc.controlReply[0] = 0 + dc.controlReply[1] = 0 + dc.controlTransmit( + uintptr(unsafe.Pointer(&dc.controlReply[0])), 2, false) + return + + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + dc.controlDescriptor(setup) + return + + // GET CONFIGURATION (0x08): + case descRequestStandardGetConfiguration: + dc.controlReply[0] = uint8(dc.class.config) + dc.controlTransmit( + uintptr(unsafe.Pointer(&dc.controlReply[0])), 1, false) + return + + default: + // Unhandled request + } + + // --- INTERFACE Tx (IN) --- + case descRequestTypeRecipientInterface | descRequestTypeDirIn: + + // Identify which request was received + switch setup.bRequest { + + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + dc.controlDescriptor(setup) + return + + default: + // Unhandled request + } + + // --- ENDPOINT Rx (OUT) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirOut: + + // Identify which request was received + switch setup.bRequest { + + // CLEAR FEATURE (0x01): + case descRequestStandardClearFeature: + // TODO + + // SET FEATURE (0x03): + case descRequestStandardSetFeature: + // TODO + + default: + // Unhandled request + } + + // --- ENDPOINT Tx (IN) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirIn: + + // Identify which request was received + switch setup.bRequest { + + // GET STATUS (0x00): + case descRequestStandardGetStatus: + num, dir := unpackEndpoint(uint8(setup.wIndex)) + var reg *volatile.Register32 + switch num { + case 0: + reg = &dc.bus.ENDPTCTRL0 + case 1: + reg = &dc.bus.ENDPTCTRL1 + case 2: + reg = &dc.bus.ENDPTCTRL2 + case 3: + reg = &dc.bus.ENDPTCTRL3 + case 4: + reg = &dc.bus.ENDPTCTRL4 + case 5: + reg = &dc.bus.ENDPTCTRL5 + case 6: + reg = &dc.bus.ENDPTCTRL6 + case 7: + reg = &dc.bus.ENDPTCTRL7 + } + if nil != reg { + dc.controlReply[0] = 0 + dc.controlReply[1] = 0 + if ((0 != dir) && reg.HasBits(nxp.USB_ENDPTCTRL0_TXS)) || + ((0 == dir) && reg.HasBits(nxp.USB_ENDPTCTRL0_RXS)) { + dc.controlReply[0] = 1 + } + dc.controlTransmit( + uintptr(unsafe.Pointer(&dc.controlReply[0])), 2, false) + return + } + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + // line coding must contain exactly 7 bytes + if descCDCACMCodingSize == setup.wLength { + dc.setup = setup + dc.controlReceive( + uintptr(unsafe.Pointer(&descCDCACM[dc.class.config-1].cx[0])), + descCDCACMCodingSize, true) + return + } + + default: + // Unhandled device class + } + + // CDC | SET CONTROL LINE STATE (0x22): + case descCDCRequestSetControlLineState: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + + // Determine interface destination of the notification + switch setup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + acm := &descCDCACM[dc.class.config-1] + acm.cticks = ticks() + acm.rtsdtr = uint8(setup.wValue) + dc.controlReceive(dcdPointerNil, 0, false) + return + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + // CDC | SEND BREAK (0x23): + case descCDCRequestSendBreak: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + dc.controlReceive(dcdPointerNil, 0, false) + return + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + case descRequestTypeTypeVendor: + default: + // Unhandled request type + } + + dc.bus.ENDPTCTRL0.Set(0x00010001) +} + +// controlTransfers returns the data and ackowledgement transfer descriptors for +// the control endpoint (i.e., endpoint 0). +//go:inline +func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { + // control endpoint is device class-specific + switch dc.class.id { + case classDeviceCDCACM: + return descCDCACM[dc.class.config-1].cd, descCDCACM[dc.class.config-1].ad + default: + return nil, nil + } +} + +func (dc *deviceController) controlDescriptor(setup dcdSetup) { + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + acm := &descCDCACM[dc.class.config-1] + dxn := uint8(0) + + // Determine the type of descriptor being requested + switch setup.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: + + var s []uint8 + + // Determine the string index requested + switch uint8(setup.wValue) { + + // Language + case 0: + if int(setup.wIndex) < len(acm.locstr) { + s = acm.locstr[setup.wIndex].index[0][:] + } + + // Manufacturer + case 1: + for i := range acm.locstr { + if acm.locstr[i].language == setup.wIndex { + s = acm.locstr[i].index[1][:] + // copy manufacturer string to uint8 buffer as UTF-16 + for n, c := range descManufacturer { + s[2+2*n] = uint8(c) + s[3+2*n] = 0 + } + break + } + } + + // Product + case 2: + for i := range acm.locstr { + if acm.locstr[i].language == setup.wIndex { + s = acm.locstr[i].index[2][:] + // copy product string to uint8 buffer as UTF-16 + for n, c := range descProduct { + s[2+2*n] = uint8(c) + s[3+2*n] = 0 + } + break + } + } + + // Serial number + case 3: + for i := range acm.locstr { + if acm.locstr[i].language == setup.wIndex { + s = acm.locstr[i].index[3][:] + // copy serial number string to uint8 buffer as UTF-16 + for n, c := range descSerialNumber { + s[2+2*n] = uint8(c) + s[3+2*n] = 0 + } + break + } + } + } + + if nil != s && len(s) > 0 { + dxn = s[0] + _ = copy(acm.dx[:], s[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(acm.dx[:], acm.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + + } + + if dxn > 0 { + if dxn > uint8(setup.wLength) { + dxn = uint8(setup.wLength) + } + nxp.FlushDeleteDcache( + uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) + dc.controlTransmit( + uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + } + + default: + // Unhandled device class + } + +} + +// controlReceive receives (Rx, OUT) data on control endpoint 0. +func (dc *deviceController) controlReceive( + data uintptr, size uint32, notify bool) { + const ( + rm = uint32(1 << descCDCACMConfigAttrRxPos) + tm = uint32(1 << descCDCACMConfigAttrTxPos) + ) + cd, ad := dc.controlTransfers() + if size > 0 { + cd.next = dcdTransferEOL + cd.token = (size << 16) | (1 << 7) + for i := range cd.pointer { + cd.pointer[i] = data + uintptr(i)*4096 + } + // linked list is empty + qr := dc.endpointQueueHead(rxEndpoint(0)) + qr.transfer.next = cd + qr.transfer.token = 0 + dc.bus.ENDPTPRIME.SetBits(rm) + for 0 != dc.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming + } + ad.next = dcdTransferEOL + ad.token = 1 << 7 + if notify { + ad.token |= 1 << 15 + } + ad.pointer[0] = 0 + qt := dc.endpointQueueHead(txEndpoint(0)) + qt.transfer.next = ad + qt.transfer.token = 0 + dc.bus.ENDPTCOMPLETE.Set(rm | tm) + dc.bus.ENDPTPRIME.SetBits(tm) + if notify { + dc.controlNotify = tm + } +} + +// controlTransmit transmits (Tx, IN) data on control endpoint 0. +func (dc *deviceController) controlTransmit( + data uintptr, size uint32, notify bool) { + const ( + rm = uint32(1 << descCDCACMConfigAttrRxPos) + tm = uint32(1 << descCDCACMConfigAttrTxPos) + ) + cd, ad := dc.controlTransfers() + if size > 0 { + cd.next = dcdTransferEOL + cd.token = (size << 16) | (1 << 7) + for i := range cd.pointer { + cd.pointer[i] = data + uintptr(i)*4096 + } + // linked list is empty + qt := dc.endpointQueueHead(txEndpoint(0)) + qt.transfer.next = cd + qt.transfer.token = 0 + dc.bus.ENDPTPRIME.SetBits(tm) + for 0 != dc.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming + } + ad.next = dcdTransferEOL + ad.token = 1 << 7 + if notify { + ad.token |= 1 << 15 + } + ad.pointer[0] = 0 + qr := dc.endpointQueueHead(rxEndpoint(0)) + qr.transfer.next = ad + qr.transfer.token = 0 + dc.bus.ENDPTCOMPLETE.Set(rm | tm) + dc.bus.ENDPTPRIME.SetBits(rm) + if notify { + dc.controlNotify = rm + } +} + +// controlComplete handles the setup completion of control endpoint 0. +func (dc *deviceController) controlComplete() { + + // First, switch on the type of request (standard, class, or vendor) + switch dc.setup.bmRequestType & descRequestTypeTypeMsk { + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch dc.setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch dc.setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + + // Determine interface destination of the notification + switch dc.setup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + + _ = copy(descCDCACM[dc.class.config-1].coding[:], + // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize]) + descCDCACM[dc.class.config-1].cx[:]) + var coding descCDCACMLineCoding + if coding.parse(descCDCACM[dc.class.config-1].coding[:]) { + if 134 == coding.baud { + dc.enableSofInterrupts(true, descCDCACMInterfaceCount) + dc.rebootTimer = 80 + } + } + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + + default: + // Unhandled request type + } + + // // determine interface destination of the notification + // switch dc.setup.wIndex { + // // communication/control interface: + // case descCDCACMInterfaceCtrl: + // // switch on the type and recepient of the request + // switch dc.setup.bmRequestType & + // (descRequestTypeTypeMsk | descRequestTypeRecipientMsk) { + // // interface class request: + // case descRequestTypeRecipientInterface | descRequestTypeTypeClass: + // // identify which request was received + // switch dc.setup.bRequest { + // // CDC_SET_LINE_CODING: + // case descCDCRequestSetLineCoding: + // // respond according to our device class + // switch dc.class.id { + // // CDC-ACM (single) + // case classDeviceCDCACM: + // _ = copy(descCDCACM[dc.class.config-1].coding[:], + // // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize]) + // descCDCACM[dc.class.config-1].cx[:]) + // var coding descCDCACMLineCoding + // if coding.parse(descCDCACM[dc.class.config-1].coding[:]) { + // if 134 == coding.baud { + // dc.enableSofInterrupts(true, descCDCACMInterfaceCount) + // dc.rebootTimer = 80 + // } + // } + // default: + // // unhandled device class + // } + // default: + // // unhandled request + // } + // default: + // // unhandled request type or recepient + // } + // default: + // // unhandled interface + // } +} + +// 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 (dc *deviceController) endpointQueueHead(endpoint uint8) *dcdEndpoint { + // endpoint queue head is device class-specific + switch dc.class.id { + case classDeviceCDCACM: + return &descCDCACM[dc.class.config-1].qh[endpointIndex(endpoint)] + default: + return nil + } +} + +func (dc *deviceController) endpointConfigure( + ep *dcdEndpoint, packetSize uint16, zlp bool, callback dcdTransferCallback) { + + ep.config = uint32(packetSize) << 16 + if !zlp { + ep.config |= 1 << 29 + } + ep.current = nil + ep.transfer.next = dcdTransferEOL + 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 +} + +func (dc *deviceController) endpointConfigureRx( + endpoint uint8, packetSize uint16, zlp bool, callback dcdTransferCallback) { + + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(rxEndpoint(endpoint)) + dc.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + dc.endpointNotify |= (uint32(1) << endpoint) << descCDCACMConfigAttrRxPos + } +} + +func (dc *deviceController) endpointConfigureTx( + endpoint uint8, packetSize uint16, zlp bool, callback dcdTransferCallback) { + + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(txEndpoint(endpoint)) + dc.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + dc.endpointNotify |= (uint32(1) << endpoint) << descCDCACMConfigAttrTxPos + } +} + +// endpointComplete handles transfer completion of a data endpoint. +func (dc *deviceController) endpointComplete(endpoint uint8) { + ep := dc.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) { + // 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 + } +} + +func (dc *deviceController) transferPrepare( + transfer *dcdTransfer, data *uint8, size uint16, param uint32) { + transfer.next = dcdTransferEOL + transfer.token = (uint32(size) << 16) | (1 << 7) + addr := uintptr(unsafe.Pointer(data)) + for i := range transfer.pointer { + transfer.pointer[i] = addr + uintptr(i)*4096 + } + transfer.param = param +} + +func (dc *deviceController) transferSchedule( + endpoint *dcdEndpoint, mask uint32, transfer *dcdTransfer) { + + if nil != endpoint.callback { + transfer.token |= 1 << 15 + } + ivm := arm.DisableInterrupts() + last := endpoint.last + if nil != last { + last.next = transfer + if dc.bus.ENDPTPRIME.HasBits(mask) { + goto endTransfer + } + start := cycleCount() + estat := uint32(0) + for !dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) && + (cycleCount()-start < 2400) { + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW) + estat = dc.bus.ENDPTSTAT.Get() + } + if 0 != estat&mask { + goto endTransfer + } + } + endpoint.transfer.next = transfer + endpoint.transfer.token = 0 + dc.bus.ENDPTPRIME.SetBits(mask) + endpoint.first = transfer +endTransfer: + endpoint.last = transfer + arm.EnableInterrupts(ivm) +} + +func (dc *deviceController) timerConfigure(timer int, usec uint32, fn func()) { + if timer < 0 || timer >= len(dc.timerInterrupt) { + return + } + dc.timerInterrupt[timer] = fn + switch timer { + case 0: + dc.bus.GPTIMER0CTRL.Set(0) + dc.bus.GPTIMER0LD.Set(usec - 1) + dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE0) + case 1: + dc.bus.GPTIMER1CTRL.Set(0) + dc.bus.GPTIMER1LD.Set(usec - 1) + dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE1) + } +} + +func (dc *deviceController) timerOneShot(timer int) { + switch timer { + case 0: + dc.bus.GPTIMER0CTRL.Set( + nxp.USB_GPTIMER0CTRL_GPTRUN | nxp.USB_GPTIMER0CTRL_GPTRST) + case 1: + dc.bus.GPTIMER1CTRL.Set( + nxp.USB_GPTIMER1CTRL_GPTRUN | nxp.USB_GPTIMER1CTRL_GPTRST) + } +} + +func (dc *deviceController) timerStop(timer int) { + switch timer { + case 0: + dc.bus.GPTIMER0CTRL.Set(0) + case 1: + dc.bus.GPTIMER1CTRL.Set(0) + } +} + +func (dc *deviceController) serialConfigure() { + acm := &descCDCACM[dc.class.config-1] + switch dc.speed { + case descDeviceSpeedHigh: + acm.rxSize = descCDCACMDataRxHSPacketSize + acm.txSize = descCDCACMDataTxHSPacketSize + default: + acm.rxSize = descCDCACMDataRxFSPacketSize + acm.txSize = descCDCACMDataTxFSPacketSize + } + acm.txHead = 0 + acm.txFree = 0 + acm.rxHead = 0 + acm.rxTail = 0 + acm.rxFree = 0 + dc.endpointConfigureTx(descCDCACMEndpointStatus, + acm.cxSize, false, nil) + dc.endpointConfigureRx(descCDCACMEndpointDataRx, + acm.rxSize, false, dc.serialNotify) + dc.endpointConfigureTx(descCDCACMEndpointDataTx, + acm.txSize, true, nil) + for i := range acm.rd { + dc.serialReceive(uint8(i)) + } + dc.timerConfigure(0, 75, dc.serialFlush) +} + +func (dc *deviceController) serialNotify(transfer *dcdTransfer) { + acm := &descCDCACM[dc.class.config-1] + len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF) + p := transfer.param + if 0 == len { + // zero-length packet (ZLP) + dc.serialReceive(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 + dc.serialReceive(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 + } +} + +func (dc *deviceController) serialReceive(endpoint uint8) { + ivm := arm.DisableInterrupts() + num := uint16(endpoint) & descEndptAddrNumberMsk + acm := &descCDCACM[dc.class.config-1] + buf := &acm.rx[num*descCDCACMRxSize] + dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) + nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) + dc.receive(descCDCACMEndpointDataRx, &acm.rd[num]) + arm.EnableInterrupts(ivm) +} + +func (dc *deviceController) serialFlush() { + const autoFlushTx = true + if !autoFlushTx { + return + } + acm := &descCDCACM[dc.class.config-1] + if 0 == acm.txFree { + return + } + xfer := &acm.td[acm.txHead] + buff := &acm.tx[uint16(acm.txHead)*descCDCACMTxSize] + size := descCDCACMTxSize - acm.txFree + dc.transferPrepare(xfer, buff, size, 0) + nxp.FlushDeleteDcache(uintptr(unsafe.Pointer(buff)), uintptr(size)) + dc.transmit(descCDCACMEndpointDataTx, xfer) + acm.txHead += 1 + if acm.txHead >= descCDCACMTDCount { + acm.txHead = 0 + } + acm.txFree = 0 +} diff --git a/src/machine/usb2/dci_mimxrt1062.go b/src/machine/usb2/dci_mimxrt1062.go deleted file mode 100644 index 2af740f25..000000000 --- a/src/machine/usb2/dci_mimxrt1062.go +++ /dev/null @@ -1,174 +0,0 @@ -// +build mimxrt1062 - -package usb2 - -// Implementation of USB device controller interface (dci) for NXP iMXRT1062. - -import ( - "device/arm" - "device/nxp" - "runtime/interrupt" - "runtime/volatile" - "strconv" -) - -// dciCount defines the number of USB cores to configure for device mode. It is -// computed as the sum of all declared device configuration descriptors. -const dciCount = descCDCACMConfigCount - -// dciInterruptPriority defines the priority for all USB device interrupts. -const dciInterruptPriority = 3 - -// deviceController implements USB device controller interface (dci). -type deviceController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - id int // deviceControllerInstance index - - bus *nxp.USB_Type - phy *nxp.USBPHY_Type - irq interrupt.Interrupt - - cri volatile.Register8 // set to 1 if in critical section, else 0 - ivm uintptr // interrupt state when entering critical section -} - -// deviceControllerInstance provides statically-allocated instances of each USB -// device controller configured on this platform. -var deviceControllerInstance [dciCount]deviceController - -// initDCI 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 initDCI(port int) (dci, status) { - if 0 == dciCount { - return nil, statusInvalidArgument // must have defined device descriptors - } - // Return the first instance whose assigned core is currently nil. - for i := range deviceControllerInstance { - if nil == deviceControllerInstance[i].core { - // Initialize device controller. - deviceControllerInstance[i].core = &coreInstance[port] - deviceControllerInstance[i].port = port - deviceControllerInstance[i].id = i - switch port { - case 0: - deviceControllerInstance[i].bus = nxp.USB1 - deviceControllerInstance[i].phy = nxp.USBPHY1 - deviceControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG1, - func(interrupt.Interrupt) { - coreInstance[0].dc.interrupt() - }) - - case 1: - deviceControllerInstance[i].bus = nxp.USB2 - deviceControllerInstance[i].phy = nxp.USBPHY2 - deviceControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG2, - func(interrupt.Interrupt) { - //coreInstance[1].dc.interrupt() - }) - } - return &deviceControllerInstance[i], statusOK - } - } - return nil, statusBusy // No free device controller instances available. -} - -func (dc *deviceController) init() status { - - // reset the controller - dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) - for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { - } - // clear interrupts - m := arm.DisableInterrupts() - switch dc.port { - case 0: - arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) - case 1: - arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) - } - dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) - dc.phy.PWD.Set(0) - - // clear the controller mode field and set to device mode: - // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only - dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, - nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) - - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold - dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout - dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness - - // TODO: configure ENDPOINTLISTADDR - - // enable interrupts - dc.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 - dc.udelay(5000) - - return statusOK -} - -func (dc *deviceController) enable(enable bool) status { - - if enable { - dc.irq.SetPriority(dciInterruptPriority) - dc.irq.Enable() - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) - } else { - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) - dc.irq.Disable() - } - return statusOK -} - -func (dc *deviceController) critical(enter bool) status { - if enter { - // check if critical section already locked - if dc.cri.Get() != 0 { - return statusRetry - } - // lock critical section - dc.cri.Set(1) - // disable interrupts, storing state in receiver - dc.ivm = arm.DisableInterrupts() - } else { - // ensure critical section is locked - if dc.cri.Get() != 0 { - // re-enable interrupts, using state stored in receiver - arm.EnableInterrupts(dc.ivm) - // unlock critical section - dc.cri.Set(0) - } - } - return statusOK -} - -func (dc *deviceController) interrupt() { - // read and clear the interrupts that fired - status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() - dc.bus.USBSTS.Set(status) - - println(strconv.FormatUint(uint64(status), 16)) -} - -// 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 (dc *deviceController) udelay(microsec uint32) { - n := cycles(microsec, descCPUFrequencyHz) - for i := uint32(0); i < n; i++ { - arm.Asm(`nop`) - } -} diff --git a/src/machine/usb2/desc.go b/src/machine/usb2/desc.go index 9a5b0e890..4642a76f2 100644 --- a/src/machine/usb2/desc.go +++ b/src/machine/usb2/desc.go @@ -1,21 +1,29 @@ package usb2 -const descUSBSpecVersion = 0x0200 // USB 2.0 +const descUSBSpecVersion = uint16(0x0200) // USB 2.0 + +const descLanguageEnglish = uint16(0x0409) + +type descIndexStrings [4][64]uint8 +type descLocalStrings struct { + language uint16 + index descIndexStrings // UTF-16, 32-character maximum length +} // USB constants defined per specification. const ( // Descriptor length - descLengthDevice = 18 - descLengthConfigure = 9 - descLengthInterface = 9 - descLengthEndpoint = 7 - descLengthDeviceQualitier = 10 - descLengthOTG = 5 - descLengthBOS = 5 - descLengthEndpointCompanion = 6 - descLengthDevCapTypeUSB20Extension = 7 - descLengthDevCapTypeSuperspeed = 10 + descLengthDevice = 18 + descLengthConfigure = 9 + descLengthInterface = 9 + descLengthEndpoint = 7 + descLengthQualification = 10 + descLengthOTG = 5 + descLengthBOS = 5 + descLengthEndpointCompanion = 6 + descLengthUSB20Extension = 7 + descLengthSuperspeed = 10 // Descriptor type descTypeDevice = 0x01 @@ -23,7 +31,7 @@ const ( descTypeString = 0x03 descTypeInterface = 0x04 descTypeEndpoint = 0x05 - descTypeDeviceQualitier = 0x06 + descTypeQualification = 0x06 descTypeOtherSpeedConfiguration = 0x07 descTypeInterfacePower = 0x08 descTypeOTG = 0x09 @@ -37,6 +45,36 @@ const ( 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 @@ -60,49 +98,55 @@ const ( descEndptAddrDirectionIn = 0x80 // Endpoint attributes - descEndptAttrTypeMsk = 0x03 - descEndptAttrNumberPos = 0 - descEndptAttrSyncTypeMsk = 0x0C - descEndptAttrSyncTypePos = 2 - descEndptAttrSyncTypeNoSync = 0x00 - descEndptAttrSyncTypeAsync = 0x04 - descEndptAttrSyncTypeAdaptive = 0x08 - descEndptAttrSyncTypeSync = 0x0C - descEndptAttrUsageTypeMsk = 0x30 - descEndptAttrUsageTypePos = 4 - descEndptAttrUsageTypeDataEndpoint = 0x00 - descEndptAttrUsageTypeFeedEndpoint = 0x10 - descEndptAttrUsageTypeFeedDataEndpoint = 0x20 + 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 - descEndptMaxPktSizeSizeMsk = 0x07FF - descEndptMaxPktSizeMultTransMsk = 0x1800 - descEndptMaxPktSizeMultTransPos = 11 - descEndptMaxPktSizeMaximum = 64 + 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 - descDevCapTypeWireless = 0x01 - descDevCapTypeUSB20Extension = 0x02 - descDevCapTypeSuperspeed = 0x03 + descDeviceCapTypeWireless = 0x01 + descDeviceCapTypeUSB20Extension = 0x02 + descDeviceCapTypeSuperspeed = 0x03 // Device capability attributes (USB 2.0 extension) - descDevCapExtAttrLPMMsk = 0x02 - descDevCapExtAttrLPMPos = 1 - descDevCapExtAttrBESLMsk = 0x04 - descDevCapExtAttrBESLPos = 2 + descDeviceCapExtAttrLPMMsk = 0x02 + descDeviceCapExtAttrLPMPos = 1 + descDeviceCapExtAttrBESLMsk = 0x04 + descDeviceCapExtAttrBESLPos = 2 ) // USB CDC constants defined per specification. const ( // Device class - descCDCComm = 0x02 // communication/control - descCDCData = 0x0A // data + descCDCTypeComm = 0x02 // communication/control + descCDCTypeData = 0x0A // data // Communication/control subclass descCDCSubNone = 0x00 @@ -146,60 +190,133 @@ const ( descCDCProtoUnitFunctional = 0xFE // Functional descriptor length - descCDCLengthFuncHeader = 5 - descCDCLengthFuncCallManagement = 5 - descCDCLengthFuncAbstractControl = 4 - descCDCLengthFuncUnion = 5 + descCDCFuncLengthHeader = 5 + descCDCFuncLengthCallManagement = 5 + descCDCFuncLengthAbstractControl = 4 + descCDCFuncLengthUnion = 5 // Functional descriptor type - descCDCTypeFuncHeader = 0x00 - descCDCTypeFuncCallManagement = 0x01 - descCDCTypeFuncAbstractControl = 0x02 - descCDCTypeFuncDirectLine = 0x03 - descCDCTypeFuncTelephoneRinger = 0x04 - descCDCTypeFuncTelephoneReport = 0x05 - descCDCTypeFuncUnion = 0x06 - descCDCTypeFuncCountrySelect = 0x07 - descCDCTypeFuncTelephoneModes = 0x08 - descCDCTypeFuncTerminal = 0x09 - descCDCTypeFuncNetworkChannel = 0x0A - descCDCTypeFuncProtocolUnit = 0x0B - descCDCTypeFuncExtensionUnit = 0x0C - descCDCTypeFuncMultiChannel = 0x0D - descCDCTypeFuncCAPIControl = 0x0E - descCDCTypeFuncEthernetNetworking = 0x0F - descCDCTypeFuncATMNetworking = 0x10 - descCDCTypeFuncWirelessControl = 0x11 - descCDCTypeFuncMobileDirectLine = 0x12 - descCDCTypeFuncMDLMDetail = 0x13 - descCDCTypeFuncDeviceManagement = 0x14 - descCDCTypeFuncOBEX = 0x15 - descCDCTypeFuncCommandSet = 0x16 - descCDCTypeFuncCommandSetDetail = 0x17 - descCDCTypeFuncTelephoneControl = 0x18 - descCDCTypeFuncOBEXServiceID = 0x19 + 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 ) // Common configuration constants for the USB CDC-ACM (single) device class. const ( - // Interfaces for the first CDC-ACM device (index 1). - descCDCACM0InterfaceCount = 2 - descCDCACM0InterfaceCtrl = 0 - descCDCACM0InterfaceData = 1 - // Endpoints for the first CDC-ACM device (index 1). - descCDCACM0EndpointCount = 4 - descCDCACM0EndpointStatus = 2 // Communication/control interrupt input - descCDCACM0EndpointDataRx = 3 // Bulk data output - descCDCACM0EndpointDataTx = 4 // Bulk data input + // String descriptor languages + descCDCACMLanguageCount = 1 + // Interfaces for all CDC-ACM configurations. + descCDCACMInterfaceCount = 2 + descCDCACMInterfaceCtrl = 0 + descCDCACMInterfaceData = 1 + // Endpoints for all CDC-ACM configurations. + descCDCACMEndpointCount = 4 + descCDCACMEndpointStatus = 2 // Communication/control interrupt input + descCDCACMEndpointDataRx = 3 // Bulk data output + descCDCACMEndpointDataTx = 4 // Bulk data input + // Endpoint configuration attributes for all CDC-ACM configurations. + descCDCACMConfigAttrStatus = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) | + (descCDCACMConfigAttrInterrupt << descCDCACMConfigAttrTxPos) + descCDCACMConfigAttrDataRx = (descCDCACMConfigAttrBulk << descCDCACMConfigAttrRxPos) | + (descCDCACMConfigAttrUnused << descCDCACMConfigAttrTxPos) + descCDCACMConfigAttrDataTx = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) | + (descCDCACMConfigAttrBulk << descCDCACMConfigAttrTxPos) // Size of all CDC-ACM configuration descriptors. descCDCACMConfigSize = uint16( descLengthConfigure + // configuration descLengthInterface + // communication/control interface - descCDCLengthFuncHeader + // CDC header - descCDCLengthFuncCallManagement + // CDC call management - descCDCLengthFuncAbstractControl + // CDC abstract control - descCDCLengthFuncUnion + // CDC union + descCDCFuncLengthHeader + // CDC header + descCDCFuncLengthCallManagement + // CDC call management + descCDCFuncLengthAbstractControl + // CDC abstract control + descCDCFuncLengthUnion + // CDC union descLengthEndpoint + // communication/control input endpoint descLengthInterface + // data interface descLengthEndpoint + // data input endpoint @@ -209,126 +326,200 @@ const ( (1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered (0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup 0 // Bits 0-4: reserved (0) + + descCDCACMConfigAttrRxPos = 0 + descCDCACMConfigAttrTxPos = 16 + descCDCACMConfigAttrUnused = 0x02 // TBD: what is this? + descCDCACMConfigAttrIsochronous = descCDCACMConfigAttr | descEndptAttrSyncTypeAsync + descCDCACMConfigAttrBulk = descCDCACMConfigAttr | descEndptAttrSyncTypeAdaptive + descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync ) -// Common descriptors for the USB CDC-ACM (single) device class. -var ( - // descDeviceCDCACM holds the default device descriptors for CDC-ACM devices. - descDeviceCDCACM = [descCDCACMConfigCount][descLengthDevice]uint8{ - { - descLengthDevice, // Size of this descriptor in bytes - descTypeDevice, // DEVICE Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - descCDCComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSizeMaximum, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) - msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) - lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) - msU8(descProductID), // Product ID (high) (assigned by the manufacturer) - lsU8(descReleaseID), // Device release number in BCD (low) - msU8(descReleaseID), // Device release number in BCD (high) - 1, // Index of string descriptor describing manufacturer - 2, // Index of string descriptor describing product - 0, // Index of string descriptor describing the device's serial number - descCDCACMConfigCount, // Number of possible configurations +// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0String = [descCDCACMLanguageCount]descLocalStrings{ + { + language: descLanguageEnglish, + index: descIndexStrings{ + { // 0: language string + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + { // 1: manufacturer + uint8(2 + 2*len(descManufacturer)), + descTypeString, + }, + { // 2: product + uint8(2 + 2*len(descProduct)), + descTypeString, + }, + { // 3: serial number + uint8(2 + 2*len(descSerialNumber)), + descTypeString, + }, }, + }, +} + +// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0Device = [descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + descCDCTypeComm, // Class code (assigned by the USB-IF). + descCDCSubNone, // Subclass code (assigned by the USB-IF). + descCDCProtoNone, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descReleaseID), // Device release number in BCD (low) + msU8(descReleaseID), // 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 +} + +// descCDCACM0Qualif holds the default device qualification descriptor for +// CDC-ACM[0], i.e., configuration index 1. +var descCDCACM0Qualif = [descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + descCDCTypeComm, // Class code (assigned by the USB-IF). + descCDCSubNone, // Subclass code (assigned by the USB-IF). + descCDCProtoNone, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descCDCACMCount, // Number of possible configurations + 0, // Reserved +} + +// descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0], +// i.e., configuration index 1. +var descCDCACM0Config = [descCDCACMConfigSize]uint8{ + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) + msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) + descCDCACMInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descCDCACMConfigAttr, // Configuration attributes + descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) + + // Communication/Control Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descCDCACMInterfaceCtrl, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descCDCTypeComm, // Class code + descCDCSubAbstractControl, // Subclass code + descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) + 0, // Interface Description String Index + + // CDC Header Functional Descriptor + descCDCFuncLengthHeader, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeHeader, // Descriptor Subtype + 0x10, // USB CDC specification version 1.10 (low) + 0x01, // USB CDC specification version 1.10 (high) + + // CDC Call Management Functional Descriptor + descCDCFuncLengthCallManagement, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeCallManagement, // Descriptor Subtype + 0x01, // Capabilities + descCDCACMInterfaceData, // Data Interface + + // CDC Abstract Control Management Functional Descriptor + descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeAbstractControl, // Descriptor Subtype + 0x06, // Capabilities + + // CDC Union Functional Descriptor + descCDCFuncLengthUnion, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeUnion, // Descriptor Subtype + descCDCACMInterfaceCtrl, // Controlling interface index + descCDCACMInterfaceData, // Controlled interface index + + // Communication/Control Notification Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointStatus | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descCDCACMStatusPacketSize), // Max packet size (low) + msU8(descCDCACMStatusPacketSize), // Max packet size (high) + 16, // Polling Interval + + // Data Interface Descriptor + descLengthInterface, // Interface length + descTypeInterface, // Interface type + descCDCACMInterfaceData, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descCDCTypeData, // Class code + descCDCSubNone, // Subclass code + descCDCProtoNone, // Protocol code + 0, // Interface Description String Index + + // Data Bulk Rx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointDataRx | // Endpoint address + descEndptAddrDirectionOut, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataRxPacketSize), // Max packet size (low) + msU8(descCDCACMDataRxPacketSize), // Max packet size (high) + 0, // Polling Interval + + // Data Bulk Tx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointDataTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) + msU8(descCDCACMDataTxPacketSize), // Max packet size (high) + 0, // Polling Interval +} + +// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. +const descCDCACMCodingSize = 7 + +// descCDCACM0Coding holds the default UART line coding for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0Coding [descCDCACMCodingSize]uint8 + +type descCDCACMLineCoding struct { + baud uint32 + stopBits uint8 + parity uint8 + numBits uint8 + dtr bool + rts bool +} + +func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool { + if len(buffer) < descCDCACMCodingSize { + return false } - - // descConfigCDCACM holds the default configuration descriptors for CDC-ACM - // devices. - descConfigCDCACM = [descCDCACMConfigCount][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) - descCDCACM0InterfaceCount, // 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 - descCDCACMConfigAttr, // Configuration attributes - descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) - - // Communication/Control Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descCDCACM0InterfaceCtrl, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descCDCComm, // 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 - descCDCLengthFuncHeader, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncHeader, // Descriptor Subtype - 0x10, // USB CDC specification version 1.10 (low) - 0x01, // USB CDC specification version 1.10 (high) - - // CDC Call Management Functional Descriptor - descCDCLengthFuncCallManagement, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncCallManagement, // Descriptor Subtype - 0x01, // Capabilities - descCDCACM0InterfaceData, // Data Interface - - // CDC Abstract Control Management Functional Descriptor - descCDCLengthFuncAbstractControl, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncAbstractControl, // Descriptor Subtype - 0x06, // Capabilities - - // CDC Union Functional Descriptor - descCDCLengthFuncUnion, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncUnion, // Descriptor Subtype - descCDCACM0InterfaceCtrl, // Controlling interface index - descCDCACM0InterfaceData, // Controlled interface index - - // Communication/Control Notification Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACM0EndpointStatus | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descCDCACMStatusPacketSize), // Max packet size (low) - msU8(descCDCACMStatusPacketSize), // Max packet size (high) - 8, // Polling Interval - - // Data Interface Descriptor - descLengthInterface, // Interface length - descTypeInterface, // Interface type - descCDCACM0InterfaceData, // Interface index - 0, // Alternate setting - 2, // Number of endpoints - descCDCData, // 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 - descCDCACM0EndpointDataRx | // 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 - descCDCACM0EndpointDataTx | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) - msU8(descCDCACMDataTxPacketSize), // Max packet size (high) - 0, // Polling Interval - }, + lc.baud = packU32(buffer) + lc.stopBits = buffer[4] + if 0 == lc.stopBits { + lc.stopBits = 1 } -) + lc.parity = buffer[5] + lc.numBits = buffer[6] + return true +} diff --git a/src/machine/usb2/desc_mimxrt1062.go b/src/machine/usb2/desc_mimxrt1062.go index ea4a40c99..985cc68ba 100644 --- a/src/machine/usb2/desc_mimxrt1062.go +++ b/src/machine/usb2/desc_mimxrt1062.go @@ -1,3 +1,5 @@ +// +build mimxrt1062 + package usb2 // descCPUFrequencyHz defines the target CPU frequency (Hz). @@ -5,28 +7,183 @@ const descCPUFrequencyHz = 600000000 // General USB device identification constants. const ( - descVendorID = 0xABCD - descProductID = 0x1234 + descVendorID = 0x16C0 + descProductID = 0x0483 descReleaseID = 0x0101 - descVendor = "NXP Semiconductors" - descProduct = "TinyGo USB" + descManufacturer = "NXP Semiconductors" + descProduct = "TinyGo USB" + descSerialNumber = "0000000000" ) // Constants for USB CDC-ACM device classes. const ( - // descCDCACMConfigCount defines the number of USB cores that will be - // configured as CDC-ACM (single) devices. - descCDCACMConfigCount = 1 + // descCDCACMCount defines the number of USB cores that will be configured as + // CDC-ACM (single) devices. + descCDCACMCount = 1 + + descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1) + descCDCACMRDCount = 2 * descCDCACMEndpointCount + descCDCACMTDCount = descCDCACMEndpointCount + descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount + descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount + descCDCACMCxCount = 8 descCDCACMMaxPower = 50 // 100 mA descCDCACMStatusPacketSize = 16 - descCDCACMDataRxPacketSize = descCDCACMDataRxFSPacketSize // full-speed - descCDCACMDataTxPacketSize = descCDCACMDataTxFSPacketSize // full-speed + descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed + descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed + descCDCACMRxSize = descCDCACMDataRxPacketSize + descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize descCDCACMDataRxFSPacketSize = 64 // full-speed descCDCACMDataTxFSPacketSize = 64 // full-speed descCDCACMDataRxHSPacketSize = 512 // high-speed descCDCACMDataTxHSPacketSize = 512 // 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]dcdEndpoint + +// descCDCACM0CD is the transfer descriptor for data messages transmitted or +// received on the status/control endpoint 0 for the default CDC-ACM (single) +// device class configuration (index 1). +//go:align 32 +var descCDCACM0CD dcdTransfer + +// 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 dcdTransfer + +// 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]dcdTransfer + +// 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]dcdTransfer + +// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of +// the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Cx [descCDCACMCxCount]uint8 + +// descCDCACM0Rx is the receive (Rx) buffer of data endpoints for the default +// CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Rx [descCDCACMRxCount]uint8 + +// descCDCACM0Tx is the transmit (Tx) buffer of data endpoints for the default +// CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Tx [descCDCACMTxCount]uint8 + +// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data for the default +// CDC-ACM (single) device class configuration (index 1). +var descCDCACM0Dx [descCDCACMConfigSize]uint8 + +var descCDCACM0RDNum [descCDCACMRDCount]uint16 +var descCDCACM0RDIdx [descCDCACMRDCount]uint16 +var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 + +type descCDCACMClass struct { + locstr *[descCDCACMLanguageCount]descLocalStrings // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descCDCACMConfigSize]uint8 // configuration descriptor + + coding *[descCDCACMCodingSize]uint8 // UART line coding + cticks int64 + rtsdtr uint8 + + qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads + + cd *dcdTransfer // control endpoint 0 Rx/Tx data transfer descriptor + ad *dcdTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + rd *[descCDCACMRDCount]dcdTransfer // bulk data endpoint Rx (OUT) transfer descriptors + td *[descCDCACMTDCount]dcdTransfer // bulk data endpoint Tx (IN) transfer descriptors + + cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer + rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer + tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer + dx *[descCDCACMConfigSize]uint8 // descriptor data Tx (IN) transfer buffer + + cxSize uint16 + rxSize uint16 + txSize uint16 + + txHead uint8 + txFree uint16 + + rxHead uint8 + rxTail uint8 + rxFree uint16 + + rxCount *[descCDCACMRDCount]uint16 + rxIndex *[descCDCACMRDCount]uint16 + rxQueue *[descCDCACMRDCount + 1]uint16 +} + +// descCDCACM holds the configuration, endpoint, and transfer descriptors, along +// with the buffers and control states, for all of the CDC-ACM (single) device +// class configurations, ordered by configuration index (offset by -1). +var descCDCACM = [descCDCACMCount]descCDCACMClass{ + { + locstr: &descCDCACM0String, + device: &descCDCACM0Device, + qualif: &descCDCACM0Qualif, + config: &descCDCACM0Config, + + coding: &descCDCACM0Coding, + + qh: &descCDCACM0QH, + + cd: &descCDCACM0CD, + ad: &descCDCACM0AD, + rd: &descCDCACM0RD, + td: &descCDCACM0TD, + + cx: &descCDCACM0Cx, + rx: &descCDCACM0Rx, + tx: &descCDCACM0Tx, + dx: &descCDCACM0Dx, + + cxSize: descCDCACMStatusPacketSize, + rxSize: descCDCACMDataRxPacketSize, + txSize: descCDCACMDataTxPacketSize, + + rxCount: &descCDCACM0RDNum, + rxIndex: &descCDCACM0RDIdx, + rxQueue: &descCDCACM0RDQue, + }, +} diff --git a/src/machine/usb2/dci.go b/src/machine/usb2/hcd.go similarity index 85% rename from src/machine/usb2/dci.go rename to src/machine/usb2/hcd.go index 4ee63081d..dbbc43d06 100644 --- a/src/machine/usb2/dci.go +++ b/src/machine/usb2/hcd.go @@ -1,6 +1,6 @@ package usb2 -type dci interface { +type hcd interface { init() status enable(enable bool) status critical(enter bool) status diff --git a/src/machine/usb2/hci_mimxrt1062.go b/src/machine/usb2/hcd_mimxrt1062.go similarity index 78% rename from src/machine/usb2/hci_mimxrt1062.go rename to src/machine/usb2/hcd_mimxrt1062.go index 85f66ecc2..caf1cb9ab 100644 --- a/src/machine/usb2/hci_mimxrt1062.go +++ b/src/machine/usb2/hcd_mimxrt1062.go @@ -2,7 +2,7 @@ package usb2 -// Implementation of USB host controller interface (hci) for NXP iMXRT1062. +// Implementation of USB host controller driver (hcd) for NXP iMXRT1062. import ( "device/arm" @@ -11,18 +11,19 @@ import ( "runtime/volatile" ) -// hciCount defines the number of USB cores to configure for host mode. It is +// 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 hciCount = 0 +const hcdCount = 0 -// hciInterruptPriority defines the priority for all USB host interrupts. -const hciInterruptPriority = 3 +// hcdInterruptPriority defines the priority for all USB host interrupts. +const hcdInterruptPriority = 3 -// hostController implements USB host controller interface (hci). +// hostController implements USB host controller driver (hcd) interface. type hostController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - id int // hostControllerInstance index + core *core // Parent USB core this instance is attached to + port int // USB port index + class class // USB host class + id int // hostControllerInstance index bus *nxp.USB_Type phy *nxp.USBPHY_Type @@ -34,14 +35,14 @@ type hostController struct { // hostControllerInstance provides statically-allocated instances of each USB // host controller configured on this platform. -var hostControllerInstance [hciCount]hostController +var hostControllerInstance [hcdCount]hostController -// initHCI initializes and assigns a free host controller instance to the given +// 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 initHCI(port int) (hci, status) { - if 0 == hciCount { - return nil, statusInvalidArgument // must have defined host descriptors +func initHCD(port int, class class) (hcd, status) { + if 0 == hcdCount { + return nil, statusInvalid // must have defined host controllers } // Return the first instance whose assigned core is currently nil. for i := range hostControllerInstance { @@ -82,7 +83,7 @@ func (hc *hostController) init() status { func (hc *hostController) enable(enable bool) status { - hc.irq.SetPriority(hciInterruptPriority) + hc.irq.SetPriority(hcdInterruptPriority) hc.irq.Enable() return statusOK diff --git a/src/machine/usb2/hci.go b/src/machine/usb2/hci.go deleted file mode 100644 index 7997c2351..000000000 --- a/src/machine/usb2/hci.go +++ /dev/null @@ -1,9 +0,0 @@ -package usb2 - -type hci interface { - init() status - enable(enable bool) status - critical(enter bool) status - interrupt() - udelay(micros uint32) -} diff --git a/src/machine/usb2/uart.go b/src/machine/usb2/uart.go index 83bd9ffe0..2ea1b222b 100644 --- a/src/machine/usb2/uart.go +++ b/src/machine/usb2/uart.go @@ -23,29 +23,13 @@ type ( func (uart *UART) Configure(config UARTConfig) error { - if uart.port >= CoreCount || uart.port >= dciCount || - uart.port >= descCDCACMConfigCount { + if uart.port >= CoreCount || uart.port >= dcdCount { return ErrInvalidPort } - // use default configuration index (1-based index; 0=invalid) - // uart.config = configDeviceCDCACMConfigurationIndex - - // modify the global basic configuration struct configDeviceCDCACM for our USB - // port and configuration index. - // - // these settings are copied into the real CDC-ACM object, using interface - // deviceClassDriver, via initialization method (*deviceCDCACM).init(). - - // change baud rate from default, if provided - //if config.BaudRate != 0 { - // configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate = - // config.BaudRate - //} - // verify we have a free USB port and take ownership of it var st status - uart.core, st = initCore(uart.port, modeDevice) + uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1}) if !st.ok() { return ErrInvalidPort } diff --git a/src/machine/usb2/usb.go b/src/machine/usb2/usb.go index c450f86f8..c5e88b10b 100644 --- a/src/machine/usb2/usb.go +++ b/src/machine/usb2/usb.go @@ -2,12 +2,20 @@ package usb2 // Hardware abstraction for USB ports configured as either host or device. +import "unsafe" + +func init() { + if unsafe.Sizeof(uintptr(0)) > 4 { + panic("USB is only supported on 32-bit systems") + } +} + // core represents the core of a USB port configured as either host or device. type core struct { port int mode int - dc dci - hc hci + dc dcd + hc hcd } // Constant definitions for USB core operating modes. @@ -17,9 +25,32 @@ const ( modeHost = 2 ) +// 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 +} + +// Constant definitions for all host/device classes. +const ( + classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) +) + +// mode returns the USB core operating mode of the receiver class cl. +//go:inline +func (cl class) mode() int { + switch cl.id { + case classDeviceCDCACM: + return modeDevice + default: + return modeIdle + } +} + // CoreCount defines the total number of USB cores to configure in device or // host mode. -const CoreCount = dciCount + hciCount +const CoreCount = dcdCount + hcdCount // coreInstance provides statically-allocated instances of each USB core // configured on this platform. @@ -30,30 +61,32 @@ type status uint8 // Constant definitions for all status codes used within the package. const ( - statusOK status = iota // Success - statusBusy // Busy - statusRetry // Retry - statusInvalidArgument // Invalid argument + statusOK status = iota // Success + statusBusy // Busy + statusRetry // Retry + statusInvalid // Invalid argument ) +// ok returns true if and only if the receiver st equals statusOK. +//go:inline func (st status) ok() bool { return statusOK == st } // 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, mode int) (*core, status) { +func initCore(port int, class class) (*core, status) { - if port < 0 || port >= CoreCount { - return nil, statusInvalidArgument + if port < 0 || port >= CoreCount || 0 == class.config { + return nil, statusInvalid } if modeIdle != coreInstance[port].mode { return nil, statusBusy } - switch mode { + switch class.mode() { case modeDevice: // Allocate a free device controller and install interrupts - dc, st := initDCI(port) + dc, st := initDCD(port, class) if !st.ok() { return nil, st } @@ -62,7 +95,7 @@ func initCore(port, mode int) (*core, status) { return nil, st } coreInstance[port].port = port - coreInstance[port].mode = mode + coreInstance[port].mode = modeDevice coreInstance[port].dc = dc // Enable interrupts and enter runtime if st = dc.enable(true); !st.ok() { @@ -73,7 +106,7 @@ func initCore(port, mode int) (*core, status) { case modeHost: // Allocate a free host controller and install interrupts - hc, st := initHCI(port) + hc, st := initHCD(port, class) if !st.ok() { return nil, st } @@ -82,7 +115,7 @@ func initCore(port, mode int) (*core, status) { return nil, st } coreInstance[port].port = port - coreInstance[port].mode = mode + coreInstance[port].mode = modeHost coreInstance[port].hc = hc // Enable interrupts and enter runtime if st = hc.enable(true); !st.ok() { @@ -92,7 +125,7 @@ func initCore(port, mode int) (*core, status) { } default: - return nil, statusInvalidArgument + return nil, statusInvalid } return &coreInstance[port], statusOK diff --git a/src/machine/usb2/util.go b/src/machine/usb2/util.go index 4b78c9fb5..35a082a7a 100644 --- a/src/machine/usb2/util.go +++ b/src/machine/usb2/util.go @@ -192,3 +192,25 @@ func lsU8(u uint16) uint8 { return uint8(u) } 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) +} diff --git a/src/machine/usb2/util_arm.go b/src/machine/usb2/util_arm.go new file mode 100644 index 000000000..8f7b8a2a9 --- /dev/null +++ b/src/machine/usb2/util_arm.go @@ -0,0 +1,16 @@ +// +build arm + +package usb2 + +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`) + } +} From 733da1226d86d2c7d8075e58fef24b397f3a4079 Mon Sep 17 00:00:00 2001 From: ardnew Date: Tue, 20 Apr 2021 16:35:59 -0500 Subject: [PATCH 4/9] USB CDC-ACM UART Rx/Tx functioning for baseline target (Teensy 4.0/4.1) --- src/machine/board_teensy40.go | 5 +- src/machine/machine_mimxrt1062_usb.go | 32 ++- src/machine/usb2/dcd.go | 1 + src/machine/usb2/dcd_mimxrt1062.go | 393 ++++++++++++++++---------- src/machine/usb2/desc.go | 128 +++++---- src/machine/usb2/desc_mimxrt1062.go | 35 ++- src/machine/usb2/hcd.go | 1 + src/machine/usb2/hcd_mimxrt1062.go | 11 +- src/machine/usb2/uart.go | 62 +++- src/machine/usb2/usb.go | 25 ++ src/runtime/runtime_mimxrt1062.go | 7 +- 11 files changed, 455 insertions(+), 245 deletions(-) diff --git a/src/machine/board_teensy40.go b/src/machine/board_teensy40.go index 0488e3b72..18c965a03 100644 --- a/src/machine/board_teensy40.go +++ b/src/machine/board_teensy40.go @@ -105,9 +105,8 @@ func init() { var ( // USBCDC is a legacy class being retained here as temporary wrapper. // See godoc comments on type USBCDC struct definition for details. - USBCDC0 = USBCDC{ - port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0) - buff: NewRingBuffer(), + UART0 = USBCDC{ + port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0/4.1) } ) diff --git a/src/machine/machine_mimxrt1062_usb.go b/src/machine/machine_mimxrt1062_usb.go index 627cb2b0c..696572842 100644 --- a/src/machine/machine_mimxrt1062_usb.go +++ b/src/machine/machine_mimxrt1062_usb.go @@ -1,10 +1,5 @@ // +build mimxrt1062 -// Compatibility wrapper for legacy type USBCDC, which provides USB CDC-ACM -// device class emulation for serial UART communication. -// This functionality is being replaced by a platform-agnostic type usb.UART in -// package "machine/usb". - package machine import ( @@ -18,7 +13,6 @@ import ( // be removed and usb.UART should be used directly instead. type USBCDC struct { port uint8 - buff *RingBuffer uart usb2.UART } @@ -27,3 +21,29 @@ type USBCDC struct { func (cdc *USBCDC) Configure(config UARTConfig) { cdc.uart.Configure(usb2.UARTConfig{BaudRate: config.BaudRate}) } + +// Buffered returns the number of bytes currently stored in the RX buffer. +func (cdc USBCDC) Buffered() int { + return cdc.uart.Buffered() +} + +// ReadByte reads a single byte from the RX buffer. +// If there is no data in the buffer, returns an error. +func (cdc USBCDC) ReadByte() (byte, error) { + return cdc.uart.ReadByte() +} + +// Read from the RX buffer. +func (cdc USBCDC) Read(data []byte) (n int, err error) { + return cdc.uart.Read(data) +} + +// WriteByte writes a single byte of data to the UART interface. +func (cdc USBCDC) WriteByte(c byte) error { + return cdc.uart.WriteByte(c) +} + +// Write data to the UART. +func (cdc USBCDC) Write(data []byte) (n int, err error) { + return cdc.uart.Write(data) +} diff --git a/src/machine/usb2/dcd.go b/src/machine/usb2/dcd.go index 510a9e95d..2c37dddb4 100644 --- a/src/machine/usb2/dcd.go +++ b/src/machine/usb2/dcd.go @@ -3,6 +3,7 @@ package usb2 import "unsafe" type dcd interface { + class() class init() status enable(enable bool) status critical(enter bool) status diff --git a/src/machine/usb2/dcd_mimxrt1062.go b/src/machine/usb2/dcd_mimxrt1062.go index 372952a47..aacd2c322 100644 --- a/src/machine/usb2/dcd_mimxrt1062.go +++ b/src/machine/usb2/dcd_mimxrt1062.go @@ -22,10 +22,10 @@ const dcdInterruptPriority = 3 // deviceController implements USB device controller driver (dcd) interface. type deviceController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - class class // USB device class - id int // deviceControllerInstance index + core *core // Parent USB core this instance is attached to + port int // USB port index + cc class // USB device class + id int // deviceControllerInstance index bus *nxp.USB_Type phy *nxp.USBPHY_Type @@ -37,8 +37,6 @@ type deviceController struct { stat *dcdEndpoint // endpoint 0 Rx ("out" direction) ctrl *dcdEndpoint // endpoint 0 Tx ("in" direction) - acm *descCDCACMClass - timerInterrupt [2]func() controlNotify uint32 endpointNotify uint32 @@ -84,7 +82,7 @@ func initDCD(port int, class class) (dcd, status) { // Initialize device controller. deviceControllerInstance[i].core = &coreInstance[port] deviceControllerInstance[i].port = port - deviceControllerInstance[i].class = class + deviceControllerInstance[i].cc = class deviceControllerInstance[i].id = i switch port { case 0: @@ -105,17 +103,14 @@ func initDCD(port int, class class) (dcd, status) { //coreInstance[1].dc.interrupt() }) } - switch class.id { - case classDeviceCDCACM: - deviceControllerInstance[i].acm = &descCDCACM[class.config-1] - default: - } return &deviceControllerInstance[i], statusOK } } return nil, statusBusy // No free device controller instances available. } +func (dc *deviceController) class() class { return dc.cc } + func (dc *deviceController) init() status { // reset the controller dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) @@ -277,7 +272,7 @@ func (dc *deviceController) interrupt() { dc.bus.ENDPTFLUSH.Set(0xFFFFFFFF) // if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { // } - switch dc.class.id { + switch dc.cc.id { case classDeviceCDCACM: // TBD: reset CDC-ACM UART? default: @@ -431,21 +426,21 @@ func (dc *deviceController) control(setup dcdSetup) { // SET CONFIGURATION (0x09): case descRequestStandardSetConfiguration: - dc.class.config = int(setup.wValue) - if 0 == dc.class.config || dc.class.config > dcdCount { + dc.cc.config = int(setup.wValue) + if 0 == dc.cc.config || dc.cc.config > dcdCount { // Use default if invalid index received - dc.class.config = 1 + dc.cc.config = 1 } // Respond based on our device class configuration - switch dc.class.id { + switch dc.cc.id { // CDC-ACM (single) case classDeviceCDCACM: dc.bus.ENDPTCTRL2.Set(descCDCACMConfigAttrStatus) // Status Tx dc.bus.ENDPTCTRL3.Set(descCDCACMConfigAttrDataRx) // Bulk data Rx dc.bus.ENDPTCTRL4.Set(descCDCACMConfigAttrDataTx) // Bulk data Tx - dc.serialConfigure() + dc.uartConfigure() dc.controlReceive(dcdPointerNil, 0, false) default: @@ -478,7 +473,7 @@ func (dc *deviceController) control(setup dcdSetup) { // GET CONFIGURATION (0x08): case descRequestStandardGetConfiguration: - dc.controlReply[0] = uint8(dc.class.config) + dc.controlReply[0] = uint8(dc.cc.config) dc.controlTransmit( uintptr(unsafe.Pointer(&dc.controlReply[0])), 1, false) return @@ -585,7 +580,7 @@ func (dc *deviceController) control(setup dcdSetup) { case descCDCRequestSetLineCoding: // Respond based on our device class configuration - switch dc.class.id { + switch dc.cc.id { // CDC-ACM (single) case classDeviceCDCACM: @@ -593,7 +588,7 @@ func (dc *deviceController) control(setup dcdSetup) { if descCDCACMCodingSize == setup.wLength { dc.setup = setup dc.controlReceive( - uintptr(unsafe.Pointer(&descCDCACM[dc.class.config-1].cx[0])), + uintptr(unsafe.Pointer(&descCDCACM[dc.cc.config-1].cx[0])), descCDCACMCodingSize, true) return } @@ -606,7 +601,7 @@ func (dc *deviceController) control(setup dcdSetup) { case descCDCRequestSetControlLineState: // Respond based on our device class configuration - switch dc.class.id { + switch dc.cc.id { // CDC-ACM (single) case classDeviceCDCACM: @@ -616,9 +611,11 @@ func (dc *deviceController) control(setup dcdSetup) { // Control/status interface: case descCDCACMInterfaceCtrl: - acm := &descCDCACM[dc.class.config-1] - acm.cticks = ticks() - acm.rtsdtr = uint8(setup.wValue) + // acm := &descCDCACM[dc.cc.config-1] + // update our emulated UART terminal status + // acm.lineActive = ticks() + // acm.lineCoding.dtr = 0 != setup.wValue&0x01 + // acm.lineCoding.rts = 0 != setup.wValue&0x02 dc.controlReceive(dcdPointerNil, 0, false) return @@ -634,7 +631,7 @@ func (dc *deviceController) control(setup dcdSetup) { case descCDCRequestSendBreak: // Respond based on our device class configuration - switch dc.class.id { + switch dc.cc.id { // CDC-ACM (single) case classDeviceCDCACM: @@ -666,9 +663,9 @@ func (dc *deviceController) control(setup dcdSetup) { //go:inline func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { // control endpoint is device class-specific - switch dc.class.id { + switch dc.cc.id { case classDeviceCDCACM: - return descCDCACM[dc.class.config-1].cd, descCDCACM[dc.class.config-1].ad + return descCDCACM[dc.cc.config-1].cd, descCDCACM[dc.cc.config-1].ad default: return nil, nil } @@ -677,11 +674,11 @@ func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { func (dc *deviceController) controlDescriptor(setup dcdSetup) { // Respond based on our device class configuration - switch dc.class.id { + switch dc.cc.id { // CDC-ACM (single) case classDeviceCDCACM: - acm := &descCDCACM[dc.class.config-1] + acm := &descCDCACM[dc.cc.config-1] dxn := uint8(0) // Determine the type of descriptor being requested @@ -699,64 +696,61 @@ func (dc *deviceController) controlDescriptor(setup dcdSetup) { // String descriptor case descTypeString: + var sd []uint8 + if 0 == uint8(setup.wValue) { + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This (setup.wValue = 0x03[00]) is a + // request from the host to determine what that language is. Subsequent + // string requests will populate setup.wIndex with the language code + // returned here in this string descriptor. + sd = acm.locale[int(setup.wIndex)].descriptor[setup.wValue&0xFF][:] + } else { + // setup.wIndex now contains a language code, which we notified 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 setup.wIndex == acm.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(setup.wValue&0xFF) < len(acm.locale[code].descriptor) { + // Found language with a string defined at the requested index. + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. - var s []uint8 + // TODO: Add fields to deviceController and design an API that + // allows the user to define and provide these strings + // prior to deviceController initialization. + // For now, we just always use the descCommon* strings. + var s string + switch uint8(setup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + case 3: + s = descCommonSerialNumber + } - // Determine the string index requested - switch uint8(setup.wValue) { - - // Language - case 0: - if int(setup.wIndex) < len(acm.locstr) { - s = acm.locstr[setup.wIndex].index[0][:] - } - - // Manufacturer - case 1: - for i := range acm.locstr { - if acm.locstr[i].language == setup.wIndex { - s = acm.locstr[i].index[1][:] - // copy manufacturer string to uint8 buffer as UTF-16 - for n, c := range descManufacturer { - s[2+2*n] = uint8(c) - s[3+2*n] = 0 + // Copy string into string descriptor as UTF-16 + sd = acm.locale[code].descriptor[int(setup.wValue&0xFF)][:] + sd[0] = uint8(2 + 2*len(s)) + sd[1] = descTypeString + 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 } - break - } - } - - // Product - case 2: - for i := range acm.locstr { - if acm.locstr[i].language == setup.wIndex { - s = acm.locstr[i].index[2][:] - // copy product string to uint8 buffer as UTF-16 - for n, c := range descProduct { - s[2+2*n] = uint8(c) - s[3+2*n] = 0 - } - break - } - } - - // Serial number - case 3: - for i := range acm.locstr { - if acm.locstr[i].language == setup.wIndex { - s = acm.locstr[i].index[3][:] - // copy serial number string to uint8 buffer as UTF-16 - for n, c := range descSerialNumber { - s[2+2*n] = uint8(c) - s[3+2*n] = 0 - } - break } } } - - if nil != s && len(s) > 0 { - dxn = s[0] - _ = copy(acm.dx[:], s[:dxn]) + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(acm.dx[:], sd[:dxn]) } // Device qualification descriptor @@ -766,7 +760,10 @@ func (dc *deviceController) controlDescriptor(setup dcdSetup) { // Alternate configuration descriptor case descTypeOtherSpeedConfiguration: + // TODO + default: + // Unhandled descriptor type } if dxn > 0 { @@ -884,23 +881,19 @@ func (dc *deviceController) controlComplete() { case descCDCRequestSetLineCoding: // Respond based on our device class configuration - switch dc.class.id { + switch dc.cc.id { // CDC-ACM (single) case classDeviceCDCACM: + acm := &descCDCACM[dc.cc.config-1] // Determine interface destination of the notification switch dc.setup.wIndex { // Control/status interface: case descCDCACMInterfaceCtrl: - - _ = copy(descCDCACM[dc.class.config-1].coding[:], - // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize]) - descCDCACM[dc.class.config-1].cx[:]) - var coding descCDCACMLineCoding - if coding.parse(descCDCACM[dc.class.config-1].coding[:]) { - if 134 == coding.baud { + if acm.lineCoding.parse(acm.cx[:]) { + if 134 == acm.lineCoding.baud { dc.enableSofInterrupts(true, descCDCACMInterfaceCount) dc.rebootTimer = 80 } @@ -925,46 +918,6 @@ func (dc *deviceController) controlComplete() { default: // Unhandled request type } - - // // determine interface destination of the notification - // switch dc.setup.wIndex { - // // communication/control interface: - // case descCDCACMInterfaceCtrl: - // // switch on the type and recepient of the request - // switch dc.setup.bmRequestType & - // (descRequestTypeTypeMsk | descRequestTypeRecipientMsk) { - // // interface class request: - // case descRequestTypeRecipientInterface | descRequestTypeTypeClass: - // // identify which request was received - // switch dc.setup.bRequest { - // // CDC_SET_LINE_CODING: - // case descCDCRequestSetLineCoding: - // // respond according to our device class - // switch dc.class.id { - // // CDC-ACM (single) - // case classDeviceCDCACM: - // _ = copy(descCDCACM[dc.class.config-1].coding[:], - // // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize]) - // descCDCACM[dc.class.config-1].cx[:]) - // var coding descCDCACMLineCoding - // if coding.parse(descCDCACM[dc.class.config-1].coding[:]) { - // if 134 == coding.baud { - // dc.enableSofInterrupts(true, descCDCACMInterfaceCount) - // dc.rebootTimer = 80 - // } - // } - // default: - // // unhandled device class - // } - // default: - // // unhandled request - // } - // default: - // // unhandled request type or recepient - // } - // default: - // // unhandled interface - // } } // endpointQueueHead returns the queue head for the given endpoint address, @@ -972,9 +925,9 @@ func (dc *deviceController) controlComplete() { //go:inline func (dc *deviceController) endpointQueueHead(endpoint uint8) *dcdEndpoint { // endpoint queue head is device class-specific - switch dc.class.id { + switch dc.cc.id { case classDeviceCDCACM: - return &descCDCACM[dc.class.config-1].qh[endpointIndex(endpoint)] + return &descCDCACM[dc.cc.config-1].qh[endpointIndex(endpoint)] default: return nil } @@ -1144,8 +1097,8 @@ func (dc *deviceController) timerStop(timer int) { } } -func (dc *deviceController) serialConfigure() { - acm := &descCDCACM[dc.class.config-1] +func (dc *deviceController) uartConfigure() { + acm := &descCDCACM[dc.cc.config-1] switch dc.speed { case descDeviceSpeedHigh: acm.rxSize = descCDCACMDataRxHSPacketSize @@ -1162,22 +1115,33 @@ func (dc *deviceController) serialConfigure() { dc.endpointConfigureTx(descCDCACMEndpointStatus, acm.cxSize, false, nil) dc.endpointConfigureRx(descCDCACMEndpointDataRx, - acm.rxSize, false, dc.serialNotify) + acm.rxSize, false, dc.uartNotify) dc.endpointConfigureTx(descCDCACMEndpointDataTx, acm.txSize, true, nil) for i := range acm.rd { - dc.serialReceive(uint8(i)) + dc.uartReceive(uint8(i)) } - dc.timerConfigure(0, 75, dc.serialFlush) + dc.timerConfigure(0, descCDCACMTxSyncUs, dc.uartSync) } -func (dc *deviceController) serialNotify(transfer *dcdTransfer) { - acm := &descCDCACM[dc.class.config-1] +func (dc *deviceController) uartReceive(endpoint uint8) { + acm := &descCDCACM[dc.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + buf := &acm.rx[num*descCDCACMRxSize] + dc.irq.Disable() + dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) + nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) + dc.receive(descCDCACMEndpointDataRx, &acm.rd[num]) + dc.irq.Enable() +} + +func (dc *deviceController) uartNotify(transfer *dcdTransfer) { + acm := &descCDCACM[dc.cc.config-1] len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF) p := transfer.param if 0 == len { // zero-length packet (ZLP) - dc.serialReceive(uint8(p)) + dc.uartReceive(uint8(p)) } else { // data packet h := acm.rxHead @@ -1191,7 +1155,7 @@ func (dc *deviceController) serialNotify(transfer *dcdTransfer) { acm.rx[p*descCDCACMRxSize:uint16(p)*descCDCACMRxSize+len]) acm.rxCount[q] = n + len acm.rxFree += len - dc.serialReceive(uint8(p)) + dc.uartReceive(uint8(p)) return } } @@ -1208,23 +1172,148 @@ func (dc *deviceController) serialNotify(transfer *dcdTransfer) { } } -func (dc *deviceController) serialReceive(endpoint uint8) { - ivm := arm.DisableInterrupts() - num := uint16(endpoint) & descEndptAddrNumberMsk - acm := &descCDCACM[dc.class.config-1] - buf := &acm.rx[num*descCDCACMRxSize] - dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) - nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) - dc.receive(descCDCACMEndpointDataRx, &acm.rd[num]) - arm.EnableInterrupts(ivm) +// uartFlush discards all buffered input (Rx) data. +func (dc *deviceController) uartFlush() { + acm := &descCDCACM[dc.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] + dc.uartReceive(uint8(i)) + acm.rxTail = tail + } } -func (dc *deviceController) serialFlush() { +func (dc *deviceController) uartAvailable() int { + return int(descCDCACM[dc.cc.config-1].rxFree) +} + +func (dc *deviceController) uartPeek() (uint8, bool) { + acm := &descCDCACM[dc.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 (dc *deviceController) uartReadByte() (uint8, bool) { + b := []uint8{0} + ok := dc.uartRead(b) > 0 + return b[0], ok +} + +func (dc *deviceController) uartRead(data []uint8) int { + acm := &descCDCACM[dc.cc.config-1] + read := uint16(0) + size := uint16(len(data)) + tail := acm.rxTail + dest := uint16(0) + dc.irq.Disable() + 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 + dc.uartReceive(uint8(i)) + } + } + dc.irq.Enable() + return int(read) +} + +func (dc *deviceController) uartWriteByte(c uint8) bool { + return 1 == dc.uartWrite([]uint8{c}) +} + +func (dc *deviceController) uartWrite(data []uint8) int { + acm := &descCDCACM[dc.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] + dc.transferPrepare(xfer, tx, descCDCACMTxSize, 0) + nxp.FlushDeleteDcache(uintptr(unsafe.Pointer(tx)), descCDCACMTxSize) + dc.transmit(descCDCACMEndpointDataTx, xfer) + acm.txHead += 1 + if acm.txHead >= descCDCACMTDCount { + acm.txHead = 0 + } + size -= int(acm.txFree) + sent += int(acm.txFree) + acm.txFree = 0 + dc.timerStop(0) + } else { + _ = copy(buff, data[:size]) + acm.txFree -= uint16(size) + sent += size + size = 0 + dc.timerOneShot(0) + } + } + return sent +} + +func (dc *deviceController) uartSync() { const autoFlushTx = true if !autoFlushTx { return } - acm := &descCDCACM[dc.class.config-1] + acm := &descCDCACM[dc.cc.config-1] if 0 == acm.txFree { return } diff --git a/src/machine/usb2/desc.go b/src/machine/usb2/desc.go index 4642a76f2..1b0858f70 100644 --- a/src/machine/usb2/desc.go +++ b/src/machine/usb2/desc.go @@ -4,12 +4,6 @@ const descUSBSpecVersion = uint16(0x0200) // USB 2.0 const descLanguageEnglish = uint16(0x0409) -type descIndexStrings [4][64]uint8 -type descLocalStrings struct { - language uint16 - index descIndexStrings // UTF-16, 32-character maximum length -} - // USB constants defined per specification. const ( @@ -335,55 +329,27 @@ const ( descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync ) -// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., -// configuration index 1. -var descCDCACM0String = [descCDCACMLanguageCount]descLocalStrings{ - { - language: descLanguageEnglish, - index: descIndexStrings{ - { // 0: language string - 4, - descTypeString, - lsU8(descLanguageEnglish), - msU8(descLanguageEnglish), - }, - { // 1: manufacturer - uint8(2 + 2*len(descManufacturer)), - descTypeString, - }, - { // 2: product - uint8(2 + 2*len(descProduct)), - descTypeString, - }, - { // 3: serial number - uint8(2 + 2*len(descSerialNumber)), - descTypeString, - }, - }, - }, -} - // descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., // configuration index 1. var descCDCACM0Device = [descLengthDevice]uint8{ - descLengthDevice, // Size of this descriptor in bytes - descTypeDevice, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - descCDCTypeComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) - msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) - lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) - msU8(descProductID), // Product ID (high) (assigned by the manufacturer) - lsU8(descReleaseID), // Device release number in BCD (low) - msU8(descReleaseID), // 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 + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + descCDCTypeComm, // Class code (assigned by the USB-IF). + descCDCSubNone, // Subclass code (assigned by the USB-IF). + descCDCProtoNone, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descCommonReleaseID), // Device release number in BCD (low) + msU8(descCommonReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descCDCACMCount, // Number of possible configurations } // descCDCACM0Qualif holds the default device qualification descriptor for @@ -497,24 +463,24 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{ // descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. const descCDCACMCodingSize = 7 -// descCDCACM0Coding holds the default UART line coding for CDC-ACM[0], i.e., -// configuration index 1. -var descCDCACM0Coding [descCDCACMCodingSize]uint8 +// descCDCACM0LineCoding holds the default UART line coding for CDC-ACM[0], +// i.e., configuration index 1. +var descCDCACM0LineCoding descCDCACMLineCoding type descCDCACMLineCoding struct { baud uint32 stopBits uint8 parity uint8 numBits uint8 - dtr bool - rts bool + rtsdtr uint8 } func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool { if len(buffer) < descCDCACMCodingSize { return false } - lc.baud = packU32(buffer) + _ = copy(buffer[:], buffer) + lc.baud = packU32(buffer[:]) lc.stopBits = buffer[4] if 0 == lc.stopBits { lc.stopBits = 1 @@ -523,3 +489,49 @@ func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool { lc.numBits = buffer[6] return true } + +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. +) + +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 + } +) + +// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{ + { // US English string descriptors + language: descLanguageEnglish, + descriptor: descStringIndex{ + { // Language (index 0) + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + // Actual string descriptors (index > 0) are copied into here at runtime! + }, + }, +} diff --git a/src/machine/usb2/desc_mimxrt1062.go b/src/machine/usb2/desc_mimxrt1062.go index 985cc68ba..91b1a8935 100644 --- a/src/machine/usb2/desc_mimxrt1062.go +++ b/src/machine/usb2/desc_mimxrt1062.go @@ -7,13 +7,14 @@ const descCPUFrequencyHz = 600000000 // General USB device identification constants. const ( - descVendorID = 0x16C0 - descProductID = 0x0483 - descReleaseID = 0x0101 + descCommonVendorID = 0x16C0 + descCommonProductID = 0x0483 + descCommonReleaseID = 0x0101 // BCD (1.1) - descManufacturer = "NXP Semiconductors" - descProduct = "TinyGo USB" - descSerialNumber = "0000000000" + descCommonLanguage = descLanguageEnglish + descCommonManufacturer = "NXP Semiconductors" + descCommonProduct = "TinyGo USB" + descCommonSerialNumber = "1" ) // Constants for USB CDC-ACM device classes. @@ -31,6 +32,9 @@ const ( descCDCACMMaxPower = 50 // 100 mA + descCDCACMTxTimeoutMs = 120 // millisec + descCDCACMTxSyncUs = 75 // microsec + descCDCACMStatusPacketSize = 16 descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed @@ -117,14 +121,13 @@ var descCDCACM0RDIdx [descCDCACMRDCount]uint16 var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 type descCDCACMClass struct { - locstr *[descCDCACMLanguageCount]descLocalStrings // string descriptors - device *[descLengthDevice]uint8 // device descriptor - qualif *[descLengthQualification]uint8 // device qualification descriptor - config *[descCDCACMConfigSize]uint8 // configuration descriptor + locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descCDCACMConfigSize]uint8 // configuration descriptor - coding *[descCDCACMCodingSize]uint8 // UART line coding - cticks int64 - rtsdtr uint8 + lineCoding *descCDCACMLineCoding // UART line coding active state + // lineActive int64 // time since last UART DTR/RTS qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads @@ -144,6 +147,7 @@ type descCDCACMClass struct { txHead uint8 txFree uint16 + txPrev bool rxHead uint8 rxTail uint8 @@ -157,14 +161,15 @@ type descCDCACMClass struct { // descCDCACM holds the configuration, endpoint, and transfer descriptors, along // with the buffers and control states, for all of the CDC-ACM (single) device // class configurations, ordered by configuration index (offset by -1). +//go:align 32 var descCDCACM = [descCDCACMCount]descCDCACMClass{ { - locstr: &descCDCACM0String, + locale: &descCDCACM0String, device: &descCDCACM0Device, qualif: &descCDCACM0Qualif, config: &descCDCACM0Config, - coding: &descCDCACM0Coding, + lineCoding: &descCDCACM0LineCoding, qh: &descCDCACM0QH, diff --git a/src/machine/usb2/hcd.go b/src/machine/usb2/hcd.go index dbbc43d06..21dc170b2 100644 --- a/src/machine/usb2/hcd.go +++ b/src/machine/usb2/hcd.go @@ -1,6 +1,7 @@ package usb2 type hcd interface { + class() class init() status enable(enable bool) status critical(enter bool) status diff --git a/src/machine/usb2/hcd_mimxrt1062.go b/src/machine/usb2/hcd_mimxrt1062.go index caf1cb9ab..7d39fa78a 100644 --- a/src/machine/usb2/hcd_mimxrt1062.go +++ b/src/machine/usb2/hcd_mimxrt1062.go @@ -20,10 +20,10 @@ const hcdInterruptPriority = 3 // hostController implements USB host controller driver (hcd) interface. type hostController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - class class // USB host class - id int // hostControllerInstance index + core *core // Parent USB core this instance is attached to + port int // USB port index + cc class // USB host class + id int // hostControllerInstance index bus *nxp.USB_Type phy *nxp.USBPHY_Type @@ -50,6 +50,7 @@ func initHCD(port int, class class) (hcd, status) { // Initialize host controller. hostControllerInstance[i].core = &coreInstance[port] hostControllerInstance[i].port = port + hostControllerInstance[i].cc = class hostControllerInstance[i].id = i switch port { case 0: @@ -76,6 +77,8 @@ func initHCD(port int, class class) (hcd, status) { return nil, statusBusy // No free host controller instances available. } +func (hc *hostController) class() class { return hc.cc } + func (hc *hostController) init() status { return statusOK diff --git a/src/machine/usb2/uart.go b/src/machine/usb2/uart.go index 2ea1b222b..a5e32ff0e 100644 --- a/src/machine/usb2/uart.go +++ b/src/machine/usb2/uart.go @@ -5,7 +5,10 @@ import ( ) var ( - ErrInvalidPort = errors.New("invalid USB port") + 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") ) type ( @@ -24,14 +27,67 @@ type ( func (uart *UART) Configure(config UARTConfig) error { if uart.port >= CoreCount || uart.port >= dcdCount { - return ErrInvalidPort + return ErrUARTInvalidPort } // 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 ErrInvalidPort + return ErrUARTInvalidPort } return nil } + +// Buffered returns the number of bytes currently stored in the RX buffer. +func (uart UART) Buffered() int { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0 + } + return 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) { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0, ErrUARTInvalidCore + } + n, ok := 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) { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0, ErrUARTInvalidCore + } + return dc.uartRead(data), nil +} + +// WriteByte writes a single byte of data to the UART interface. +func (uart UART) WriteByte(c byte) error { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return ErrUARTInvalidCore + } + if !dc.uartWriteByte(c) { + return ErrUARTWriteFailed + } + return nil +} + +// Write data to the UART. +func (uart UART) Write(data []byte) (n int, err error) { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0, ErrUARTInvalidCore + } + return dc.uartWrite(data), nil +} diff --git a/src/machine/usb2/usb.go b/src/machine/usb2/usb.go index c5e88b10b..e78bdbd04 100644 --- a/src/machine/usb2/usb.go +++ b/src/machine/usb2/usb.go @@ -48,6 +48,13 @@ func (cl class) mode() int { } } +// equals returns true if and only if all fields of the given class are equal to +// those of the receiver cl. +//go:inline +func (cl class) equals(class class) bool { + return cl.id == class.id && cl.config == class.config +} + // CoreCount defines the total number of USB cores to configure in device or // host mode. const CoreCount = dcdCount + hcdCount @@ -79,7 +86,25 @@ func initCore(port int, class class) (*core, status) { 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. + // For instance, this will allow 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 } diff --git a/src/runtime/runtime_mimxrt1062.go b/src/runtime/runtime_mimxrt1062.go index 3831ce146..490627e40 100644 --- a/src/runtime/runtime_mimxrt1062.go +++ b/src/runtime/runtime_mimxrt1062.go @@ -125,13 +125,12 @@ func initUART() { } func initUSB() { - machine.USBCDC0.Configure(machine.UARTConfig{}) + machine.UART0.Configure(machine.UARTConfig{}) } func putchar(c byte) { - // ** TESTING: print byte to both serial UART interfaces ** - //machine.USBCDC0.WriteByte(c) // print to USB UART - machine.UART1.WriteByte(c) // print to hardware UART + machine.UART0.WriteByte(c) // print to USB UART + // machine.UART1.WriteByte(c) // print to hardware UART } func abort() { From eb705f0d4fac295e6126a472849075048b4410fa Mon Sep 17 00:00:00 2001 From: ardnew Date: Tue, 20 Apr 2021 21:16:06 -0500 Subject: [PATCH 5/9] rename package machine/usb2 to machine/usb --- src/machine/machine_mimxrt1062_usb.go | 6 +- src/machine/usb/config.go | 25 - src/machine/usb/config_mimxrt1062.go | 233 ---- src/machine/{usb2 => usb}/dcd.go | 2 +- src/machine/{usb2 => usb}/dcd_mimxrt1062.go | 2 +- src/machine/{usb2 => usb}/desc.go | 2 +- src/machine/{usb2 => usb}/desc_mimxrt1062.go | 2 +- src/machine/usb/descriptor.go | 162 --- src/machine/usb/device.go | 1066 ----------------- src/machine/usb/device_cdc.go | 135 --- src/machine/usb/device_cdc_acm.go | 690 ----------- src/machine/usb/device_controller.go | 288 ----- .../usb/device_controller_mimxrt1062.go | 709 ----------- src/machine/{usb2 => usb}/hcd.go | 2 +- src/machine/{usb2 => usb}/hcd_mimxrt1062.go | 2 +- src/machine/usb/host.go | 25 - src/machine/usb/host_controller.go | 13 - src/machine/usb/spec.go | 167 --- src/machine/usb/uart.go | 389 +----- src/machine/usb/usb.go | 535 +++------ src/machine/{usb2 => usb}/util.go | 2 +- src/machine/{usb2 => usb}/util_arm.go | 2 +- src/machine/usb2/uart.go | 93 -- src/machine/usb2/usb.go | 157 --- 24 files changed, 207 insertions(+), 4502 deletions(-) delete mode 100644 src/machine/usb/config.go delete mode 100644 src/machine/usb/config_mimxrt1062.go rename src/machine/{usb2 => usb}/dcd.go (99%) rename src/machine/{usb2 => usb}/dcd_mimxrt1062.go (99%) rename src/machine/{usb2 => usb}/desc.go (99%) rename src/machine/{usb2 => usb}/desc_mimxrt1062.go (99%) delete mode 100644 src/machine/usb/descriptor.go delete mode 100644 src/machine/usb/device.go delete mode 100644 src/machine/usb/device_cdc.go delete mode 100644 src/machine/usb/device_cdc_acm.go delete mode 100644 src/machine/usb/device_controller.go delete mode 100644 src/machine/usb/device_controller_mimxrt1062.go rename src/machine/{usb2 => usb}/hcd.go (91%) rename src/machine/{usb2 => usb}/hcd_mimxrt1062.go (99%) delete mode 100644 src/machine/usb/host.go delete mode 100644 src/machine/usb/host_controller.go delete mode 100644 src/machine/usb/spec.go rename src/machine/{usb2 => usb}/util.go (99%) rename src/machine/{usb2 => usb}/util_arm.go (96%) delete mode 100644 src/machine/usb2/uart.go delete mode 100644 src/machine/usb2/usb.go diff --git a/src/machine/machine_mimxrt1062_usb.go b/src/machine/machine_mimxrt1062_usb.go index 696572842..22857351b 100644 --- a/src/machine/machine_mimxrt1062_usb.go +++ b/src/machine/machine_mimxrt1062_usb.go @@ -3,7 +3,7 @@ package machine import ( - "machine/usb2" + "machine/usb" ) // USBCDC is the legacy TinyGo type used to implement USB CDC-ACM device class @@ -13,13 +13,13 @@ import ( // be removed and usb.UART should be used directly instead. type USBCDC struct { port uint8 - uart usb2.UART + uart usb.UART } // Configure the embedded usb.UART with our receiver's settings and the given // UART configuration. This provides compatibility with machine.UART. func (cdc *USBCDC) Configure(config UARTConfig) { - cdc.uart.Configure(usb2.UARTConfig{BaudRate: config.BaudRate}) + cdc.uart.Configure(usb.UARTConfig{BaudRate: config.BaudRate}) } // Buffered returns the number of bytes currently stored in the RX buffer. diff --git a/src/machine/usb/config.go b/src/machine/usb/config.go deleted file mode 100644 index 7663029a3..000000000 --- a/src/machine/usb/config.go +++ /dev/null @@ -1,25 +0,0 @@ -package usb - -// The following constants must be defined for USB 2.0 host/device support. -// -// However, some or all of these constants may be unused in the core driver -// code, depending on which components are allocated by the USB driver. -// -// These values are __PLATFORM-AGNOSTIC__, and they serve two primary roles: -// 1. Aggregate and export values derived from platform-specific constants. -// 2. Configure core attributes defined by the USB 2.0 specification. -// -const ( - // ConfigPortCount defines the number of USB cores supported on this platform. - ConfigPortCount = configDeviceCount + configHostCount -) - -// ConfigDeviceDescriptor defines the fields used to populate host-enumerated -// device descriptors. -type ConfigDeviceDescriptor struct { - Manufacturer string - Product string - VID uint16 - PID uint16 - BCD uint16 -} diff --git a/src/machine/usb/config_mimxrt1062.go b/src/machine/usb/config_mimxrt1062.go deleted file mode 100644 index b14464e1b..000000000 --- a/src/machine/usb/config_mimxrt1062.go +++ /dev/null @@ -1,233 +0,0 @@ -// +build mimxrt1062 - -package usb - -const configCPUFrequencyHz = 600000000 - -// The following constants must be defined for USB 2.0 host/device support. -// -// However, some or all of these constants may be unused in the core driver -// code, depending on which components are allocated by the USB driver. -// -// These values are __PLATFORM-SPECIFIC__, and they serve two primary roles: -// 1. Determine which components to allocate from the USB driver. -// 2. Configure driver attributes NOT defined by the USB 2.0 specification. -// -const ( - // configDeviceCount defines the number of USB device-mode ports available. - configDeviceCount = configDeviceCDCACMCount - - // configHostCount defines the number of USB host-mode ports available. - configHostCount = 0 - - // configInterruptQueueSize defines the number of interrupts to retain in the - // queue of runtime processes. - configInterruptQueueSize = 8 - - // configDeviceBufferSize defines the size of the send and receive data - // endpoint buffers. - configDeviceBufferSize = 512 - - // configDeviceMaxEndpoints defines the maximum number of endpoints supported. - configDeviceMaxEndpoints = 4 - - // configDeviceControllerMaxPacketSize defines the maximum packet size for - // communication with an endpoint. The maximum per USB 2.0 spec is 64 bytes, - // although a platform may restrict this to something lower if needed. - configDeviceControllerMaxPacketSize = 64 - - // configDeviceMaxPower defines the maximum power consumption of the USB - // device when fully-operational, expressed in 2 mA units. - configDeviceMaxPower = 50 // 100 mA - - // configDeviceSelfPowered defines whether the device is self-powered (1) or - // not (0). - configDeviceSelfPowered = 1 - - // configDeviceRemoteWakeup defines whether the device supports remote-wakeup - // (1) or not (0). - configDeviceRemoteWakeup = 0 // (NOT YET SUPPORTED) - - // configDeviceControllerMaxDTD defines the maximum number of DTD supported. - configDeviceControllerMaxDTD = 16 - - // configDeviceControllerQHAlign defines the memory alignment of QH buffer. - // Ensure this agrees with the go:align pragma on deviceControllerQHBuffer. - configDeviceControllerQHAlign = 2048 - - // configDeviceControllerDTDAlign defines the memory alignment of DTD buffer. - // Ensure this agrees with the go:align pragma on deviceControllerDTDBuffer. - configDeviceControllerDTDAlign = 32 -) - -// If USB CDC-ACM device support is required, the following constants must be -// defined. -const ( - // configDeviceCDCACMCount defines the number of USB CDC-ACM interfaces - // to initialize; must be less-than or equal to configDeviceCount. - configDeviceCDCACMCount = 1 - - // configDeviceCDCACMConfigurationCount defines the number of configurations - // required for each CDC-ACM device port. - configDeviceCDCACMConfigurationCount = 1 - - // configDeviceCDCACMConfigurationIndex defines the default configuration - // index used to initialize CDC-ACM device class configurations. This is a - // 1-based index into the list of CDC-ACM configurations (which are 0-based - // slices, and thus this index is offset by -1). A configuration index of 0 - // represents the invalid configuration index. - configDeviceCDCACMConfigurationIndex = 1 - - // configDeviceCDCACMInterfaceCount defines the number of interfaces required - // for each CDC-ACM configuration. - configDeviceCDCACMInterfaceCount = 2 - - // configDeviceCDCACMInterruptInPacketSize defines the packet size of CDC-ACM - // communication interface's interrupt input endpoint. - configDeviceCDCACMInterruptInPacketSize = configDeviceCDCACMFSInterruptInPacketSize // (full-speed) - - // configDeviceCDCACMBulkInPacketSize defines the packet size of CDC-ACM data - // interface's bulk input endpoint. - configDeviceCDCACMBulkInPacketSize = configDeviceCDCACMFSBulkInPacketSize // (full-speed) - - // configDeviceCDCACMBulkOutPacketSize defines the packet size of CDC-ACM data - // interface's bulk output endpoint. - configDeviceCDCACMBulkOutPacketSize = configDeviceCDCACMFSBulkOutPacketSize // (full-speed) - - // configDeviceCDCACMInterruptInInterval defines the interval of CDC-ACM - // communication interface's interrupt input endpoint. - configDeviceCDCACMInterruptInInterval = configDeviceCDCACMFSInterruptInInterval // (full-speed) -) - -// If USB CDC-ACM device support is required, the following arrays must be -// initialized with each device's CDC-ACM configuration. -var ( - // configDeviceCDCACM defines the configuration specific to CDC-ACM devices - // including the properties of endpoints required by the device class driver, - // as well as the serial UART line coding properties. - configDeviceCDCACM = [configDeviceCDCACMCount][configDeviceCDCACMConfigurationCount]deviceCDCACMConfig{ - {{ // USB CDC-ACM [0] - interfaceSpeed: specSpeedFull, // USB full-speed (12 Mbit/s) - // Serial line configuration - lineCodingSize: 7, // Size of line-coding message - lineCodingBaudRate: 115200, // Data terminal rate - lineCodingCharFormat: 0, // Character format - lineCodingParityType: 0, // Parity type - lineCodingDataBits: 8, // Data word size - // Communication/control interface - commInterfaceIndex: descriptorInterfaceCDCACMComm, // communication/control interface index - commInterruptInEndpoint: descriptorEndpointCDCACMCommInterruptIn, // interrupt input endpoint index (address) - commInterruptInPacketSize: configDeviceCDCACMInterruptInPacketSize, - commInterruptInInterval: configDeviceCDCACMInterruptInInterval, - // Data interface - dataInterfaceIndex: descriptorInterfaceCDCACMData, // data interface index - dataBulkInEndpoint: descriptorEndpointCDCACMDataBulkIn, // bulk input endpoint index (address) - dataBulkInPacketSize: configDeviceCDCACMBulkInPacketSize, - dataBulkOutEndpoint: descriptorEndpointCDCACMDataBulkOut, // bulk output endpoint index (address) - dataBulkOutPacketSize: configDeviceCDCACMBulkOutPacketSize, - }}, - } - - // configDeviceCDCACMDescriptor defines the generic USB 2.0 descriptors used - // to describe each CDC-ACM device enabled. - // - // Note that the actual descriptor byte slices need not (should not) be - // defined here. Instead, a reusable global slice should be declared as a - // standalone type and not a member of a composite type; this allows both - // reusability and better control over the memory layout/alignment for USB - // PHYs that may require constraints of this sort. The fields of struct - // deviceDescriptor are therefore all pointers to byte slices so that they - // can be initialized with references to the aforementioned global arrays. - configDeviceCDCACMDescriptor = [configDeviceCDCACMCount]deviceDescriptor{ - { // USB CDC-ACM [0] - pDevice: &descriptorDeviceCDCACM, - pConfig: &descriptorConfigurationCDCACM, - language: []deviceDescriptorLanguage{ - { - pString: []deviceDescriptorString{ - &descriptorStringCDCACMLanguage, - &configDescriptorStringCDCACMManufacturer, - &configDescriptorStringCDCACMProduct, - }, - ident: 0x0409, - }, - }, - }, - } - - // default device descriptor info (little-endian byte order). - configDescriptorDeviceCDCACMVID = []uint8{0xC9, 0x1F} // USB Vendor ID (0x1FC9) - configDescriptorDeviceCDCACMPID = []uint8{0x94, 0x00} // USB Product ID (0x0094) - configDescriptorDeviceCDCACMBCD = []uint8{0x01, 0x01} // USB Device Version (0x0101) - - configDescriptorStringCDCACMManufacturer = []uint8{ - 2 + 2*18, specDescriptorTypeString, - 'N', 0, - 'X', 0, - 'P', 0, - ' ', 0, - 'S', 0, - 'e', 0, - 'm', 0, - 'i', 0, - 'c', 0, - 'o', 0, - 'n', 0, - 'd', 0, - 'u', 0, - 'c', 0, - 't', 0, - 'o', 0, - 'r', 0, - 's', 0, - } - - configDescriptorStringCDCACMProduct = []uint8{ - 2 + 2*20, specDescriptorTypeString, - 'T', 0, - 'i', 0, - 'n', 0, - 'y', 0, - 'G', 0, - 'o', 0, - ' ', 0, - 'U', 0, - 'S', 0, - 'B', 0, - ' ', 0, - '(', 0, - 'C', 0, - 'D', 0, - 'C', 0, - '-', 0, - 'A', 0, - 'C', 0, - 'M', 0, - ')', 0, - } -) - -// The following additional constants are not required by the USB driver but are -// used by the usb package on this platform. -const ( - - // configInterruptPriority defines the priority number for USB interrupts. - configInterruptPriority = 3 - - // configDeviceControllerMaxPrimeAttempts defines the maximum number of - // attempts to prime an endpoint for transfer. If attempts exceeds this - // value, then the endpoint status has been reset. - configDeviceControllerMaxPrimeAttempts = 10000000 - - // USB CDC-ACM high-speed (480 Mbit/s) packet size - configDeviceCDCACMHSInterruptInPacketSize = 16 - configDeviceCDCACMHSInterruptInInterval = 7 // 2^(7-1)/8 = 8ms - configDeviceCDCACMHSBulkInPacketSize = 512 - configDeviceCDCACMHSBulkOutPacketSize = 512 - - // USB CDC-ACM full-speed (12 Mbit/s) packet size - configDeviceCDCACMFSInterruptInPacketSize = 16 - configDeviceCDCACMFSInterruptInInterval = 8 // 2^(8-1)/8 = 16ms - configDeviceCDCACMFSBulkInPacketSize = 64 - configDeviceCDCACMFSBulkOutPacketSize = 64 -) diff --git a/src/machine/usb2/dcd.go b/src/machine/usb/dcd.go similarity index 99% rename from src/machine/usb2/dcd.go rename to src/machine/usb/dcd.go index 2c37dddb4..d6527f9b0 100644 --- a/src/machine/usb2/dcd.go +++ b/src/machine/usb/dcd.go @@ -1,4 +1,4 @@ -package usb2 +package usb import "unsafe" diff --git a/src/machine/usb2/dcd_mimxrt1062.go b/src/machine/usb/dcd_mimxrt1062.go similarity index 99% rename from src/machine/usb2/dcd_mimxrt1062.go rename to src/machine/usb/dcd_mimxrt1062.go index aacd2c322..38aa4ddb0 100644 --- a/src/machine/usb2/dcd_mimxrt1062.go +++ b/src/machine/usb/dcd_mimxrt1062.go @@ -1,6 +1,6 @@ // +build mimxrt1062 -package usb2 +package usb // Implementation of USB device controller interface (dcd) for NXP iMXRT1062. diff --git a/src/machine/usb2/desc.go b/src/machine/usb/desc.go similarity index 99% rename from src/machine/usb2/desc.go rename to src/machine/usb/desc.go index 1b0858f70..92bc22375 100644 --- a/src/machine/usb2/desc.go +++ b/src/machine/usb/desc.go @@ -1,4 +1,4 @@ -package usb2 +package usb const descUSBSpecVersion = uint16(0x0200) // USB 2.0 diff --git a/src/machine/usb2/desc_mimxrt1062.go b/src/machine/usb/desc_mimxrt1062.go similarity index 99% rename from src/machine/usb2/desc_mimxrt1062.go rename to src/machine/usb/desc_mimxrt1062.go index 91b1a8935..1b0108888 100644 --- a/src/machine/usb2/desc_mimxrt1062.go +++ b/src/machine/usb/desc_mimxrt1062.go @@ -1,6 +1,6 @@ // +build mimxrt1062 -package usb2 +package usb // descCPUFrequencyHz defines the target CPU frequency (Hz). const descCPUFrequencyHz = 600000000 diff --git a/src/machine/usb/descriptor.go b/src/machine/usb/descriptor.go deleted file mode 100644 index 51f0ad82f..000000000 --- a/src/machine/usb/descriptor.go +++ /dev/null @@ -1,162 +0,0 @@ -package usb - -// USB specification version number (BCD) -var descriptorUSBSpecification = []uint8{0x00, 0x02} // USB 2.0 - -// USB CDC-ACM descriptor constants -const ( - // interface indices - descriptorInterfaceCDCACMComm = 0 // communication/control interface - descriptorInterfaceCDCACMData = 1 // data interface - - descriptorEndpointCDCACMCommInterruptIn = 1 // interrupt endpoint (input) - descriptorEndpointCDCACMDataBulkIn = 2 // data endpoint (input) - descriptorEndpointCDCACMDataBulkOut = 3 // data endpoint (output) - - descriptorConfigurationCDCACMHeaderFuncSize = 5 - descriptorConfigurationCDCACMCallManageSize = 5 - descriptorConfigurationCDCACMAbstractSize = 4 - descriptorConfigurationCDCACMUnionFuncSize = 5 - - descriptorConfigurationCDCACMSize = uint16( - specDescriptorLengthConfigure + // configuration - specDescriptorLengthInterface + // communication/control interface - descriptorConfigurationCDCACMHeaderFuncSize + // CDC header - descriptorConfigurationCDCACMCallManageSize + // CDC call management - descriptorConfigurationCDCACMAbstractSize + // CDC abstract control - descriptorConfigurationCDCACMUnionFuncSize + // CDC union - specDescriptorLengthEndpoint + // communication/control input endpoint - specDescriptorLengthInterface + // data interface - specDescriptorLengthEndpoint + // data input endpoint - specDescriptorLengthEndpoint) // data output endpoint - - descriptorConfigurationCDCACMAttributes = (specDescriptorConfigureAttributeD7Msk) | // Bit 7: reserved (1) - (configDeviceSelfPowered << specDescriptorConfigureAttributeSelfPoweredPos) | // Bit 6: self-powered - (configDeviceRemoteWakeup << specDescriptorConfigureAttributeRemoteWakeupPos) | // Bit 5: remote wakeup - 0 // Bits 0-4: reserved (0) -) - -// USB 2.0 CDC-ACM descriptors -var ( - // descriptorDeviceCDCACM is the default device descriptor for CDC-ACM - // devices. - descriptorDeviceCDCACM = []uint8{ - specDescriptorLengthDevice, // Size of this descriptor in bytes - specDescriptorTypeDevice, // DEVICE Descriptor Type - descriptorUSBSpecification[0], // USB Specification Release Number in BCD (low) - descriptorUSBSpecification[1], // USB Specification Release Number in BCD (high) - uint8(deviceClassCDC), // Class code (assigned by the USB-IF). - 0, // Subclass code (assigned by the USB-IF). - deviceCDCNoClassSpecificProtocol, // Protocol code (assigned by the USB-IF). - configDeviceControllerMaxPacketSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - configDescriptorDeviceCDCACMVID[0], // Vendor ID (low) (assigned by the USB-IF) - configDescriptorDeviceCDCACMVID[1], // Vendor ID (high) (assigned by the USB-IF) - configDescriptorDeviceCDCACMPID[0], // Product ID (low) (assigned by the manufacturer) - configDescriptorDeviceCDCACMPID[1], // Product ID (high) (assigned by the manufacturer) - configDescriptorDeviceCDCACMBCD[0], // Device release number in BCD (low) - configDescriptorDeviceCDCACMBCD[1], // Device release number in BCD (high) - 0x01, // Index of string descriptor describing manufacturer - 0x02, // Index of string descriptor describing product - 0x00, // Index of string descriptor describing the device's serial number - configDeviceCDCACMConfigurationCount, // Number of possible configurations - } - - // descriptorConfigurationCDCACM is the default configuration descriptor for - // CDC-ACM devices. - descriptorConfigurationCDCACM = []uint8{ - specDescriptorLengthConfigure, // Size of this descriptor in bytes - specDescriptorTypeConfigure, // Descriptor Type - uint8(descriptorConfigurationCDCACMSize), // Total length of data returned for this configuration (low) - uint8(descriptorConfigurationCDCACMSize >> 8), // Total length of data returned for this configuration (high) - configDeviceCDCACMInterfaceCount, // Number of interfaces supported by this configuration - configDeviceCDCACMConfigurationIndex, // Value to use to select this configuration - 0, // Index of string descriptor describing this configuration - descriptorConfigurationCDCACMAttributes, // Configuration attributes - configDeviceMaxPower, // Max power consumption when fully-operational (2 mA units) - - // Communication/Control Interface Descriptor - specDescriptorLengthInterface, // Descriptor length - specDescriptorTypeInterface, // Descriptor type - descriptorInterfaceCDCACMComm, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - deviceCDCCommClass, // Class code - deviceCDCAbstractControlModel, // Subclass code - deviceCDCNoClassSpecificProtocol, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) - 0, // Interface Description String Index - - // CDC Header Functional Descriptor - descriptorConfigurationCDCACMHeaderFuncSize, // Size of this descriptor in bytes - specDescriptorTypeCDCInterface, // Descriptor Type - deviceCDCHeaderFuncDesc, // Descriptor Subtype - 0x10, // USB CDC specification version 1.10 (low) - 0x01, // USB CDC specification version 1.10 (high) - - // CDC Call Management Functional Descriptor - descriptorConfigurationCDCACMCallManageSize, // Size of this descriptor in bytes - specDescriptorTypeCDCInterface, // Descriptor Type - deviceCDCCallManagementFuncDesc, // Descriptor Subtype - 0x01, // Capabilities - 1, // Data Interface - - // CDC Abstract Control Management Functional Descriptor - descriptorConfigurationCDCACMAbstractSize, // Size of this descriptor in bytes - specDescriptorTypeCDCInterface, // Descriptor Type - deviceCDCAbstractControlFuncDesc, // Descriptor Subtype - 0x06, // Capabilities - - // CDC Union Functional Descriptor - descriptorConfigurationCDCACMUnionFuncSize, // Size of this descriptor in bytes - specDescriptorTypeCDCInterface, // Descriptor Type - deviceCDCUnionFuncDesc, // Descriptor Subtype - 0, // Controlling interface index - 1, // Controlled interface index - - // Communication/Control Notification Endpoint descriptor - specDescriptorLengthEndpoint, // Size of this descriptor in bytes - specDescriptorTypeEndpoint, // Descriptor Type - descriptorEndpointCDCACMCommInterruptIn | // Endpoint address - specDescriptorEndpointAddressDirectionIn, - uint8(specEndpointInterrupt), // Attributes - uint8(configDeviceCDCACMInterruptInPacketSize), // Max packet size (low) - uint8(configDeviceCDCACMInterruptInPacketSize >> 8), // Max packet size (high) - configDeviceCDCACMInterruptInInterval, // Polling Interval - - // Data Interface Descriptor - specDescriptorLengthInterface, // Interface length - specDescriptorTypeInterface, // Interface type - descriptorInterfaceCDCACMData, // Interface index - 0, // Alternate setting - 2, // Number of endpoints - deviceCDCDataClass, // Class code - 0, // Subclass code - deviceCDCNoClassSpecificProtocol, // Protocol code - 0, // Interface Description String Index - - // Data Bulk Input Endpoint descriptor - specDescriptorLengthEndpoint, // Size of this descriptor in bytes - specDescriptorTypeEndpoint, // Descriptor Type - descriptorEndpointCDCACMDataBulkIn | // Endpoint address - specDescriptorEndpointAddressDirectionIn, - uint8(specEndpointBulk), // Attributes - uint8(configDeviceCDCACMBulkInPacketSize), // Max packet size (low) - uint8(configDeviceCDCACMBulkInPacketSize >> 8), // Max packet size (high) - 0, // Polling Interval - - // Data Bulk Output Endpoint descriptor - specDescriptorLengthEndpoint, // Size of this descriptor in bytes - specDescriptorTypeEndpoint, // Descriptor Type - descriptorEndpointCDCACMDataBulkOut | // Endpoint address - specDescriptorEndpointAddressDirectionOut, - uint8(specEndpointBulk), // Attributes - uint8(configDeviceCDCACMBulkOutPacketSize), // Max packet size (low) - uint8(configDeviceCDCACMBulkOutPacketSize >> 8), // Max packet size (high) - 0, // Polling Interval - } - - descriptorStringCDCACMLanguage = []uint8{ - 2 + 2*1, // Size of this descriptor in bytes - specDescriptorTypeString, - 0x09, 0x04, - } -) diff --git a/src/machine/usb/device.go b/src/machine/usb/device.go deleted file mode 100644 index ce853190f..000000000 --- a/src/machine/usb/device.go +++ /dev/null @@ -1,1066 +0,0 @@ -package usb - -// Unexported USB device type definitions. -type ( - // deviceClassDriver defines the class driver interface. - // - // This interface is used internally to abstract communication between a USB - // device and the device class (e.g., CDC, HID, MSC, etc.) in which it is - // configured. - deviceClassDriver interface { - init(device *device, config *deviceClassConfig, id uint8) status // Class driver initialization- entry of the class driver - deinit() status // Class driver de-initialization - event(event deviceClassEventID, param interface{}) status // Class driver event callback - send(ep uint8, buffer []uint8, length uint32) status // Class driver send to endpoint - receive(ep uint8, buffer []uint8, length uint32) status // Class driver receive from endpoint - } - - // deviceClassEventHandler defines the methods required to receive all device- - // and class-level event notifications on a given USB port. - // - // This interface is intended as the primary communication mechanism between - // a USB device (of any class) and the application layer using that device. - // Thus, it is meant to be implemented internally by one of the application's - // USB handlers (e.g., a serial UART driver using the CDC-ACM device class). - deviceClassEventHandler interface { - deviceEvent(ev deviceEventID, param interface{}) status - classEvent(ev uint32, param interface{}) status - } - - // deviceEndpointController defines the method(s) required - deviceEndpointController interface { - controlEndpoint(message deviceEndpointControlMessage, param interface{}) status - } - - deviceEventFunc func(ev deviceEventID, param interface{}) status - deviceRequestCallbackFunc func(setup *deviceSetup, buffer *[]uint8, length *uint32) status - - deviceNotificationID uint8 - deviceControlID uint8 - deviceStatusID uint8 - deviceStateID uint8 - deviceEndpointStatusID uint8 - deviceClassEventID uint8 - deviceEventID uint8 - deviceClassID uint8 - deviceControlRWSequence uint8 - - deviceDescriptorString *[]uint8 - - deviceDescriptorLanguage struct { - pString []deviceDescriptorString - ident uint16 - } - - deviceDescriptor struct { - pDevice *[]uint8 - pConfig *[]uint8 - language []deviceDescriptorLanguage - } - - deviceEndpointConfig struct { - maxPacketSize uint16 // Endpoint maximum packet size - address uint8 // Endpoint address - transferType uint8 // Endpoint transfer type - zlt uint8 // ZLT flag - interval uint8 // Endpoint interval - } - - deviceEndpointStatus struct { - address uint8 // Endpoint address - status uint16 // Endpoint status (idle or stalled) - } - - deviceNotification struct { - buffer []uint8 // Transferred buffer - length uint32 // Transferred data length - code deviceNotificationID // Notification code - isSetup bool // Is in a setup phase - } - - // deviceSetup contains the setup information for a USB device. - deviceSetupBitmap uint64 - deviceSetupBuffer [deviceSetupSize]uint8 - deviceSetup struct { - bmRequestType uint8 // 8, 1 (bits, bytes) - bRequest uint8 // 8, 1 - wValue uint16 // 16, 2 - wIndex uint16 // 16, 2 - wLength uint16 // 16, 2 (= 64 bits, 8 bytes) - } - - deviceEndpointControlMessage struct { - buffer []uint8 // Transferred buffer - length uint32 // Transferred data length - isSetup bool // Is in a setup phase - } - - deviceEndpointControlList [2 * configDeviceMaxEndpoints]deviceEndpointControl - deviceEndpointControl struct { - handler deviceEndpointController - param interface{} // Parameter for callback function - isBusy bool - } - - // deviceEndpoint contains the information for a USB device endpoint. - deviceEndpoint struct { - address uint8 // Endpoint address - transferType uint8 // Endpoint transfer type - maxPacketSize uint16 // Endpoint maximum packet size - interval uint8 // Endpoint interval - } - - // deviceInterface contains the endpoints and class-specific information for - // a USB device interface. - deviceInterface struct { - alternateSetting uint8 // Alternate setting number - endpoint []deviceEndpoint // Endpoints of the interface - classSpecific interface{} // Class specific structure handle - } - - // deviceClassInterface contains the USB device class details, including - // all of its device interfaces. - deviceClassInterface struct { - classCode uint8 // Class code of the interface - subclassCode uint8 // Subclass code of the interface - protocolCode uint8 // Protocol code of the interface - interfaceNumber uint8 // Interface number - deviceInterface []deviceInterface // Interface structure list - } - - // deviceClassInfo contains the USB device class ID and all of its class - // interfaces. - deviceClassInfo struct { - classID deviceClassID // Class type - interfaceList []deviceClassInterface // Interfaces of the class - } - - // deviceClassConfig contains the configuration for a USB device class. - deviceClassConfig struct { - driver deviceClassDriver // USB device class driver interface - info deviceClassInfo // Detailed information of the class - } - - // deviceClass contains common device class state information. - deviceClass struct { - device *device // USB device handle - config []deviceClassConfig // USB device class configuration list - handler deviceClassEventHandler // application callback - setupBuffer []uint8 // Setup packet data buffer - transcationBuffer uint16 // Get status/configuration, get/set interface, get sync frame - } - - device struct { - port uint8 // USB port (core index) - controller deviceController // Controller interface - class *deviceClass // USB device class - endpointControl deviceEndpointControlList // Endpoint callback function structure - deviceAddress uint8 // Current device address - state deviceStateID // Current device state - isResetting bool // Is doing device reset or not - hwTick int64 // (volatile) Current hw tick (ms) - } - - // // This structure is used to pass the control request information. - // // The structure is used in following two cases. - // // 1. Case one, the host wants to send data to the device in the control data stage: @n - // // a. If a setup packet is received, the structure is used to pass the setup packet data and wants to get the - // // buffer to receive data sent from the host. - // // The field isSetup is 1. - // // The length is the requested buffer length. - // // The buffer is filled by the class or application by using the valid buffer address. - // // The setup is the setup packet address. - // // b. If the data received is sent by the host, the structure is used to pass the data buffer address and the - // // data - // // length sent by the host. - // // In this way, the field isSetup is 0. - // // The buffer is the address of the data sent from the host. - // // The length is the received data length. - // // The setup is the setup packet address. @n - // // 2. Case two, the host wants to get data from the device in control data stage: @n - // // If the setup packet is received, the structure is used to pass the setup packet data and wants to get the - // // data buffer address to send data to the host. - // // The field isSetup is 1. - // // The length is the requested data length. - // // The buffer is filled by the class or application by using the valid buffer address. - // // The setup is the setup packet address. - deviceControlRequest struct { - setup deviceSetup // Setup data - buffer []uint8 // Buffer - length uint32 // Buffer length or requested length - isSetup bool // Indicates whether a setup packet is received - } - - // // deviceGetDescriptorCommon contains the result of a control request for: - // // get descriptor common - // deviceGetDescriptorCommon struct { - // buffer []uint8 // Buffer - // length uint32 // Buffer length - // } - - // deviceGetDeviceDescriptor contains the result of a control request for: - // get device descriptor - deviceGetDeviceDescriptor struct { - buffer []uint8 // Buffer - length uint32 // Buffer length - } - - // // deviceGetDeviceQualifierDescriptor contains the result of a control - // // request for: get device qualifier descriptor - // deviceGetDeviceQualifierDescriptor struct { - // buffer []uint8 // Buffer - // length uint32 // Buffer length - // } - - // deviceGetConfigurationDescriptor contains the result of a control request - // for: get configuration descriptor - deviceGetConfigurationDescriptor struct { - buffer []uint8 // Buffer - length uint32 // Buffer length - configuration uint8 // The configuration number - } - - // // deviceGetBOSDescriptor contains the result of a control request for: get - // // bos descriptor - // deviceGetBOSDescriptor struct { - // buffer []uint8 // Buffer - // length uint32 // Buffer length - // } - - // deviceGetStringDescriptor contains the result of a control request for: - // get string descriptor - deviceGetStringDescriptor struct { - buffer []uint8 // Buffer - length uint32 // Buffer length - languageID uint16 // Language ID - stringIndex uint8 // String index - } - - // // deviceGetHIDDescriptor contains the result of a control request for: get - // // HID descriptor - // deviceGetHIDDescriptor struct { - // buffer []uint8 // Buffer - // length uint32 // Buffer length - // interfaceNumber uint8 // The interface number - // } - - // // deviceGetHIDReportDescriptor contains the result of a control request for: - // // get HID report descriptor - // deviceGetHIDReportDescriptor struct { - // buffer []uint8 // Buffer - // length uint32 // Buffer length - // interfaceNumber uint8 // The interface number - // } - - // // deviceGetHIDPhysicalDescriptor contains the result of a control request - // // for: get HID physical descriptor - // deviceGetHIDPhysicalDescriptor struct { - // buffer []uint8 // Buffer - // length uint32 // Buffer length - // index uint8 // Physical index - // interfaceNumber uint8 // The interface number - // } - -) - -// Unexported enumerated constant values for USB device. -const ( - deviceNotifyBusReset deviceNotificationID = iota + 0x10 // Reset signal detected - deviceNotifySuspend // Suspend signal detected - deviceNotifyResume // Resume signal detected - deviceNotifyLPMSleep // LPM signal detected - deviceNotifyLPMResume // Resume signal detected - deviceNotifyError // Errors happened in bus - deviceNotifyDetach // Device disconnected from a host - deviceNotifyAttach // Device connected to a host - deviceNotifyDCDDetectFinished // Device charger detection finished -) - -const ( - deviceControlRun deviceControlID = iota // Enable the device functionality - deviceControlStop // Disable the device functionality - deviceControlEndpointInit // Initialize a specified endpoint - deviceControlEndpointDeinit // De-initialize a specified endpoint - deviceControlEndpointStall // Stall a specified endpoint - deviceControlEndpointUnstall // Un-stall a specified endpoint - deviceControlGetDeviceStatus // Get device status - deviceControlGetEndpointStatus // Get endpoint status - deviceControlSetDeviceAddress // Set device address - deviceControlGetSynchFrame // Get current frame - deviceControlResume // Drive controller to generate a resume signal in USB bus - deviceControlSleepResume // Drive controller to generate a LPM resume signal in USB bus - deviceControlSuspend // Drive controller to enter into suspend mode - deviceControlSleep // Drive controller to enter into sleep mode - deviceControlSetDefaultStatus // Set controller to default status - deviceControlGetSpeed // Get current speed - deviceControlGetOTGStatus // Get OTG status - deviceControlSetOTGStatus // Set OTG status - deviceControlSetTestMode // Drive xCHI into test mode - deviceControlGetRemoteWakeUp // Get flag of LPM Remote Wake-up Enabled by USB host. - deviceControlDCDDisable // disable dcd module function. - deviceControlDCDEnable // enable dcd module function. - deviceControlPreSetDeviceAddress // Pre set device address - deviceControlUpdateHwTick // update hardware tick -) - -const ( - deviceStatusTestMode deviceStatusID = iota + 1 // Test mode - deviceStatusSpeed // Current speed - deviceStatusOTG // OTG status - deviceStatusDevice // Device status - deviceStatusEndpoint // Endpoint state usb_device_endpoint_status_t - deviceStatusDeviceState // Device state - deviceStatusAddress // Device address - deviceStatusSynchFrame // Current frame - deviceStatusBus // Bus status - deviceStatusBusSuspend // Bus suspend - deviceStatusBusSleep // Bus suspend - deviceStatusBusResume // Bus resume - deviceStatusRemoteWakeup // Remote wakeup state - deviceStatusBusSleepResume // Bus resume -) - -const ( - deviceStateConfigured deviceStateID = iota // Device state, Configured - deviceStateAddress // Device state, Address - deviceStateDefault // Device state, Default - deviceStateAddressing // Device state, Address setting - deviceStateTestMode // Device state, Test mode - deviceStateInit // Device state, initializing -) - -const ( - deviceEndpointStateIdle deviceEndpointStatusID = iota // Endpoint state, idle - deviceEndpointStateStalled // Endpoint state, stalled -) - -const ( - deviceEventBusReset deviceEventID = iota + 1 // USB bus reset signal detected - deviceEventSuspend // USB bus suspend signal detected - deviceEventResume // USB bus resume signal detected. The resume signal is driven by itself or a host - deviceEventSleeped // USB bus LPM suspend signal detected - deviceEventLPMResume // USB bus LPM resume signal detected. The resume signal is driven by itself or a host - deviceEventError // An error is happened in the bus. - deviceEventDetach // USB device is disconnected from a host. - deviceEventAttach // USB device is connected to a host. - deviceEventSetConfiguration // Set configuration. - deviceEventSetInterface // Set interface. - deviceEventGetDeviceDescriptor // Get device descriptor. - deviceEventGetConfigurationDescriptor // Get configuration descriptor. - deviceEventGetStringDescriptor // Get string descriptor. - deviceEventGetHIDDescriptor // Get HID descriptor. - deviceEventGetHIDReportDescriptor // Get HID report descriptor. - deviceEventGetHIDPhysicalDescriptor // Get HID physical descriptor. - deviceEventGetBOSDescriptor // Get configuration descriptor. - deviceEventGetDeviceQualifierDescriptor // Get device qualifier descriptor. - deviceEventVendorRequest // Vendor request. - deviceEventSetRemoteWakeup // Enable or disable remote wakeup function. - deviceEventGetConfiguration // Get current configuration index - deviceEventGetInterface // Get current interface alternate setting value - deviceEventSetBHNPEnable // Enable or disable BHNP. - deviceEventDCDDetectionfinished // The DCD detection finished -) - -const ( - deviceClassInvalid deviceClassID = iota - deviceClassHID - deviceClassCDC - deviceClassMSC - deviceClassAudio - deviceClassPHDC - deviceClassVideo - deviceClassPrinter - deviceClassDFU - deviceClassCCID -) - -const ( - deviceClassEventInvalid deviceClassEventID = iota - deviceClassEventClassRequest - deviceClassEventDeviceReset - deviceClassEventSetConfiguration - deviceClassEventSetInterface - deviceClassEventSetEndpointHalt - deviceClassEventClearEndpointHalt -) - -const ( - deviceControlPipeSetupStage deviceControlRWSequence = iota // Setup stage - deviceControlPipeDataStage // Data stage - deviceControlPipeStatusStage // status stage -) - -// Sizes of various device structures and buffers. -const ( - deviceSetupSize = 8 // deviceSetup struct (bytes) -) - -var ( - deviceClassInstance [configDeviceCount]deviceClass - deviceSetupBufferInstance [configDeviceCount]deviceSetupBuffer - - deviceEndpointControlInConfig = deviceEndpointConfig{ - maxPacketSize: configDeviceControllerMaxPacketSize, - address: uint8(specEndpointControl | specDescriptorEndpointAddressDirectionIn), - transferType: specEndpointControl, - zlt: 1, - interval: 0, - } - deviceEndpointControlOutConfig = deviceEndpointConfig{ - maxPacketSize: configDeviceControllerMaxPacketSize, - address: uint8(specEndpointControl | specDescriptorEndpointAddressDirectionOut), - transferType: specEndpointControl, - zlt: 1, - interval: 0, - } -) - -func (d *device) init(port uint8) (s status) { - - // initialize device - d.port = port - d.controller = d.initController() // obtain a device controller handle - d.deviceAddress = 0 - d.state = deviceStateDefault - d.isResetting = false - d.hwTick = 0 - for i := range d.endpointControl { - d.endpointControl[i].handler = nil - d.endpointControl[i].param = nil - d.endpointControl[i].isBusy = false - } - - // initialize platform via device controller interface - return d.controller.init() -} - -func (d *device) deinit() status { - - // de=initialize device - s := d.controller.deinit() - d.controller = nil - return s -} - -func (d *device) initClass(id uint8, config []deviceClassConfig, - handler deviceClassEventHandler) *deviceClass { - - // verify a device configuration was provided - if nil == d || nil == config || len(config) == 0 { - return nil - } - - if 0 == id || int(id) > len(config) { - return nil - } - - c := &deviceClassInstance[d.port] - b := &deviceSetupBufferInstance[d.port] - - // initialize device class - c.device = d - c.config = config - c.handler = handler - c.setupBuffer = b[:] - c.transcationBuffer = 0 - - // add a class reference to the receiver - d.class = c - - // initialze each of the device class drivers - for i := range c.config { - if nil != c.config[i].driver { - if !c.config[i].driver.init(d, &c.config[i], id).OK() { - // remove the driver from configuration if it fails initialization - c.config[i].driver = nil - } - } - } - - return c -} - -func (d *device) transfer(address uint8, buffer []uint8, length uint32) status { - - if nil == d.controller { - return statusInvalidController - } - - endpoint, direction := unpackEndpoint(address) - index := (endpoint << 1) | direction - - if d.endpointControl[index].isBusy { - return statusBusy - } - d.endpointControl[index].isBusy = true - - var s status - if specDescriptorEndpointAddressDirectionIn == - address&specDescriptorEndpointAddressDirectionMsk { - s = d.controller.send(address, buffer, length) - } else { - s = d.controller.receive(address, buffer, length) - } - if !s.OK() { - d.endpointControl[index].isBusy = false - } - return s -} - -func (d *device) send(address uint8, buffer []uint8, length uint32) status { - return d.transfer((address&specDescriptorEndpointAddressNumberMsk)| - (specDescriptorEndpointAddressDirectionIn), buffer, length) -} - -func (d *device) receive(address uint8, buffer []uint8, length uint32) status { - return d.transfer((address&specDescriptorEndpointAddressNumberMsk)| - (specDescriptorEndpointAddressDirectionOut), buffer, length) -} - -func (d *device) cancel(address uint8) status { - if nil == d.controller { - return statusInvalidController - } - return d.controller.cancel(address) -} - -func (d *device) control(command deviceControlID, param interface{}) status { - if nil == d.controller { - return statusInvalidController - } - return d.controller.control(command, param) -} - -func (d *device) initControlPipes() status { - - if s := d.initEndpoint(&deviceEndpointControlInConfig, d, d.class); !s.OK() { - return s - } - - if s := d.initEndpoint(&deviceEndpointControlOutConfig, d, d.class); !s.OK() { - _ = d.deinitEndpoint(deviceEndpointControlInConfig.address) - return s - } - - return statusSuccess -} - -func (d *device) initEndpoint(config *deviceEndpointConfig, - handler deviceEndpointController, param interface{}) status { - - endpoint, direction := unpackEndpoint(config.address) - - if endpoint >= configDeviceMaxEndpoints { - return statusInvalidParameter - } - - d.endpointControl[(endpoint<<1)|direction].handler = handler - d.endpointControl[(endpoint<<1)|direction].param = param - d.endpointControl[(endpoint<<1)|direction].isBusy = false - - return d.control(deviceControlEndpointInit, config) -} - -func (d *device) deinitEndpoint(address uint8) status { - - s := d.control(deviceControlEndpointDeinit, address) - - endpoint, direction := unpackEndpoint(address) - - if endpoint >= configDeviceMaxEndpoints { - return statusInvalidParameter - } - - d.endpointControl[(endpoint<<1)|direction].handler = nil - d.endpointControl[(endpoint<<1)|direction].param = nil - d.endpointControl[(endpoint<<1)|direction].isBusy = false - - return s -} - -func (d *device) stallEndpoint(address uint8) status { - - endpoint, _ := unpackEndpoint(address) - - if endpoint >= configDeviceMaxEndpoints { - return statusInvalidParameter - } - return d.control(deviceControlEndpointStall, address) -} - -func (d *device) unstallEndpoint(address uint8) status { - - endpoint, _ := unpackEndpoint(address) - - if endpoint >= configDeviceMaxEndpoints { - return statusInvalidParameter - } - return d.control(deviceControlEndpointUnstall, address) -} - -func (d *device) feedback(setup *deviceSetup, status status, stage deviceControlRWSequence, - buffer *[]uint8, length *uint32) status { - direction := uint8(specIn) - if !status.OK() { - if (setup.bmRequestType&specRequestTypeTypeMsk) == specRequestTypeTypeStandard && - (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirOut && - 0 != setup.wLength && deviceControlPipeSetupStage == stage { - direction = specOut - } - return d.stallEndpoint(specEndpointControl | - (direction << specDescriptorEndpointAddressDirectionPos)) - } else { - if *length > uint32(setup.wLength) { - *length = uint32(setup.wLength) - } - s := d.send(specEndpointControl, *buffer, *length) - if s.OK() && (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirIn { - s = d.receive(specEndpointControl, nil, 0) - } - return s - } -} - -func (d *device) controlEndpoint(message deviceEndpointControlMessage, param interface{}) status { - - // verify request and parameters - if message.length == 0xFFFFFFFF || nil == param { - return statusInvalidRequest - } - class, ok := param.(*deviceClass) - if !ok { - return statusInvalidParameter - } - // read current setup buffer from given deviceClass in param - var setup deviceSetup - setupStatus := setup.parse(class.setupBuffer) - if !setupStatus.OK() { - return setupStatus - } - - // read current device state of receiver - var state deviceStateID - s := d.status(deviceStatusDeviceState, &state) - if !s.OK() { - return statusInvalidHandle - } - - // allocate buffer for request responses - buffer := []uint8{} - length := uint32(0) - - if message.isSetup { - // verify message received contains expected setup data - if nil == message.buffer || deviceSetupSize != message.length { - return statusInvalidRequest - } - // read setup data from given message buffer - s = setup.parse(message.buffer) - if !s.OK() { - return statusInvalidRequest - } - // process message as a received setup request - if (setup.bmRequestType & specRequestTypeTypeMsk) == specRequestTypeTypeStandard { - // handle standard request - if int(setup.bRequest) <= specRequestStandardSynchFrame { - _ = d.requestStandard(&setup, &buffer, &length) - } - } else { - if 0 != setup.wLength && - (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirOut { - if (setup.bmRequestType & specRequestTypeTypeClass) == specRequestTypeTypeClass { - req := deviceControlRequest{ - setup: setup, - buffer: nil, - length: uint32(setup.wLength), - isSetup: true, - } - d.classEvent(deviceClassEventClassRequest, &req) - buffer = req.buffer - length = req.length - } else if (setup.bmRequestType & specRequestTypeTypeVendor) == specRequestTypeTypeVendor { - req := deviceControlRequest{ - setup: setup, - buffer: nil, - length: uint32(setup.wLength), - isSetup: true, - } - d.event(deviceEventVendorRequest, &req) - buffer = req.buffer - length = req.length - } - if s.OK() { - return d.receive(specEndpointControl, buffer, uint32(setup.wLength)) - } - } else { - if (setup.bmRequestType & specRequestTypeTypeClass) == specRequestTypeTypeClass { - req := deviceControlRequest{ - setup: setup, - buffer: nil, - length: uint32(setup.wLength), - isSetup: true, - } - d.classEvent(deviceClassEventClassRequest, &req) - buffer = req.buffer - length = req.length - } else if (setup.bmRequestType & specRequestTypeTypeVendor) == specRequestTypeTypeVendor { - req := deviceControlRequest{ - setup: setup, - buffer: nil, - length: uint32(setup.wLength), - isSetup: true, - } - d.event(deviceEventVendorRequest, &req) - buffer = req.buffer - length = req.length - } - } - } - s = d.feedback(&setup, s, deviceControlPipeSetupStage, &buffer, &length) - } else if deviceStateAddressing == state { - // handle standard request - if int(setup.bRequest) <= specRequestStandardSynchFrame { - _ = d.requestStandard(&setup, &buffer, &length) - } - } else if 0 != message.length && 0 != setup.wLength && - (setup.bmRequestType&specRequestTypeDirMsk) == specRequestTypeDirOut { - if setup.bmRequestType&specRequestTypeTypeClass == specRequestTypeTypeClass { - req := deviceControlRequest{ - setup: setup, - buffer: message.buffer, - length: message.length, - isSetup: false, - } - d.classEvent(deviceClassEventClassRequest, &req) - } else if setup.bmRequestType&specRequestTypeTypeVendor == specRequestTypeTypeVendor { - req := deviceControlRequest{ - setup: setup, - buffer: message.buffer, - length: message.length, - isSetup: false, - } - d.event(deviceEventVendorRequest, &req) - } - s = d.feedback(&setup, s, deviceControlPipeDataStage, &buffer, &length) - } - - return s -} - -func (d *device) requestStandard( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - switch setup.bRequest { - case specRequestStandardGetStatus: - return d.requestGetStatus(setup, buffer, length) - case specRequestStandardClearFeature, specRequestStandardSetFeature: - return d.requestSetClearFeature(setup, buffer, length) - case specRequestStandardSetAddress: - return d.requestSetAddress(setup, buffer, length) - case specRequestStandardGetDescriptor: - return d.requestGetDescriptor(setup, buffer, length) - case specRequestStandardGetConfiguration: - return d.requestGetConfiguration(setup, buffer, length) - case specRequestStandardSetConfiguration: - return d.requestSetConfiguration(setup, buffer, length) - case specRequestStandardGetInterface: - return d.requestGetInterface(setup, buffer, length) - case specRequestStandardSetInterface: - return d.requestSetInterface(setup, buffer, length) - case specRequestStandardSynchFrame: - return d.requestSynchFrame(setup, buffer, length) - default: - return statusInvalidRequest - } -} - -func (d *device) requestGetStatus( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestSetClearFeature( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestSetAddress( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestGetDescriptor( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestGetConfiguration( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestSetConfiguration( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestGetInterface( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestSetInterface( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) requestSynchFrame( - setup *deviceSetup, buffer *[]uint8, length *uint32) status { - return statusSuccess -} - -func (d *device) classEvent(ev deviceClassEventID, param interface{}) { - if nil != d.class && nil != d.class.config { - // route the event to all class configurations - for i := range d.class.config { - _ = d.class.config[i].driver.event(ev, param) - } - } -} - -func (d *device) event(ev deviceEventID, param interface{}) { - - switch ev { - case deviceEventBusReset: - // initialize control pipes - d.initControlPipes() - - // notify all classes of a bus reset signal - for i := range d.class.config { - _ = d.class.config[i].driver.event(deviceClassEventDeviceReset, d.class) - } - } - // notify the application driver of all events - if nil != d.class.handler { - _ = d.class.handler.deviceEvent(ev, param) - } -} - -func (d *device) notify(message deviceNotification) { - - switch message.code { - case deviceNotifyBusReset: - d.notifyReset(message) - - default: - endpoint, direction := unpackEndpoint(uint8(message.code)) - - if endpoint < configDeviceMaxEndpoints { - if nil != d.endpointControl[(endpoint<<1)|direction].handler { - if message.isSetup { - d.endpointControl[0].isBusy = false - d.endpointControl[1].isBusy = false - } else { - d.endpointControl[(endpoint<<1)|direction].isBusy = false - } - // call endpoint callback - _ = d.endpointControl[(endpoint<<1)|direction].handler.controlEndpoint( - deviceEndpointControlMessage{ - buffer: message.buffer, - length: message.length, - isSetup: message.isSetup, - }, - d.endpointControl[(endpoint<<1)|direction].param, - ) - } - } - } -} - -func (d *device) notifyReset(message deviceNotification) { - - d.isResetting = true - _ = d.control(deviceControlSetDefaultStatus, nil) - - d.state = deviceStateDefault - d.deviceAddress = 0 - - for count := 0; count < 2*configDeviceMaxEndpoints; count++ { - d.endpointControl[count].handler = nil - d.endpointControl[count].param = nil - d.endpointControl[count].isBusy = false - } - - d.event(deviceEventBusReset, nil) - d.isResetting = false -} - -func (d *device) status(deviceStatus deviceStatusID, param interface{}) status { - - if nil == param { - return statusInvalidParameter - } - - switch deviceStatus { - case deviceStatusSpeed: - return d.control(deviceControlGetSpeed, param) - - case deviceStatusOTG: - return d.control(deviceControlGetOTGStatus, param) - - case deviceStatusDeviceState: - if state, ok := param.(*deviceStateID); ok { - *state = d.state - return statusSuccess - } - return statusInvalidParameter - - case deviceStatusAddress: - if address, ok := param.(*uint8); ok { - *address = d.deviceAddress - return statusSuccess - } - return statusInvalidParameter - - case deviceStatusDevice: - return d.control(deviceControlGetDeviceStatus, param) - - case deviceStatusEndpoint: - return d.control(deviceControlGetEndpointStatus, param) - - case deviceStatusSynchFrame: - return d.control(deviceControlGetSynchFrame, param) - - default: - return statusInvalidParameter - } -} - -func (d *device) setStatus(deviceStatus deviceStatusID, param interface{}) status { - - switch deviceStatus { - case deviceStatusOTG: - return d.control(deviceControlSetOTGStatus, param) - - case deviceStatusDeviceState: - if state, ok := param.(deviceStateID); ok { - d.state = state - return statusSuccess - } - return statusInvalidParameter - - case deviceStatusAddress: - if d.state != deviceStateAddressing { - if address, ok := param.(uint8); ok { - d.deviceAddress = address - d.state = deviceStateAddressing - return d.control(deviceControlPreSetDeviceAddress, d.deviceAddress) - } - return statusInvalidParameter - } - return d.control(deviceControlSetDeviceAddress, d.deviceAddress) - - case deviceStatusBusResume: - return d.control(deviceControlResume, param) - - case deviceStatusBusSleepResume: - return d.control(deviceControlSleepResume, param) - - case deviceStatusBusSuspend: - return d.control(deviceControlSuspend, param) - - case deviceStatusBusSleep: - return d.control(deviceControlSleep, param) - - default: - return statusInvalidParameter - } -} - -func (d *device) busSpeed(speed *uint8) status { - return d.status(deviceStatusSpeed, speed) -} - -func (d *device) setBusSpeed(speed uint8) status { - // TODO - return statusSuccess -} - -func (d *device) deviceDescriptor(desc *deviceGetDeviceDescriptor) status { - if int(d.port) < len(configDeviceCDCACMDescriptor) { - if nil != configDeviceCDCACMDescriptor[d.port].pDevice { - desc.buffer = *configDeviceCDCACMDescriptor[d.port].pDevice - desc.length = specDescriptorLengthDevice - return statusSuccess - } - } - return statusInvalidHandle -} - -func (d *device) configurationDescriptor(desc *deviceGetConfigurationDescriptor) status { - if int(d.port) < len(configDeviceCDCACMDescriptor) { - if nil != configDeviceCDCACMDescriptor[d.port].pConfig { - desc.buffer = *configDeviceCDCACMDescriptor[d.port].pConfig - desc.length = uint32(descriptorConfigurationCDCACMSize) - return statusSuccess - } - } - return statusInvalidHandle -} - -func (d *device) stringDescriptor(desc *deviceGetStringDescriptor) status { - if 0 == desc.stringIndex { - desc.buffer = descriptorStringCDCACMLanguage - desc.length = uint32(len(descriptorStringCDCACMLanguage)) - return statusSuccess - } - if int(d.port) < len(configDeviceCDCACMDescriptor) { - for _, lang := range configDeviceCDCACMDescriptor[d.port].language { - if desc.languageID == lang.ident { - if int(desc.stringIndex) < len(lang.pString) { - desc.buffer = *lang.pString[desc.stringIndex] - desc.length = uint32(len(desc.buffer)) - } - } - } - } - return statusInvalidRequest -} - -func (s deviceSetup) pack() deviceSetupBitmap { - return deviceSetupBitmap( - ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8 // 8 (bits) - ((uint64(s.bRequest) & 0xFF) << 8) | // uint8 // 8 - ((uint64(s.wValue) & 0xFFFF) << 16) | // uint16 // 16 - ((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16 // 16 - ((uint64(s.wLength) & 0xFFFF) << 48)) // uint16 // 16 (= 64 bits) -} - -func (s deviceSetup) bytes() deviceSetupBuffer { - return [deviceSetupSize]uint8{ - s.bmRequestType, - s.bRequest, - // for 16-bit words: low-byte is at index N, high-byte is at N+1 - uint8(s.wValue), uint8(s.wValue >> 8), - uint8(s.wIndex), uint8(s.wIndex >> 8), - uint8(s.wLength), uint8(s.wLength >> 8), - } -} - -func (s deviceSetupBitmap) bytes() deviceSetupBuffer { - return [deviceSetupSize]uint8{ - uint8(s >> 0), uint8(s >> 8), uint8(s >> 16), uint8(s >> 24), - uint8(s >> 32), uint8(s >> 40), uint8(s >> 48), uint8(s >> 56), - } -} - -func (s *deviceSetup) parse(buffer []uint8) status { - if nil == buffer || len(buffer) < deviceSetupSize { - return statusInvalidParameter - } - s.bmRequestType = buffer[0] - s.bRequest = buffer[1] - s.wValue = (uint16(buffer[3]) << 8) | uint16(buffer[2]) - s.wIndex = (uint16(buffer[5]) << 8) | uint16(buffer[4]) - s.wLength = (uint16(buffer[7]) << 8) | uint16(buffer[6]) - return statusSuccess -} diff --git a/src/machine/usb/device_cdc.go b/src/machine/usb/device_cdc.go deleted file mode 100644 index fadec6193..000000000 --- a/src/machine/usb/device_cdc.go +++ /dev/null @@ -1,135 +0,0 @@ -package usb - -const ( - // Communication Class - deviceCDCCommClass = 0x02 - // Data Class - deviceCDCDataClass = 0x0A - - // Communication Class SubClass Codes - deviceCDCDirectLineControlModel = 0x01 - deviceCDCAbstractControlModel = 0x02 - deviceCDCTelephoneControlModel = 0x03 - deviceCDCMultiChannelControlModel = 0x04 - deviceCDCCAPIControlMopdel = 0x05 - deviceCDCEthernetNetworkingControlModel = 0x06 - deviceCDCATMNetworkingControlModel = 0x07 - deviceCDCWirelessHandsetControlModel = 0x08 - deviceCDCDeviceManagement = 0x09 - deviceCDCMobileDirectLineModel = 0x0A - deviceCDCOBEX = 0x0B - deviceCDCEthernetEmulationModel = 0x0C - - // Communication Class Protocol Codes - deviceCDCNoClassSpecificProtocol = 0x00 // also for Data Class Protocol Code - deviceCDCAT250Protocol = 0x01 - deviceCDCATPCCA101Protocol = 0x02 - deviceCDCATPCCA101AnnexO = 0x03 - deviceCDCATGSM707 = 0x04 - deviceCDCAT3GPP27007 = 0x05 - deviceCDCATTIACDMA = 0x06 - deviceCDCEthernetEmulationProtocol = 0x07 - deviceCDCExternalProtocol = 0xFE - deviceCDCVendorSpecific = 0xFF // also for Data Class Protocol Code - - // Data Class Protocol Codes - deviceCDCPyhsicalInterfaceProtocol = 0x30 - deviceCDCHDLCProtocol = 0x31 - deviceCDCTransparentProtocol = 0x32 - deviceCDCManagementProtocol = 0x50 - deviceCDCDataLinkQ931Protocol = 0x51 - deviceCDCDataLinkQ921Protocol = 0x52 - deviceCDCDataCompressionV42BIS = 0x90 - deviceCDCEuroISDNProtocol = 0x91 - deviceCDCRateAdaptionISDNV24 = 0x92 - deviceCDCCAPICommands = 0x93 - deviceCDCHostBasedDriver = 0xFD - deviceCDCUnitFunctional = 0xFE - - // Descriptor SubType in Communications Class Functional Descriptors - deviceCDCHeaderFuncDesc = 0x00 - deviceCDCCallManagementFuncDesc = 0x01 - deviceCDCAbstractControlFuncDesc = 0x02 - deviceCDCDirectLineFuncDesc = 0x03 - deviceCDCTelephoneRingerFuncDesc = 0x04 - deviceCDCTelephoneReportFuncDesc = 0x05 - deviceCDCUnionFuncDesc = 0x06 - deviceCDCCountrySelectFuncDesc = 0x07 - deviceCDCTelephoneModesFuncDesc = 0x08 - deviceCDCTerminalFuncDesc = 0x09 - deviceCDCNetworkChannelFuncDesc = 0x0A - deviceCDCProtocolUnitFuncDesc = 0x0B - deviceCDCExtensionUnitFuncDesc = 0x0C - deviceCDCMultiChannelFuncDesc = 0x0D - deviceCDCCAPIControlFuncDesc = 0x0E - deviceCDCEthernetNetworkingFuncDesc = 0x0F - deviceCDCATMNetworkingFuncDesc = 0x10 - deviceCDCWirelessControlFuncDesc = 0x11 - deviceCDCMobileDirectLineFuncDesc = 0x12 - deviceCDCMDLMDetailFuncDesc = 0x13 - deviceCDCDeviceManagementFuncDesc = 0x14 - deviceCDCOBEXFuncDesc = 0x15 - deviceCDCCommandSetFuncDesc = 0x16 - deviceCDCCommandSetDetailFuncDesc = 0x17 - deviceCDCTelephoneControlFuncDesc = 0x18 - deviceCDCOBEXServiceIDFuncDesc = 0x19 - - deviceCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND - deviceCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE - deviceCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE - deviceCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE - deviceCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE - deviceCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE - deviceCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE - deviceCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP - deviceCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE - deviceCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME - deviceCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK - deviceCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING - deviceCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING - deviceCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE - deviceCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK - deviceCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS - deviceCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS - deviceCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM - deviceCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM - deviceCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS - deviceCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS - deviceCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS - deviceCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER - deviceCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER - deviceCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER - deviceCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS - deviceCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER - deviceCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER - deviceCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER - deviceCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC - deviceCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT - deviceCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS - deviceCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC - deviceCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS - deviceCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK - - deviceCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION - deviceCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL - deviceCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE - deviceCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT - deviceCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE - deviceCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE - deviceCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE - deviceCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE - - deviceCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE - deviceCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING - - deviceCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION - deviceCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE - deviceCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER - deviceCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER - deviceCDCUARTStateBreak = 0x04 // UART state BREAK - deviceCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL - deviceCDCUARTStateFraming = 0x10 // UART state FRAMING - deviceCDCUARTStateParity = 0x20 // UART state PARITY - deviceCDCUARTStateOverrun = 0x40 // UART state OVERRUN - -) diff --git a/src/machine/usb/device_cdc_acm.go b/src/machine/usb/device_cdc_acm.go deleted file mode 100644 index 0b0052e1c..000000000 --- a/src/machine/usb/device_cdc_acm.go +++ /dev/null @@ -1,690 +0,0 @@ -package usb - -import "bytes" - -type ( - deviceCDCACMEventID uint8 - - // deviceCDCACMConfig defines the platform-specific configuration settings for - // a single CDC-ACM device class on a given USB port. - // - // To connect the USB CDC-ACM device driver to a particular platform, there - // should be one instance for each USB CDC-ACM port, defined in global array - // configDeviceCDCACM, indexed by USB port. - deviceCDCACMConfig struct { - // USB configuration - interfaceSpeed uint8 // Low/Full/High-speed - // CDC-ACM Serial line configuration - lineCodingSize uint32 // Size of line-coding message - lineCodingBaudRate uint32 // Data terminal rate - lineCodingCharFormat uint32 // Character format - lineCodingParityType uint32 // Parity type - lineCodingDataBits uint32 // Data word size - // CDC-ACM Communication/control interface - commInterfaceIndex uint8 // Communication/control interface index - commInterruptInEndpoint uint8 // Interrupt input endpoint index (address) - commInterruptInPacketSize uint16 // Interrupt input packet size - commInterruptInInterval uint8 // Interrupt input interval - // CDC-ACM Data interface - dataInterfaceIndex uint8 // Data interface index - dataBulkInEndpoint uint8 // Bulk input endpoint index (address) - dataBulkInPacketSize uint16 // Bulk input packet size - dataBulkOutEndpoint uint8 // Bulk output endpoint index (address) - dataBulkOutPacketSize uint16 // Bulk output packet size - } - - deviceCDCACM struct { - device *device // The handle of the USB device. - config *deviceClassConfig // The class configure structure. - info deviceCDCACMInfo // The ACM serial state. - comm *deviceInterface // The CDC communication interface handle. - data *deviceInterface // The CDC data interface handle. - sendBuffer *deviceCDCACMDataBuffer // Pointer to the global send buffer - recvBuffer *deviceCDCACMDataBuffer // Pointer to the global receive buffer - bulkIn deviceCDCACMPipe // The bulk in pipe for sending packet to host. - bulkOut deviceCDCACMPipe // The bulk out pipe for receiving packet from host. - interruptIn deviceCDCACMPipe // The interrupt in pipe for notifying the device state to host. - interfaceNumber uint8 // The current interface number. - alternate uint8 // The alternate setting value of the interface. - hasSentState bool // The device has primed the state in interrupt pipe - speed uint8 // Speed of USB device (Full/Low/High) - lineCodingSize uint32 // Size of line-coding message - baudRate uint32 // Data terminal rate - charFormat uint32 // Character format - parityType uint32 // Parity type - dataBits uint32 // Data word size - sendSize uint32 // Number of bytes scheduled in global send buffer - recvSize uint32 // Number of bytes scheduled in global receive buffer - } - - deviceCDCACMDataBuffer [configDeviceBufferSize]uint8 - deviceCDCACMAlternateList [configDeviceCDCACMInterfaceCount]uint16 - deviceCDCACMSerialState [deviceCDCACMSerialStateSize]uint8 - - deviceCDCACMRequestParam struct { - buffer *[]uint8 // The pointer to the address of the buffer for CDC class request. - length *uint32 // The pointer to the length of the buffer for CDC class request. - interfaceIndex uint16 // The interface index of the setup packet. - setupValue uint16 // The wValue field of the setup packet. - isSetup bool // The flag indicates if it is a setup packet - } - - deviceCDCACMPipe struct { - pipeDataBuffer []uint8 // pipe data buffer backup when stall - pipeDataLen uint32 // pipe data length backup when stall - pipeStall bool // pipe is stall - ep uint8 // The endpoint number of the pipe. - isBusy bool // The pipe is transferring packet - } - - deviceCDCACMInfo struct { - serialState deviceCDCACMSerialState // Serial state buffer of the CDC device to notify the serial state to host. - dtePresent bool // A flag to indicate whether DTE is present. - breakDuration uint16 // Length of time in milliseconds of the break signal - dteStatus uint8 // Status of data terminal equipment - currentInterface uint8 // Current interface index. - uartState uint16 // UART state of the CDC device. - } -) - -const ( - deviceCDCACMEventSendResponse deviceCDCACMEventID = iota + 1 // This event indicates the bulk send transfer is complete or cancelled etc. - deviceCDCACMEventRecvResponse // This event indicates the bulk receive transfer is complete or cancelled etc.. - deviceCDCACMEventSerialStateNotify // This event indicates the serial state has been sent to the host. - deviceCDCACMEventSendEncapsulatedCommand // This event indicates the device received the SEND_ENCAPSULATED_COMMAND request. - deviceCDCACMEventGetEncapsulatedResponse // This event indicates the device received the GET_ENCAPSULATED_RESPONSE request. - deviceCDCACMEventSetCommFeature // This event indicates the device received the SET_COMM_FEATURE request. - deviceCDCACMEventGetCommFeature // This event indicates the device received the GET_COMM_FEATURE request. - deviceCDCACMEventClearCommFeature // This event indicates the device received the CLEAR_COMM_FEATURE request. - deviceCDCACMEventGetLineCoding // This event indicates the device received the GET_LINE_CODING request. - deviceCDCACMEventSetLineCoding // This event indicates the device received the SET_LINE_CODING request. - deviceCDCACMEventSetControlLineState // This event indicates the device received the SET_CONTRL_LINE_STATE request. - deviceCDCACMEventSendBreak // This event indicates the device received the SEND_BREAK request. - - deviceCDCACMRequestNotify = 0xA1 - - deviceCDCACMInfoNotifyPacketSize = 8 // (bytes) - deviceCDCACMInfoUARTBitmapSize = 2 // - deviceCDCACMSerialStateSize = deviceCDCACMInfoNotifyPacketSize + deviceCDCACMInfoUARTBitmapSize -) - -var ( - deviceCDCACMDataSendBuffer = [configDeviceCDCACMCount]deviceCDCACMDataBuffer{} - deviceCDCACMDataRecvBuffer = [configDeviceCDCACMCount]deviceCDCACMDataBuffer{} - - deviceCDCACMLineCoding = [configDeviceCDCACMCount][][]uint8{ - { // USB CDC-ACM port 0 - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // configuration index 1 - }, - } - - deviceCDCACMConfigInstance = [configDeviceCDCACMCount][]deviceClassConfig{ - {{ - driver: &deviceCDCACM{}, - info: deviceClassInfo{ - classID: deviceClassCDC, - interfaceList: []deviceClassInterface{ - { // USB CDC-ACM communications/control interface - classCode: deviceCDCCommClass, - subclassCode: deviceCDCAbstractControlModel, - protocolCode: deviceCDCNoClassSpecificProtocol, - deviceInterface: []deviceInterface{{ - alternateSetting: 0, - endpoint: []deviceEndpoint{ - {transferType: specEndpointInterrupt}, - }, - }}, - }, - { // USB CDC-ACM serial data interface - classCode: deviceCDCDataClass, - subclassCode: 0x00, - protocolCode: deviceCDCNoClassSpecificProtocol, - deviceInterface: []deviceInterface{{ - alternateSetting: 0, - endpoint: []deviceEndpoint{ - {transferType: specEndpointBulk}, - {transferType: specEndpointBulk}, - }, - }}, - }, - }, - }, - }}, - } - - deviceCDCACMBufferInvalid32 = leU32(0xFFFFFFFF) -) - -func (acm *deviceCDCACM) init(device *device, config *deviceClassConfig, id uint8) status { - - // initialize our associated object references - acm.device = device - acm.config = config - - // grab references to the global data buffers - acm.sendBuffer = &deviceCDCACMDataSendBuffer[device.port] - acm.recvBuffer = &deviceCDCACMDataRecvBuffer[device.port] - - // initialize line-coding details from global configuration - acm.speed = configDeviceCDCACM[device.port][id-1].interfaceSpeed - acm.lineCodingSize = configDeviceCDCACM[device.port][id-1].lineCodingSize - acm.baudRate = configDeviceCDCACM[device.port][id-1].lineCodingBaudRate - acm.charFormat = configDeviceCDCACM[device.port][id-1].lineCodingCharFormat - acm.parityType = configDeviceCDCACM[device.port][id-1].lineCodingParityType - acm.dataBits = configDeviceCDCACM[device.port][id-1].lineCodingDataBits - - lineCoding := deviceCDCACMLineCoding[device.port][id-1] - lineCoding[0] = uint8(acm.baudRate >> 0) - lineCoding[1] = uint8(acm.baudRate >> 8) - lineCoding[2] = uint8(acm.baudRate >> 16) - lineCoding[3] = uint8(acm.baudRate >> 24) - lineCoding[4] = uint8(acm.charFormat) - lineCoding[5] = uint8(acm.parityType) - lineCoding[6] = uint8(acm.dataBits) - - // initialize remaining state data - acm.alternate = 0xFF - - return statusSuccess -} - -func (acm *deviceCDCACM) deinit() status { - return statusSuccess -} - -func (acm *deviceCDCACM) initEndpoints() status { - - // find the communication/control interface in our CDC-ACM configuration list - acm.interfaceNumber, acm.comm = acm.findInterface(deviceCDCCommClass) - - // verify we found a valid communication/control interface - if nil == acm.comm { - return statusInvalidHandle - } - - // initialize all of the communication/control input endpoints - if s := acm.initInterfaceEndpoints(specIn, specEndpointInterrupt); !s.OK() { - return s - } - - // find the data interface in our CDC-ACM configuration list - _, acm.data = acm.findInterface(deviceCDCDataClass) - - // verify we found a valid data interface - if nil == acm.data { - return statusInvalidHandle - } - - // initialize all of the data input endpoints - if s := acm.initInterfaceEndpoints(specIn, specEndpointBulk); !s.OK() { - return s - } - // initialize all of the data output endpoints - if s := acm.initInterfaceEndpoints(specOut, specEndpointBulk); !s.OK() { - return s - } - - return statusSuccess -} - -func (acm *deviceCDCACM) initInterfaceEndpoints(direction, transferType uint8) status { - - // select the relevant fields and callbacks for our given interface type - pipe, deviceInterface := acm.endpointInterface(direction, transferType) - if nil == deviceInterface { - return statusInvalidParameter - } - - // initialize each endpoint with given direction and transfer type - for _, ep := range deviceInterface.endpoint { - num, dir := unpackEndpoint(ep.address) - if dir == direction && ep.transferType == transferType { - pipe.pipeDataBuffer = deviceCDCACMBufferInvalid32 - pipe.pipeDataLen = 0 - pipe.pipeStall = false - pipe.ep = num - pipe.isBusy = false - - if s := acm.device.initEndpoint(&deviceEndpointConfig{ - maxPacketSize: ep.maxPacketSize, - address: ep.address, - transferType: ep.transferType, - zlt: 0, - interval: ep.interval, - }, acm, pipe); !s.OK() { - return s - } - } - } - - return statusSuccess -} - -func (acm *deviceCDCACM) deinitEndpoints() status { - - if nil == acm.device { - return statusInvalidHandle - } - - if nil != acm.comm { - for i := range acm.comm.endpoint { - _ = acm.device.deinitEndpoint(acm.comm.endpoint[i].address) - } - acm.comm = nil - } - if nil != acm.data { - for i := range acm.data.endpoint { - _ = acm.device.deinitEndpoint(acm.data.endpoint[i].address) - } - acm.data = nil - } - - return statusSuccess -} - -func (acm *deviceCDCACM) event(event deviceClassEventID, param interface{}) (s status) { - - // assume success unless error condition deliberately detected - s = statusSuccess - - switch event { - case deviceClassEventDeviceReset: - // bus reset, clear the selected configuration - acm.config = nil - - case deviceClassEventSetConfiguration: - if id, ok := param.(uint8); ok { - // configuration index is 1-based, meaning configuration 0 is invalid - if 0 == id || int(id) > len(acm.device.class.config) { - return statusInvalidParameter - } - if &acm.device.class.config[id-1] == acm.config { - break // configuration already selected - } - // de-initialize endpoints of current configuration - if s = acm.deinitEndpoints(); !s.OK() { - break - } - // select new configuration, reset alternate setting - acm.config = &acm.device.class.config[id-1] - acm.alternate = 0 - // initialize endpoints of new configuration - if s = acm.initEndpoints(); !s.OK() { - break - } - } else { - // unexpected parameter, should be uint8 (configuration index) - s = statusInvalidParameter - } - - case deviceClassEventSetInterface: - if interfaceAlternate, ok := param.(uint16); ok { - alternate := uint8(interfaceAlternate & 0xFF) - if acm.interfaceNumber != uint8(interfaceAlternate>>8) { - break // requested alternate from interface different than current - } - if alternate == acm.alternate { - break // requested alternate same as current - } - // de-initialize endpoints of current configuration - if s = acm.deinitEndpoints(); !s.OK() { - break - } - // select new alternate - acm.alternate = alternate - // initialize endpoints of new configuration - if s = acm.initEndpoints(); !s.OK() { - break - } - } else { - // unexpected parameter, should be uint16: interface (hi), alternate (lo) - s = statusInvalidParameter - } - - case deviceClassEventSetEndpointHalt: - // verify parameter and fields - if address, ok := param.(uint8); ok { - if nil != acm.config && nil != acm.comm && nil != acm.data { - // check if given endpoint is a communication/control endpoint - for _, e := range acm.comm.endpoint { - if address == e.address { - // found endpoint, set stall flag - acm.interruptIn.pipeStall = true - // notify device - c := acm.device.control(deviceControlEndpointStall, address) - if s.OK() && !c.OK() { - s = c - } - } - } - // check if given endpoint is a data endpoint - for _, e := range acm.data.endpoint { - if address == e.address { - _, direction := unpackEndpoint(address) - // found endpoint, set stall flag - if specIn == direction { - acm.bulkIn.pipeStall = true - } else { - acm.bulkOut.pipeStall = true - } - // notify device - c := acm.device.control(deviceControlEndpointStall, address) - if s.OK() && !c.OK() { - s = c - } - } - } - } else { - s = statusInvalidHandle - } - } else { - // unexpected parameter, should be uint8 (endpoint address) - s = statusInvalidParameter - } - - case deviceClassEventClearEndpointHalt: - // verify parameter and fields - if address, ok := param.(uint8); ok { - if nil != acm.config && nil != acm.comm && nil != acm.data { - // check if given endpoint is a communication/control endpoint - for _, e := range acm.comm.endpoint { - if address == e.address { - // found endpoint, notify device - c := acm.device.control(deviceControlEndpointUnstall, address) - if s.OK() && !c.OK() { - s = c - } - _, direction := unpackEndpoint(address) - if specIn == direction { - // flush any buffered data written to the stalled endpoint - if acm.interruptIn.pipeStall { - // clear stall flag - acm.interruptIn.pipeStall = false - // verify the buffer has valid data - if !bytes.Equal(acm.interruptIn.pipeDataBuffer, deviceCDCACMBufferInvalid32) { - // transmit - u := acm.device.send( - acm.interruptIn.ep, - acm.interruptIn.pipeDataBuffer, - acm.interruptIn.pipeDataLen) - if !u.OK() { - // notify upper layer driver of communication/control event - _ = acm.controlEndpoint( - deviceEndpointControlMessage{ - buffer: acm.interruptIn.pipeDataBuffer, - length: acm.interruptIn.pipeDataLen, - isSetup: false, - }, - &acm.interruptIn, - ) - if s.OK() { - s = u - } - } - // clear the stalled endpoint buffer - acm.interruptIn.pipeDataBuffer = deviceCDCACMBufferInvalid32 - acm.interruptIn.pipeDataLen = 0 - } - } - } - } - } - // check if given endpoint is a data endpoint - for _, e := range acm.data.endpoint { - if address == e.address { - // found endpoint, notify device - c := acm.device.control(deviceControlEndpointUnstall, address) - if s.OK() && !c.OK() { - s = c - } - // check if endpoint is an input or output - _, direction := unpackEndpoint(address) - if specIn == direction { - // flush any buffered data written to the stalled endpoint - if acm.bulkIn.pipeStall { - // clear stall flag - acm.bulkIn.pipeStall = false - // verify the buffer has valid data - if !bytes.Equal(acm.bulkIn.pipeDataBuffer, deviceCDCACMBufferInvalid32) { - // transmit - u := acm.device.send( - acm.bulkIn.ep, - acm.bulkIn.pipeDataBuffer, - acm.bulkIn.pipeDataLen) - if !u.OK() { - // notify upper layer driver of data input event - _ = acm.controlEndpoint( - deviceEndpointControlMessage{ - buffer: acm.bulkIn.pipeDataBuffer, - length: acm.bulkIn.pipeDataLen, - isSetup: false, - }, - &acm.bulkIn, - ) - if s.OK() { - s = u - } - } - // clear the stalled endpoint buffer - acm.bulkIn.pipeDataBuffer = deviceCDCACMBufferInvalid32 - acm.bulkIn.pipeDataLen = 0 - } - } - } else { - // flush any buffered data read from the stalled endpoint - if acm.bulkOut.pipeStall { - // clear stall flag - acm.bulkOut.pipeStall = false - // verify the buffer has valid data - if !bytes.Equal(acm.bulkOut.pipeDataBuffer, deviceCDCACMBufferInvalid32) { - // receive - u := acm.device.receive( - acm.bulkOut.ep, - acm.bulkOut.pipeDataBuffer, - acm.bulkOut.pipeDataLen) - if !u.OK() { - // notify upper layer driver of data output event - _ = acm.controlEndpoint( - deviceEndpointControlMessage{ - buffer: acm.bulkOut.pipeDataBuffer, - length: acm.bulkOut.pipeDataLen, - isSetup: false, - }, - &acm.bulkOut, - ) - if s.OK() { - s = u - } - } - // clear the stalled endpoint buffer - acm.bulkOut.pipeDataBuffer = deviceCDCACMBufferInvalid32 - acm.bulkOut.pipeDataLen = 0 - } - } - } - } - } - } else { - s = statusInvalidHandle - } - } else { - // unexpected parameter, should be uint8 (endpoint address) - s = statusInvalidParameter - } - case deviceClassEventClassRequest: - // verify parameter and fields - if request, ok := param.(*deviceControlRequest); ok { - // verify requested interface is receiver's interface and request is CDC - if (request.setup.wIndex&0xFF) != uint16(acm.interfaceNumber) || - (request.setup.bmRequestType&specRequestTypeTypeMsk) != specRequestTypeTypeClass { - // construct standard parameter to be passed on to upper layer drivevr - param := deviceCDCACMRequestParam{ - buffer: &request.buffer, - length: &request.length, - interfaceIndex: request.setup.wIndex, - setupValue: request.setup.wValue, - isSetup: request.isSetup, - } - // translate request code to event code - var event deviceCDCACMEventID - switch request.setup.bRequest { - case deviceCDCRequestSendEncapsulatedCommand: - event = deviceCDCACMEventSendEncapsulatedCommand - case deviceCDCRequestGetEncapsulatedResponse: - event = deviceCDCACMEventGetEncapsulatedResponse - case deviceCDCRequestSetCommFeature: - event = deviceCDCACMEventSetCommFeature - case deviceCDCRequestGetCommFeature: - event = deviceCDCACMEventGetCommFeature - case deviceCDCRequestClearCommFeature: - event = deviceCDCACMEventClearCommFeature - case deviceCDCRequestGetLineCoding: - event = deviceCDCACMEventGetLineCoding - case deviceCDCRequestSetLineCoding: - event = deviceCDCACMEventSetLineCoding - case deviceCDCRequestSetControlLineState: - event = deviceCDCACMEventSetControlLineState - case deviceCDCRequestSendBreak: - event = deviceCDCACMEventSendBreak - default: - s = statusInvalidRequest - } - // notify request to upper layer driver - if nil != acm.device && nil != acm.device.class && - nil != acm.device.class.handler { - acm.device.class.handler.classEvent(uint32(event), param) - } - } else { - s = statusInvalidRequest - } - } else { - s = statusInvalidParameter - } - } - - return s -} - -func (acm *deviceCDCACM) send(ep uint8, buffer []uint8, length uint32) status { - var pipe *deviceCDCACMPipe - // select which endpoint pipe to write into - switch ep { - case acm.interruptIn.ep: - pipe = &acm.interruptIn - case acm.bulkIn.ep: - pipe = &acm.bulkIn - default: - return statusInvalidParameter - } - if pipe.isBusy { - return statusBusy - } - // set pipe busy flag - pipe.isBusy = true - // check if pipe stall flag is set - if pipe.pipeStall { - // write data to pipe buffer - pipe.pipeDataBuffer = buffer - pipe.pipeDataLen = length - return statusSuccess - } - // pass data to device for transmission - if s := acm.device.send(ep, buffer, length); !s.OK() { - pipe.isBusy = false - return s - } - return statusSuccess -} - -func (acm *deviceCDCACM) receive(ep uint8, buffer []uint8, length uint32) status { - var pipe *deviceCDCACMPipe - // select which endpoint pipe to read from - switch ep { - case acm.bulkOut.ep: - pipe = &acm.bulkOut - default: - return statusInvalidParameter - } - if pipe.isBusy { - return statusBusy - } - // set pipe busy flag - pipe.isBusy = true - // check if pipe stall flag is set - if pipe.pipeStall { - // write data to pipe buffer - pipe.pipeDataBuffer = buffer - pipe.pipeDataLen = length - return statusSuccess - } - // pass data to device for reception - if s := acm.device.receive(ep, buffer, length); !s.OK() { - pipe.isBusy = false - return s - } - return statusSuccess -} - -// findInterface returns the interface number and deviceInterface from the -// receiver's CDC-ACM configuration list whose classCode and alternateSetting -// equals the given classCode and receiver's current alternateSetting; -// otherwise, no such interface is found, returns 0 and nil. -func (acm *deviceCDCACM) findInterface(classCode uint8) (uint8, *deviceInterface) { - if nil == acm.device || nil == acm.config { - return 0, nil - } - for c := range acm.config.info.interfaceList { - dci := &acm.config.info.interfaceList[c] - if classCode == dci.classCode { - for d := range dci.deviceInterface { - if acm.alternate == dci.deviceInterface[d].alternateSetting { - return dci.interfaceNumber, &dci.deviceInterface[d] - } - } - } - } - return 0, nil -} - -func (acm *deviceCDCACM) endpointInterface( - direction, transferType uint8) (*deviceCDCACMPipe, *deviceInterface) { - - switch transferType { - case specEndpointInterrupt: - switch direction { - case specIn: - return &acm.interruptIn, acm.comm - } - case specEndpointBulk: - switch direction { - case specIn: - return &acm.bulkIn, acm.data - case specOut: - return &acm.bulkOut, acm.data - } - } - return nil, nil -} - -func (acm *deviceCDCACM) controlEndpoint( - message deviceEndpointControlMessage, param interface{}) status { - - if pipe, ok := param.(*deviceCDCACMPipe); ok { - var event deviceCDCACMEventID - switch pipe { - case &acm.interruptIn: - event = deviceCDCACMEventSerialStateNotify - case &acm.bulkIn: - event = deviceCDCACMEventSendResponse - case &acm.bulkOut: - event = deviceCDCACMEventRecvResponse - } - pipe.isBusy = false - if nil != acm.device && nil != acm.device.class && - nil != acm.device.class.handler { - acm.device.class.handler.classEvent(uint32(event), message) - return statusSuccess - } - } - return statusInvalidParameter -} diff --git a/src/machine/usb/device_controller.go b/src/machine/usb/device_controller.go deleted file mode 100644 index bf30719c2..000000000 --- a/src/machine/usb/device_controller.go +++ /dev/null @@ -1,288 +0,0 @@ -package usb - -import "unsafe" - -// deviceController provides hardware abstraction over a platform's physical -// USB device port controller (e.g., an EHCI-compliant peripheral). -// -// To connect the USB 2.0 device driver stack to a particular platform, the -// following method must be implemented: -// -// func (d *device) initController() deviceController -// -// This method shall return an implementation of deviceController, or nil if a -// controller cannot be allocated for the given port. -type ( - deviceController interface { - init() status // Controller initialization - deinit() status // Controller de-initialization - enable(enable bool) status // Controller enable runtime - interrupt() // Controller interrupt handler - process() status // Controller process interrupts - send(address uint8, buffer []uint8, length uint32) status // Controller send data - receive(address uint8, buffer []uint8, length uint32) status // Controller receive data - cancel(address uint8) status // Controller cancel transfer - control(command deviceControlID, param interface{}) status // Controller device control - critical(enter bool) status // Controller critical section - } - - // deviceControllerInterruptQueue represents a fixed-length, circular queue - // (FIFO) of interrupts. - // - // The enqueue and dequeue operations (methods enq and deq, respectively) are - // NOT interrupt-/thread-safe. The user must protect against race conditions - // using appropriate resources for their system. For example, the interface - // method (deviceController).critical(bool) implements the concept of critical - // sections, which could be used to prevent concurrent accesses. - // This restriction also applies to the higher-level methods fill and drain. - deviceControllerInterruptQueue struct { - queue [configInterruptQueueSize]uintptr // circular queue - head int - count int - } - - deviceControllerCapabilitiesBitmap uint32 - deviceControllerCapabilities struct { - reserved1 uint16 // 15 (bits) - ios uint8 // 1 - maxPacketSize uint16 // 11 - reserved2 uint8 // 2 - zlt uint8 // 1 - mult uint8 // 2 (= 32 bits) - } - - deviceControllerDTDTokenBitmap uint32 - deviceControllerDTDToken struct { - status uint8 // 8 (bits) - reserved1 uint8 // 2 - multiplierOverride uint8 // 2 - reserved2 uint8 // 3 - ioc uint8 // 1 - totalBytes uint16 // 15 - reserved3 uint8 // 1 (= 32 bits) - } - - deviceControllerEndpointStatusBitmap uint32 - deviceControllerEndpointStatus struct { - isOpened uint8 // 1 (bits) - zlt uint8 // 1 - _ uint32 // 30 (= 32 bits) - } - - deviceControllerOriginalBufferBitmap uint32 - deviceControllerOriginalBuffer struct { - originalBufferOffset uint16 // 12 (bits) - originalBufferLength uint32 // 19 - dtdInvalid uint8 // 1 (= 32 bits) - } - - deviceControllerQH struct { - capabilities deviceControllerCapabilitiesBitmap // 4 (bytes) - currentDTDPointer *deviceControllerDTD // 4 - nextDTDPointer *deviceControllerDTD // 4 - dtdToken deviceControllerDTDTokenBitmap // 4 - bufferPointerPage [5]uint32 // 20 - reserved1 uint32 // 4 - setupBuffer deviceSetupBuffer // 8 - setupBufferBack deviceSetupBuffer // 8 - endpointStatus deviceControllerEndpointStatusBitmap // 4 - reserved2 uint32 // 4 (= 64 bytes) - } - - deviceControllerDTDList [2 * configDeviceMaxEndpoints]*deviceControllerDTD - deviceControllerDTD struct { - nextDTDPointer *deviceControllerDTD // 4 (bytes) - dtdToken deviceControllerDTDTokenBitmap // 4 - bufferPointerPage [5]uint32 // 20 - originalBuffer deviceControllerOriginalBufferBitmap // 4 (= 32 bytes) - } -) - -const ( - - // device QH - deviceControllerQHSize = 64 // bytes - deviceControllerQHBufferSize = (configDeviceCount-1)*configDeviceControllerQHAlign + - 2*configDeviceMaxEndpoints*2*deviceControllerQHSize - - deviceControllerQHPointerMsk = 0xFFFFFFC0 - deviceControllerQHMultMsk = 0xC0000000 - deviceControllerQHZLTMsk = 0x20000000 - deviceControllerQHMaxPacketSizeMsk = 0x07FF0000 - deviceControllerQHMaxPacketSize = 0x00000800 - deviceControllerQHIOSMsk = 0x00008000 - - // device DTD - deviceControllerDTDSize = 32 // bytes - deviceControllerDTDBufferSize = (configDeviceCount-1)*configDeviceControllerDTDAlign + - configDeviceControllerMaxDTD*deviceControllerDTDSize - - deviceControllerDTDPointerMsk = 0xFFFFFFE0 - deviceControllerDTDTerminateMsk = 0x00000001 - deviceControllerDTDPageMsk = 0xFFFFF000 - deviceControllerDTDPageOffsetMsk = 0x00000FFF - deviceControllerDTDPageBlock = 0x00001000 - deviceControllerDTDTotalBytesMsk = 0x7FFF0000 - deviceControllerDTDTotalBytes = 0x00004000 - deviceControllerDTDIOCMsk = 0x00008000 - deviceControllerDTDMultIOMsk = 0x00000C00 - deviceControllerDTDStatusMsk = 0x000000FF - deviceControllerDTDStatusErrorMsk = 0x00000068 - deviceControllerDTDStatusActive = 0x00000080 - deviceControllerDTDStatusHalted = 0x00000040 - deviceControllerDTDStatusDataBufferError = 0x00000020 - deviceControllerDTDStatusTransactionError = 0x00000008 -) - -var ( - // special invalid pointer, indicating the end of a list of DTDs - deviceControllerDTDTerminate = (*deviceControllerDTD)(unsafe.Pointer(uintptr( - deviceControllerDTDTerminateMsk))) -) - -// enq enqueues the given mask into tail position of the receiver iq's queue, -// increasing queue length by 1. -// -// When the queue fills to capacity, any subsequent enqueue will overwrite the -// current head with the given value, positioning its following element at the -// front of the queue, and leaves queue length unaffected. -func (iq *deviceControllerInterruptQueue) enq(mask uintptr) { - if iq.count == configInterruptQueueSize { - // queue is full; overwrite oldest element (queue head) and increment head - iq.queue[iq.head] = mask - iq.head++ - iq.head %= configInterruptQueueSize - } else { - // queue is not full; place element at queue tail and increment count - iq.queue[(iq.head+iq.count)%configInterruptQueueSize] = mask - iq.count++ - } -} - -// deq dequeues the mask in tail position from the receiver iq's queue, reducing -// queue length by 1, and returns the dequeued value with true. -// -// When the queue is empty, queue length remains unaffected, and it returns 0 -// with false. -func (iq *deviceControllerInterruptQueue) deq() (uintptr, bool) { - if iq.count == 0 { - // queue is empty; reset head and return an invalid value - iq.head = 0 - return 0, false - } - // queue is not empty; decrement count, reset and return value at queue tail - iq.count-- - n := (iq.head + iq.count) % configInterruptQueueSize - m := iq.queue[n] - iq.queue[n] = 0 // ensure the queue contains no spurious data - return m, true -} - -// fill enqueues each given mask (in order) into tail position of the receiver -// iq's queue, increasing queue length up to capacity, if possible. -// -// If the number of values given is greater than available queue positions, each -// element in head position - at the time the value is enqueued - will be -// overwritten, so that queue length never exceeds capacity, and the element in -// tail position is always the final element given. -func (iq *deviceControllerInterruptQueue) fill(mask ...uintptr) { - for _, m := range mask { - iq.enq(m) - } -} - -// drain dequeues all elements in the receiver iq's queue, reducing queue length -// to 0, and returns the dequeued values (in order) and the number of number of -// values dequeued. -func (iq *deviceControllerInterruptQueue) drain() ( - q [configInterruptQueueSize]uintptr, n int, -) { - for { - m, ok := iq.deq() - if !ok { - return - } - q[n] = m - n++ - } -} - -func getQHBuffer(port uint8, qh int, ep int) *deviceControllerQH { - if port < configDeviceCount && qh < 2 && ep < 2*configDeviceMaxEndpoints { - return (*deviceControllerQH)(unsafe.Pointer( - &deviceControllerQHBuffer[int(port)*configDeviceControllerQHAlign+ - qh*2*configDeviceMaxEndpoints*deviceControllerQHSize+ - ep*deviceControllerQHSize])) - } - return nil -} - -func getDTDBuffer(port uint8, dtd int) *deviceControllerDTD { - if port < configDeviceCount && dtd < configDeviceControllerMaxDTD { - return (*deviceControllerDTD)(unsafe.Pointer( - &deviceControllerDTDBuffer[int(port)*configDeviceControllerDTDAlign+ - dtd*deviceControllerDTDSize])) - } - return nil -} - -func (s deviceControllerCapabilities) pack() deviceControllerCapabilitiesBitmap { - return deviceControllerCapabilitiesBitmap( - ((uint32(s.reserved1) & 0x7FFF) << 0) | // uint16 // 15 (bits) - ((uint32(s.ios) & 0x1) << 15) | // uint8 // 1 - ((uint32(s.maxPacketSize) & 0x7FF) << 16) | // uint16 // 11 - ((uint32(s.reserved2) & 0x3) << 27) | // uint8 // 2 - ((uint32(s.zlt) & 0x1) << 29) | // uint8 // 1 - ((uint32(s.mult) & 0x3) << 30)) // uint8 // 2 (= 32 bits) -} - -func (s deviceControllerDTDToken) pack() deviceControllerDTDTokenBitmap { - return deviceControllerDTDTokenBitmap( - ((uint32(s.status) & 0xFF) << 0) | // uint8 // 8 (bits) - ((uint32(s.reserved1) & 0x3) << 8) | // uint8 // 2 - ((uint32(s.multiplierOverride) & 0x3) << 10) | // uint8 // 2 - ((uint32(s.reserved2) & 0x7) << 12) | // uint8 // 3 - ((uint32(s.ioc) & 0x1) << 15) | // uint8 // 1 - ((uint32(s.totalBytes) & 0x7FFF) << 16) | // uint16 // 15 - ((uint32(s.reserved3) & 0x1) << 31)) // uint8 // 1 (= 32 bits) -} - -func (b deviceControllerDTDTokenBitmap) unpack() deviceControllerDTDToken { - return deviceControllerDTDToken{ - status: uint8(b>>0) & 0xFF, - reserved1: uint8(b>>8) & 0x3, - multiplierOverride: uint8(b>>10) & 0x3, - reserved2: uint8(b>>12) & 0x7, - ioc: uint8(b>>15) & 0x1, - totalBytes: uint16(b>>16) & 0x7FFF, - reserved3: uint8(b>>31) & 0x1, - } -} - -func (s deviceControllerEndpointStatus) pack() deviceControllerEndpointStatusBitmap { - return deviceControllerEndpointStatusBitmap( - ((uint32(s.isOpened) & 0x1) << 0) | // uint8 // 1 (bits) - ((uint32(s.zlt) & 0x1) << 1)) // uint8 // 1 -} - -func (s deviceControllerOriginalBuffer) pack() deviceControllerOriginalBufferBitmap { - return deviceControllerOriginalBufferBitmap( - ((uint32(s.originalBufferOffset) & 0xFFF) << 0) | // uint16 // 12 (bits) - ((uint32(s.originalBufferLength) & 0x7FFFF) << 12) | // uint32 // 19 - ((uint32(s.dtdInvalid) & 0x1) << 31)) // uint8 // 1 (= 32 bits) -} - -func (b deviceControllerOriginalBufferBitmap) unpack() deviceControllerOriginalBuffer { - return deviceControllerOriginalBuffer{ - originalBufferOffset: uint16(b>>0) & 0xFFF, - originalBufferLength: uint32(b>>12) & 0x7FFFF, - dtdInvalid: uint8(b>>31) & 0x1, - } -} - -// 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) -} diff --git a/src/machine/usb/device_controller_mimxrt1062.go b/src/machine/usb/device_controller_mimxrt1062.go deleted file mode 100644 index 90ad58f39..000000000 --- a/src/machine/usb/device_controller_mimxrt1062.go +++ /dev/null @@ -1,709 +0,0 @@ -// +build mimxrt1062 - -package usb - -// Implementation of a port controller for USB device mode on NXP iMXRT1062. - -import ( - "device/arm" - "device/nxp" - "runtime/interrupt" - "runtime/volatile" - "unsafe" -) - -// deviceControl represents a USB device controller for NXP iMXRT1062. -// It implements the USB API's deviceController interface. -type deviceControl struct { - port uint8 - device *device - irq interrupt.Interrupt - - messages deviceControllerInterruptQueue - - interruptMask uintptr // interrupt state upon entering critical section - criticalState volatile.Register8 // set to 1 if in critical section, else 0 - - bus *nxp.USB_Type - phy *nxp.USBPHY_Type - nc *nxp.USBNC_Type - qh *deviceControllerQH // The QH structure base address - dtd *deviceControllerDTD // The DTD structure base address - dtdFree *deviceControllerDTD // The idle DTD list head - dtdHead deviceControllerDTDList // The transferring DTD list head for each endpoint - dtdTail deviceControllerDTDList // The transferring DTD list tail for each endpoint - dtdCount uint8 // The idle DTD node count - endpointCount uint8 // The endpoint number of EHCI - isResetting bool // Whether a PORT reset is occurring or not - controllerId uint8 // Controller ID - speed uint8 // Current speed of EHCI - isSuspending bool // Is suspending of the PORT -} - -var ( - // deviceControlInstance holds instances for all USB device controllers - // available on the platform. - deviceControlInstance [configDeviceCount]deviceControl - - //go:align 2048 - deviceControllerQHBuffer [deviceControllerQHBufferSize]uint8 - //go:align 32 - deviceControllerDTDBuffer [deviceControllerDTDBufferSize]uint8 -) - -// 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 delayMicrosec(microsec uint32) { - n := cycles(microsec, configCPUFrequencyHz) - for i := uint32(0); i < n; i++ { - arm.Asm(`nop`) - } -} - -// initController returns a deviceController for the receiver USB device. -// It allocates the registers and installs/enables an interrupt handler for -// the receiver USB device. It also initializes the shared buffers used by the -// USB controller hardware. -func (d *device) initController() deviceController { - - dc := &deviceControlInstance[d.port] - - dc.port = d.port - dc.device = d - - // based on the selected port, install interrupt handler and initialize - // register references - switch d.port { - case 0: - dc.irq = interrupt.New(nxp.IRQ_USB_OTG1, func(interrupt.Interrupt) { - portInstance[0].device.controller.interrupt() - }) - dc.bus = nxp.USB1 - dc.phy = nxp.USBPHY1 - dc.nc = nxp.USBNC1 - case 1: - dc.irq = interrupt.New(nxp.IRQ_USB_OTG2, func(interrupt.Interrupt) { - //portInstance[1].device.controller.interrupt() - }) - dc.bus = nxp.USB2 - dc.phy = nxp.USBPHY2 - dc.nc = nxp.USBNC2 - } - - // get base address of QH and DTD buffers - dc.qh = (*deviceControllerQH)(unsafe.Pointer( - &deviceControllerQHBuffer[int(d.port)*configDeviceControllerQHAlign])) - dc.dtd = (*deviceControllerDTD)(unsafe.Pointer( - &deviceControllerDTDBuffer[int(d.port)*configDeviceControllerDTDAlign])) - - dc.irq.SetPriority(configInterruptPriority) - dc.irq.Enable() - - return dc -} - -// init initializes the USB device subsystem for the receiver deviceControl. -func (dc *deviceControl) init() status { - - // reset the controller - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) - for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { - } - - // get hardware's endpoint count - dc.endpointCount = uint8(dc.bus.DCCPARAMS.Get() & nxp.USB_DCCPARAMS_DEN_Msk) - if dc.endpointCount < configDeviceMaxEndpoints { - return statusError - } - - // clear the controller mode field and set to device mode: - // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only - dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, - nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) - - // reset the constroller state to default - return dc.resetState() -} - -func (dc *deviceControl) deinit() status { - return statusSuccess -} - -func (dc *deviceControl) enable(enable bool) status { - if enable { - // ensure D+ pulled down long enough for host to detect previous disconnect - delayMicrosec(5000) - return dc.control(deviceControlRun, nil) - } else { - return dc.control(deviceControlStop, nil) - } -} - -// interrupt is the base interrupt handler for all USB device interrupts. -func (dc *deviceControl) interrupt() { - - // protect access to message queue - for statusRetry == dc.critical(true) { - } - - // read and clear the interrupts that fired - status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() - dc.bus.USBSTS.Set(status) - - // enqueue interrupts for runtime processing - dc.messages.enq(uintptr(status)) - - // release message queue - _ = dc.critical(false) -} - -func (dc *deviceControl) process() status { - - // protect access to message queue - if dc.critical(true).OK() { - - // dequeue oldest interrupt in message queue - status, ok := dc.messages.deq() - - // release message queue - _ = dc.critical(false) - - // process message if queue was not empty - if ok { - - if 0 != (status & nxp.USB_USBSTS_URI_Msk) { // USB reset - dc.reset() - } - - if 0 != (status & nxp.USB_USBSTS_UI_Msk) { // USB token done - dc.tokenDone() - } - - if 0 != (status & nxp.USB_USBSTS_PCI_Msk) { // USB port status change - dc.portChange() - } - - if 0 != (status & nxp.USB_USBSTS_SRI_Msk) { // USB start of frame (SOF) - dc.frameStart() - } - } - - // message queue read and processed - return statusSuccess - } - - // could not acquire lock on message queue - return statusBusy -} - -func (dc *deviceControl) transfer(address uint8, buffer []uint8, length uint32) status { - - if dc.isResetting { - return statusError - } - - endpoint, direction := unpackEndpoint(address) - endpointIndex := int((endpoint << 1) | direction) - currentIndex := 0 - - primeBit := uint32(1) << ((address & specDescriptorEndpointAddressNumberMsk) + - ((address & specDescriptorEndpointAddressDirectionMsk) >> 3)) - epStatus, qhIdle := primeBit, false - - qh := getQHBuffer(dc.port, 0, endpointIndex) - if 0 == qh.endpointStatus&0x1 { // bit 0: isOpened - return statusError - } - - dtdRequestCount := (length + deviceControllerDTDTotalBytes - 1) / - deviceControllerDTDTotalBytes - if 0 == dtdRequestCount { - dtdRequestCount = 1 - } - - if dtdRequestCount > uint32(dc.dtdCount) { - return statusBusy - } - - var ( - dtdHead *deviceControllerDTD - sendLength uint32 - ) - - for { - - // limit transfer length to total DTD bytes - sendLength = length - if length > deviceControllerDTDTotalBytes { - sendLength = deviceControllerDTDTotalBytes - } - length -= sendLength - - // select a free DTD - dtd := dc.dtdFree - dc.dtdFree = dtd.nextDTDPointer - dc.dtdCount-- - - // save DTD head when current active buffer offset is 0 - if 0 == currentIndex { - dtdHead = dtd - } - - // set DTD field - dtd.nextDTDPointer = deviceControllerDTDTerminate - dtd.bufferPointerPage[0] = - uint32(uintptr(unsafe.Pointer(&buffer[0]))) + uint32(currentIndex) - dtd.bufferPointerPage[1] = - (dtd.bufferPointerPage[0] + deviceControllerDTDPageBlock) & deviceControllerDTDPageMsk - dtd.bufferPointerPage[2] = - dtd.bufferPointerPage[1] + deviceControllerDTDPageBlock - dtd.bufferPointerPage[3] = - dtd.bufferPointerPage[2] + deviceControllerDTDPageBlock - dtd.bufferPointerPage[4] = - dtd.bufferPointerPage[3] + deviceControllerDTDPageBlock - - // save original buffer and length to transfer - dtd.originalBuffer = deviceControllerOriginalBuffer{ - originalBufferOffset: uint16(dtd.bufferPointerPage[0]) & - deviceControllerDTDPageOffsetMsk, - originalBufferLength: sendLength, - dtdInvalid: 0, - }.pack() - - // set IOC field in final DTD - ioc := uint8(0) - if 0 == length { - ioc = 1 - } - // set DTD active flag - dtd.dtdToken = deviceControllerDTDToken{ - status: deviceControllerDTDStatusActive, - ioc: ioc, - totalBytes: uint16(sendLength), - }.pack() - - // update buffer offset - currentIndex += int(sendLength) - - // add DTD to in-use queue - if nil != dc.dtdTail[endpointIndex] { - dc.dtdTail[endpointIndex].nextDTDPointer = dtd - dc.dtdTail[endpointIndex] = dtd - } else { - dc.dtdHead[endpointIndex] = dtd - dc.dtdTail[endpointIndex] = dtd - qhIdle = true - } - - if 0 == length { - break - } - } - - if specEndpointControl == endpoint && specIn == direction { - // get last setup packet - setupIndex := int(endpoint << 1) - setupQH := getQHBuffer(dc.port, 0, setupIndex) - setupMaxSize := uint32(setupQH.capabilities&0x07FF0000) >> 16 // bits 15-26: maxPacketSize - var setup deviceSetup - setup.parse(setupQH.setupBufferBack[:]) - if 0 != qh.endpointStatus&0x2 { // bit 1: ZLT - if (0 != sendLength) && (sendLength < uint32(setup.wLength)) && - (0 == (sendLength % setupMaxSize)) { - // enable ZLT (zlt==0) - setupQH.capabilities &^= deviceControllerCapabilities{zlt: 1}.pack() - } - } - } - - // check if QH is empty - if !qhIdle { - // if prime bit is set, nothing left to do - if dc.bus.ENDPTPRIME.HasBits(primeBit) { - return statusSuccess - } - // wait to safely transmit DTD - for { - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW) - _ = dc.bus.ENDPTSTAT.Get() // read-clear endpoint status register - if dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) { - break - } - } - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ATDTW) - } - - // if QH is empty or the endpoint is not primed, need to link current DTD head - // to the QH. if endpoint is not primed and qhIdle is false, QH is empty. - if qhIdle || 0 == epStatus&primeBit { - qh.nextDTDPointer = dtdHead - qh.dtdToken = 0 - dc.bus.ENDPTPRIME.Set(primeBit) - primeAttempt := 0 - for !dc.bus.ENDPTSTAT.HasBits(primeBit) { - if primeAttempt++; primeAttempt >= configDeviceControllerMaxPrimeAttempts { - return statusError - } - if dc.bus.ENDPTCOMPLETE.HasBits(primeBit) { - break - } - dc.bus.ENDPTPRIME.Set(primeBit) - } - } - - return statusSuccess -} - -func (dc *deviceControl) send(address uint8, buffer []uint8, length uint32) status { - return dc.transfer((address&specDescriptorEndpointAddressNumberMsk)| - (specDescriptorEndpointAddressDirectionIn), buffer, length) -} - -func (dc *deviceControl) receive(address uint8, buffer []uint8, length uint32) status { - return dc.transfer((address&specDescriptorEndpointAddressNumberMsk)| - (specDescriptorEndpointAddressDirectionOut), buffer, length) -} - -func (dc *deviceControl) cancel(address uint8) status { - return statusSuccess -} - -func (dc *deviceControl) control(command deviceControlID, param interface{}) (s status) { - - // assume success unless error condition deliberately detected - s = statusSuccess - - switch command { - case deviceControlRun: - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) - - case deviceControlStop: - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) - - case deviceControlEndpointInit: - config, ok := param.(*deviceEndpointConfig) - if !ok { - return statusInvalidParameter - } - s = dc.initEndpoint(config) - - case deviceControlEndpointDeinit: - address, ok := param.(uint8) - if !ok { - return statusInvalidParameter - } - s = dc.deinitEndpoint(address) - - case deviceControlEndpointStall: - address, ok := param.(uint8) - if !ok { - return statusInvalidParameter - } - s = dc.stallEndpoint(address) - - case deviceControlEndpointUnstall: - address, ok := param.(uint8) - if !ok { - return statusInvalidParameter - } - s = dc.unstallEndpoint(address) - - case deviceControlGetDeviceStatus: - // param should be a pointer to uint16, acting as output parameter. - stat, ok := param.(*uint16) - if !ok { - return statusInvalidParameter - } - // configDeviceSelfPowered is a configuration constant on iMXRT1062 - *stat = configDeviceSelfPowered << - specRequestStandardGetStatusDeviceSelfPoweredPos - - case deviceControlGetEndpointStatus: - // param should be pointer to deviceEndpointStatus, acting as output - // parameter. - stat, ok := param.(*deviceEndpointStatus) - if !ok { - return statusInvalidParameter - } - endpoint, direction := unpackEndpoint(stat.address) - if endpoint >= configDeviceMaxEndpoints { - return statusInvalidParameter - } - mask := uint32(nxp.USB_ENDPTCTRL0_RXS) - if specOut != direction { - mask = nxp.USB_ENDPTCTRL0_TXS - } - ctrl := dc.endpointControlRegister(endpoint) - if nil == ctrl { - return statusInvalidParameter - } - if ctrl.HasBits(mask) { - stat.status = uint16(deviceEndpointStateStalled) - } else { - stat.status = uint16(deviceEndpointStateIdle) - } - - case deviceControlPreSetDeviceAddress: - address, ok := param.(uint8) - if !ok { - return statusInvalidParameter - } - dc.bus.DEVICEADDR.Set((uint32(address) << nxp.USB_DEVICEADDR_USBADR_Pos) | - nxp.USB_DEVICEADDR_USBADRA_Msk) - - case deviceControlSetDeviceAddress: - // TODO - - case deviceControlGetSynchFrame: - return statusNotSupported - - case deviceControlSetDefaultStatus: - for i := uint8(0); i < configDeviceMaxEndpoints; i++ { - _ = dc.deinitEndpoint(i | specDescriptorEndpointAddressDirectionIn) - _ = dc.deinitEndpoint(i | specDescriptorEndpointAddressDirectionOut) - } - s = dc.resetState() - - case deviceControlGetSpeed: - // param should be a pointer to uint8, acting as output parameter. - speed, ok := param.(*uint8) - if !ok { - return statusInvalidParameter - } - *speed = dc.speed - - case deviceControlGetOTGStatus: - return statusNotSupported - - case deviceControlSetOTGStatus: - return statusNotSupported - } - - return -} - -func (dc *deviceControl) critical(enter bool) status { - if enter { - // check if critical section already locked - if dc.criticalState.Get() != 0 { - return statusRetry - } - // lock critical section - dc.criticalState.Set(1) - // disable interrupts, storing state in receiver - dc.interruptMask = arm.DisableInterrupts() - } else { - // ensure critical section is locked - if dc.criticalState.Get() != 0 { - // re-enable interrupts, using state stored in receiver - arm.EnableInterrupts(dc.interruptMask) - // unlock critical section - dc.criticalState.Set(0) - } - } - return statusSuccess -} - -func (dc *deviceControl) resetState() status { - - dc.dtdFree = dc.dtd - p := dc.dtdFree - for i := 1; i < configDeviceControllerMaxDTD; i++ { - p.nextDTDPointer = getDTDBuffer(dc.port, i) - p = p.nextDTDPointer - } - p.nextDTDPointer = nil - dc.dtdCount = configDeviceControllerMaxDTD - - // no interrupt threshold - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) - - // disable setup lockout - dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) - - // use little-endianness - dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) - - for i := 0; i < 2*configDeviceMaxEndpoints; i++ { - qh := getQHBuffer(dc.port, 0, i) - qh.capabilities = - deviceControllerCapabilities{ - maxPacketSize: configDeviceControllerMaxPacketSize, - }.pack() - qh.endpointStatus = - deviceControllerEndpointStatus{ - isOpened: 0, - }.pack() - qh.nextDTDPointer = deviceControllerDTDTerminate - dc.dtdHead[i] = nil - dc.dtdTail[i] = nil - } - dc.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer( - getQHBuffer(dc.port, 0, 0))))) - - dc.bus.DEVICEADDR.Set(0) - - // enable interrupts: bus enable, bus error, port change detect, bus reset - dc.bus.USBINTR.Set(nxp.USB_USBINTR_UE_Msk | nxp.USB_USBINTR_UEE_Msk | - nxp.USB_USBINTR_PCE_Msk | nxp.USB_USBINTR_URE_Msk) - - dc.isResetting = false - - return statusSuccess -} - -func (dc *deviceControl) reset() { - - println("reset") - - // clear setup flag - dc.bus.ENDPTSETUPSTAT.Set(dc.bus.ENDPTSETUPSTAT.Get()) - // clear endpoint complete flag - dc.bus.ENDPTCOMPLETE.Set(dc.bus.ENDPTCOMPLETE.Get()) - - // flush any pending transfers - for dc.bus.ENDPTPRIME.HasBits(nxp.USB_ENDPTPRIME_PERB_Msk | nxp.USB_ENDPTPRIME_PETB_Msk) { - dc.bus.ENDPTFLUSH.Set(nxp.USB_ENDPTFLUSH_FERB_Msk | nxp.USB_ENDPTFLUSH_FETB_Msk) - } - - // set receiver flag if port reset bit is set; otherwise, notify device class. - if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR_Msk) { - dc.isResetting = true - } else { - // send reset notification to common device - dc.device.notify(deviceNotification{code: deviceNotifyBusReset}) - } -} - -func (dc *deviceControl) tokenDone() { - println("token done") -} - -func (dc *deviceControl) portChange() { - // check if port is resetting - if !dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { - // not resetting, update bus speed - if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) { - dc.speed = specSpeedHigh - } else { - dc.speed = specSpeedFull - } - // if reset flag is set, notify device layer reset has finished - if dc.isResetting { - dc.device.notify( - deviceNotification{ - buffer: nil, - length: 0, - code: deviceNotifyBusReset, - isSetup: false, - }) - dc.isResetting = false - } - } -} - -func (dc *deviceControl) frameStart() { - println("frame start") -} - -func (dc *deviceControl) endpointControlRegister(endpoint uint8) *volatile.Register32 { - endpoint &= specDescriptorEndpointAddressNumberMsk - if endpoint < configDeviceMaxEndpoints { - switch endpoint { - case 0: - return &dc.bus.ENDPTCTRL0 - case 1: - return &dc.bus.ENDPTCTRL1 - case 2: - return &dc.bus.ENDPTCTRL2 - case 3: - return &dc.bus.ENDPTCTRL3 - case 4: - return &dc.bus.ENDPTCTRL4 - case 5: - return &dc.bus.ENDPTCTRL5 - case 6: - return &dc.bus.ENDPTCTRL6 - case 7: - return &dc.bus.ENDPTCTRL7 - } - } - return nil -} - -func (dc *deviceControl) cancelControlPipe(endpoint, direction uint8) status { - index := (endpoint << 1) + direction - message := deviceNotification{ - buffer: nil, - length: 0, - } - - // get DTD of control pipe - currentDTD := (*deviceControllerDTD)(unsafe.Pointer( - uintptr(unsafe.Pointer(dc.dtdHead[index])) & deviceControllerDTDPointerMsk)) - - for nil != currentDTD { - originalBuffer := currentDTD.originalBuffer.unpack() - dtdToken := currentDTD.dtdToken.unpack() - - // pass transfer buffer address - if nil == message.buffer { - message.buffer = *(*[]uint8)(unsafe.Pointer( - uintptr((currentDTD.bufferPointerPage[0] & deviceControllerDTDPageMsk) | - uint32(originalBuffer.originalBufferOffset)))) - } - if 0 != dtdToken.status&deviceControllerDTDStatusActive { - message.length = packU32(deviceCDCACMBufferInvalid32) - } else { - message.length += originalBuffer.originalBufferLength - uint32(dtdToken.totalBytes) - } - - if dc.dtdHead[index] == dc.dtdTail[index] { - dc.dtdHead[index] = nil - dc.dtdTail[index] = nil - qh := getQHBuffer(dc.port, 0, int(index)) - qh.nextDTDPointer = deviceControllerDTDTerminate - qh.dtdToken = 0 - } else { - dc.dtdHead[index] = dc.dtdHead[index].nextDTDPointer - } - - if 0 != currentDTD.dtdToken.unpack().ioc || - 0 == uintptr(unsafe.Pointer(dc.dtdHead[index]))&deviceControllerDTDPointerMsk { - message.code = deviceNotificationID(endpoint | - (direction << specDescriptorEndpointAddressDirectionPos)) - message.isSetup = false - dc.device.notify(message) - message.buffer = nil - message.length = 0 - } - - currentDTD.dtdToken = 0 - currentDTD.nextDTDPointer = dc.dtdFree - dc.dtdFree = currentDTD - dc.dtdCount++ - - // get DTD of control pipe - currentDTD = (*deviceControllerDTD)(unsafe.Pointer( - uintptr(unsafe.Pointer(dc.dtdHead[index])) & deviceControllerDTDPointerMsk)) - - } - return statusSuccess -} - -func (dc *deviceControl) initEndpoint(config *deviceEndpointConfig) status { - return statusSuccess -} - -func (dc *deviceControl) deinitEndpoint(address uint8) status { - return statusSuccess -} - -func (dc *deviceControl) stallEndpoint(address uint8) status { - return statusSuccess -} - -func (dc *deviceControl) unstallEndpoint(address uint8) status { - return statusSuccess -} diff --git a/src/machine/usb2/hcd.go b/src/machine/usb/hcd.go similarity index 91% rename from src/machine/usb2/hcd.go rename to src/machine/usb/hcd.go index 21dc170b2..366bdcc9e 100644 --- a/src/machine/usb2/hcd.go +++ b/src/machine/usb/hcd.go @@ -1,4 +1,4 @@ -package usb2 +package usb type hcd interface { class() class diff --git a/src/machine/usb2/hcd_mimxrt1062.go b/src/machine/usb/hcd_mimxrt1062.go similarity index 99% rename from src/machine/usb2/hcd_mimxrt1062.go rename to src/machine/usb/hcd_mimxrt1062.go index 7d39fa78a..1282765e6 100644 --- a/src/machine/usb2/hcd_mimxrt1062.go +++ b/src/machine/usb/hcd_mimxrt1062.go @@ -1,6 +1,6 @@ // +build mimxrt1062 -package usb2 +package usb // Implementation of USB host controller driver (hcd) for NXP iMXRT1062. diff --git a/src/machine/usb/host.go b/src/machine/usb/host.go deleted file mode 100644 index 61a13886c..000000000 --- a/src/machine/usb/host.go +++ /dev/null @@ -1,25 +0,0 @@ -package usb - -// Unexported USB device type definitions. -// In device mode, a USB device represents the whole USB port controller. -// In host mode, a USB device represents a peripheral device connected to the -// USB port controller. -type ( - host struct { - port uint8 - } -) - -func (h *host) init(port uint8) (s status) { - - // initialize host - h.port = port - - return statusSuccess -} - -func (h *host) deinit() status { - - // de-initialize host - return statusSuccess -} diff --git a/src/machine/usb/host_controller.go b/src/machine/usb/host_controller.go deleted file mode 100644 index c4a6ff719..000000000 --- a/src/machine/usb/host_controller.go +++ /dev/null @@ -1,13 +0,0 @@ -package usb - -// hostController provides hardware abstraction over a platform's physical -// USB host port controller (e.g., an EHCI-compliant peripheral). -// -// To connect the USB 2.0 host driver stack to a particular platform, the -// following method must be implemented: -// -// func (h *host) initController() hostController -// -// This method shall return an implementation of hostController, or nil if a -// controller cannot be allocated for the given port. -type hostController interface{} diff --git a/src/machine/usb/spec.go b/src/machine/usb/spec.go deleted file mode 100644 index 856deb36c..000000000 --- a/src/machine/usb/spec.go +++ /dev/null @@ -1,167 +0,0 @@ -package usb - -// Constants defined per USB 2.0 specification. -const ( - // USB speed - specSpeedFull = 0x00 - specSpeedLow = 0x01 - specSpeedHigh = 0x02 - specSpeedSuper = 0x04 - - // USB standard descriptor endpoint type - specEndpointControl = 0x00 - specEndpointIsochronous = 0x01 - specEndpointBulk = 0x02 - specEndpointInterrupt = 0x03 - - // USB standard descriptor transfer direction - specOut = 0 - specIn = 1 - - // USB standard descriptor length - specDescriptorLengthDevice = 18 - specDescriptorLengthConfigure = 9 - specDescriptorLengthInterface = 9 - specDescriptorLengthEndpoint = 7 - specDescriptorLengthEndpointCompanion = 6 - specDescriptorLengthDeviceQualitier = 10 - specDescriptorLengthOTGDescriptor = 5 - specDescriptorLengthBOSDescriptor = 5 - specDescriptorLengthDeviceCapabilityUSB20Extension = 7 - specDescriptorLengthDeviceCapabilitySuperspeed = 10 - - // USB device capability type codes - specDescriptorTypeDeviceCapabilityWireless = 0x01 - specDescriptorTypeDeviceCapabilityUSB20Extension = 0x02 - specDescriptorTypeDeviceCapabilitySuperspeed = 0x03 - - // USB standard descriptor type - specDescriptorTypeDevice = 0x01 - specDescriptorTypeConfigure = 0x02 - specDescriptorTypeString = 0x03 - specDescriptorTypeInterface = 0x04 - specDescriptorTypeEndpoint = 0x05 - specDescriptorTypeDeviceQualitier = 0x06 - specDescriptorTypeOtherSpeedConfiguration = 0x07 - specDescriptorTypeInterfaacePower = 0x08 - specDescriptorTypeOTG = 0x09 - specDescriptorTypeInterfaceAssociation = 0x0B - specDescriptorTypeBOS = 0x0F - specDescriptorTypeDeviceCapability = 0x10 - - specDescriptorTypeHID = 0x21 - specDescriptorTypeHIDReport = 0x22 - specDescriptorTypeHIDPhysical = 0x23 - - specDescriptorTypeCDCInterface = 0x24 - specDescriptorTypeCDCEndpoint = 0x25 - - specDescriptorTypeEndpointCompanion = 0x30 - - // USB standard request type - specRequestTypeDirMsk = 0x80 - specRequestTypeDirPos = 7 - specRequestTypeDirOut = 0x00 - specRequestTypeDirIn = 0x80 - - specRequestTypeTypeMsk = 0x60 - specRequestTypeTypePos = 5 - specRequestTypeTypeStandard = 0 - specRequestTypeTypeClass = 0x20 - specRequestTypeTypeVendor = 0x40 - - specRequestTypeRecipientMsk = 0x1F - specRequestTypeRecipientPos = 0 - specRequestTypeRecipientDevice = 0x00 - specRequestTypeRecipientInterface = 0x01 - specRequestTypeRecipientEndpoint = 0x02 - specRequestTypeRecipientOther = 0x03 - - // USB standard request - specRequestStandardGetStatus = 0x00 - specRequestStandardClearFeature = 0x01 - specRequestStandardSetFeature = 0x03 - specRequestStandardSetAddress = 0x05 - specRequestStandardGetDescriptor = 0x06 - specRequestStandardSetDescriptor = 0x07 - specRequestStandardGetConfiguration = 0x08 - specRequestStandardSetConfiguration = 0x09 - specRequestStandardGetInterface = 0x0A - specRequestStandardSetInterface = 0x0B - specRequestStandardSynchFrame = 0x0C - - // USB standard request: GET status - specRequestStandardGetStatusDeviceSelfPoweredPos = 0 - specRequestStandardGetStatusDeviceRemoteWakeupPos = 1 - - specRequestStandardGetStatusEndpointHaltMsk = 0x01 - specRequestStandardGetStatusEndpointHaltPos = 0 - - specRequestStandardGetStatusOTGStatusSelector = 0xF000 - - // USB standard request: CLEAR/SET feature - specRequestStandardFeatureSelectorEndpointHalt = 0 - specRequestStandardFeatureSelectorDeviceRemoteWakeup = 1 - specRequestStandardFeatureSelectorDeviceTestMode = 2 - specRequestStandardFeatureSelectorBHNPEnable = 3 - specRequestStandardFeatureSelectorAHNPSupport = 4 - specRequestStandardFeatureSelectorAAltHNPSupport = 5 - - // USB standard descriptor: configure attributes - specDescriptorConfigureAttributeD7Msk = 0x80 - specDescriptorConfigureAttributeD7Pos = 7 - - specDescriptorConfigureAttributeSelfPoweredMsk = 0x40 - specDescriptorConfigureAttributeSelfPoweredPos = 6 - - specDescriptorConfigureAttributeRemoteWakeupMsk = 0x20 - specDescriptorConfigureAttributeRemoteWakeupPos = 5 - - // USB standard descriptor: endpoint attributes - specDescriptorEndpointAddressDirectionMsk = 0x80 - specDescriptorEndpointAddressDirectionPos = 7 - specDescriptorEndpointAddressDirectionOut = 0 - specDescriptorEndpointAddressDirectionIn = 0x80 - - specDescriptorEndpointAddressNumberMsk = 0x0F - specDescriptorEndpointAddressNumberPos = 0 - - specDescriptorEndpointAttributeTypeMsk = 0x03 - specDescriptorEndpointAttributeNumberPos = 0 - - specDescriptorEndpointAttributeSyncTypeMsk = 0x0C - specDescriptorEndpointAttributeSyncTypePos = 2 - specDescriptorEndpointAttributeSyncTypeNoSync = 0x00 - specDescriptorEndpointAttributeSyncTypeAsync = 0x04 - specDescriptorEndpointAttributeSyncTypeAdaptive = 0x08 - specDescriptorEndpointAttributeSyncTypeSync = 0x0C - - specDescriptorEndpointAttributeUsageTypeMsk = 0x30 - specDescriptorEndpointAttributeUsageTypePos = 4 - specDescriptorEndpointAttributeUsageTypeDataEndpoint = 0x00 - specDescriptorEndpointAttributeUsageTypeFeedbackEndpoint = 0x10 - specDescriptorEndpointAttributeUsageTypeImplicitFeedbackDataEndpoint = 0x20 - - specDescriptorEndpointMaxpacketsizeSizeMsk = 0x07FF - specDescriptorEndpointMaxpacketsizeMultTransactionsMsk = 0x1800 - specDescriptorEndpointMaxpacketsizeMultTransactionsPos = 11 - - // USB standard descriptor: OTG attributes - specDescriptorOTGAttributesSRPMsk = 0x01 - specDescriptorOTGAttributesHNPMsk = 0x02 - specDescriptorOTGAttributesADPMsk = 0x04 - - // USB standard descriptor: device capability attributes (USB 2.0 extension) - specDescriptorDeviceCapabilityUSB20ExtensionLPMMsk = 0x02 - specDescriptorDeviceCapabilityUSB20ExtensionLPMPos = 1 - specDescriptorDeviceCapabilityUSB20ExtensionBESLMsk = 0x04 - specDescriptorDeviceCapabilityUSB20ExtensionBESLPos = 2 -) - -//go:inline -func unpackEndpoint(address uint8) (number, direction uint8) { - return (address & specDescriptorEndpointAddressNumberMsk) >> - specDescriptorEndpointAddressNumberPos, - (address & specDescriptorEndpointAddressDirectionMsk) >> - specDescriptorEndpointAddressDirectionPos -} diff --git a/src/machine/usb/uart.go b/src/machine/usb/uart.go index 1925aea59..a5ea35c05 100644 --- a/src/machine/usb/uart.go +++ b/src/machine/usb/uart.go @@ -5,7 +5,10 @@ import ( ) var ( - ErrInvalidPort = errors.New("invalid USB port") + 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") ) type ( @@ -16,351 +19,75 @@ type ( // UART represents a virtual serial (UART) device emulation using the USB // CDC-ACM device class driver. UART struct { - port uint8 // USB port (core index, e.g., 0-1) - desc *ConfigDeviceDescriptor // User-provided USB device descriptor information - class *deviceClass // USB device class handle - acm *deviceCDCACM // USB CDC-ACM device class handle - - attached bool - transact bool - config uint8 // selected configuration index (1-based, 0=invalid) - speed uint8 // enumerated bus speed (low, full, high) - alternate deviceCDCACMAlternateList + port int // USB port (core index, e.g., 0-1) + core *core } ) -var ( - uartAbstractState = []uint8{0x00, 0x00} - uartCountryCode = []uint8{0x00, 0x00} -) - -func (uart *UART) SetPort(port uint8) { - if port >= ConfigPortCount || port >= configDeviceCount || - port >= configDeviceCDCACMCount { - return // invalid port for USB CDC-ACM device class - } - // TODO: if changed, may need to reset port controller and tell host to - // re-enumerate devices - uart.port = port -} - -func (uart *UART) SetDeviceDescriptor(desc *ConfigDeviceDescriptor) { - if nil == desc { - return // invalid device descriptor information - } - // TODO: if changed, may need to reset port controller and tell host to - // re-enumerate devices - uart.desc = desc -} - func (uart *UART) Configure(config UARTConfig) error { - if uart.port >= ConfigPortCount || uart.port >= configDeviceCount || - uart.port >= configDeviceCDCACMCount { - return ErrInvalidPort - } - - // use default configuration index (1-based index; 0=invalid) - uart.config = configDeviceCDCACMConfigurationIndex - - // modify the global basic configuration struct configDeviceCDCACM for our USB - // port and configuration index. - // - // these settings are copied into the real CDC-ACM object, using interface - // deviceClassDriver, via initialization method (*deviceCDCACM).init(). - - // change baud rate from default, if provided - if config.BaudRate != 0 { - configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate = - config.BaudRate + if uart.port >= CoreCount || uart.port >= dcdCount { + return ErrUARTInvalidPort } // verify we have a free USB port and take ownership of it - port, status := initPort(uart.port, modeDevice) - if !status.OK() { - return status + var st status + uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1}) + if !st.ok() { + return ErrUARTInvalidPort } - - // apply the CDC-ACM configuration to our port - uart.acm, uart.class = port.initCDCACM(uart.config, uart) - - // enable USB device mode functionality, which allows a host to enumerate us - status = port.device.controller.enable(true) - if !status.OK() { - return status - } - return nil } -func (uart *UART) deviceEvent(ev deviceEventID, param interface{}) status { - - switch ev { - case deviceEventBusReset: - uart.attached = false - uart.config = 0 // clear selected configuration (1-based index) - s := uart.acm.device.busSpeed(&uart.speed) - if s.OK() { - s = uart.acm.device.setBusSpeed(uart.speed) - } - return s - - case deviceEventSetConfiguration: - if id, ok := param.(uint8); ok { - if 0 == id { - uart.attached = false - uart.config = 0 // clear selected configuration (1-based index) - return statusSuccess - } - // verify we have a config struct at given index - if nil != uart.class && nil != uart.class.config && - int(id) <= len(uart.class.config) && - int(id) <= len(configDeviceCDCACM[uart.port]) { - config := uart.class.config[id-1] // receiver configuration - system := configDeviceCDCACM[uart.port][id-1] // platform configuration - // verify the config is a CDC device and has a defined driver - if deviceClassCDC == config.info.classID && nil != config.driver { - // finally, verify the config driver is an ACM device driver - if _, ok := config.driver.(*deviceCDCACM); ok { - uart.attached = true - uart.config = id - return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:], - uint32(system.dataBulkOutPacketSize)) - } - } - } - } - return statusNotSupported // all non-error paths return early - - case deviceEventSetInterface: - if uart.attached { - if setting, ok := param.(uint16); ok { - inf := (setting & 0xFF00) >> 8 - if inf < configDeviceCDCACMInterfaceCount { - uart.alternate[inf] = setting & 0x00FF - } - } - } - - case deviceEventGetConfiguration: - case deviceEventGetInterface: - case deviceEventGetDeviceDescriptor: - case deviceEventGetConfigurationDescriptor: - case deviceEventGetStringDescriptor: +// Buffered returns the number of bytes currently stored in the RX buffer. +func (uart UART) Buffered() int { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0 } - - return statusSuccess + return dc.uartAvailable() } -func (uart *UART) classEvent(ev uint32, param interface{}) status { - - if nil == uart.acm { - return statusInvalidHandle +// 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) { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0, ErrUARTInvalidCore } - - switch deviceCDCACMEventID(ev) { - case deviceCDCACMEventSendResponse: - if ctl, ok := param.(deviceEndpointControlMessage); ok { - - // verify receiver configuration - if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 || - int(uart.config) > len(configDeviceCDCACM[uart.port]) { - return statusNotSupported - } - // get platform configuration - system := configDeviceCDCACM[uart.port][uart.config-1] - - if ctl.length > 0 && 0 != ctl.length%uint32(system.dataBulkInPacketSize) { - // If the last packet is the size of endpoint, then send an additional - // zero-ended packet to notify the host it may flush output. - return uart.acm.send(system.dataBulkInEndpoint, nil, 0) - } - - if uart.attached && uart.transact && - (nil != ctl.buffer || (nil == ctl.buffer && 0 == ctl.length)) { - // send complete, schedule buffer for next receive event - return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:], - uint32(system.dataBulkOutPacketSize)) - } - } - return statusError // all non-error paths return early - - case deviceCDCACMEventRecvResponse: - if ctl, ok := param.(deviceEndpointControlMessage); ok { - if uart.attached && uart.transact { - uart.acm.recvSize = ctl.length - if 0 == ctl.length { - - // verify receiver configuration - if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 || - int(uart.config) > len(configDeviceCDCACM[uart.port]) { - return statusNotSupported - } - // get platform configuration - system := configDeviceCDCACM[uart.port][uart.config-1] - - // schedule buffer for next receive event - return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:], - uint32(system.dataBulkOutPacketSize)) - } - } - } - return statusError // all non-error paths return early - - case deviceCDCACMEventSerialStateNotify: - uart.acm.hasSentState = false - return statusSuccess - - case deviceCDCACMEventSendEncapsulatedCommand: - return statusNotImplemented - - case deviceCDCACMEventGetEncapsulatedResponse: - return statusNotImplemented - - case deviceCDCACMEventSetCommFeature: - if req, ok := param.(deviceCDCACMRequestParam); ok { - switch req.setupValue { - case deviceCDCFeatureAbstractState: - if req.isSetup { - *(req.buffer) = uartAbstractState - } else { - *(req.length) = 0 - } - return statusSuccess - - case deviceCDCFeatureCountrySetting: - if req.isSetup { - *(req.buffer) = uartCountryCode - } else { - *(req.length) = 0 - } - return statusSuccess - } - return statusInvalidParameter // unrecognized request code - } - return statusError // all non-error paths return early - - case deviceCDCACMEventGetCommFeature: - if req, ok := param.(deviceCDCACMRequestParam); ok { - switch req.setupValue { - case deviceCDCFeatureAbstractState: - *(req.buffer) = uartAbstractState - *(req.length) = uint32(len(uartAbstractState)) - return statusSuccess - - case deviceCDCFeatureCountrySetting: - *(req.buffer) = uartCountryCode - *(req.length) = uint32(len(uartCountryCode)) - return statusSuccess - } - return statusInvalidParameter // unrecognized request code - } - return statusError // all non-error paths return early - - case deviceCDCACMEventClearCommFeature: - return statusNotImplemented - - case deviceCDCACMEventGetLineCoding: - if req, ok := param.(deviceCDCACMRequestParam); ok { - // verify receiver configuration - if int(uart.port) >= len(deviceCDCACMLineCoding) || uart.config == 0 || - int(uart.config) > len(deviceCDCACMLineCoding[uart.port]) { - return statusNotSupported - } - lineCoding := deviceCDCACMLineCoding[uart.port][uart.config-1] - *(req.buffer) = lineCoding - *(req.length) = uint32(len(lineCoding)) - return statusSuccess - } - return statusError // all non-error paths return early - - case deviceCDCACMEventSetLineCoding: - if req, ok := param.(deviceCDCACMRequestParam); ok { - // verify receiver configuration - if int(uart.port) >= len(deviceCDCACMLineCoding) || uart.config == 0 || - int(uart.config) > len(deviceCDCACMLineCoding[uart.port]) { - return statusNotSupported - } - lineCoding := deviceCDCACMLineCoding[uart.port][uart.config-1] - if req.isSetup { - *(req.buffer) = lineCoding - } else { - *(req.length) = uint32(len(lineCoding)) - } - return statusSuccess - } - return statusError // all non-error paths return early - - case deviceCDCACMEventSetControlLineState: - if req, ok := param.(deviceCDCACMRequestParam); ok { - // verify receiver configuration - if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 || - int(uart.config) > len(configDeviceCDCACM[uart.port]) { - return statusNotSupported - } - // get platform configuration - system := configDeviceCDCACM[uart.port][uart.config-1] - - uart.acm.info.dteStatus = uint8(req.setupValue) - - // activate/deactivate Tx carrier - if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapCarrierActivation { - uart.acm.info.uartState |= deviceCDCUARTStateTxCarrier - } else { - uart.acm.info.uartState &^= deviceCDCUARTStateTxCarrier - } - - // activate/deactivate DTE and carrier - if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapDTEPresence { - uart.acm.info.uartState |= deviceCDCUARTStateRxCarrier - uart.acm.info.dtePresent = true // serial device is now open on host - } else { - uart.acm.info.uartState &^= deviceCDCUARTStateRxCarrier - uart.acm.info.dtePresent = false // serial device is now closed on host - } - - uart.acm.info.serialState[0] = deviceCDCACMRequestNotify // bmRequestType - uart.acm.info.serialState[1] = deviceCDCNotifySerialState // bNotification - uart.acm.info.serialState[2] = 0 // wValue (lo) - uart.acm.info.serialState[3] = 0 // wValue (hi) - uart.acm.info.serialState[4] = uint8(req.interfaceIndex) // wIndex (lo) - uart.acm.info.serialState[5] = uint8(req.interfaceIndex >> 8) // wIndex (hi) - uart.acm.info.serialState[6] = deviceCDCACMInfoUARTBitmapSize // wLength (lo) - uart.acm.info.serialState[7] = 0 // wLength (hi) - uart.acm.info.serialState[8] = uint8(uart.acm.info.uartState) // UART bitmap (lo) - uart.acm.info.serialState[9] = uint8(uart.acm.info.uartState >> 8) // UART bitmap (hi) - - s := statusSuccess - if !uart.acm.hasSentState { - s = uart.acm.send(system.commInterruptInEndpoint, - uart.acm.info.serialState[:], deviceCDCACMSerialStateSize) - uart.acm.hasSentState = true - } - - // update status - if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapCarrierActivation { - // carrier activated - } - if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapDTEPresence { - // DTE activated - if uart.attached { - uart.transact = true - } - } else { - // DTE deactivated - if uart.attached { - uart.transact = false - } - } - - return s - } - return statusError // all non-error paths return early - - case deviceCDCACMEventSendBreak: - return statusNotImplemented - - default: - return statusInvalidParameter + n, ok := 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) { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0, ErrUARTInvalidCore + } + return dc.uartRead(data), nil +} + +// WriteByte writes a single byte of data to the UART interface. +func (uart UART) WriteByte(c byte) error { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return ErrUARTInvalidCore + } + if !dc.uartWriteByte(c) { + return ErrUARTWriteFailed + } + return nil +} + +// Write data to the UART. +func (uart UART) Write(data []byte) (n int, err error) { + dc, ok := uart.core.dc.(*deviceController) + if !ok { + return 0, ErrUARTInvalidCore + } + return dc.uartWrite(data), nil } diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go index 888d2cfcd..4f3d4e2cf 100644 --- a/src/machine/usb/usb.go +++ b/src/machine/usb/usb.go @@ -1,416 +1,157 @@ package usb -type ( - // status is the common error code used internally for package operations. - status int8 - // mode defines the operating mode of a USB port. - mode int8 +// Hardware abstraction for USB ports configured as either host or device. - // port represents a physical USB port, which may be configured as either a - // device or as a host. - port struct { - mode mode - device device - host host - } -) - -// Constants for unexported types shared across entire package. -const ( - statusSuccess status = iota // Success - statusError // Failed - statusBusy // Busy - statusInvalidHandle // Invalid handle - statusInvalidParameter // Invalid parameter - statusInvalidRequest // Invalid request - statusControllerNotFound // Controller cannot be found - statusInvalidController // Invalid controller interface - statusNotSupported // Configuration is not supported - statusRetry // Enumeration get configuration retry - statusTransferStall // Transfer stalled - statusTransferFailed // Transfer failed - statusAllocFail // Allocation failed - statusLackSwapBuffer // Insufficient swap buffer for KHCI - statusTransferCancel // The transfer cancelled - statusBandwidthFail // Allocate bandwidth failed - statusMSDStatusFail // For MSD, the CSW status means fail - statusEHCIAttached // EHCI attached - statusEHCIDetached // EHCI detached - statusDataOverRun // Endpoint data (Rx) exceeds max size - statusNotImplemented // Supported feature not implemented - - modeIdle mode = iota // USB core idle (unallocated) - modeDevice // USB device mode - modeHost // USB host mode -) - -var ( - // portInstance holds instances for all available ports on the platform. - portInstance [ConfigPortCount]port -) +import "unsafe" func init() { - // ensure all ports are in idle state by default - for i := range portInstance { - portInstance[i].mode = modeIdle + if unsafe.Sizeof(uintptr(0)) > 4 { + panic("USB is only supported on 32-bit systems") } } -func ProcessMessages() (err error) { - for i := range portInstance { - s := portInstance[i].process() - if !s.OK() && nil == err { - err = s +// 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 + modeDevice = 1 + modeHost = 2 +) + +// 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 +} + +// Constant definitions for all host/device classes. +const ( + classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) +) + +// mode returns the USB core operating mode of the receiver class cl. +//go:inline +func (cl class) mode() int { + switch cl.id { + case classDeviceCDCACM: + 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 cl. +//go:inline +func (cl class) equals(class class) bool { + return cl.id == class.id && cl.config == class.config +} + +// 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 + +// 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 + statusRetry // Retry + statusInvalid // Invalid argument +) + +// ok returns true if and only if the receiver st equals statusOK. +//go:inline +func (st status) ok() bool { return statusOK == st } + +// 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) { + + 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. + // For instance, this will allow 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 -} - -// initPort configures the mode for a given USB port and initializes the -// hardware's port controller. If the port is invalid or not idle (it has -// already been configured), it returns nil and a status code. -func initPort(port uint8, mode mode) (*port, status) { - if port >= ConfigPortCount || int(port) >= len(portInstance) { - return nil, statusInvalidController - } - if modeIdle != portInstance[port].mode { return nil, statusBusy } - portInstance[port].mode = mode - switch mode { + + switch class.mode() { case modeDevice: - if s := portInstance[port].device.init(port); !s.OK() { - return nil, s + // Allocate a free device controller and install interrupts + dc, st := initDCD(port, class) + if !st.ok() { + return nil, st } - case modeHost: - if s := portInstance[port].host.init(port); !s.OK() { - return nil, s + // 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 + // Enable interrupts and enter runtime + if st = dc.enable(true); !st.ok() { + coreInstance[port].mode = modeIdle + coreInstance[port].dc = nil + return nil, st } - } - return &portInstance[port], statusSuccess -} -// deinit disables the receiver USB port, changing its mode to idle, freeing it -// for reuse or reconfiguration. -func (p *port) deinit() status { - switch p.mode { - case modeDevice: - return p.device.deinit() case modeHost: - return p.host.deinit() + // 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 + // Enable interrupts and enter runtime + if st = hc.enable(true); !st.ok() { + coreInstance[port].mode = modeIdle + coreInstance[port].hc = nil + return nil, st + } + default: - return statusInvalidController + return nil, statusInvalid } -} - -func (p *port) process() status { - switch p.mode { - case modeDevice: - return p.device.controller.process() - case modeHost: - return statusSuccess // TODO: not implemented - default: - return statusInvalidController - } -} - -// initCDCACM applies a CDC-ACM configuration to the receiver port p and then -// returns the configured deviceClassDriver and deviceClass that were assigned -// to the receiver. -// -// The given deviceClassEventHandler is called for any USB device-level event -// notifications received, which allows an upper-layer CDC-ACM driver (such as -// a UART interface implementation) the opportunity to handle device events. -func (p *port) initCDCACM(id uint8, handler deviceClassEventHandler) (*deviceCDCACM, *deviceClass) { - - // verify a valid port was provided - if nil == p || nil == p.device.controller || p.mode != modeDevice { - return nil, nil - } - - if 0 == id || int(id) > len(configDeviceCDCACM[p.device.port]) { - return nil, nil - } - - // get a reference to each of the class interfaces - comm := &deviceCDCACMConfigInstance[p.device.port][id-1].info.interfaceList[0] - data := &deviceCDCACMConfigInstance[p.device.port][id-1].info.interfaceList[1] - - // configDeviceCDCACM must be defined per package API. these settings will be - // platform-specific, and will probably be implemented in a build tag- - // constrained source file. the length of this array corresponds to the number - // of USB CDC-ACM ports that are being created, and the index of each element - // corresponds to the physical USB port (core index). Each element is a slice - // of alternate device configurations that may be selected for a given port. - - // CDC-ACM Communication/control interface - comm.interfaceNumber = - configDeviceCDCACM[p.device.port][id-1].commInterfaceIndex - - comm.deviceInterface[0].endpoint[0].address = - configDeviceCDCACM[p.device.port][id-1].commInterruptInEndpoint | - specDescriptorEndpointAddressDirectionIn - - comm.deviceInterface[0].endpoint[0].maxPacketSize = - configDeviceCDCACM[p.device.port][id-1].commInterruptInPacketSize - - comm.deviceInterface[0].endpoint[0].interval = - configDeviceCDCACM[p.device.port][id-1].commInterruptInInterval - - // CDC-ACM Data interface - data.interfaceNumber = - configDeviceCDCACM[p.device.port][id-1].dataInterfaceIndex - - data.deviceInterface[0].endpoint[0].address = - configDeviceCDCACM[p.device.port][id-1].dataBulkInEndpoint | - specDescriptorEndpointAddressDirectionIn - - data.deviceInterface[0].endpoint[0].maxPacketSize = - configDeviceCDCACM[p.device.port][id-1].dataBulkInPacketSize - - data.deviceInterface[0].endpoint[1].address = - configDeviceCDCACM[p.device.port][id-1].dataBulkOutEndpoint | - specDescriptorEndpointAddressDirectionOut - - data.deviceInterface[0].endpoint[1].maxPacketSize = - configDeviceCDCACM[p.device.port][id-1].dataBulkOutPacketSize - - // assign our configured CDC-ACM class to the receiver's device and call its - // class initialization routine(s). - cls := p.device.initClass(id, deviceCDCACMConfigInstance[p.device.port], handler) - acm := cls.config[0].driver.(*deviceCDCACM) - - return acm, cls -} - -// OK returns true if and only if the receiver s is equal to statusSuccess. -//go:inline -func (s status) OK() bool { return statusSuccess == s } - -// Error returns a simple descriptive error string of the receiver s. -func (s status) Error() string { - switch s { - case statusSuccess: - return "" - case statusError: - return "failed" - case statusBusy: - return "busy" - case statusInvalidHandle: - return "invalid handle" - case statusInvalidParameter: - return "invalid parameter" - case statusInvalidRequest: - return "invalid request" - case statusControllerNotFound: - return "controller not found" - case statusInvalidController: - return "invalid controller interface" - case statusNotSupported: - return "configuration not supported" - case statusRetry: - return "retry enumeration" - case statusTransferStall: - return "transfer stalled" - case statusTransferFailed: - return "transfer failed" - case statusAllocFail: - return "allocation failed" - case statusLackSwapBuffer: - return "insufficient swap buffer" - case statusTransferCancel: - return "transfer cancelled" - case statusBandwidthFail: - return "bandwidth allocation failed" - case statusMSDStatusFail: - return "mass-storage device failed" - case statusEHCIAttached: - return "host attached" - case statusEHCIDetached: - return "host detached" - case statusDataOverRun: - return "data overrun" - case statusNotImplemented: - return "feature not implemented" - default: - return "unknown error" - } -} - -// 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 + + return &coreInstance[port], statusOK } diff --git a/src/machine/usb2/util.go b/src/machine/usb/util.go similarity index 99% rename from src/machine/usb2/util.go rename to src/machine/usb/util.go index 35a082a7a..94dfff99d 100644 --- a/src/machine/usb2/util.go +++ b/src/machine/usb/util.go @@ -1,4 +1,4 @@ -package usb2 +package usb // leU64 returns a slice containing 8 bytes from the given uint64 u. // diff --git a/src/machine/usb2/util_arm.go b/src/machine/usb/util_arm.go similarity index 96% rename from src/machine/usb2/util_arm.go rename to src/machine/usb/util_arm.go index 8f7b8a2a9..89ab4f646 100644 --- a/src/machine/usb2/util_arm.go +++ b/src/machine/usb/util_arm.go @@ -1,6 +1,6 @@ // +build arm -package usb2 +package usb import "device/arm" diff --git a/src/machine/usb2/uart.go b/src/machine/usb2/uart.go deleted file mode 100644 index a5e32ff0e..000000000 --- a/src/machine/usb2/uart.go +++ /dev/null @@ -1,93 +0,0 @@ -package usb2 - -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") -) - -type ( - UARTConfig struct { - BaudRate uint32 - } - - // UART represents a virtual serial (UART) device emulation using the USB - // CDC-ACM device class driver. - UART struct { - port int // USB port (core index, e.g., 0-1) - core *core - } -) - -func (uart *UART) Configure(config UARTConfig) error { - - if uart.port >= CoreCount || uart.port >= dcdCount { - return ErrUARTInvalidPort - } - - // 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 { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0 - } - return 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) { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0, ErrUARTInvalidCore - } - n, ok := 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) { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0, ErrUARTInvalidCore - } - return dc.uartRead(data), nil -} - -// WriteByte writes a single byte of data to the UART interface. -func (uart UART) WriteByte(c byte) error { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return ErrUARTInvalidCore - } - if !dc.uartWriteByte(c) { - return ErrUARTWriteFailed - } - return nil -} - -// Write data to the UART. -func (uart UART) Write(data []byte) (n int, err error) { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0, ErrUARTInvalidCore - } - return dc.uartWrite(data), nil -} diff --git a/src/machine/usb2/usb.go b/src/machine/usb2/usb.go deleted file mode 100644 index e78bdbd04..000000000 --- a/src/machine/usb2/usb.go +++ /dev/null @@ -1,157 +0,0 @@ -package usb2 - -// Hardware abstraction for USB ports configured as either host or device. - -import "unsafe" - -func init() { - if unsafe.Sizeof(uintptr(0)) > 4 { - panic("USB is only supported on 32-bit systems") - } -} - -// 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 - modeDevice = 1 - modeHost = 2 -) - -// 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 -} - -// Constant definitions for all host/device classes. -const ( - classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) -) - -// mode returns the USB core operating mode of the receiver class cl. -//go:inline -func (cl class) mode() int { - switch cl.id { - case classDeviceCDCACM: - 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 cl. -//go:inline -func (cl class) equals(class class) bool { - return cl.id == class.id && cl.config == class.config -} - -// 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 - -// 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 - statusRetry // Retry - statusInvalid // Invalid argument -) - -// ok returns true if and only if the receiver st equals statusOK. -//go:inline -func (st status) ok() bool { return statusOK == st } - -// 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) { - - 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. - // For instance, this will allow 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 - // Enable interrupts and enter runtime - if st = dc.enable(true); !st.ok() { - coreInstance[port].mode = modeIdle - coreInstance[port].dc = nil - return nil, st - } - - 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 - // Enable interrupts and enter runtime - if st = hc.enable(true); !st.ok() { - coreInstance[port].mode = modeIdle - coreInstance[port].hc = nil - return nil, st - } - - default: - return nil, statusInvalid - } - - return &coreInstance[port], statusOK -} From 0ff290cecc411abd7a3f0bdb8fe4cfa045a80347 Mon Sep 17 00:00:00 2001 From: ardnew Date: Thu, 22 Apr 2021 18:14:30 -0500 Subject: [PATCH 6/9] prevent heap alloc in control complete --- src/device/nxp/mimxrt1062_mpu.go | 1 - src/machine/usb/dcd_mimxrt1062.go | 149 ++++++++++++++++-------------- src/machine/usb/desc.go | 15 --- src/machine/usb/hcd_mimxrt1062.go | 20 ++-- 4 files changed, 88 insertions(+), 97 deletions(-) diff --git a/src/device/nxp/mimxrt1062_mpu.go b/src/device/nxp/mimxrt1062_mpu.go index adf5bb5c7..5bef7aaa1 100644 --- a/src/device/nxp/mimxrt1062_mpu.go +++ b/src/device/nxp/mimxrt1062_mpu.go @@ -278,7 +278,6 @@ func enableDcache(enable bool) { } } - // FlushDcache flushes data from cache to memory // // Normally FlushDcache is used when metadata written to memory will be used by diff --git a/src/machine/usb/dcd_mimxrt1062.go b/src/machine/usb/dcd_mimxrt1062.go index 38aa4ddb0..a66065cdc 100644 --- a/src/machine/usb/dcd_mimxrt1062.go +++ b/src/machine/usb/dcd_mimxrt1062.go @@ -658,16 +658,72 @@ func (dc *deviceController) control(setup dcdSetup) { dc.bus.ENDPTCTRL0.Set(0x00010001) } -// controlTransfers returns the data and ackowledgement transfer descriptors for -// the control endpoint (i.e., endpoint 0). -//go:inline -func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { - // control endpoint is device class-specific - switch dc.cc.id { - case classDeviceCDCACM: - return descCDCACM[dc.cc.config-1].cd, descCDCACM[dc.cc.config-1].ad +// controlComplete handles the setup completion of control endpoint 0. +func (dc *deviceController) controlComplete() { + + // First, switch on the type of request (standard, class, or vendor) + switch dc.setup.bmRequestType & descRequestTypeTypeMsk { + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch dc.setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch dc.setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch dc.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + acm := &descCDCACM[dc.cc.config-1] + + // Determine interface destination of the notification + switch dc.setup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + + acm.lineCoding.baud = packU32(acm.cx[:]) + acm.lineCoding.stopBits = acm.cx[4] + if 0 == acm.lineCoding.stopBits { + acm.lineCoding.stopBits = 1 + } + acm.lineCoding.parity = acm.cx[5] + acm.lineCoding.numBits = acm.cx[6] + + if 134 == acm.lineCoding.baud { + dc.enableSofInterrupts(true, descCDCACMInterfaceCount) + dc.rebootTimer = 80 + } + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + default: - return nil, nil + // Unhandled request type } } @@ -782,6 +838,19 @@ func (dc *deviceController) controlDescriptor(setup dcdSetup) { } +// controlTransfers returns the data and ackowledgement transfer descriptors for +// the control endpoint (i.e., endpoint 0). +//go:inline +func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { + // control endpoint is device class-specific + switch dc.cc.id { + case classDeviceCDCACM: + return descCDCACM[dc.cc.config-1].cd, descCDCACM[dc.cc.config-1].ad + default: + return nil, nil + } +} + // controlReceive receives (Rx, OUT) data on control endpoint 0. func (dc *deviceController) controlReceive( data uintptr, size uint32, notify bool) { @@ -858,68 +927,6 @@ func (dc *deviceController) controlTransmit( } } -// controlComplete handles the setup completion of control endpoint 0. -func (dc *deviceController) controlComplete() { - - // First, switch on the type of request (standard, class, or vendor) - switch dc.setup.bmRequestType & descRequestTypeTypeMsk { - - // === CLASS REQUEST === - case descRequestTypeTypeClass: - - // Switch on the recepient and direction of the request - switch dc.setup.bmRequestType & - (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { - - // --- INTERFACE Rx (OUT) --- - case descRequestTypeRecipientInterface | descRequestTypeDirOut: - - // Identify which request was received - switch dc.setup.bRequest { - - // CDC | SET LINE CODING (0x20): - case descCDCRequestSetLineCoding: - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - acm := &descCDCACM[dc.cc.config-1] - - // Determine interface destination of the notification - switch dc.setup.wIndex { - - // Control/status interface: - case descCDCACMInterfaceCtrl: - if acm.lineCoding.parse(acm.cx[:]) { - if 134 == acm.lineCoding.baud { - dc.enableSofInterrupts(true, descCDCACMInterfaceCount) - dc.rebootTimer = 80 - } - } - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - default: - // Unhandled request - } - - default: - // Unhandled recepient or direction - } - - default: - // Unhandled request type - } -} - // 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 diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go index 92bc22375..6f6d37948 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -475,21 +475,6 @@ type descCDCACMLineCoding struct { rtsdtr uint8 } -func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool { - if len(buffer) < descCDCACMCodingSize { - return false - } - _ = copy(buffer[:], buffer) - lc.baud = packU32(buffer[:]) - lc.stopBits = buffer[4] - if 0 == lc.stopBits { - lc.stopBits = 1 - } - lc.parity = buffer[5] - lc.numBits = buffer[6] - return true -} - const ( descStringIndexCount = 4 // Language, Manufacturer, Product, Serial Number descStringSize = 64 // (64-2)/2 = 31 chars each (UTF-16 code points) diff --git a/src/machine/usb/hcd_mimxrt1062.go b/src/machine/usb/hcd_mimxrt1062.go index 1282765e6..99d8b59f1 100644 --- a/src/machine/usb/hcd_mimxrt1062.go +++ b/src/machine/usb/hcd_mimxrt1062.go @@ -56,20 +56,20 @@ func initHCD(port int, class class) (hcd, status) { case 0: hostControllerInstance[i].bus = nxp.USB1 hostControllerInstance[i].phy = nxp.USBPHY1 - hostControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG1, - func(interrupt.Interrupt) { - coreInstance[0].hc.interrupt() - }) + //hostControllerInstance[i].irq = + // interrupt.New(nxp.IRQ_USB_OTG1, + // func(interrupt.Interrupt) { + // coreInstance[0].hc.interrupt() + // }) case 1: hostControllerInstance[i].bus = nxp.USB2 hostControllerInstance[i].phy = nxp.USBPHY2 - hostControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG2, - func(interrupt.Interrupt) { - //coreInstance[1].hc.interrupt() - }) + //hostControllerInstance[i].irq = + // interrupt.New(nxp.IRQ_USB_OTG2, + // func(interrupt.Interrupt) { + // //coreInstance[1].hc.interrupt() + // }) } return &hostControllerInstance[i], statusOK } From 43d5eee1e614d00fd85a045d25fbf13244a5bf3f Mon Sep 17 00:00:00 2001 From: ardnew Date: Tue, 27 Apr 2021 10:14:23 -0500 Subject: [PATCH 7/9] begin isolating target-specific USB code --- ...imxrt1062_usb.go => machine_common_usb.go} | 0 src/machine/usb/dcd.go | 615 +++++++- src/machine/usb/dcd_mimxrt1062.go | 1338 ----------------- src/machine/usb/desc.go | 190 ++- src/machine/usb/desc_mimxrt1062.go | 64 +- src/machine/usb/dhw_mimxrt1062.go | 1073 +++++++++++++ src/machine/usb/hcd.go | 57 +- src/machine/usb/hcd_mimxrt1062.go | 130 -- src/machine/usb/hhw_mimxrt1062.go | 59 + src/machine/usb/uart.go | 30 +- src/machine/usb/usb.go | 143 +- src/machine/usb/util_arm.go | 11 + 12 files changed, 1968 insertions(+), 1742 deletions(-) rename src/machine/{machine_mimxrt1062_usb.go => machine_common_usb.go} (100%) delete mode 100644 src/machine/usb/dcd_mimxrt1062.go create mode 100644 src/machine/usb/dhw_mimxrt1062.go delete mode 100644 src/machine/usb/hcd_mimxrt1062.go create mode 100644 src/machine/usb/hhw_mimxrt1062.go diff --git a/src/machine/machine_mimxrt1062_usb.go b/src/machine/machine_common_usb.go similarity index 100% rename from src/machine/machine_mimxrt1062_usb.go rename to src/machine/machine_common_usb.go diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go index d6527f9b0..ba4393439 100644 --- a/src/machine/usb/dcd.go +++ b/src/machine/usb/dcd.go @@ -1,50 +1,74 @@ package usb +// Implementation of 32-bit target-agnostic USB device controller driver (dcd). + import "unsafe" -type dcd interface { - class() class - init() status - enable(enable bool) status - critical(enter bool) status - interrupt() - receive(endpoint uint8, transfer *dcdTransfer) - transmit(endpoint uint8, transfer *dcdTransfer) - control(setup dcdSetup) +func init() { + if unsafe.Sizeof(uintptr(0)) > 4 { + panic("USB device controller is only supported on 32-bit systems") + } } -const ( - dcdEndpointSize = 64 // bytes - dcdTransferSize = 32 // - dcdSetupSize = 8 // -) +// 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 // + ... -type dcdEndpoint struct { - config uint32 // 4 - current *dcdTransfer // 4 *dcdTransfer - transfer dcdTransfer // 32 - setup dcdSetup // 8 - // Endpoints are 48-byte data structures. The remaining data extends this to - // 64-byte, and also makes it simpler for implementations to align endpoints - // allocated contiguously on 64-byte boundaries. - first *dcdTransfer // 4 *dcdTransfer - last *dcdTransfer // 4 *dcdTransfer - // 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 descriptor. - callback dcdTransferCallback // 8 +// 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 } -type dcdTransferCallback func(transfer *dcdTransfer) -type dcdTransfer struct { - next *dcdTransfer // 4 *dcdTransfer - token uint32 // 4 - pointer [5]uintptr // 20 - param uint32 // 4 +// 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 @@ -53,30 +77,507 @@ type dcdSetup struct { wLength uint16 } +// pack returns the receiver setup packet encoded as uint64. func (s dcdSetup) pack() uint64 { - return ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8 - ((uint64(s.bRequest) & 0xFF) << 8) | // uint8 - ((uint64(s.wValue) & 0xFFFF) << 16) | // uint16 - ((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16 - ((uint64(s.wLength) & 0xFFFF) << 48) // uint16 + 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) } -var ( - // dcdPointerNil is a sentinel value used to indicate a pointer to an invalid - // value. Do not attempt to dereference! - dcdPointerNil = uintptr(0) - // dcdTransferEOL is a sentinel value used to indicate the final node in a - // linked list of transfer descriptors. - dcdTransferEOL = (*dcdTransfer)(unsafe.Pointer(dcdTransferPointerEOL)) - // dcdTransferPointerEOL is the notional memory address of dcdTransferEOL. - // The address does not refer to an actual dcdTransfer, so no attempt should - // be made to dereference it. - dcdTransferPointerEOL = uintptr(1) +// 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 ) -// 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 *dcdTransfer) nextTransfer() (*dcdTransfer, bool) { - return t.next, uintptr(unsafe.Pointer(t.next)) == dcdTransferPointerEOL +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: + d.controlStatus() + 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.controlDeviceAddress(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) + + 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: + d.controlDescriptor(sup) + return dcdStageSetup + + // 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: + d.controlDescriptor(sup) + return dcdStageSetup + + default: + // Unhandled request + } + + // --- ENDPOINT Rx (OUT) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirOut: + + // Identify which request was received + switch sup.bRequest { + + // CLEAR FEATURE (0x01): + case descRequestStandardClearFeature: + // TODO + + // SET FEATURE (0x03): + case descRequestStandardSetFeature: + // TODO + + 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) + 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 notification + switch sup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + 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 + } + + 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 notification + switch d.setup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + + // Notify PHY to handle triggers like special baud rates, which + // signal to reboot into bootloader or begin receiving OTA updates + d.controlLineCoding(descCDCACMLineCoding{ + baud: packU32(acm.cx[:]), + stopBits: acm.cx[4], + parity: acm.cx[5], + numBits: acm.cx[6], + }) + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + + default: + // Unhandled request type + } +} + +func (d *dcd) controlDescriptor(sup dcdSetup) { + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + acm := &descCDCACM[d.cc.config-1] + dxn := uint8(0) + + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { + + // 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 + 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) + } + + default: + // Unhandled device class + } + } diff --git a/src/machine/usb/dcd_mimxrt1062.go b/src/machine/usb/dcd_mimxrt1062.go deleted file mode 100644 index a66065cdc..000000000 --- a/src/machine/usb/dcd_mimxrt1062.go +++ /dev/null @@ -1,1338 +0,0 @@ -// +build mimxrt1062 - -package usb - -// Implementation of USB device controller interface (dcd) for NXP iMXRT1062. - -import ( - "device/arm" - "device/nxp" - "math/bits" - "runtime/interrupt" - "runtime/volatile" - "unsafe" -) - -// 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 - -// dcdInterruptPriority defines the priority for all USB device interrupts. -const dcdInterruptPriority = 3 - -// deviceController implements USB device controller driver (dcd) interface. -type deviceController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - cc class // USB device class - id int // deviceControllerInstance index - - bus *nxp.USB_Type - phy *nxp.USBPHY_Type - irq interrupt.Interrupt - - cri volatile.Register8 // set to 1 if in critical section, else 0 - ivm uintptr // interrupt state when entering critical section - - stat *dcdEndpoint // endpoint 0 Rx ("out" direction) - ctrl *dcdEndpoint // endpoint 0 Tx ("in" direction) - - timerInterrupt [2]func() - controlNotify uint32 - endpointNotify uint32 - sofUsage uint8 - rebootTimer uint8 - setup dcdSetup - controlReply [8]uint8 - - speed uint8 // bus speed (0=full, 1=low, 2=high, 4=super) -} - -// deviceControllerInstance provides statically-allocated instances of each USB -// device controller configured on this platform. -var deviceControllerInstance [dcdCount]deviceController - -// 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() -} - -//go:linkname ticks runtime.ticks -func ticks() int64 - -// 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 deviceControllerInstance { - if nil == deviceControllerInstance[i].core { - // Initialize device controller. - deviceControllerInstance[i].core = &coreInstance[port] - deviceControllerInstance[i].port = port - deviceControllerInstance[i].cc = class - deviceControllerInstance[i].id = i - switch port { - case 0: - deviceControllerInstance[i].bus = nxp.USB1 - deviceControllerInstance[i].phy = nxp.USBPHY1 - deviceControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG1, - func(interrupt.Interrupt) { - coreInstance[0].dc.interrupt() - }) - - case 1: - deviceControllerInstance[i].bus = nxp.USB2 - deviceControllerInstance[i].phy = nxp.USBPHY2 - deviceControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG2, - func(interrupt.Interrupt) { - //coreInstance[1].dc.interrupt() - }) - } - return &deviceControllerInstance[i], statusOK - } - } - return nil, statusBusy // No free device controller instances available. -} - -func (dc *deviceController) class() class { return dc.cc } - -func (dc *deviceController) init() status { - // reset the controller - dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) - for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { - } - // clear interrupts - m := arm.DisableInterrupts() - switch dc.port { - case 0: - arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) - case 1: - arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) - } - dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) - dc.phy.PWD.Set(0) - - // clear the controller mode field and set to device mode: - // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only - dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, - nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) - - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold - dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout - dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness - - dc.stat = dc.endpointQueueHead(rxEndpoint(0)) - dc.ctrl = dc.endpointQueueHead(txEndpoint(0)) - dc.stat.config = (descEndptMaxPktSize << 16) | (1 << 15) - dc.ctrl.config = (descEndptMaxPktSize << 16) - - dc.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer(dc.stat)))) - - // clear installed timer callbacks - dc.timerInterrupt[0] = nil - dc.timerInterrupt[1] = nil - - // enable interrupts - dc.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 -} - -func (dc *deviceController) enable(enable bool) status { - if enable { - dc.irq.SetPriority(dcdInterruptPriority) - dc.irq.Enable() - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) - } else { - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) - dc.irq.Disable() - } - return statusOK -} - -func (dc *deviceController) critical(enter bool) status { - if enter { - // check if critical section already locked - if dc.cri.Get() != 0 { - return statusRetry - } - // lock critical section - dc.cri.Set(1) - // disable interrupts, storing state in receiver - dc.ivm = arm.DisableInterrupts() - } else { - // ensure critical section is locked - if dc.cri.Get() != 0 { - // re-enable interrupts, using state stored in receiver - arm.EnableInterrupts(dc.ivm) - // unlock critical section - dc.cri.Set(0) - } - } - return statusOK -} - -func (dc *deviceController) interrupt() { - // read and clear the interrupts that fired - status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() - dc.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 := dc.bus.ENDPTSETUPSTAT.Get() - for 0 != setupStatus { - dc.bus.ENDPTSETUPSTAT.Set(setupStatus) - var setup dcdSetup - ready := false - for !ready { - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_SUTW) - setup = dc.stat.setup - ready = dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_SUTW) - } - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_SUTW) - // flush endpoint 0 (bit 0=Rx, 16=Tx) - dc.bus.ENDPTFLUSH.Set(0x00010001) - for dc.bus.ENDPTFLUSH.HasBits(0x00010001) { - } // wait for flush to complete - dc.controlNotify = 0 - dc.control(setup) - setupStatus = dc.bus.ENDPTSETUPSTAT.Get() - } - - completeStatus := dc.bus.ENDPTCOMPLETE.Get() - if 0 != completeStatus { - dc.bus.ENDPTCOMPLETE.Set(completeStatus) - if 0 != completeStatus&dc.controlNotify { - dc.controlNotify = 0 - dc.controlComplete() - } - completeStatus &= dc.endpointNotify - if 0 != completeStatus { - tx := completeStatus >> 16 - for 0 != tx { - num := uint8(bits.TrailingZeros32(tx)) - dc.endpointComplete(txEndpoint(num)) - tx &^= 1 << num - } - rx := completeStatus & 0xFFFF - for 0 != rx { - num := uint8(bits.TrailingZeros32(rx)) - dc.endpointComplete(rxEndpoint(num)) - rx &^= 1 << num - } - } - } - } - - // 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 - dc.bus.ENDPTSETUPSTAT.Set(dc.bus.ENDPTSETUPSTAT.Get()) - // clear all endpoint complete status - dc.bus.ENDPTCOMPLETE.Set(dc.bus.ENDPTCOMPLETE.Get()) - // wait on any endpoint priming - for 0 != dc.bus.ENDPTPRIME.Get() { - } - dc.bus.ENDPTFLUSH.Set(0xFFFFFFFF) - // if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { - // } - switch dc.cc.id { - case classDeviceCDCACM: - // TBD: reset CDC-ACM UART? - default: - } - dc.endpointNotify = 0 - } - - // 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 != dc.timerInterrupt[0] { - dc.timerInterrupt[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 != dc.timerInterrupt[1] { - dc.timerInterrupt[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 dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) { - dc.speed = descDeviceSpeedHigh // 480 Mbit/sec - } else { - dc.speed = descDeviceSpeedFull // 12 Mbit/sec - } - } - - // 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 { - //println("suspend") - } - - // 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 { - //println("error") - } - - // 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 125ms 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 dc.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) && - 0 != status&nxp.USB_USBSTS_SRI { - if 0 != dc.rebootTimer { - dc.rebootTimer -= 1 - if 0 == dc.rebootTimer { - dc.enableSofInterrupts(false, descCDCACMInterfaceCount) - } - } - } -} - -func (dc *deviceController) enableSofInterrupts(enable bool, iface uint8) { - if enable { - ivm := arm.DisableInterrupts() - dc.sofUsage |= 1 << iface - if !dc.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) { - dc.bus.USBSTS.Set(nxp.USB_USBSTS_SRI) - dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_SRE) - } - arm.EnableInterrupts(ivm) - } else { - dc.sofUsage &^= 1 << iface - if 0 == dc.sofUsage { - dc.bus.USBINTR.ClearBits(nxp.USB_USBINTR_SRE) - } - } -} - -// receive schedules a receive (Rx, OUT) transfer on the given endpoint. -func (dc *deviceController) receive(endpoint uint8, transfer *dcdTransfer) { - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - ep := dc.endpointQueueHead(rxEndpoint(endpoint)) - em := (uint32(1) << endpoint) << descCDCACMConfigAttrRxPos - dc.transferSchedule(ep, em, transfer) -} - -// transmit schedules a transmit (Tx, IN) transfer on the given endpoint. -func (dc *deviceController) transmit(endpoint uint8, transfer *dcdTransfer) { - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - ep := dc.endpointQueueHead(txEndpoint(endpoint)) - em := (uint32(1) << endpoint) << descCDCACMConfigAttrTxPos - dc.transferSchedule(ep, em, transfer) -} - -// control handles setup messages on control endpoint 0. -func (dc *deviceController) control(setup dcdSetup) { - - // println(strconv.FormatUint(setup.pack(), 16)) - - // First, switch on the type of request (standard, class, or vendor) - switch setup.bmRequestType & descRequestTypeTypeMsk { - - // === STANDARD REQUEST === - case descRequestTypeTypeStandard: - - // Switch on the recepient and direction of the request - switch setup.bmRequestType & - (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { - - // --- DEVICE Rx (OUT) --- - case descRequestTypeRecipientDevice | descRequestTypeDirOut: - - // Identify which request was received - switch setup.bRequest { - - // SET ADDRESS (0x05): - case descRequestStandardSetAddress: - dc.controlReceive(dcdPointerNil, 0, false) - dc.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | - ((uint32(setup.wValue) << nxp.USB_DEVICEADDR_USBADR_Pos) & - nxp.USB_DEVICEADDR_USBADR_Msk)) - return - - // SET CONFIGURATION (0x09): - case descRequestStandardSetConfiguration: - dc.cc.config = int(setup.wValue) - if 0 == dc.cc.config || dc.cc.config > dcdCount { - // Use default if invalid index received - dc.cc.config = 1 - } - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - dc.bus.ENDPTCTRL2.Set(descCDCACMConfigAttrStatus) // Status Tx - dc.bus.ENDPTCTRL3.Set(descCDCACMConfigAttrDataRx) // Bulk data Rx - dc.bus.ENDPTCTRL4.Set(descCDCACMConfigAttrDataTx) // Bulk data Tx - dc.uartConfigure() - dc.controlReceive(dcdPointerNil, 0, false) - - default: - // Unhandled device class - } - return - - default: - // Unhandled request - } - - // --- DEVICE Tx (IN) --- - case descRequestTypeRecipientDevice | descRequestTypeDirIn: - - // Identify which request was received - switch setup.bRequest { - - // GET STATUS (0x00): - case descRequestStandardGetStatus: - dc.controlReply[0] = 0 - dc.controlReply[1] = 0 - dc.controlTransmit( - uintptr(unsafe.Pointer(&dc.controlReply[0])), 2, false) - return - - // GET DESCRIPTOR (0x06): - case descRequestStandardGetDescriptor: - dc.controlDescriptor(setup) - return - - // GET CONFIGURATION (0x08): - case descRequestStandardGetConfiguration: - dc.controlReply[0] = uint8(dc.cc.config) - dc.controlTransmit( - uintptr(unsafe.Pointer(&dc.controlReply[0])), 1, false) - return - - default: - // Unhandled request - } - - // --- INTERFACE Tx (IN) --- - case descRequestTypeRecipientInterface | descRequestTypeDirIn: - - // Identify which request was received - switch setup.bRequest { - - // GET DESCRIPTOR (0x06): - case descRequestStandardGetDescriptor: - dc.controlDescriptor(setup) - return - - default: - // Unhandled request - } - - // --- ENDPOINT Rx (OUT) --- - case descRequestTypeRecipientEndpoint | descRequestTypeDirOut: - - // Identify which request was received - switch setup.bRequest { - - // CLEAR FEATURE (0x01): - case descRequestStandardClearFeature: - // TODO - - // SET FEATURE (0x03): - case descRequestStandardSetFeature: - // TODO - - default: - // Unhandled request - } - - // --- ENDPOINT Tx (IN) --- - case descRequestTypeRecipientEndpoint | descRequestTypeDirIn: - - // Identify which request was received - switch setup.bRequest { - - // GET STATUS (0x00): - case descRequestStandardGetStatus: - num, dir := unpackEndpoint(uint8(setup.wIndex)) - var reg *volatile.Register32 - switch num { - case 0: - reg = &dc.bus.ENDPTCTRL0 - case 1: - reg = &dc.bus.ENDPTCTRL1 - case 2: - reg = &dc.bus.ENDPTCTRL2 - case 3: - reg = &dc.bus.ENDPTCTRL3 - case 4: - reg = &dc.bus.ENDPTCTRL4 - case 5: - reg = &dc.bus.ENDPTCTRL5 - case 6: - reg = &dc.bus.ENDPTCTRL6 - case 7: - reg = &dc.bus.ENDPTCTRL7 - } - if nil != reg { - dc.controlReply[0] = 0 - dc.controlReply[1] = 0 - if ((0 != dir) && reg.HasBits(nxp.USB_ENDPTCTRL0_TXS)) || - ((0 == dir) && reg.HasBits(nxp.USB_ENDPTCTRL0_RXS)) { - dc.controlReply[0] = 1 - } - dc.controlTransmit( - uintptr(unsafe.Pointer(&dc.controlReply[0])), 2, false) - return - } - - default: - // Unhandled request - } - - default: - // Unhandled request recepient or direction - } - - // === CLASS REQUEST === - case descRequestTypeTypeClass: - - // Switch on the recepient and direction of the request - switch setup.bmRequestType & - (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { - - // --- INTERFACE Rx (OUT) --- - case descRequestTypeRecipientInterface | descRequestTypeDirOut: - - // Identify which request was received - switch setup.bRequest { - - // CDC | SET LINE CODING (0x20): - case descCDCRequestSetLineCoding: - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - // line coding must contain exactly 7 bytes - if descCDCACMCodingSize == setup.wLength { - dc.setup = setup - dc.controlReceive( - uintptr(unsafe.Pointer(&descCDCACM[dc.cc.config-1].cx[0])), - descCDCACMCodingSize, true) - return - } - - default: - // Unhandled device class - } - - // CDC | SET CONTROL LINE STATE (0x22): - case descCDCRequestSetControlLineState: - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - - // Determine interface destination of the notification - switch setup.wIndex { - - // Control/status interface: - case descCDCACMInterfaceCtrl: - // acm := &descCDCACM[dc.cc.config-1] - // update our emulated UART terminal status - // acm.lineActive = ticks() - // acm.lineCoding.dtr = 0 != setup.wValue&0x01 - // acm.lineCoding.rts = 0 != setup.wValue&0x02 - dc.controlReceive(dcdPointerNil, 0, false) - return - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - // CDC | SEND BREAK (0x23): - case descCDCRequestSendBreak: - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - dc.controlReceive(dcdPointerNil, 0, false) - return - - default: - // Unhandled device class - } - - default: - // Unhandled request - } - - default: - // Unhandled request recepient or direction - } - - case descRequestTypeTypeVendor: - default: - // Unhandled request type - } - - dc.bus.ENDPTCTRL0.Set(0x00010001) -} - -// controlComplete handles the setup completion of control endpoint 0. -func (dc *deviceController) controlComplete() { - - // First, switch on the type of request (standard, class, or vendor) - switch dc.setup.bmRequestType & descRequestTypeTypeMsk { - - // === CLASS REQUEST === - case descRequestTypeTypeClass: - - // Switch on the recepient and direction of the request - switch dc.setup.bmRequestType & - (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { - - // --- INTERFACE Rx (OUT) --- - case descRequestTypeRecipientInterface | descRequestTypeDirOut: - - // Identify which request was received - switch dc.setup.bRequest { - - // CDC | SET LINE CODING (0x20): - case descCDCRequestSetLineCoding: - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - acm := &descCDCACM[dc.cc.config-1] - - // Determine interface destination of the notification - switch dc.setup.wIndex { - - // Control/status interface: - case descCDCACMInterfaceCtrl: - - acm.lineCoding.baud = packU32(acm.cx[:]) - acm.lineCoding.stopBits = acm.cx[4] - if 0 == acm.lineCoding.stopBits { - acm.lineCoding.stopBits = 1 - } - acm.lineCoding.parity = acm.cx[5] - acm.lineCoding.numBits = acm.cx[6] - - if 134 == acm.lineCoding.baud { - dc.enableSofInterrupts(true, descCDCACMInterfaceCount) - dc.rebootTimer = 80 - } - - default: - // Unhandled device interface - } - - default: - // Unhandled device class - } - - default: - // Unhandled request - } - - default: - // Unhandled recepient or direction - } - - default: - // Unhandled request type - } -} - -func (dc *deviceController) controlDescriptor(setup dcdSetup) { - - // Respond based on our device class configuration - switch dc.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - acm := &descCDCACM[dc.cc.config-1] - dxn := uint8(0) - - // Determine the type of descriptor being requested - switch setup.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: - var sd []uint8 - if 0 == uint8(setup.wValue) { - // setup.wIndex contains an arbitrary index referring to a collection of - // strings in some given language. This (setup.wValue = 0x03[00]) is a - // request from the host to determine what that language is. Subsequent - // string requests will populate setup.wIndex with the language code - // returned here in this string descriptor. - sd = acm.locale[int(setup.wIndex)].descriptor[setup.wValue&0xFF][:] - } else { - // setup.wIndex now contains a language code, which we notified 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 setup.wIndex == acm.locale[code].language { - // Found language, check if string descriptor at given index exists - if int(setup.wValue&0xFF) < len(acm.locale[code].descriptor) { - // Found language with a string defined at the requested index. - // Construct a string descriptor dynamically to be transmitted on - // the serial bus. - - // TODO: Add fields to deviceController and design an API that - // allows the user to define and provide these strings - // prior to deviceController initialization. - // For now, we just always use the descCommon* strings. - var s string - switch uint8(setup.wValue) { - case 1: - s = descCommonManufacturer - case 2: - s = descCommonProduct - case 3: - s = descCommonSerialNumber - } - - // Copy string into string descriptor as UTF-16 - sd = acm.locale[code].descriptor[int(setup.wValue&0xFF)][:] - sd[0] = uint8(2 + 2*len(s)) - sd[1] = descTypeString - 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(setup.wLength) { - dxn = uint8(setup.wLength) - } - nxp.FlushDeleteDcache( - uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) - dc.controlTransmit( - uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) - } - - default: - // Unhandled device class - } - -} - -// controlTransfers returns the data and ackowledgement transfer descriptors for -// the control endpoint (i.e., endpoint 0). -//go:inline -func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { - // control endpoint is device class-specific - switch dc.cc.id { - case classDeviceCDCACM: - return descCDCACM[dc.cc.config-1].cd, descCDCACM[dc.cc.config-1].ad - default: - return nil, nil - } -} - -// controlReceive receives (Rx, OUT) data on control endpoint 0. -func (dc *deviceController) controlReceive( - data uintptr, size uint32, notify bool) { - const ( - rm = uint32(1 << descCDCACMConfigAttrRxPos) - tm = uint32(1 << descCDCACMConfigAttrTxPos) - ) - cd, ad := dc.controlTransfers() - if size > 0 { - cd.next = dcdTransferEOL - cd.token = (size << 16) | (1 << 7) - for i := range cd.pointer { - cd.pointer[i] = data + uintptr(i)*4096 - } - // linked list is empty - qr := dc.endpointQueueHead(rxEndpoint(0)) - qr.transfer.next = cd - qr.transfer.token = 0 - dc.bus.ENDPTPRIME.SetBits(rm) - for 0 != dc.bus.ENDPTPRIME.Get() { - } // wait for endpoint finish priming - } - ad.next = dcdTransferEOL - ad.token = 1 << 7 - if notify { - ad.token |= 1 << 15 - } - ad.pointer[0] = 0 - qt := dc.endpointQueueHead(txEndpoint(0)) - qt.transfer.next = ad - qt.transfer.token = 0 - dc.bus.ENDPTCOMPLETE.Set(rm | tm) - dc.bus.ENDPTPRIME.SetBits(tm) - if notify { - dc.controlNotify = tm - } -} - -// controlTransmit transmits (Tx, IN) data on control endpoint 0. -func (dc *deviceController) controlTransmit( - data uintptr, size uint32, notify bool) { - const ( - rm = uint32(1 << descCDCACMConfigAttrRxPos) - tm = uint32(1 << descCDCACMConfigAttrTxPos) - ) - cd, ad := dc.controlTransfers() - if size > 0 { - cd.next = dcdTransferEOL - cd.token = (size << 16) | (1 << 7) - for i := range cd.pointer { - cd.pointer[i] = data + uintptr(i)*4096 - } - // linked list is empty - qt := dc.endpointQueueHead(txEndpoint(0)) - qt.transfer.next = cd - qt.transfer.token = 0 - dc.bus.ENDPTPRIME.SetBits(tm) - for 0 != dc.bus.ENDPTPRIME.Get() { - } // wait for endpoint finish priming - } - ad.next = dcdTransferEOL - ad.token = 1 << 7 - if notify { - ad.token |= 1 << 15 - } - ad.pointer[0] = 0 - qr := dc.endpointQueueHead(rxEndpoint(0)) - qr.transfer.next = ad - qr.transfer.token = 0 - dc.bus.ENDPTCOMPLETE.Set(rm | tm) - dc.bus.ENDPTPRIME.SetBits(rm) - if notify { - dc.controlNotify = rm - } -} - -// 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 (dc *deviceController) endpointQueueHead(endpoint uint8) *dcdEndpoint { - // endpoint queue head is device class-specific - switch dc.cc.id { - case classDeviceCDCACM: - return &descCDCACM[dc.cc.config-1].qh[endpointIndex(endpoint)] - default: - return nil - } -} - -func (dc *deviceController) endpointConfigure( - ep *dcdEndpoint, packetSize uint16, zlp bool, callback dcdTransferCallback) { - - ep.config = uint32(packetSize) << 16 - if !zlp { - ep.config |= 1 << 29 - } - ep.current = nil - ep.transfer.next = dcdTransferEOL - 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 -} - -func (dc *deviceController) endpointConfigureRx( - endpoint uint8, packetSize uint16, zlp bool, callback dcdTransferCallback) { - - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - ep := dc.endpointQueueHead(rxEndpoint(endpoint)) - dc.endpointConfigure(ep, packetSize, zlp, callback) - if nil != callback { - dc.endpointNotify |= (uint32(1) << endpoint) << descCDCACMConfigAttrRxPos - } -} - -func (dc *deviceController) endpointConfigureTx( - endpoint uint8, packetSize uint16, zlp bool, callback dcdTransferCallback) { - - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - ep := dc.endpointQueueHead(txEndpoint(endpoint)) - dc.endpointConfigure(ep, packetSize, zlp, callback) - if nil != callback { - dc.endpointNotify |= (uint32(1) << endpoint) << descCDCACMConfigAttrTxPos - } -} - -// endpointComplete handles transfer completion of a data endpoint. -func (dc *deviceController) endpointComplete(endpoint uint8) { - ep := dc.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) { - // 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 - } -} - -func (dc *deviceController) transferPrepare( - transfer *dcdTransfer, data *uint8, size uint16, param uint32) { - transfer.next = dcdTransferEOL - transfer.token = (uint32(size) << 16) | (1 << 7) - addr := uintptr(unsafe.Pointer(data)) - for i := range transfer.pointer { - transfer.pointer[i] = addr + uintptr(i)*4096 - } - transfer.param = param -} - -func (dc *deviceController) transferSchedule( - endpoint *dcdEndpoint, mask uint32, transfer *dcdTransfer) { - - if nil != endpoint.callback { - transfer.token |= 1 << 15 - } - ivm := arm.DisableInterrupts() - last := endpoint.last - if nil != last { - last.next = transfer - if dc.bus.ENDPTPRIME.HasBits(mask) { - goto endTransfer - } - start := cycleCount() - estat := uint32(0) - for !dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) && - (cycleCount()-start < 2400) { - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW) - estat = dc.bus.ENDPTSTAT.Get() - } - if 0 != estat&mask { - goto endTransfer - } - } - endpoint.transfer.next = transfer - endpoint.transfer.token = 0 - dc.bus.ENDPTPRIME.SetBits(mask) - endpoint.first = transfer -endTransfer: - endpoint.last = transfer - arm.EnableInterrupts(ivm) -} - -func (dc *deviceController) timerConfigure(timer int, usec uint32, fn func()) { - if timer < 0 || timer >= len(dc.timerInterrupt) { - return - } - dc.timerInterrupt[timer] = fn - switch timer { - case 0: - dc.bus.GPTIMER0CTRL.Set(0) - dc.bus.GPTIMER0LD.Set(usec - 1) - dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE0) - case 1: - dc.bus.GPTIMER1CTRL.Set(0) - dc.bus.GPTIMER1LD.Set(usec - 1) - dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE1) - } -} - -func (dc *deviceController) timerOneShot(timer int) { - switch timer { - case 0: - dc.bus.GPTIMER0CTRL.Set( - nxp.USB_GPTIMER0CTRL_GPTRUN | nxp.USB_GPTIMER0CTRL_GPTRST) - case 1: - dc.bus.GPTIMER1CTRL.Set( - nxp.USB_GPTIMER1CTRL_GPTRUN | nxp.USB_GPTIMER1CTRL_GPTRST) - } -} - -func (dc *deviceController) timerStop(timer int) { - switch timer { - case 0: - dc.bus.GPTIMER0CTRL.Set(0) - case 1: - dc.bus.GPTIMER1CTRL.Set(0) - } -} - -func (dc *deviceController) uartConfigure() { - acm := &descCDCACM[dc.cc.config-1] - switch dc.speed { - case descDeviceSpeedHigh: - acm.rxSize = descCDCACMDataRxHSPacketSize - acm.txSize = descCDCACMDataTxHSPacketSize - default: - acm.rxSize = descCDCACMDataRxFSPacketSize - acm.txSize = descCDCACMDataTxFSPacketSize - } - acm.txHead = 0 - acm.txFree = 0 - acm.rxHead = 0 - acm.rxTail = 0 - acm.rxFree = 0 - dc.endpointConfigureTx(descCDCACMEndpointStatus, - acm.cxSize, false, nil) - dc.endpointConfigureRx(descCDCACMEndpointDataRx, - acm.rxSize, false, dc.uartNotify) - dc.endpointConfigureTx(descCDCACMEndpointDataTx, - acm.txSize, true, nil) - for i := range acm.rd { - dc.uartReceive(uint8(i)) - } - dc.timerConfigure(0, descCDCACMTxSyncUs, dc.uartSync) -} - -func (dc *deviceController) uartReceive(endpoint uint8) { - acm := &descCDCACM[dc.cc.config-1] - num := uint16(endpoint) & descEndptAddrNumberMsk - buf := &acm.rx[num*descCDCACMRxSize] - dc.irq.Disable() - dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) - nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) - dc.receive(descCDCACMEndpointDataRx, &acm.rd[num]) - dc.irq.Enable() -} - -func (dc *deviceController) uartNotify(transfer *dcdTransfer) { - acm := &descCDCACM[dc.cc.config-1] - len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF) - p := transfer.param - if 0 == len { - // zero-length packet (ZLP) - dc.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 - dc.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 (dc *deviceController) uartFlush() { - acm := &descCDCACM[dc.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] - dc.uartReceive(uint8(i)) - acm.rxTail = tail - } -} - -func (dc *deviceController) uartAvailable() int { - return int(descCDCACM[dc.cc.config-1].rxFree) -} - -func (dc *deviceController) uartPeek() (uint8, bool) { - acm := &descCDCACM[dc.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 (dc *deviceController) uartReadByte() (uint8, bool) { - b := []uint8{0} - ok := dc.uartRead(b) > 0 - return b[0], ok -} - -func (dc *deviceController) uartRead(data []uint8) int { - acm := &descCDCACM[dc.cc.config-1] - read := uint16(0) - size := uint16(len(data)) - tail := acm.rxTail - dest := uint16(0) - dc.irq.Disable() - 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 - dc.uartReceive(uint8(i)) - } - } - dc.irq.Enable() - return int(read) -} - -func (dc *deviceController) uartWriteByte(c uint8) bool { - return 1 == dc.uartWrite([]uint8{c}) -} - -func (dc *deviceController) uartWrite(data []uint8) int { - acm := &descCDCACM[dc.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] - dc.transferPrepare(xfer, tx, descCDCACMTxSize, 0) - nxp.FlushDeleteDcache(uintptr(unsafe.Pointer(tx)), descCDCACMTxSize) - dc.transmit(descCDCACMEndpointDataTx, xfer) - acm.txHead += 1 - if acm.txHead >= descCDCACMTDCount { - acm.txHead = 0 - } - size -= int(acm.txFree) - sent += int(acm.txFree) - acm.txFree = 0 - dc.timerStop(0) - } else { - _ = copy(buff, data[:size]) - acm.txFree -= uint16(size) - sent += size - size = 0 - dc.timerOneShot(0) - } - } - return sent -} - -func (dc *deviceController) uartSync() { - const autoFlushTx = true - if !autoFlushTx { - return - } - acm := &descCDCACM[dc.cc.config-1] - if 0 == acm.txFree { - return - } - xfer := &acm.td[acm.txHead] - buff := &acm.tx[uint16(acm.txHead)*descCDCACMTxSize] - size := descCDCACMTxSize - acm.txFree - dc.transferPrepare(xfer, buff, size, 0) - nxp.FlushDeleteDcache(uintptr(unsafe.Pointer(buff)), uintptr(size)) - dc.transmit(descCDCACMEndpointDataTx, xfer) - acm.txHead += 1 - if acm.txHead >= descCDCACMTDCount { - acm.txHead = 0 - } - acm.txFree = 0 -} diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go index 6f6d37948..192e93d47 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -135,6 +135,28 @@ const ( 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 ( @@ -282,53 +304,6 @@ const ( descCDCUARTStateOverrun = 0x40 // UART state OVERRUN ) -// Common configuration constants for the USB CDC-ACM (single) device class. -const ( - // String descriptor languages - descCDCACMLanguageCount = 1 - // Interfaces for all CDC-ACM configurations. - descCDCACMInterfaceCount = 2 - descCDCACMInterfaceCtrl = 0 - descCDCACMInterfaceData = 1 - // Endpoints for all CDC-ACM configurations. - descCDCACMEndpointCount = 4 - descCDCACMEndpointStatus = 2 // Communication/control interrupt input - descCDCACMEndpointDataRx = 3 // Bulk data output - descCDCACMEndpointDataTx = 4 // Bulk data input - // Endpoint configuration attributes for all CDC-ACM configurations. - descCDCACMConfigAttrStatus = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) | - (descCDCACMConfigAttrInterrupt << descCDCACMConfigAttrTxPos) - descCDCACMConfigAttrDataRx = (descCDCACMConfigAttrBulk << descCDCACMConfigAttrRxPos) | - (descCDCACMConfigAttrUnused << descCDCACMConfigAttrTxPos) - descCDCACMConfigAttrDataTx = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) | - (descCDCACMConfigAttrBulk << descCDCACMConfigAttrTxPos) - - // Size of all CDC-ACM configuration descriptors. - descCDCACMConfigSize = uint16( - descLengthConfigure + // configuration - descLengthInterface + // communication/control interface - descCDCFuncLengthHeader + // CDC header - descCDCFuncLengthCallManagement + // CDC call management - descCDCFuncLengthAbstractControl + // CDC abstract control - descCDCFuncLengthUnion + // CDC union - descLengthEndpoint + // communication/control input endpoint - descLengthInterface + // data interface - descLengthEndpoint + // data input endpoint - descLengthEndpoint) // data output endpoint - // Attributes of all CDC-ACM configuration descriptors. - descCDCACMConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1) - (1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered - (0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup - 0 // Bits 0-4: reserved (0) - - descCDCACMConfigAttrRxPos = 0 - descCDCACMConfigAttrTxPos = 16 - descCDCACMConfigAttrUnused = 0x02 // TBD: what is this? - descCDCACMConfigAttrIsochronous = descCDCACMConfigAttr | descEndptAttrSyncTypeAsync - descCDCACMConfigAttrBulk = descCDCACMConfigAttr | descEndptAttrSyncTypeAdaptive - descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync -) - // descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., // configuration index 1. var descCDCACM0Device = [descLengthDevice]uint8{ @@ -367,6 +342,21 @@ var descCDCACM0Qualif = [descLengthQualification]uint8{ 0, // Reserved } +const ( + // Size of all CDC-ACM configuration descriptors. + descCDCACMConfigSize = uint16( + descLengthConfigure + // configuration + descLengthInterface + // communication/control interface + descCDCFuncLengthHeader + // CDC header + descCDCFuncLengthCallManagement + // CDC call management + descCDCFuncLengthAbstractControl + // CDC abstract control + descCDCFuncLengthUnion + // CDC union + descLengthEndpoint + // communication/control input endpoint + descLengthInterface + // data interface + descLengthEndpoint + // data input endpoint + descLengthEndpoint) // data output endpoint +) + // descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0], // i.e., configuration index 1. var descCDCACM0Config = [descCDCACMConfigSize]uint8{ @@ -377,7 +367,7 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{ 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 - descCDCACMConfigAttr, // Configuration attributes + descEndptConfigAttr, // Configuration attributes descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) // Communication/Control Interface Descriptor @@ -460,32 +450,6 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{ 0, // Polling Interval } -// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. -const descCDCACMCodingSize = 7 - -// descCDCACM0LineCoding holds the default UART line coding for CDC-ACM[0], -// i.e., configuration index 1. -var descCDCACM0LineCoding descCDCACMLineCoding - -type descCDCACMLineCoding struct { - baud uint32 - stopBits uint8 - parity uint8 - numBits uint8 - rtsdtr uint8 -} - -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. -) - 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 @@ -504,19 +468,93 @@ type ( } ) +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. +) + // descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., // configuration index 1. var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{ - { // US English string descriptors + + { // [0x0409] US English language: descLanguageEnglish, descriptor: descStringIndex{ - { // Language (index 0) + { /* [0] Language */ 4, descTypeString, lsU8(descLanguageEnglish), msU8(descLanguageEnglish), }, // Actual string descriptors (index > 0) are copied into here at runtime! + // This allows for application- or even user-defined string descriptors. + { /* [1] Manufacturer */ }, + { /* [2] Product */ }, + { /* [3] Serial Number */ }, }, }, } + +// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. +const descCDCACMCodingSize = 7 + +// 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 ( + // String descriptor languages available + descCDCACMLanguageCount = 1 + + // Interfaces for all CDC-ACM configurations. + descCDCACMInterfaceCount = 2 + descCDCACMInterfaceCtrl = 0 + descCDCACMInterfaceData = 1 + + // Endpoints for all CDC-ACM configurations. + descCDCACMEndpointCount = 4 + descCDCACMEndpointStatus = 2 // Communication/control interrupt input + descCDCACMEndpointDataRx = 3 // Bulk data output + descCDCACMEndpointDataTx = 4 // Bulk data input + + // Endpoint configuration attributes for all CDC-ACM configurations. + descCDCACMConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt + descCDCACMConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused + 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 = [descCDCACMCount]descCDCACMClass{ + + { // CDC-ACM (single) class configuration index 1 + descCDCACMClassData: &descCDCACMData[0], + + locale: &descCDCACM0String, + device: &descCDCACM0Device, + qualif: &descCDCACM0Qualif, + config: &descCDCACM0Config, + }, +} diff --git a/src/machine/usb/desc_mimxrt1062.go b/src/machine/usb/desc_mimxrt1062.go index 1b0108888..4fa7b5527 100644 --- a/src/machine/usb/desc_mimxrt1062.go +++ b/src/machine/usb/desc_mimxrt1062.go @@ -69,33 +69,39 @@ const ( // so the Device Controller can readily respond to incoming requests without // having to traverse a linked list. //go:align 4096 -var descCDCACM0QH [descCDCACMQHCount]dcdEndpoint +var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint // descCDCACM0CD is the transfer descriptor for data messages transmitted or // received on the status/control endpoint 0 for the default CDC-ACM (single) // device class configuration (index 1). //go:align 32 -var descCDCACM0CD dcdTransfer +var descCDCACM0CD dhwTransfer // 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 dcdTransfer +var descCDCACM0AD dhwTransfer // 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]dcdTransfer +var descCDCACM0RD [descCDCACMRDCount]dhwTransfer // 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]dcdTransfer +var descCDCACM0TD [descCDCACMTDCount]dhwTransfer + +// descCDCACM0LineCoding holds the emulated UART line coding for the default +// CDC-ACM (single) device class configuration (index 1). +// i.e., configuration index 1. +//go:align 32 +var descCDCACM0LC descCDCACMLineCoding // descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of // the default CDC-ACM (single) device class configuration (index 1). @@ -120,21 +126,22 @@ var descCDCACM0RDNum [descCDCACMRDCount]uint16 var descCDCACM0RDIdx [descCDCACMRDCount]uint16 var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 -type descCDCACMClass struct { - locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors - device *[descLengthDevice]uint8 // device descriptor - qualif *[descLengthQualification]uint8 // device qualification descriptor - config *[descCDCACMConfigSize]uint8 // configuration descriptor +// descCDCACM holds the buffers and control states for all of the 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 an additional hardware abstraction layer. +type descCDCACMClassData struct { + qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads - lineCoding *descCDCACMLineCoding // UART line coding active state - // lineActive int64 // time since last UART DTR/RTS - - qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads - - cd *dcdTransfer // control endpoint 0 Rx/Tx data transfer descriptor - ad *dcdTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor - rd *[descCDCACMRDCount]dcdTransfer // bulk data endpoint Rx (OUT) transfer descriptors - td *[descCDCACMTDCount]dcdTransfer // bulk data endpoint Tx (IN) transfer descriptors + cd *dhwTransfer // control endpoint 0 Rx/Tx data transfer descriptor + ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors + td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer @@ -156,21 +163,18 @@ type descCDCACMClass struct { rxCount *[descCDCACMRDCount]uint16 rxIndex *[descCDCACMRDCount]uint16 rxQueue *[descCDCACMRDCount + 1]uint16 + + _ [2]uint8 } -// descCDCACM holds the configuration, endpoint, and transfer descriptors, along -// with the buffers and control states, for all of the CDC-ACM (single) device -// class configurations, ordered by configuration index (offset by -1). +// descCDCACMData holds statically-allocated instances for each of the target- +// specific (iMXRT1062) CDC-ACM (single) device class configurations' control +// and data structures, ordered by configuration index (offset by -1). Each +// element is embedded in a corresponding element of descCDCACM. //go:align 32 -var descCDCACM = [descCDCACMCount]descCDCACMClass{ - { - locale: &descCDCACM0String, - device: &descCDCACM0Device, - qualif: &descCDCACM0Qualif, - config: &descCDCACM0Config, - - lineCoding: &descCDCACM0LineCoding, +var descCDCACMData = [descCDCACMCount]descCDCACMClassData{ + { // CDC-ACM (single) class configuration index 1 data qh: &descCDCACM0QH, cd: &descCDCACM0CD, diff --git a/src/machine/usb/dhw_mimxrt1062.go b/src/machine/usb/dhw_mimxrt1062.go new file mode 100644 index 000000000..e2d95da40 --- /dev/null +++ b/src/machine/usb/dhw_mimxrt1062.go @@ -0,0 +1,1073 @@ +// +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 +} + +// 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) + } else { + 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) + } + } + } +} + +func (d *dhw) controlBusSpeed() uint8 { return d.busSpeed } + +func (d *dhw) controlDeviceAddress(addr uint16) { + d.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | + ((uint32(addr) << nxp.USB_DEVICEADDR_USBADR_Pos) & + nxp.USB_DEVICEADDR_USBADR_Msk)) +} + +func (d *dhw) controlLineState(coding descCDCACMLineCoding, dtr, rts bool) { + // TBD: does the PHY need to handle on iMXRT1062 (e.g., Teensyduino Loader)? +} + +func (d *dhw) controlLineCoding(coding descCDCACMLineCoding) { + if 134 == coding.baud { + d.enableSOF(true, descCDCACMInterfaceCount) + } +} + +// controlStatus transitions transfers on control endpoint 0 into status stage. +func (d *dhw) controlStatus() { + // Not used on iMXRT1062 +} + +// 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 + 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 + } +} + +// 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 + 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 + } +} + +// dhwEndpointSize defines the size (bytes) of a structure containing a USB +// standard endpoint. +const dhwEndpointSize = 64 // bytes + +// 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) +} + +// 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)] + 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) endpointPrime(mask uint32, transfer *dhwTransfer) { +// d.bus.ENDPTPRIME.Set(mask) +// } + +// func (d *dhw) endpointPrimed() uint32 { +// return d.bus.ENDPTPRIME.Get() +// } + +// func (d *dhw) endpointUnprime(mask uint32) { +// d.bus.ENDPTCOMPLETE.Set(mask) +// } + +// 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) { + // 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 + } + ep := d.endpointQueueHead(rxEndpoint(endpoint)) + d.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrRxPos + } + + default: + // Unhandled device class + } +} + +// 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 + } + ep := d.endpointQueueHead(txEndpoint(endpoint)) + d.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrTxPos + } + + default: + // Unhandled device class + } +} + +// 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 + } + ep := d.endpointQueueHead(rxEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descEndptConfigAttrRxPos + d.transferSchedule(ep, em, transfer) + + default: + // Unhandled device class + } +} + +// 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 + } + ep := d.endpointQueueHead(txEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descEndptConfigAttrTxPos + d.transferSchedule(ep, em, transfer) + + default: + // Unhandled device class + } +} + +// dhwTransferSize defines the size (bytes) of a USB standard transfer packet. +const dhwTransferSize = 32 // bytes + +// 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 +} + +// 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 + default: + return nil, nil + } +} + +func (d *dhw) transferPrepare( + transfer *dhwTransfer, data *uint8, size uint16, param uint32) { + + transfer.next = dhwTransferEOL + transfer.token = (uint32(size) << 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) +} + +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) + } +} + +func (d *dhw) uartConfigure() { + acm := &descCDCACM[d.cc.config-1] + switch d.controlBusSpeed() { + case descDeviceSpeedHigh: + acm.rxSize = descCDCACMDataRxHSPacketSize + acm.txSize = descCDCACMDataTxHSPacketSize + default: + acm.rxSize = descCDCACMDataRxFSPacketSize + acm.txSize = descCDCACMDataTxFSPacketSize + } + acm.txHead = 0 + acm.txFree = 0 + 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.cxSize, 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) 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 +} diff --git a/src/machine/usb/hcd.go b/src/machine/usb/hcd.go index 366bdcc9e..4e7f34d8d 100644 --- a/src/machine/usb/hcd.go +++ b/src/machine/usb/hcd.go @@ -1,10 +1,53 @@ package usb -type hcd interface { - class() class - init() status - enable(enable bool) status - critical(enter bool) status - interrupt() - udelay(micros uint32) +// 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/hcd_mimxrt1062.go b/src/machine/usb/hcd_mimxrt1062.go deleted file mode 100644 index 99d8b59f1..000000000 --- a/src/machine/usb/hcd_mimxrt1062.go +++ /dev/null @@ -1,130 +0,0 @@ -// +build mimxrt1062 - -package usb - -// Implementation of USB host controller driver (hcd) for NXP iMXRT1062. - -import ( - "device/arm" - "device/nxp" - "runtime/interrupt" - "runtime/volatile" -) - -// 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 - -// hcdInterruptPriority defines the priority for all USB host interrupts. -const hcdInterruptPriority = 3 - -// hostController implements USB host controller driver (hcd) interface. -type hostController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - cc class // USB host class - id int // hostControllerInstance index - - bus *nxp.USB_Type - phy *nxp.USBPHY_Type - irq interrupt.Interrupt - - cri volatile.Register8 // set to 1 if in critical section, else 0 - ivm uintptr // interrupt state when entering critical section -} - -// hostControllerInstance provides statically-allocated instances of each USB -// host controller configured on this platform. -var hostControllerInstance [hcdCount]hostController - -// 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 - } - // Return the first instance whose assigned core is currently nil. - for i := range hostControllerInstance { - if nil == hostControllerInstance[i].core { - // Initialize host controller. - hostControllerInstance[i].core = &coreInstance[port] - hostControllerInstance[i].port = port - hostControllerInstance[i].cc = class - hostControllerInstance[i].id = i - switch port { - case 0: - hostControllerInstance[i].bus = nxp.USB1 - hostControllerInstance[i].phy = nxp.USBPHY1 - //hostControllerInstance[i].irq = - // interrupt.New(nxp.IRQ_USB_OTG1, - // func(interrupt.Interrupt) { - // coreInstance[0].hc.interrupt() - // }) - - case 1: - hostControllerInstance[i].bus = nxp.USB2 - hostControllerInstance[i].phy = nxp.USBPHY2 - //hostControllerInstance[i].irq = - // interrupt.New(nxp.IRQ_USB_OTG2, - // func(interrupt.Interrupt) { - // //coreInstance[1].hc.interrupt() - // }) - } - return &hostControllerInstance[i], statusOK - } - } - return nil, statusBusy // No free host controller instances available. -} - -func (hc *hostController) class() class { return hc.cc } - -func (hc *hostController) init() status { - - return statusOK -} - -func (hc *hostController) enable(enable bool) status { - - hc.irq.SetPriority(hcdInterruptPriority) - hc.irq.Enable() - - return statusOK -} - -func (hc *hostController) critical(enter bool) status { - if enter { - // check if critical section already locked - if hc.cri.Get() != 0 { - return statusRetry - } - // lock critical section - hc.cri.Set(1) - // disable interrupts, storing state in receiver - hc.ivm = arm.DisableInterrupts() - } else { - // ensure critical section is locked - if hc.cri.Get() != 0 { - // re-enable interrupts, using state stored in receiver - arm.EnableInterrupts(hc.ivm) - // unlock critical section - hc.cri.Set(0) - } - } - return statusOK -} - -func (hc *hostController) interrupt() { - -} - -// 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 (hc *hostController) udelay(microsec uint32) { - n := cycles(microsec, descCPUFrequencyHz) - for i := uint32(0); i < n; i++ { - arm.Asm(`nop`) - } -} 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/uart.go b/src/machine/usb/uart.go index a5ea35c05..7c1ae2e90 100644 --- a/src/machine/usb/uart.go +++ b/src/machine/usb/uart.go @@ -41,21 +41,13 @@ func (uart *UART) Configure(config UARTConfig) error { // Buffered returns the number of bytes currently stored in the RX buffer. func (uart UART) Buffered() int { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0 - } - return dc.uartAvailable() + 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) { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0, ErrUARTInvalidCore - } - n, ok := dc.uartReadByte() + n, ok := uart.core.dc.uartReadByte() if !ok { return 0, ErrUARTEmptyBuffer } @@ -64,20 +56,12 @@ func (uart UART) ReadByte() (byte, error) { // Read from the RX buffer. func (uart UART) Read(data []byte) (n int, err error) { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0, ErrUARTInvalidCore - } - return dc.uartRead(data), nil + 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 { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return ErrUARTInvalidCore - } - if !dc.uartWriteByte(c) { + if !uart.core.dc.uartWriteByte(c) { return ErrUARTWriteFailed } return nil @@ -85,9 +69,5 @@ func (uart UART) WriteByte(c byte) error { // Write data to the UART. func (uart UART) Write(data []byte) (n int, err error) { - dc, ok := uart.core.dc.(*deviceController) - if !ok { - return 0, ErrUARTInvalidCore - } - return dc.uartWrite(data), nil + return uart.core.dc.uartWrite(data), nil } diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go index 4f3d4e2cf..40d325ddb 100644 --- a/src/machine/usb/usb.go +++ b/src/machine/usb/usb.go @@ -2,59 +2,6 @@ package usb // Hardware abstraction for USB ports configured as either host or device. -import "unsafe" - -func init() { - if unsafe.Sizeof(uintptr(0)) > 4 { - panic("USB is only supported on 32-bit systems") - } -} - -// 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 - modeDevice = 1 - modeHost = 2 -) - -// 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 -} - -// Constant definitions for all host/device classes. -const ( - classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) -) - -// mode returns the USB core operating mode of the receiver class cl. -//go:inline -func (cl class) mode() int { - switch cl.id { - case classDeviceCDCACM: - 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 cl. -//go:inline -func (cl class) equals(class class) bool { - return cl.id == class.id && cl.config == class.config -} - // CoreCount defines the total number of USB cores to configure in device or // host mode. const CoreCount = dcdCount + hcdCount @@ -63,26 +10,29 @@ const CoreCount = dcdCount + hcdCount // configured on this platform. var coreInstance [CoreCount]core -// status represents the return code of a subroutine. -type status uint8 +// 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 all status codes used within the package. +// Constant definitions for USB core operating modes. const ( - statusOK status = iota // Success - statusBusy // Busy - statusRetry // Retry - statusInvalid // Invalid argument + modeIdle = 0 // USB port has not been configured + modeDevice = 1 + modeHost = 2 ) -// ok returns true if and only if the receiver st equals statusOK. -//go:inline -func (st status) ok() bool { return statusOK == st } - // 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 } @@ -90,9 +40,10 @@ func initCore(port int, class class) (*core, status) { 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. - // For instance, this will allow TinyGo examples that try to reconfigure the - // USB (CDC-ACM) UART port (which is already configured by the runtime) to - // continue without 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: @@ -122,12 +73,7 @@ func initCore(port int, class class) (*core, status) { coreInstance[port].port = port coreInstance[port].mode = modeDevice coreInstance[port].dc = dc - // Enable interrupts and enter runtime - if st = dc.enable(true); !st.ok() { - coreInstance[port].mode = modeIdle - coreInstance[port].dc = nil - return nil, st - } + dc.enable(true) // Enable interrupts and enter runtime case modeHost: // Allocate a free host controller and install interrupts @@ -142,12 +88,7 @@ func initCore(port int, class class) (*core, status) { coreInstance[port].port = port coreInstance[port].mode = modeHost coreInstance[port].hc = hc - // Enable interrupts and enter runtime - if st = hc.enable(true); !st.ok() { - coreInstance[port].mode = modeIdle - coreInstance[port].hc = nil - return nil, st - } + hc.enable(true) // Enable interrupts and enter runtime default: return nil, statusInvalid @@ -155,3 +96,47 @@ func initCore(port int, class class) (*core, status) { 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 // The only currently-supported class (CDC-ACM) +) + +// mode returns the USB core operating mode of the receiver class c. +//go:inline +func (c class) mode() int { + switch c.id { + case classDeviceCDCACM: + 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_arm.go b/src/machine/usb/util_arm.go index 89ab4f646..750629357 100644 --- a/src/machine/usb/util_arm.go +++ b/src/machine/usb/util_arm.go @@ -4,6 +4,9 @@ package usb import "device/arm" +//go:linkname ticks runtime.ticks +func ticks() int64 + // udelay waits for the given number of microseconds before returning. // We cannot use the sleep timer from this context (import cycle), but we need // an approximate method to spin CPU cycles for short periods of time. @@ -14,3 +17,11 @@ func udelay(microsec uint32) { arm.Asm(`nop`) } } + +func disableInterrupts() uintptr { + return arm.DisableInterrupts() +} + +func enableInterrupts(mask uintptr) { + arm.EnableInterrupts(mask) +} From e9ddbdf4a8366ca7c073ee3d5a589fd9521b1672 Mon Sep 17 00:00:00 2001 From: ardnew Date: Sun, 2 May 2021 18:05:36 -0500 Subject: [PATCH 8/9] implemented USB HID composite keyboard support --- src/examples/usb/demo/README.md | 0 src/examples/usb/keyboard/keyboard.go | 251 ++++++ src/examples/usb/serial/echo.go | 37 + src/machine/board_teensy40.go | 9 +- src/machine/machine_common_usb.go | 49 -- src/machine/usb/dcd.go | 586 +++++++++++--- src/machine/usb/desc.go | 841 +++++++++++++++----- src/machine/usb/desc_mimxrt1062.go | 556 +++++++++++-- src/machine/usb/dhw_mimxrt1062.go | 435 ++++++++-- src/machine/usb/hid.go | 42 + src/machine/usb/keyboard.go | 1055 +++++++++++++++++++++++++ src/machine/usb/uart.go | 27 +- src/machine/usb/usb.go | 5 +- src/machine/usb/util.go | 55 ++ src/machine/usb/util_arm.go | 3 - src/runtime/runtime_mimxrt1062.go | 8 +- 16 files changed, 3461 insertions(+), 498 deletions(-) create mode 100644 src/examples/usb/demo/README.md create mode 100644 src/examples/usb/keyboard/keyboard.go create mode 100644 src/examples/usb/serial/echo.go delete mode 100644 src/machine/machine_common_usb.go create mode 100644 src/machine/usb/hid.go create mode 100644 src/machine/usb/keyboard.go diff --git a/src/examples/usb/demo/README.md b/src/examples/usb/demo/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/examples/usb/keyboard/keyboard.go b/src/examples/usb/keyboard/keyboard.go new file mode 100644 index 000000000..7e7530067 --- /dev/null +++ b/src/examples/usb/keyboard/keyboard.go @@ -0,0 +1,251 @@ +package main + +import ( + "machine" + "machine/usb" + "time" +) + +var keyboard = machine.HID0.Keyboard() + +func main() { + + println("USB HID keyboard demo") + + for { + time.Sleep(5 * time.Second) + + // Open a new text editor + keyboard.Down(usb.KeyModifierAlt) + keyboard.Press(usb.KeySpace) + keyboard.Up(usb.KeyModifierAlt) + time.Sleep(2 * time.Second) + keyboard.Write([]byte("kate")) + time.Sleep(time.Second) + keyboard.Press(usb.KeyEnter) + + time.Sleep(5 * time.Second) + + // Use the io.Writer interface + keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n")) + time.Sleep(2 * time.Second) + + // Or manually specify keycodes and Unicode codepoints + testKeys([]Key{ + // Print alphabet out-of-order + {Press: usb.KeyX}, + {Press: usb.KeyY}, + {Press: usb.KeyZ}, + {Press: usb.KeyG}, + {Press: usb.KeyH}, + {Press: usb.KeyI}, + {Press: usb.KeyJ}, + {Press: usb.KeyK}, + {Press: usb.KeyL}, + {Press: usb.KeyM}, + {Press: usb.KeyN}, + {Press: usb.KeyO}, + {Press: usb.KeyP}, + {Press: usb.KeyQ}, + {Press: usb.KeyR}, + {Press: usb.KeyS}, + {Press: usb.KeyT}, + {Press: usb.KeyA}, + {Press: usb.KeyB}, + {Press: usb.KeyC}, + {Press: usb.KeyD}, + {Press: usb.KeyE}, + {Press: usb.KeyF}, + {Press: usb.KeyU}, + {Press: usb.KeyV}, + {Press: usb.KeyW}, + // Pause 1 second + {Time: time.Second}, + // Move cursor left x3 + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + // Pause 1 second + {Time: time.Second}, + // Highlight 6 symbols to the left + {Down: usb.KeyModifierShift}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Press: usb.KeyLeft}, + {Up: usb.KeyModifierShift}, + // Pause 1 second + {Time: time.Second}, + // Use Ctrl-X to cut + {Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Move to beginning of line + {Press: usb.KeyHome}, + // Pause 1 second + {Time: time.Second}, + // Use Ctrl-V to paste + {Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Highlight 3 symbols to the right + {Down: usb.KeyModifierShift}, + {Press: usb.KeyRight}, + {Press: usb.KeyRight}, + {Press: usb.KeyRight}, + {Up: usb.KeyModifierShift}, + // Use Ctrl-X to cut + {Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Move to end of line + {Press: usb.KeyEnd}, + // Pause 1 second + {Time: time.Second}, + // Use Ctrl-V to paste + {Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl}, + // Pause 1 second + {Time: time.Second}, + // Newline + {Press: usb.KeyEnter}, + {Press: usb.KeyEnter}, + }, 150*time.Millisecond) + + // Highlight all text and delete + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyA) + keyboard.Up(usb.KeyModifierCtrl) + time.Sleep(time.Second) + keyboard.Press(usb.KeyDelete) + time.Sleep(time.Second) + + // Close window + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyQ) + keyboard.Up(usb.KeyModifierCtrl) + time.Sleep(2 * time.Second) + // Confirm discard file + keyboard.Down(usb.KeyModifierAlt) + keyboard.Press(usb.KeyD) + keyboard.Up(usb.KeyModifierAlt) + + time.Sleep(5 * time.Second) + + // Open a new terminal + keyboard.Down(usb.KeyModifierAlt) + keyboard.Press(usb.KeySpace) + keyboard.Up(usb.KeyModifierAlt) + time.Sleep(2 * time.Second) + keyboard.Write([]byte("konsole")) + keyboard.Press(usb.KeyEnter) + + time.Sleep(5 * time.Second) + + // Open serial connection + keyboard.Write([]byte("screen /dev/ttyACM0 115200")) + time.Sleep(2 * time.Second) + keyboard.Press(usb.KeyEnter) + time.Sleep(4 * time.Second) + + // Write to UART + keyboard.Write([]byte("hello!")) + time.Sleep(time.Second) + keyboard.Press(usb.KeyEnter) + time.Sleep(2 * time.Second) + keyboard.Write([]byte("NO U")) + time.Sleep(time.Second) + keyboard.Press(usb.KeyEnter) + time.Sleep(2 * time.Second) + + // Close serial connection + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyX) + keyboard.Up(usb.KeyModifierCtrl) + time.Sleep(time.Second) + keyboard.Press(usb.KeyBackslash) + time.Sleep(time.Second) + keyboard.Press(usb.KeyY) + keyboard.Press(usb.KeyEnter) + time.Sleep(2 * time.Second) + + // Close terminal + keyboard.Down(usb.KeyModifierCtrl) + keyboard.Press(usb.KeyD) + keyboard.Up(usb.KeyModifierCtrl) + + time.Sleep(25 * time.Second) + } +} + +type Key struct { + Press usb.Keycode + Down usb.Keycode + Up usb.Keycode + Time time.Duration +} + +func testKeys(key []Key, delay time.Duration) { + for _, k := range key { + if 0 != k.Down { + keyboard.Down(k.Down) + } + if 0 != k.Press { + keyboard.Press(k.Press) + } + if 0 != k.Up { + keyboard.Up(k.Up) + } + if 0 != k.Time { + time.Sleep(k.Time) + } else { + time.Sleep(delay) + } + } +} + +func testInternationalLayout() { + // International keyboard layouts also supported + keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Numbers: 0123456789\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬­®¯°±\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Cedilla: Çç\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Ring Above: Åå\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("AE: Ææ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Thorn: Þþ\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Sharp S: ß\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("O-Stroke: Øø\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Eth: Ðð\n")) + time.Sleep(250 * time.Millisecond) + keyboard.Write([]byte("Euro: €\n")) +} diff --git a/src/examples/usb/serial/echo.go b/src/examples/usb/serial/echo.go new file mode 100644 index 000000000..b4a61fea7 --- /dev/null +++ b/src/examples/usb/serial/echo.go @@ -0,0 +1,37 @@ +// This is a echo console running on the device UART. +// Connect using default baudrate for this hardware, 8-N-1 with your terminal program. +package main + +import ( + "machine" + "time" +) + +func main() { + uart := machine.UART0 + uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n")) + + input := make([]byte, 4096) + i := 0 + for { + if uart.Buffered() > 0 { + data, _ := uart.ReadByte() + + switch data { + case 13: + // return key + uart.Write([]byte("\r\n")) + uart.Write([]byte("You typed: ")) + uart.Write(input[:i]) + uart.Write([]byte("\r\n")) + i = 0 + default: + // just echo the character + uart.WriteByte(data) + input[i] = data + i++ + } + } + time.Sleep(10 * time.Millisecond) + } +} diff --git a/src/machine/board_teensy40.go b/src/machine/board_teensy40.go index 18c965a03..66fad2938 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" ) @@ -103,11 +104,9 @@ func init() { // | USB | // #=====================================================# var ( - // USBCDC is a legacy class being retained here as temporary wrapper. - // See godoc comments on type USBCDC struct definition for details. - UART0 = USBCDC{ - port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0/4.1) - } + // UART0 = usb.UART{Port: 0} + HID0 = usb.HID{Port: 0} + UART0 = &UART1 ) // #=====================================================# diff --git a/src/machine/machine_common_usb.go b/src/machine/machine_common_usb.go deleted file mode 100644 index 22857351b..000000000 --- a/src/machine/machine_common_usb.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build mimxrt1062 - -package machine - -import ( - "machine/usb" -) - -// USBCDC is the legacy TinyGo type used to implement USB CDC-ACM device class -// emulation for serial UART communication. It is retained here as a temporary -// wrapper for type usb.UART from new package "machine/usb", which is still in -// active development. Once that package has stabilized a bit, this type should -// be removed and usb.UART should be used directly instead. -type USBCDC struct { - port uint8 - uart usb.UART -} - -// Configure the embedded usb.UART with our receiver's settings and the given -// UART configuration. This provides compatibility with machine.UART. -func (cdc *USBCDC) Configure(config UARTConfig) { - cdc.uart.Configure(usb.UARTConfig{BaudRate: config.BaudRate}) -} - -// Buffered returns the number of bytes currently stored in the RX buffer. -func (cdc USBCDC) Buffered() int { - return cdc.uart.Buffered() -} - -// ReadByte reads a single byte from the RX buffer. -// If there is no data in the buffer, returns an error. -func (cdc USBCDC) ReadByte() (byte, error) { - return cdc.uart.ReadByte() -} - -// Read from the RX buffer. -func (cdc USBCDC) Read(data []byte) (n int, err error) { - return cdc.uart.Read(data) -} - -// WriteByte writes a single byte of data to the UART interface. -func (cdc USBCDC) WriteByte(c byte) error { - return cdc.uart.WriteByte(c) -} - -// Write data to the UART. -func (cdc USBCDC) Write(data []byte) (n int, err error) { - return cdc.uart.Write(data) -} diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go index ba4393439..d4cbdfa73 100644 --- a/src/machine/usb/dcd.go +++ b/src/machine/usb/dcd.go @@ -12,7 +12,7 @@ func init() { // dcdCount defines the number of USB cores to configure for device mode. It is // computed as the sum of all declared device configuration descriptors. -const dcdCount = descCDCACMCount // + ... +const dcdCount = descCDCACMCount + descHIDCount // dcdInstance provides statically-allocated instances of each USB device // controller configured on this platform. @@ -134,7 +134,6 @@ func (d *dcd) event(ev dcdEvent) { case dcdStageSetup: case dcdStageData: case dcdStageStatus: - d.controlStatus() case dcdStageStall: d.controlStall() } @@ -180,7 +179,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // SET ADDRESS (0x05): case descRequestStandardSetAddress: - d.controlDeviceAddress(sup.wValue) + d.setDeviceAddress(sup.wValue) d.controlReceive(uintptr(0), 0, false) return dcdStageSetup @@ -200,6 +199,14 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { d.uartConfigure() d.controlReceive(uintptr(0), 0, false) + // HID + case classDeviceHID: + d.serialConfigure() + d.keyboardConfigure() + d.mouseConfigure() + d.joystickConfigure() + d.controlReceive(uintptr(0), 0, false) + default: // Unhandled device class } @@ -225,8 +232,23 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET DESCRIPTOR (0x06): case descRequestStandardGetDescriptor: - d.controlDescriptor(sup) - return dcdStageSetup + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlDescriptorCDCACM(sup) + return dcdStageSetup + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } // GET CONFIGURATION (0x08): case descRequestStandardGetConfiguration: @@ -247,8 +269,38 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET DESCRIPTOR (0x06): case descRequestStandardGetDescriptor: - d.controlDescriptor(sup) - return dcdStageSetup + + // Respond based on our device class configuration + switch d.cc.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + d.controlDescriptorCDCACM(sup) + return dcdStageSetup + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } + + // GET DESCRIPTOR (0x06): + case descHIDRequestGetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + d.controlDescriptorHID(sup) + return dcdStageSetup + + default: + // Unhandled device class + } default: // Unhandled request @@ -262,11 +314,15 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CLEAR FEATURE (0x01): case descRequestStandardClearFeature: - // TODO + d.endpointClearFeature(uint8(sup.wIndex)) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup // SET FEATURE (0x03): case descRequestStandardSetFeature: - // TODO + d.endpointSetFeature(uint8(sup.wIndex)) + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup default: // Unhandled request @@ -322,6 +378,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { d.controlReceive( uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])), descCDCACMCodingSize, true) + // CDC Line Coding packet receipt handling occurs in method + // controlComplete(). return dcdStageSetup } @@ -338,11 +396,13 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CDC-ACM (single) case classDeviceCDCACM: - // Determine interface destination of the notification + // Determine interface destination of the request switch sup.wIndex { // Control/status interface: case descCDCACMInterfaceCtrl: + // DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02) + d.uartSetLineState(0 != sup.wValue&0x01, 0 != sup.wValue&0x02) d.controlReceive(uintptr(0), 0, false) return dcdStageSetup @@ -369,6 +429,79 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // Unhandled device class } + // HID | SET REPORT (0x09) + case descHIDRequestSetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + if sup.wLength <= descHIDCxCount { + d.setup = sup + descHID[d.cc.config-1].cx[0] = 0xE9 + d.controlReceive( + uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])), + uint32(sup.wLength), true) + return dcdStageSetup + } + + default: + // Unhandled device class + } + + // HID | SET IDLE (0x0A) + case descHIDRequestSetIdle: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + idleRate := sup.wValue >> 8 + // TBD: do we need to handle this request? wIndex contains the target + // interface of the request. + _ = idleRate + d.controlReceive(uintptr(0), 0, false) + return dcdStageSetup + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + // --- INTERFACE Tx (IN) --- + case descRequestTypeRecipientInterface | descRequestTypeDirIn: + + // Identify which request was received + switch sup.bRequest { + + // HID | GET REPORT (0x01) + case descHIDRequestGetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + reportType := uint8(sup.wValue >> 8) + reportID := uint8(sup.wValue) + // TBD: do we need to handle this request? wIndex contains the target + // interface of the request. + _, _ = reportType, reportID + d.controlReply[0] = 0 + d.controlReply[1] = 0 + d.controlTransmit( + uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) + return dcdStageSetup + + default: + // Unhandled device class + } + default: // Unhandled request } @@ -419,15 +552,14 @@ func (d *dcd) controlComplete(status uint32) { case classDeviceCDCACM: acm := &descCDCACM[d.cc.config-1] - // Determine interface destination of the notification + // Determine interface destination of the request switch d.setup.wIndex { - // Control/status interface: + // CDC-ACM Control Interface: case descCDCACMInterfaceCtrl: - // Notify PHY to handle triggers like special baud rates, which // signal to reboot into bootloader or begin receiving OTA updates - d.controlLineCoding(descCDCACMLineCoding{ + d.uartSetLineCoding(descCDCACMLineCoding{ baud: packU32(acm.cx[:]), stopBits: acm.cx[4], parity: acm.cx[5], @@ -442,6 +574,60 @@ func (d *dcd) controlComplete(status uint32) { // Unhandled device class } + // HID | SET REPORT (0x09) + case descHIDRequestSetReport: + + // Respond based on our device class configuration + switch d.cc.id { + + // HID + case classDeviceHID: + hid := &descHID[d.cc.config-1] + + // Determine interface destination of the request + switch d.setup.wIndex { + + // HID Keyboard Interface + case descHIDInterfaceKeyboard: + + // Determine the type of descriptor being requested + switch d.setup.wValue >> 8 { + + // Configuration descriptor + case descTypeConfigure: + if 1 == d.setup.wLength { + hid.keyboard.led = hid.cx[0] + d.controlTransmit(uintptr(0), 0, false) + } + + default: + // Unhandled descriptor type + } + + // HID Serial Interface + case descHIDInterfaceSerial: + + // Determine the type of descriptor being requested + switch d.setup.wValue >> 8 { + + // String descriptor + case descTypeString: + if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) { + d.enableSOF(true, descHIDInterfaceCount) + } + + default: + // Unhandled descriptor type + } + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + default: // Unhandled request } @@ -455,129 +641,295 @@ func (d *dcd) controlComplete(status uint32) { } } -func (d *dcd) controlDescriptor(sup dcdSetup) { +func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) { - // Respond based on our device class configuration - switch d.cc.id { + acm := &descCDCACM[d.cc.config-1] + dxn := uint8(0) - // CDC-ACM (single) - case classDeviceCDCACM: - acm := &descCDCACM[d.cc.config-1] - dxn := uint8(0) + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { - // Determine the type of descriptor being requested - switch sup.wValue >> 8 { + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(acm.dx[:], acm.device[:dxn]) - // Device descriptor - case descTypeDevice: - dxn = descLengthDevice - _ = copy(acm.dx[:], acm.device[:dxn]) + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descCDCACMConfigSize) + _ = copy(acm.dx[:], acm.config[:dxn]) - // Configuration descriptor - case descTypeConfigure: - dxn = uint8(descCDCACMConfigSize) - _ = copy(acm.dx[:], acm.config[:dxn]) + // String descriptor + case descTypeString: + if 0 == len(acm.locale) { + break // No string descriptors defined! + } + var sd []uint8 + if 0 == uint8(sup.wValue) { - // String descriptor - case descTypeString: - if 0 == len(acm.locale) { - break // No string descriptors defined! + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This case (setup.wValue = [0x03]00) + // is a string request from the host to determine what that language is. + // + // In subsequent string requests, the host will populate setup.wIndex + // with the language code we return here in this string descriptor. + // + // This way all strings returned to the host are in the same language, + // whatever language that may be. + code := int(sup.wIndex) + if code >= len(acm.locale) { + code = 0 } - var sd []uint8 - if 0 == uint8(sup.wValue) { + sd = acm.locale[code].descriptor[sup.wValue&0xFF][:] - // setup.wIndex contains an arbitrary index referring to a collection of - // strings in some given language. This case (setup.wValue = [0x03]00) - // is a string request from the host to determine what that language is. - // - // In subsequent string requests, the host will populate setup.wIndex - // with the language code we return here in this string descriptor. - // - // This way all strings returned to the host are in the same language, - // whatever language that may be. - code := int(sup.wIndex) - if code >= len(acm.locale) { - code = 0 - } - sd = acm.locale[code].descriptor[sup.wValue&0xFF][:] + } else { - } else { + // setup.wIndex now contains a language code, which we specified in a + // previous request (above: setup.wValue = [0x03]00). We need to locate + // the set of strings whose language matches the language code given in + // this new setup.wIndex. + for code := range acm.locale { + if sup.wIndex == acm.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) { - // setup.wIndex now contains a language code, which we specified in a - // previous request (above: setup.wValue = [0x03]00). We need to locate - // the set of strings whose language matches the language code given in - // this new setup.wIndex. - for code := range acm.locale { - if sup.wIndex == acm.locale[code].language { - // Found language, check if string descriptor at given index exists - if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) { - - // Found language with a string defined at the requested index. - // - // TODO: Add API methods to device controller that allows the user - // to provide these strings at/before driver initialization. - // - // For now, we just always use the descCommon* strings. - var s string - switch uint8(sup.wValue) { - case 1: - s = descCommonManufacturer - case 2: - s = descCommonProduct - case 3: - s = descCommonSerialNumber - } - - // Construct a string descriptor dynamically to be transmitted on - // the serial bus. - sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:] - // String descriptor format is 2-byte header + 2-bytes per rune - sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length - sd[1] = descTypeString // header[1] = descriptor type - // Copy UTF-8 string into string descriptor as UTF-16 - for n, c := range s { - if 2+2*n >= len(sd) { - break - } - sd[2+2*n] = uint8(c) - sd[3+2*n] = 0 - } - break // end search for matching language code + // Found language with a string defined at the requested index. + // + // TODO: Add API methods to device controller that allows the user + // to provide these strings at/before driver initialization. + // + // For now, we just always use the descCommon* strings. + var s string + switch uint8(sup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + " CDC-ACM" + case 3: + s = descCommonSerialNumber } + + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. + sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:] + // String descriptor format is 2-byte header + 2-bytes per rune + sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length + sd[1] = descTypeString // header[1] = descriptor type + // Copy UTF-8 string into string descriptor as UTF-16 + for n, c := range s { + if 2+2*n >= len(sd) { + break + } + sd[2+2*n] = uint8(c) + sd[3+2*n] = 0 + } + break // end search for matching language code } } } - // Copy string descriptor into descriptor transmit buffer - if nil != sd && len(sd) >= 0 { - dxn = sd[0] - _ = copy(acm.dx[:], sd[:dxn]) - } - - // Device qualification descriptor - case descTypeQualification: - dxn = descLengthQualification - _ = copy(acm.dx[:], acm.qualif[:dxn]) - - // Alternate configuration descriptor - case descTypeOtherSpeedConfiguration: - // TODO - - default: - // Unhandled descriptor type + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(acm.dx[:], sd[:dxn]) } - if dxn > 0 { - if dxn > uint8(sup.wLength) { - dxn = uint8(sup.wLength) + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(acm.dx[:], acm.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + // TODO + + default: + // Unhandled descriptor type + } + + if dxn > 0 { + if dxn > uint8(sup.wLength) { + dxn = uint8(sup.wLength) + } + flushCache( + uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) + d.controlTransmit( + uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + } +} + +func (d *dcd) controlDescriptorHID(sup dcdSetup) { + + hid := &descHID[d.cc.config-1] + dxn := uint8(0) + pos := uint8(0) + + // Determine the type of descriptor being requested + switch sup.wValue >> 8 { + + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(hid.dx[:], hid.device[:dxn]) + + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descHIDConfigSize) + _ = copy(hid.dx[:], hid.config[:dxn]) + + // String descriptor + case descTypeString: + if 0 == len(hid.locale) { + break // No string descriptors defined! + } + var sd []uint8 + if 0 == uint8(sup.wValue) { + + // setup.wIndex contains an arbitrary index referring to a collection of + // strings in some given language. This case (setup.wValue = [0x03]00) + // is a string request from the host to determine what that language is. + // + // In subsequent string requests, the host will populate setup.wIndex + // with the language code we return here in this string descriptor. + // + // This way all strings returned to the host are in the same language, + // whatever language that may be. + code := int(sup.wIndex) + if code >= len(hid.locale) { + code = 0 } - flushCache( - uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) - d.controlTransmit( - uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + sd = hid.locale[code].descriptor[sup.wValue&0xFF][:] + + } else { + + // setup.wIndex now contains a language code, which we specified in a + // previous request (above: setup.wValue = [0x03]00). We need to locate + // the set of strings whose language matches the language code given in + // this new setup.wIndex. + for code := range hid.locale { + if sup.wIndex == hid.locale[code].language { + // Found language, check if string descriptor at given index exists + if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) { + + // Found language with a string defined at the requested index. + // + // TODO: Add API methods to device controller that allows the user + // to provide these strings at/before driver initialization. + // + // For now, we just always use the descCommon* strings. + var s string + switch uint8(sup.wValue) { + case 1: + s = descCommonManufacturer + case 2: + s = descCommonProduct + " HID" + case 3: + s = descCommonSerialNumber + } + + // Construct a string descriptor dynamically to be transmitted on + // the serial bus. + sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:] + // String descriptor format is 2-byte header + 2-bytes per rune + sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length + sd[1] = descTypeString // header[1] = descriptor type + // Copy UTF-8 string into string descriptor as UTF-16 + for n, c := range s { + if 2+2*n >= len(sd) { + break + } + sd[2+2*n] = uint8(c) + sd[3+2*n] = 0 + } + break // end search for matching language code + } + } + } + } + // Copy string descriptor into descriptor transmit buffer + if nil != sd && len(sd) >= 0 { + dxn = sd[0] + _ = copy(hid.dx[:], sd[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(hid.dx[:], hid.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + // TODO + + // HID descriptor + case descTypeHID: + + // Determine interface destination of the request + switch sup.wIndex { + case descHIDInterfaceKeyboard: + pos = descHIDConfigKeyboardPos + + case descHIDInterfaceMouse: + pos = descHIDConfigMousePos + + case descHIDInterfaceSerial: + pos = descHIDConfigSerialPos + + case descHIDInterfaceJoystick: + pos = descHIDConfigJoystickPos + + case descHIDInterfaceMediaKey: + pos = descHIDConfigMediaKeyPos + + default: + // Unhandled HID interface + } + + if 0 != pos { + dxn = descLengthInterface + _ = copy(hid.dx[:], hid.config[pos:pos+dxn]) + } + + // HID report descriptor + case descTypeHIDReport: + + // Determine interface destination of the request + switch sup.wIndex { + case descHIDInterfaceKeyboard: + dxn = uint8(len(descHIDReportKeyboard)) + _ = copy(hid.dx[:], descHIDReportKeyboard[:]) + + case descHIDInterfaceMouse: + dxn = uint8(len(descHIDReportMouse)) + _ = copy(hid.dx[:], descHIDReportMouse[:]) + + case descHIDInterfaceSerial: + dxn = uint8(len(descHIDReportSerial)) + _ = copy(hid.dx[:], descHIDReportSerial[:]) + + case descHIDInterfaceJoystick: + dxn = uint8(len(descHIDReportJoystick)) + _ = copy(hid.dx[:], descHIDReportJoystick[:]) + + case descHIDInterfaceMediaKey: + dxn = uint8(len(descHIDReportMediaKey)) + _ = copy(hid.dx[:], descHIDReportMediaKey[:]) + + default: + // Unhandled HID interface } default: - // Unhandled device class + // Unhandled descriptor type } + if dxn > 0 { + if dxn > uint8(sup.wLength) { + dxn = uint8(sup.wLength) + } + flushCache( + uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn)) + d.controlTransmit( + uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false) + } } diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go index 192e93d47..ae5c83d22 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -304,152 +304,75 @@ const ( descCDCUARTStateOverrun = 0x40 // UART state OVERRUN ) -// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., -// configuration index 1. -var descCDCACM0Device = [descLengthDevice]uint8{ - descLengthDevice, // Size of this descriptor in bytes - descTypeDevice, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - descCDCTypeComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) - msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) - lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) - msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) - lsU8(descCommonReleaseID), // Device release number in BCD (low) - msU8(descCommonReleaseID), // Device release number in BCD (high) - 1, // Index of string descriptor describing manufacturer - 2, // Index of string descriptor describing product - 3, // Index of string descriptor describing the device's serial number - descCDCACMCount, // Number of possible configurations -} +// USB HID constants defined per specification +const ( + // HID class + descHIDType = 0x03 -// descCDCACM0Qualif holds the default device qualification descriptor for -// CDC-ACM[0], i.e., configuration index 1. -var descCDCACM0Qualif = [descLengthQualification]uint8{ - descLengthQualification, // Size of this descriptor in bytes - descTypeQualification, // Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - descCDCTypeComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - descCDCACMCount, // Number of possible configurations - 0, // Reserved -} + // HID subclass + descHIDSubNone = 0x00 + descHIDSubBoot = 0x01 + + // HID protocol + descHIDProtoNone = 0x00 + descHIDProtoKeyboard = 0x01 + descHIDProtoMouse = 0x02 + + descHIDRequestGetReport = 0x01 // HID request GET_REPORT + descHIDRequestGetReportTypeInput = 0x01 // HID request GET_REPORT type INPUT + descHIDRequestGetReportTypeOutput = 0x02 // HID request GET_REPORT type OUTPUT + descHIDRequestGetReportTypeFeature = 0x03 // HID request GET_REPORT type FEATURE + descHIDRequestGetIdle = 0x02 // HID request GET_IDLE + descHIDRequestGetProtocol = 0x03 // HID request GET_PROTOCOL + descHIDRequestSetReport = 0x09 // HID request SET_REPORT + descHIDRequestSetIdle = 0x0A // HID request SET_IDLE + descHIDRequestSetProtocol = 0x0B // HID request SET_PROTOCOL +) const ( // Size of all CDC-ACM configuration descriptors. descCDCACMConfigSize = uint16( - descLengthConfigure + // configuration - descLengthInterface + // communication/control interface - descCDCFuncLengthHeader + // CDC header - descCDCFuncLengthCallManagement + // CDC call management - descCDCFuncLengthAbstractControl + // CDC abstract control - descCDCFuncLengthUnion + // CDC union - descLengthEndpoint + // communication/control input endpoint - descLengthInterface + // data interface - descLengthEndpoint + // data input endpoint - descLengthEndpoint) // data output endpoint + descLengthConfigure + // Configuration Header + descLengthInterface + // CDC Interface Descriptor + descCDCFuncLengthHeader + // CDC Header + descCDCFuncLengthCallManagement + // CDC Call Management Func Descriptor + descCDCFuncLengthAbstractControl + // CDC Abstract Control Func Descriptor + descCDCFuncLengthUnion + // CDC Union Functional Descriptor + descLengthEndpoint + // CDC Status IN Endpoint Descriptor + descLengthInterface + // CDC Data Interface Descriptor + descLengthEndpoint + // CDC Data IN Endpoint Descriptor + descLengthEndpoint) // CDC Data OUT Endpoint Descriptor + + // Size of all HID configuration descriptors. + descHIDConfigSize = uint16( + descLengthConfigure + // Configuration Header + descLengthInterface + // Keyboard Interface Descriptor + descLengthInterface + // Keyboard HID Interface Descriptor + descLengthEndpoint + // Keyboard Endpoint Descriptor + descLengthInterface + // Mouse Interface Descriptor + descLengthInterface + // Mouse HID Interface Descriptor + descLengthEndpoint + // Mouse Endpoint Descriptor + descLengthInterface + // Serial Interface Descriptor + descLengthInterface + // Serial HID Interface Descriptor + descLengthEndpoint + // Serial Endpoint Descriptor + descLengthEndpoint + + descLengthInterface + // Joystick Interface Descriptor + descLengthInterface + // Joystick HID Interface Descriptor + descLengthEndpoint + // Joystick Endpoint Descriptor + descLengthInterface + // Keyboard Media Keys Interface Descriptor + descLengthInterface + // Keyboard Media Keys HID Interface Descriptor + descLengthEndpoint) // Keyboard Media Keys Endpoint Descriptor + + // Position of each HID interface descriptor as offsets into the configuration + // descriptor. See comments in the configuration descriptor definition for the + // incremental tally that computes these. + descHIDConfigKeyboardPos = 18 + descHIDConfigMousePos = 43 + descHIDConfigSerialPos = 68 + descHIDConfigJoystickPos = 100 + descHIDConfigMediaKeyPos = 125 ) -// descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0], -// i.e., configuration index 1. -var descCDCACM0Config = [descCDCACMConfigSize]uint8{ - descLengthConfigure, // Size of this descriptor in bytes - descTypeConfigure, // Descriptor Type - lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) - msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) - descCDCACMInterfaceCount, // Number of interfaces supported by this configuration - 1, // Value to use to select this configuration (1 = CDC-ACM[0]) - 0, // Index of string descriptor describing this configuration - descEndptConfigAttr, // Configuration attributes - descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) - - // Communication/Control Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descCDCACMInterfaceCtrl, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descCDCTypeComm, // Class code - descCDCSubAbstractControl, // Subclass code - descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) - 0, // Interface Description String Index - - // CDC Header Functional Descriptor - descCDCFuncLengthHeader, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeHeader, // Descriptor Subtype - 0x10, // USB CDC specification version 1.10 (low) - 0x01, // USB CDC specification version 1.10 (high) - - // CDC Call Management Functional Descriptor - descCDCFuncLengthCallManagement, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeCallManagement, // Descriptor Subtype - 0x01, // Capabilities - descCDCACMInterfaceData, // Data Interface - - // CDC Abstract Control Management Functional Descriptor - descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeAbstractControl, // Descriptor Subtype - 0x06, // Capabilities - - // CDC Union Functional Descriptor - descCDCFuncLengthUnion, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCFuncTypeUnion, // Descriptor Subtype - descCDCACMInterfaceCtrl, // Controlling interface index - descCDCACMInterfaceData, // Controlled interface index - - // Communication/Control Notification Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACMEndpointStatus | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descCDCACMStatusPacketSize), // Max packet size (low) - msU8(descCDCACMStatusPacketSize), // Max packet size (high) - 16, // Polling Interval - - // Data Interface Descriptor - descLengthInterface, // Interface length - descTypeInterface, // Interface type - descCDCACMInterfaceData, // Interface index - 0, // Alternate setting - 2, // Number of endpoints - descCDCTypeData, // Class code - descCDCSubNone, // Subclass code - descCDCProtoNone, // Protocol code - 0, // Interface Description String Index - - // Data Bulk Rx Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACMEndpointDataRx | // Endpoint address - descEndptAddrDirectionOut, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataRxPacketSize), // Max packet size (low) - msU8(descCDCACMDataRxPacketSize), // Max packet size (high) - 0, // Polling Interval - - // Data Bulk Tx Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACMEndpointDataTx | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) - msU8(descCDCACMDataTxPacketSize), // Max packet size (high) - 0, // Polling Interval -} - type ( // descString is the actual byte array used to hold string descriptors. The // first two bytes are a USB-specified header (0=length, 1=type), and the @@ -479,28 +402,6 @@ const ( // for each string) seems a good compromise. ) -// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., -// configuration index 1. -var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{ - - { // [0x0409] US English - language: descLanguageEnglish, - descriptor: descStringIndex{ - { /* [0] Language */ - 4, - descTypeString, - lsU8(descLanguageEnglish), - msU8(descLanguageEnglish), - }, - // Actual string descriptors (index > 0) are copied into here at runtime! - // This allows for application- or even user-defined string descriptors. - { /* [1] Manufacturer */ }, - { /* [2] Product */ }, - { /* [3] Serial Number */ }, - }, - }, -} - // descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. const descCDCACMCodingSize = 7 @@ -514,23 +415,19 @@ type descCDCACMLineCoding struct { // Common configuration constants for the USB CDC-ACM (single) device class. const ( - // String descriptor languages available - descCDCACMLanguageCount = 1 + descCDCACMLanguageCount = 1 // String descriptor languages available - // Interfaces for all CDC-ACM configurations. - descCDCACMInterfaceCount = 2 - descCDCACMInterfaceCtrl = 0 - descCDCACMInterfaceData = 1 + descCDCACMInterfaceCount = 2 // Interfaces for all CDC-ACM configurations. + descCDCACMEndpointCount = 4 // Endpoints for all CDC-ACM configurations. - // Endpoints for all CDC-ACM configurations. - descCDCACMEndpointCount = 4 - descCDCACMEndpointStatus = 2 // Communication/control interrupt input - descCDCACMEndpointDataRx = 3 // Bulk data output - descCDCACMEndpointDataTx = 4 // Bulk data input - - // Endpoint configuration attributes for all CDC-ACM configurations. + descCDCACMInterfaceCtrl = 0 // CDC-ACM Control Interface + descCDCACMEndpointStatus = 2 // CDC-ACM Interrupt IN Endpoint descCDCACMConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt + + descCDCACMInterfaceData = 1 // CDC-ACM Data Interface + descCDCACMEndpointDataRx = 3 // CDC-ACM Bulk Data OUT (Rx) Endpoint descCDCACMConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused + descCDCACMEndpointDataTx = 4 // CDC-ACM Bulk Data IN (Tx) Endpoint descCDCACMConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk ) @@ -547,14 +444,600 @@ type descCDCACMClass struct { // descCDCACM holds statically-allocated instances for each of the CDC-ACM // (single) device class configurations, ordered by index (offset by -1). -var descCDCACM = [descCDCACMCount]descCDCACMClass{ +var descCDCACM = [dcdCount]descCDCACMClass{ { // CDC-ACM (single) class configuration index 1 descCDCACMClassData: &descCDCACMData[0], - locale: &descCDCACM0String, - device: &descCDCACM0Device, - qualif: &descCDCACM0Qualif, - config: &descCDCACM0Config, + locale: &[descCDCACMLanguageCount]descStringLanguage{ + + { // [0x0409] US English + language: descLanguageEnglish, + descriptor: descStringIndex{ + { /* [0] Language */ + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + // Actual string descriptors (index > 0) are copied into here at runtime! + // This allows for application- or even user-defined string descriptors. + { /* [1] Manufacturer */ }, + { /* [2] Product */ }, + { /* [3] Serial Number */ }, + }, + }, + }, + device: &[descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descCommonReleaseID), // Device release number in BCD (low) + msU8(descCommonReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descCDCACMCount, // Number of possible configurations + }, + qualif: &[descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descCDCACMCount, // Number of possible configurations + 0, // Reserved + }, + config: &[descCDCACMConfigSize]uint8{ + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) + msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) + descCDCACMInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descEndptConfigAttr, // Configuration attributes + descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) + + // Communication/Control Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descCDCACMInterfaceCtrl, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descCDCTypeComm, // Class code + descCDCSubAbstractControl, // Subclass code + descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) + 0, // Interface Description String Index + + // CDC Header Functional Descriptor + descCDCFuncLengthHeader, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeHeader, // Descriptor Subtype + 0x10, // USB CDC specification version 1.10 (low) + 0x01, // USB CDC specification version 1.10 (high) + + // CDC Call Management Functional Descriptor + descCDCFuncLengthCallManagement, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeCallManagement, // Descriptor Subtype + 0x01, // Capabilities + descCDCACMInterfaceData, // Data Interface + + // CDC Abstract Control Management Functional Descriptor + descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeAbstractControl, // Descriptor Subtype + 0x06, // Capabilities + + // CDC Union Functional Descriptor + descCDCFuncLengthUnion, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeUnion, // Descriptor Subtype + descCDCACMInterfaceCtrl, // Controlling interface index + descCDCACMInterfaceData, // Controlled interface index + + // Communication/Control Notification Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointStatus | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descCDCACMStatusPacketSize), // Max packet size (low) + msU8(descCDCACMStatusPacketSize), // Max packet size (high) + descCDCACMStatusInterval, // Polling Interval + + // Data Interface Descriptor + descLengthInterface, // Interface length + descTypeInterface, // Interface type + descCDCACMInterfaceData, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descCDCTypeData, // Class code + descCDCSubNone, // Subclass code + descCDCProtoNone, // Protocol code + 0, // Interface Description String Index + + // Data Bulk Rx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointDataRx | // Endpoint address + descEndptAddrDirectionOut, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataRxPacketSize), // Max packet size (low) + msU8(descCDCACMDataRxPacketSize), // Max packet size (high) + 0, // Polling Interval + + // Data Bulk Tx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointDataTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) + msU8(descCDCACMDataTxPacketSize), // Max packet size (high) + 0, // Polling Interval + }, }, } + +// Common configuration constants for the USB HID device class. +const ( + descHIDLanguageCount = 1 // String descriptor languages available + + descHIDInterfaceCount = 5 // Interfaces for all HID configurations. + descHIDEndpointCount = 6 // Endpoints for all HID configurations. + + descHIDInterfaceKeyboard = 0 // HID Keyboard Interface + descHIDEndpointKeyboard = 3 // HID Keyboard IN Endpoint + descHIDConfigAttrKeyboard = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused + + descHIDInterfaceMouse = 1 // HID Mouse Interface + descHIDEndpointMouse = 5 // HID Mouse IN Endpoint + descHIDConfigAttrMouse = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused + + descHIDInterfaceSerial = 2 // HID Serial (UART emulation) Interface + descHIDEndpointSerialRx = 2 // HID Serial OUT (Rx) Endpoint + descHIDEndpointSerialTx = 2 // HID Serial IN (Tx) Endpoint + descHIDConfigAttrSerial = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxInterrupt + + descHIDInterfaceJoystick = 3 // HID Joystick Interface + descHIDEndpointJoystick = 6 // HID Joystick IN Endpoint + descHIDConfigAttrJoystick = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused + + descHIDInterfaceMediaKey = 4 // HID Keyboard Media Keys Interface + descHIDEndpointMediaKey = 4 // HID Keyboard Media Keys IN Endpoint + descHIDConfigAttrMediaKey = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused +) + +// descHIDClass holds references to all descriptors, buffers, and control +// structures for the USB HID device class. +type descHIDClass struct { + *descHIDClassData // Target-defined, class-specific data + + locale *[descHIDLanguageCount]descStringLanguage // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descHIDConfigSize]uint8 // configuration descriptor +} + +// descHID holds statically-allocated instances for each of the HID device class +// configurations, ordered by index (offset by -1). +var descHID = [dcdCount]descHIDClass{ + + { // HID class configuration index 1 + descHIDClassData: &descHIDData[0], + + locale: &[descHIDLanguageCount]descStringLanguage{ + + { // [0x0409] US English + language: descLanguageEnglish, + descriptor: descStringIndex{ + { /* [0] Language */ + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + // Actual string descriptors (index > 0) are copied into here at runtime! + // This allows for application- or even user-defined string descriptors. + { /* [1] Manufacturer */ }, + { /* [2] Product */ }, + { /* [3] Serial Number */ }, + }, + }, + }, + device: &[descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descCommonReleaseID), // Device release number in BCD (low) + msU8(descCommonReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descHIDCount, // Number of possible configurations + }, + qualif: &[descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + 0, // Class code (assigned by the USB-IF). + 0, // Subclass code (assigned by the USB-IF). + 0, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descHIDCount, // Number of possible configurations + 0, // Reserved + }, + config: &[descHIDConfigSize]uint8{ + // [0+9] + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descHIDConfigSize), // Total length of data returned for this configuration (low) + msU8(descHIDConfigSize), // Total length of data returned for this configuration (high) + descHIDInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descEndptConfigAttr, // Configuration attributes + descHIDMaxPower, // Max power consumption when fully-operational (2 mA units) + + // [9+9] Keyboard Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceKeyboard, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubBoot, // Subclass code (Boot = 0x01) + descHIDProtoKeyboard, // Protocol code (Keyboard = 0x01) + 0, // Interface Description String Index + + // [18+9] Keyboard HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (low) + msU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (high) + + // [27+7] Keyboard Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointKeyboard | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDKeyboardTxPacketSize), // Max packet size (low) + msU8(descHIDKeyboardTxPacketSize), // Max packet size (high) + descHIDKeyboardTxInterval, // Polling Interval + + // [34+9] Mouse Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceMouse, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubBoot, // Subclass code (Boot = 0x01) + descHIDProtoMouse, // Protocol code (Mouse = 0x02) + 0, // Interface Description String Index + + // [43+9] Mouse HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportMouse))), // Descriptor length (low) + msU8(uint16(len(descHIDReportMouse))), // Descriptor length (high) + + // [52+7] Mouse Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointMouse | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDMouseTxPacketSize), // Max packet size (low) + msU8(descHIDMouseTxPacketSize), // Max packet size (high) + descHIDMouseTxInterval, // Polling Interval + + // [59+9] Serial Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceSerial, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubNone, // Subclass code + descHIDProtoNone, // Protocol code + 0, // Interface Description String Index + + // [68+9] Serial HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportSerial))), // Descriptor length (low) + msU8(uint16(len(descHIDReportSerial))), // Descriptor length (high) + + // [77+7] Serial Tx Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointSerialTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDSerialTxPacketSize), // Max packet size (low) + msU8(descHIDSerialTxPacketSize), // Max packet size (high) + descHIDSerialTxInterval, // Polling Interval + + // [84+7] Serial Rx Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointSerialRx | // Endpoint address + descEndptAddrDirectionOut, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDSerialRxPacketSize), // Max packet size (low) + msU8(descHIDSerialRxPacketSize), // Max packet size (high) + descHIDSerialRxInterval, // Polling Interval + + // [91+9] Joystick Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceJoystick, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubNone, // Subclass code + descHIDProtoNone, // Protocol code + 0, // Interface Description String Index + + // [100+9] Joystick HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportJoystick))), // Descriptor length (low) + msU8(uint16(len(descHIDReportJoystick))), // Descriptor length (high) + + // [109+7] Joystick Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointJoystick | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDJoystickTxPacketSize), // Max packet size (low) + msU8(descHIDJoystickTxPacketSize), // Max packet size (high) + descHIDJoystickTxInterval, // Polling Interval + + // [116+9] Keyboard Media Keys Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descHIDInterfaceMediaKey, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descHIDType, // Class code (HID = 0x03) + descHIDSubNone, // Subclass code + descHIDProtoNone, // Protocol code + 0, // Interface Description String Index + + // [125+9] Keyboard Media Keys HID Interface Descriptor + descLengthInterface, // Descriptor length + descTypeHID, // Descriptor type + 0x11, // HID BCD (low) + 0x01, // HID BCD (high) + 0, // Country code + 1, // Number of descriptors + descTypeHIDReport, // Descriptor type + lsU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (low) + msU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (high) + + // [134+7] Keyboard Media Keys Endpoint Descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descHIDEndpointMediaKey | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descHIDMediaKeyTxPacketSize), // Max packet size (low) + msU8(descHIDMediaKeyTxPacketSize), // Max packet size (high) + descHIDMediaKeyTxInterval, // Polling Interval + }, + }, +} + +var descHIDReportSerial = [...]uint8{ + 0x06, 0xC9, 0xFF, // Usage Page 0xFFC9 (vendor defined) + 0x09, 0x04, // Usage 0x04 + 0xA1, 0x5C, // Collection 0x5C + 0x75, 0x08, // report size = 8 bits (global) + 0x15, 0x00, // logical minimum = 0 (global) + 0x26, 0xFF, 0x00, // logical maximum = 255 (global) + 0x95, descHIDSerialTxPacketSize, // report count (global) + 0x09, 0x75, // usage (local) + 0x81, 0x02, // Input + 0x95, descHIDSerialRxPacketSize, // report count (global) + 0x09, 0x76, // usage (local) + 0x91, 0x02, // Output + 0x95, 0x04, // report count (global) + 0x09, 0x76, // usage (local) + 0xB1, 0x02, // Feature + 0xC0, // end collection +} + +var descHIDReportKeyboard = [...]uint8{ + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ; Modifier keys + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x03, // Input (Constant), ; Reserved byte + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), ; LED report + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), ; LED report padding + 0x95, 0x06, // Report Count (6), + 0x75, 0x08, // Report Size (8), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x7F, // Logical Maximum(104), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0x7F, // Usage Maximum (104), + 0x81, 0x00, // Input (Data, Array), ; Normal keys + 0xC0, // End Collection +} + +var descHIDReportMediaKey = [...]uint8{ + 0x05, 0x0C, // Usage Page (Consumer) + 0x09, 0x01, // Usage (Consumer Controls) + 0xA1, 0x01, // Collection (Application) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x04, // Report Count (4) + 0x19, 0x00, // Usage Minimum (0) + 0x2A, 0x9C, 0x02, // Usage Maximum (0x29C) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0x9C, 0x02, // Logical Maximum (0x29C) + 0x81, 0x00, // Input (Data, Array) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x75, 0x08, // Report Size (8) + 0x95, 0x03, // Report Count (3) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0xB7, // Usage Maximum (0xB7) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xB7, 0x00, // Logical Maximum (0xB7) + 0x81, 0x00, // Input (Data, Array) + 0xC0, // End Collection +} + +var descHIDReportMouse = [...]uint8{ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x01, // REPORT_ID (1) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x08, // Usage Maximum (Button #8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x08, // Report Count (8) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8), + 0x95, 0x03, // Report Count (3), + 0x81, 0x06, // Input (Data, Variable, Relative) + 0x05, 0x0C, // Usage Page (Consumer) + 0x0A, 0x38, 0x02, // Usage (AC Pan) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8), + 0x95, 0x01, // Report Count (1), + 0x81, 0x06, // Input (Data, Variable, Relative) + 0xC0, // End Collection + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x02, // REPORT_ID (2) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x75, 0x10, // Report Size (16), + 0x95, 0x02, // Report Count (2), + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0xC0, // End Collection +} + +var descHIDReportJoystick = [...]uint8{ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x20, // Report Count (32) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x20, // Usage Maximum (Button #32) + 0x81, 0x02, // Input (variable,absolute) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x65, 0x14, // Unit (20) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x39, // Usage (Hat switch) + 0x81, 0x42, // Input (variable,absolute,null_state) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection () + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x03, // Logical Maximum (1023) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x04, // Report Count (4) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x35, // Usage (Rz) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x03, // Logical Maximum (1023) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x02, // Report Count (2) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection +} diff --git a/src/machine/usb/desc_mimxrt1062.go b/src/machine/usb/desc_mimxrt1062.go index 4fa7b5527..fd8e3dbd9 100644 --- a/src/machine/usb/desc_mimxrt1062.go +++ b/src/machine/usb/desc_mimxrt1062.go @@ -5,6 +5,14 @@ package usb // descCPUFrequencyHz defines the target CPU frequency (Hz). const descCPUFrequencyHz = 600000000 +// descCDCACMCount defines the number of USB cores that may be configured as +// CDC-ACM (single) devices. +const descCDCACMCount = 0 + +// descHIDCount defines the number of USB cores that may be configured as a +// composite (keyboard + mouse + joystick) human interface device (HID). +const descHIDCount = 1 + // General USB device identification constants. const ( descCommonVendorID = 0x16C0 @@ -12,39 +20,166 @@ const ( descCommonReleaseID = 0x0101 // BCD (1.1) descCommonLanguage = descLanguageEnglish - descCommonManufacturer = "NXP Semiconductors" - descCommonProduct = "TinyGo USB" - descCommonSerialNumber = "1" + descCommonManufacturer = "TinyGo" + descCommonProduct = "USB" + descCommonSerialNumber = "00000" ) // Constants for USB CDC-ACM device classes. const ( - // descCDCACMCount defines the number of USB cores that will be configured as - // CDC-ACM (single) devices. - descCDCACMCount = 1 + descCDCACMMaxPower = 50 // 100 mA + + // CDC-ACM Control Buffers descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1) - descCDCACMRDCount = 2 * descCDCACMEndpointCount - descCDCACMTDCount = descCDCACMEndpointCount - descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount - descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount descCDCACMCxCount = 8 - descCDCACMMaxPower = 50 // 100 mA + // CDC-ACM Data Buffers + + descCDCACMRDCount = 2 * descCDCACMEndpointCount + descCDCACMRxSize = descCDCACMDataRxPacketSize + descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount + + descCDCACMTDCount = descCDCACMEndpointCount + descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize + descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount descCDCACMTxTimeoutMs = 120 // millisec descCDCACMTxSyncUs = 75 // microsec - descCDCACMStatusPacketSize = 16 - descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed - descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed - descCDCACMRxSize = descCDCACMDataRxPacketSize - descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize + // Default CDC-ACM Endpoint Configurations (High-Speed) - descCDCACMDataRxFSPacketSize = 64 // full-speed - descCDCACMDataTxFSPacketSize = 64 // full-speed - descCDCACMDataRxHSPacketSize = 512 // high-speed - descCDCACMDataTxHSPacketSize = 512 // high-speed + descCDCACMStatusInterval = descCDCACMStatusHSInterval // Status + descCDCACMStatusPacketSize = descCDCACMStatusHSPacketSize // + + descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // Data Rx + + descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // Data Tx + + // CDC-ACM Endpoint Configurations for Full-Speed Device + + descCDCACMStatusFSInterval = 5 // Status + descCDCACMStatusFSPacketSize = 16 // (full-speed) + + descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed) + + descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed) + + // CDC-ACM Endpoint Configurations for High-Speed Device + + descCDCACMStatusHSInterval = 5 // Status + descCDCACMStatusHSPacketSize = 16 // (high-speed) + + descCDCACMDataRxHSPacketSize = 512 // Data Rx (high-speed) + + descCDCACMDataTxHSPacketSize = 512 // Data Tx (high-speed) +) + +// Constants for USB HID (keyboard, mouse, joystick) device classes. +const ( + descHIDMaxPower = 50 // 100 mA + + // HID Control Buffers + + descHIDQHCount = 2 * (descHIDEndpointCount + 1) + descHIDCxCount = 8 + + // HID Serial Buffers + + descHIDSerialRDCount = 8 + descHIDSerialRxSize = descHIDSerialRxPacketSize + descHIDSerialRxCount = descHIDSerialRxSize * descHIDSerialRDCount + + descHIDSerialTDCount = 12 + descHIDSerialTxSize = descHIDSerialTxPacketSize + descHIDSerialTxCount = descHIDSerialTxSize * descHIDSerialTDCount + + descHIDSerialTxTimeoutMs = 50 // millisec + descHIDSerialTxSyncUs = 75 // microsec + + // HID Keyboard Buffers + + descHIDKeyboardTDCount = 12 + descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize + descHIDKeyboardTxCount = descHIDKeyboardTxSize * descHIDKeyboardTDCount + + descHIDKeyboardTxTimeoutMs = 50 // millisec + + // HID Mouse Buffers + + descHIDMouseTDCount = 4 + descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize + descHIDMouseTxCount = descHIDMouseTxSize * descHIDMouseTDCount + + descHIDMouseTxTimeoutMs = 30 // millisec + + // HID Joystick Buffers + + descHIDJoystickTDCount = 4 + descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize + descHIDJoystickTxCount = descHIDJoystickTxSize * descHIDJoystickTDCount + + descHIDJoystickTxTimeoutMs = 30 // millisec + + // Default HID Endpoint Configurations (High-Speed) + + descHIDSerialRxInterval = descHIDSerialRxHSInterval // Serial Rx + descHIDSerialRxPacketSize = descHIDSerialRxHSPacketSize // + + descHIDSerialTxInterval = descHIDSerialTxHSInterval // Serial Tx + descHIDSerialTxPacketSize = descHIDSerialTxHSPacketSize // + + descHIDKeyboardTxInterval = descHIDKeyboardTxHSInterval // Keyboard + descHIDKeyboardTxPacketSize = descHIDKeyboardTxHSPacketSize // + + descHIDMediaKeyTxInterval = descHIDMediaKeyTxHSInterval // Keyboard Media Keys + descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxHSPacketSize // + + descHIDMouseTxInterval = descHIDMouseTxHSInterval // Mouse + descHIDMouseTxPacketSize = descHIDMouseTxHSPacketSize // + + descHIDJoystickTxInterval = descHIDJoystickTxHSInterval // Joystick + descHIDJoystickTxPacketSize = descHIDJoystickTxHSPacketSize // + + // HID Endpoint Configurations for Full-Speed Device + + descHIDSerialRxFSInterval = 2 // Serial Rx + descHIDSerialRxFSPacketSize = 8 // (full-speed) + + descHIDSerialTxFSInterval = 1 // Serial Tx + descHIDSerialTxFSPacketSize = 16 // (full-speed) + + descHIDKeyboardTxFSInterval = 4 // Keyboard + descHIDKeyboardTxFSPacketSize = 8 // (full-speed) + + descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys + descHIDMediaKeyTxFSPacketSize = 8 // (full-speed) + + descHIDMouseTxFSInterval = 4 // Mouse + descHIDMouseTxFSPacketSize = 8 // (full-speed) + + descHIDJoystickTxFSInterval = 4 // Joystick + descHIDJoystickTxFSPacketSize = 12 // (full-speed) + + // HID Endpoint Configurations for High-Speed Device + + descHIDSerialRxHSInterval = 2 // Serial + descHIDSerialRxHSPacketSize = 32 // (high-speed) + + descHIDSerialTxHSInterval = 1 // Serial Tx + descHIDSerialTxHSPacketSize = 64 // (high-speed) + + descHIDKeyboardTxHSInterval = 1 // Keyboard + descHIDKeyboardTxHSPacketSize = 8 // (high-speed) + + descHIDMediaKeyTxHSInterval = 4 // Keyboard Media Keys + descHIDMediaKeyTxHSPacketSize = 8 // (high-speed) + + descHIDMouseTxHSInterval = 1 // Mouse + descHIDMouseTxHSPacketSize = 8 // (high-speed) + + descHIDJoystickTxHSInterval = 2 // Joystick + descHIDJoystickTxHSPacketSize = 12 // (high-speed) ) // descCDCACM0QH is an array of endpoint queue heads, which is where all @@ -71,18 +206,28 @@ const ( //go:align 4096 var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint -// descCDCACM0CD is the transfer descriptor for data messages transmitted or -// received on the status/control endpoint 0 for the default CDC-ACM (single) -// device class configuration (index 1). +// descCDCACM0CD is the transfer descriptor for messages transmitted or received +// on the status/control endpoint 0 for the default CDC-ACM (single) device +// class configuration (index 1). //go:align 32 var descCDCACM0CD dhwTransfer +// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of +// the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Cx [descCDCACMCxCount]uint8 + // descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages // transmitted or received on the status/control endpoint 0 for the default // CDC-ACM (single) device class configuration (index 1). //go:align 32 var descCDCACM0AD dhwTransfer +// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 +// for the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Dx [descCDCACMConfigSize]uint8 + // descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers, // which describe to the device controller the location and quantity of data // being received for a given transfer, for the default CDC-ACM (single) device @@ -90,6 +235,11 @@ var descCDCACM0AD dhwTransfer //go:align 32 var descCDCACM0RD [descCDCACMRDCount]dhwTransfer +// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Rx [descCDCACMRxCount]uint8 + // descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers, // which describe to the device controller the location and quantity of data // being transmitted for a given transfer, for the default CDC-ACM (single) @@ -97,36 +247,16 @@ var descCDCACM0RD [descCDCACMRDCount]dhwTransfer //go:align 32 var descCDCACM0TD [descCDCACMTDCount]dhwTransfer -// descCDCACM0LineCoding holds the emulated UART line coding for the default -// CDC-ACM (single) device class configuration (index 1). -// i.e., configuration index 1. -//go:align 32 -var descCDCACM0LC descCDCACMLineCoding - -// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of -// the default CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Cx [descCDCACMCxCount]uint8 - -// descCDCACM0Rx is the receive (Rx) buffer of data endpoints for the default -// CDC-ACM (single) device class configuration (index 1). -//go:align 32 -var descCDCACM0Rx [descCDCACMRxCount]uint8 - -// descCDCACM0Tx is the transmit (Tx) buffer of data endpoints for the default -// CDC-ACM (single) device class configuration (index 1). +// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM +// (single) device class configuration (index 1). //go:align 32 var descCDCACM0Tx [descCDCACMTxCount]uint8 -// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data for the default -// CDC-ACM (single) device class configuration (index 1). -var descCDCACM0Dx [descCDCACMConfigSize]uint8 - var descCDCACM0RDNum [descCDCACMRDCount]uint16 var descCDCACM0RDIdx [descCDCACMRDCount]uint16 -var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 +var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16 -// descCDCACM holds the buffers and control states for all of the CDC-ACM +// descCDCACMClassData holds the buffers and control states for all CDC-ACM // (single) device class configurations, ordered by index (offset by -1), for // iMXRT1062 targets only. // @@ -134,21 +264,30 @@ var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 // of the common/target-agnostic CDC-ACM class configurations (descCDCACM). // Methods defined on this type implement target-specific functionality, and // some of these methods are required by the common device controller driver. -// Thus, this type functions as an additional hardware abstraction layer. +// Thus, this type functions as a hardware abstraction layer (HAL). type descCDCACMClassData struct { + + // CDC-ACM Control Buffers + qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads - cd *dhwTransfer // control endpoint 0 Rx/Tx data transfer descriptor - ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor - rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors - td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors - + cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer - rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer - tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer - dx *[descCDCACMConfigSize]uint8 // descriptor data Tx (IN) transfer buffer + ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer - cxSize uint16 + // CDC-ACM Data Buffers + + rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors + rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer + td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors + tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer + + rxCount *[descCDCACMRDCount]uint16 + rxIndex *[descCDCACMRDCount]uint16 + rxQueue *[(descCDCACMRDCount + 1)]uint16 + + sxSize uint16 rxSize uint16 txSize uint16 @@ -159,40 +298,307 @@ type descCDCACMClassData struct { rxHead uint8 rxTail uint8 rxFree uint16 - - rxCount *[descCDCACMRDCount]uint16 - rxIndex *[descCDCACMRDCount]uint16 - rxQueue *[descCDCACMRDCount + 1]uint16 - - _ [2]uint8 } // descCDCACMData holds statically-allocated instances for each of the target- // specific (iMXRT1062) CDC-ACM (single) device class configurations' control // and data structures, ordered by configuration index (offset by -1). Each // element is embedded in a corresponding element of descCDCACM. -//go:align 32 -var descCDCACMData = [descCDCACMCount]descCDCACMClassData{ +//go:align 64 +var descCDCACMData = [dcdCount]descCDCACMClassData{ + + { // -- CDC-ACM (single) Class Configuration Index 1 -- + + // CDC-ACM Control Buffers - { // CDC-ACM (single) class configuration index 1 data qh: &descCDCACM0QH, cd: &descCDCACM0CD, - ad: &descCDCACM0AD, - rd: &descCDCACM0RD, - td: &descCDCACM0TD, - cx: &descCDCACM0Cx, - rx: &descCDCACM0Rx, - tx: &descCDCACM0Tx, + ad: &descCDCACM0AD, dx: &descCDCACM0Dx, - cxSize: descCDCACMStatusPacketSize, - rxSize: descCDCACMDataRxPacketSize, - txSize: descCDCACMDataTxPacketSize, + // CDC-ACM Data Buffers + + rd: &descCDCACM0RD, + rx: &descCDCACM0Rx, + td: &descCDCACM0TD, + tx: &descCDCACM0Tx, rxCount: &descCDCACM0RDNum, rxIndex: &descCDCACM0RDIdx, rxQueue: &descCDCACM0RDQue, + + sxSize: descCDCACMStatusPacketSize, + rxSize: descCDCACMDataRxPacketSize, + txSize: descCDCACMDataTxPacketSize, + }, +} + +// descHID0QH is an array of endpoint queue heads, which is where all transfers +// for a given endpoint are managed, for the default HID device class +// configuration (index 1). +// +// From the iMXRT1062 Reference Manual: +// +// Software must ensure that no interface data structure reachable +// by the Device Controller spans a 4K-page boundary. +// +// The [queue head] is a 48-byte data structure, but must be aligned on +// 64-byte boundaries. +// +// Endpoint queue heads are arranged in an array in a continuous area of +// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered +// device queue heads in the list support receive endpoints (OUT/SETUP) and +// the odd-numbered queue heads in the list are used for transmit endpoints +// (IN/INTERRUPT). The device controller will index into this array based upon +// the endpoint number received from the USB bus. All information necessary to +// respond to transactions for all primed transfers is contained in this list +// so the Device Controller can readily respond to incoming requests without +// having to traverse a linked list. +//go:align 4096 +var descHID0QH [descHIDQHCount]dhwEndpoint + +// descHID0CD is the transfer descriptor for messages transmitted or received on +// the status/control endpoint 0 for the default HID device class configuration +// (index 1). +//go:align 32 +var descHID0CD dhwTransfer + +// descHID0Cx is the buffer for control/status data received on endpoint 0 of +// the default HID device class configuration (index 1). +//go:align 32 +var descHID0Cx [descHIDCxCount]uint8 + +// descHID0AD is the transfer descriptor for ackowledgement (ACK) messages +// transmitted or received on the status/control endpoint 0 for the default HID +// device class configuration (index 1). +//go:align 32 +var descHID0AD dhwTransfer + +// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for +// the default HID device class configuration (index 1). +//go:align 32 +var descHID0Dx [descHIDConfigSize]uint8 + +// descHID0SerialRD is an array of transfer descriptors for serial Rx (OUT) +// transfers, which describe to the device controller the location and quantity +// of data being received for a given transfer, for the default HID device class +// configuration (index 1). +//go:align 32 +var descHID0SerialRD [descHIDSerialRDCount]dhwTransfer + +// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +var descHID0SerialRx [descHIDSerialRxCount]uint8 + +// descHID0SerialTD is an array of transfer descriptors for serial Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0SerialTD [descHIDSerialTDCount]dhwTransfer + +// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +var descHID0SerialTx [descHIDSerialTxCount]uint8 + +var descHID0SerialRDIdx [descHIDSerialRDCount]uint16 +var descHID0SerialRDQue [(descHIDSerialRDCount + 1)]uint16 + +// descHID0KeyboardTD is an array of transfer descriptors for keyboard Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0KeyboardTD [descHIDKeyboardTDCount]dhwTransfer + +// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the +// default HID device class configuration (index 1). +//go:align 32 +var descHID0KeyboardTx [descHIDKeyboardTxCount]uint8 + +// descHID0KeyboardTp is the keyboard HID report transmit (Tx) transfer buffer +// for the default HID device class configuration (index 1). +//go:align 32 +var descHID0KeyboardTp [descHIDKeyboardTxPacketSize]uint8 + +//go:align 32 +var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8 + +//go:align 32 +var descHID0KeyboardTxCon [hidKeyboardConCount]uint16 + +//go:align 32 +var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8 + +// descHID0MouseTD is an array of transfer descriptors for mouse Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0MouseTD [descHIDMouseTDCount]dhwTransfer + +// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default +// HID device class configuration (index 1). +//go:align 32 +var descHID0MouseTx [descHIDMouseTxCount]uint8 + +// descHID0JoystickTD is an array of transfer descriptors for joystick Tx (IN) +// transfers, which describe to the device controller the location and quantity +// of data being transmitted for a given transfer, for the default HID device +// class configuration (index 1). +//go:align 32 +var descHID0JoystickTD [descHIDJoystickTDCount]dhwTransfer + +// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the +// default HID device class configuration (index 1). +//go:align 32 +var descHID0JoystickTx [descHIDJoystickTxCount]uint8 + +// descHID0Keyboard is the Keyboard instance with which the user may interact +// when using the default HID device class configuration (index 1). +//go:align 64 +var descHID0Keyboard = Keyboard{ + key: &descHID0KeyboardTxKey, + con: &descHID0KeyboardTxCon, + sys: &descHID0KeyboardTxSys, +} + +// descHIDClassData holds the buffers and control states for all of the HID +// device class configurations, ordered by index (offset by -1), for iMXRT1062 +// targets only. +// +// Instances of this type (elements of descHIDData) are embedded in elements +// of the common/target-agnostic HID class configurations (descHID). +// Methods defined on this type implement target-specific functionality, and +// some of these methods are required by the common device controller driver. +// Thus, this type functions as a hardware abstraction layer (HAL). +type descHIDClassData struct { + + // HID Control Buffers + + qh *[descHIDQHCount]dhwEndpoint // endpoint queue heads + + cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor + cx *[descHIDCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer + ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer + + // HID Serial Buffers + + rdSerial *[descHIDSerialRDCount]dhwTransfer // interrupt endpoint serial Rx (OUT) transfer descriptors + rxSerial *[descHIDSerialRxCount]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer + tdSerial *[descHIDSerialTDCount]dhwTransfer // interrupt endpoint serial Tx (IN) transfer descriptors + txSerial *[descHIDSerialTxCount]uint8 // interrupt endpoint serial Tx (IN) transfer buffer + + rxSerialIndex *[descHIDSerialRDCount]uint16 + rxSerialQueue *[(descHIDSerialRDCount + 1)]uint16 + + rxSerialSize uint16 + txSerialSize uint16 + + txSerialHead uint8 + txSerialFree uint16 + txSerialPrev bool + + rxSerialHead uint8 + rxSerialTail uint8 + rxSerialFree uint16 + + // HID Keyboard Buffers + + tdKeyboard *[descHIDKeyboardTDCount]dhwTransfer // interrupt endpoint keyboard Tx (IN) transfer descriptors + txKeyboard *[descHIDKeyboardTxCount]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer + tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer + + txKeyboardSize uint16 + + txKeyboardHead uint8 + txKeyboardPrev bool + + // HID Mouse Buffers + + tdMouse *[descHIDMouseTDCount]dhwTransfer // interrupt endpoint mouse Tx (IN) transfer descriptors + txMouse *[descHIDMouseTxCount]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer + + txMouseSize uint16 + + txMouseHead uint8 + txMousePrev bool + + // HID Joystick Buffers + + tdJoystick *[descHIDJoystickTDCount]dhwTransfer // interrupt endpoint joystick Tx (IN) transfer descriptors + txJoystick *[descHIDJoystickTxCount]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer + + txJoystickSize uint16 + + txJoystickHead uint8 + txJoystickPrev bool + + // HID Device Instances + + keyboard *Keyboard +} + +// descHIDData holds statically-allocated instances for each of the target- +// specific (iMXRT1062) HID device class configurations' control and data +// structures, ordered by configuration index (offset by -1). Each element is +// embedded in a corresponding element of descHID. +//go:align 64 +var descHIDData = [dcdCount]descHIDClassData{ + + { // -- HID Class Configuration Index 1 -- + + // HID Control Buffers + + qh: &descHID0QH, + + cd: &descHID0CD, + cx: &descHID0Cx, + ad: &descHID0AD, + dx: &descHID0Dx, + + // HID Serial Buffers + + rdSerial: &descHID0SerialRD, + rxSerial: &descHID0SerialRx, + tdSerial: &descHID0SerialTD, + txSerial: &descHID0SerialTx, + + rxSerialIndex: &descHID0SerialRDIdx, + rxSerialQueue: &descHID0SerialRDQue, + + rxSerialSize: descHIDSerialRxPacketSize, + txSerialSize: descHIDSerialTxPacketSize, + + // HID Keyboard Buffers + + tdKeyboard: &descHID0KeyboardTD, + txKeyboard: &descHID0KeyboardTx, + tpKeyboard: &descHID0KeyboardTp, + + txKeyboardSize: descHIDKeyboardTxPacketSize, + + // HID Mouse Buffers + + tdMouse: &descHID0MouseTD, + txMouse: &descHID0MouseTx, + + txMouseSize: descHIDMouseTxPacketSize, + + // HID Joystick Buffers + + tdJoystick: &descHID0JoystickTD, + txJoystick: &descHID0JoystickTx, + + txJoystickSize: descHIDJoystickTxPacketSize, + + // HID Device Instances + + keyboard: &descHID0Keyboard, }, } diff --git a/src/machine/usb/dhw_mimxrt1062.go b/src/machine/usb/dhw_mimxrt1062.go index e2d95da40..a2700c9a7 100644 --- a/src/machine/usb/dhw_mimxrt1062.go +++ b/src/machine/usb/dhw_mimxrt1062.go @@ -40,6 +40,8 @@ type dhw struct { sofUsage uint8 } +func runBootloader() { arm.Asm(`bkpt #251`) } + // cycleCount uses the ARM debug cycle counter available on iMXRT1062 (enabled // in runtime_mimxrt1062_time.go) to return the number of CPU cycles since boot. //go:inline @@ -184,7 +186,9 @@ func (d *dhw) enableSOF(enable bool, iface uint8) { d.bus.USBINTR.SetBits(nxp.USB_USBINTR_SRE) } arm.EnableInterrupts(ivm) + d.timerReboot = 80 } else { + d.timerReboot = 0 d.sofUsage &^= 1 << iface if 0 == d.sofUsage { d.bus.USBINTR.ClearBits(nxp.USB_USBINTR_SRE) @@ -354,33 +358,23 @@ func (d *dhw) interrupt() { d.timerReboot -= 1 if 0 == d.timerReboot { d.enableSOF(false, descCDCACMInterfaceCount) + runBootloader() } } } } -func (d *dhw) controlBusSpeed() uint8 { return d.busSpeed } +func (d *dhw) speed() uint8 { return d.busSpeed } -func (d *dhw) controlDeviceAddress(addr uint16) { +func (d *dhw) setDeviceAddress(addr uint16) { d.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | ((uint32(addr) << nxp.USB_DEVICEADDR_USBADR_Pos) & nxp.USB_DEVICEADDR_USBADR_Msk)) } -func (d *dhw) controlLineState(coding descCDCACMLineCoding, dtr, rts bool) { - // TBD: does the PHY need to handle on iMXRT1062 (e.g., Teensyduino Loader)? -} - -func (d *dhw) controlLineCoding(coding descCDCACMLineCoding) { - if 134 == coding.baud { - d.enableSOF(true, descCDCACMInterfaceCount) - } -} - -// controlStatus transitions transfers on control endpoint 0 into status stage. -func (d *dhw) controlStatus() { - // Not used on iMXRT1062 -} +// ============================================================================= +// Control Endpoint 0 +// ============================================================================= // controlStall stalls a transfer on control endpoint 0. To stall a transfer on // any other endpoint, use method endpointStall(). @@ -411,7 +405,7 @@ func (d *dhw) controlReceive( } // wait for endpoint finish priming } ad.next = dhwTransferEOL - ad.token = 1 << 7 + ad.token = 1 << 7 // Bit 7: active transfer if notify { ad.token |= 1 << 15 } @@ -424,6 +418,8 @@ func (d *dhw) controlReceive( if notify { d.controlMask = tm } + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming } // controlTransmit transmits (Tx, IN) data on control endpoint 0. @@ -449,7 +445,7 @@ func (d *dhw) controlTransmit( } // wait for endpoint finish priming } ad.next = dhwTransferEOL - ad.token = 1 << 7 + ad.token = 1 << 7 // Bit 7: active transfer if notify { ad.token |= 1 << 15 } @@ -462,11 +458,13 @@ func (d *dhw) controlTransmit( if notify { d.controlMask = rm } + for 0 != d.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming } -// dhwEndpointSize defines the size (bytes) of a structure containing a USB -// standard endpoint. -const dhwEndpointSize = 64 // bytes +// ============================================================================= +// Endpoint Descriptor +// ============================================================================= // dhwEndpoint defines a USB standard endpoint, used as the general channel of // communication between host and device. @@ -488,6 +486,10 @@ type dhwEndpoint struct { callback func(transfer *dhwTransfer) } +// dhwEndpointSize defines the size (bytes) of a structure containing a USB +// standard endpoint. +const dhwEndpointSize = 64 // bytes + // endpointQueueHead returns the queue head for the given endpoint address, // encoded as direction D and endpoint number N with the 8-bit mask DxxxNNNN. //go:inline @@ -496,6 +498,8 @@ func (d *dhw) endpointQueueHead(endpoint uint8) *dhwEndpoint { switch d.cc.id { case classDeviceCDCACM: return &descCDCACM[d.cc.config-1].qh[endpointIndex(endpoint)] + case classDeviceHID: + return &descHID[d.cc.config-1].qh[endpointIndex(endpoint)] default: return nil } @@ -552,17 +556,23 @@ func (d *dhw) endpointStall(endpoint uint8) { ) } -// func (d *dhw) endpointPrime(mask uint32, transfer *dhwTransfer) { -// d.bus.ENDPTPRIME.Set(mask) -// } +func (d *dhw) endpointClearFeature(endpoint uint8) { + switch endpoint { + case rxEndpoint(endpoint): + d.endpointControlRegister(endpoint).ClearBits(nxp.USB_ENDPTCTRL0_RXS) + case txEndpoint(endpoint): + d.endpointControlRegister(endpoint).ClearBits(nxp.USB_ENDPTCTRL0_TXS) + } +} -// func (d *dhw) endpointPrimed() uint32 { -// return d.bus.ENDPTPRIME.Get() -// } - -// func (d *dhw) endpointUnprime(mask uint32) { -// d.bus.ENDPTCOMPLETE.Set(mask) -// } +func (d *dhw) endpointSetFeature(endpoint uint8) { + switch endpoint { + case rxEndpoint(endpoint): + d.endpointControlRegister(endpoint).SetBits(nxp.USB_ENDPTCTRL0_RXS) + case txEndpoint(endpoint): + d.endpointControlRegister(endpoint).SetBits(nxp.USB_ENDPTCTRL0_TXS) + } +} // endpointConfigure configures the given bulk data endpoint for transfer. func (d *dhw) endpointConfigure( @@ -603,8 +613,8 @@ func (d *dhw) endpointComplete(endpoint uint8) { ep.first = nil ep.last = nil } else { - if 0 != t.token&(1<<7) { - // active transfer, new list begins here + if 0 != t.token&(1<<7) { // Bit 7: active transfer + // new list begins here ep.first = t break } else { @@ -634,15 +644,23 @@ func (d *dhw) endpointConfigureRx( endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(rxEndpoint(endpoint)) - d.endpointConfigure(ep, packetSize, zlp, callback) - if nil != callback { - d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrRxPos + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return } default: // Unhandled device class } + + ep := d.endpointQueueHead(rxEndpoint(endpoint)) + d.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrRxPos + } } // endpointConfigureTx configures the given bulk data transmit (Tx, IN) endpoint @@ -659,15 +677,23 @@ func (d *dhw) endpointConfigureTx( endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(txEndpoint(endpoint)) - d.endpointConfigure(ep, packetSize, zlp, callback) - if nil != callback { - d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrTxPos + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return } default: // Unhandled device class } + + ep := d.endpointQueueHead(txEndpoint(endpoint)) + d.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + d.endpointMask |= (uint32(1) << endpoint) << descEndptConfigAttrTxPos + } } // endpointReceive schedules a receive (Rx, OUT) transfer on the given endpoint. @@ -682,13 +708,21 @@ func (d *dhw) endpointReceive(endpoint uint8, transfer *dhwTransfer) { endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(rxEndpoint(endpoint)) - em := (uint32(1) << endpoint) << descEndptConfigAttrRxPos - d.transferSchedule(ep, em, transfer) + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return + } default: // Unhandled device class } + + ep := d.endpointQueueHead(rxEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descEndptConfigAttrRxPos + d.transferSchedule(ep, em, transfer) } // endpointTransmit schedules a transmit (Tx, IN) transfer on the given @@ -704,17 +738,26 @@ func (d *dhw) endpointTransmit(endpoint uint8, transfer *dhwTransfer) { endpoint > descCDCACMEndpointCount { return } - ep := d.endpointQueueHead(txEndpoint(endpoint)) - em := (uint32(1) << endpoint) << descEndptConfigAttrTxPos - d.transferSchedule(ep, em, transfer) + + // HID + case classDeviceHID: + if endpoint < descHIDEndpointSerialRx || + endpoint > descHIDEndpointCount { + return + } default: // Unhandled device class } + + ep := d.endpointQueueHead(txEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descEndptConfigAttrTxPos + d.transferSchedule(ep, em, transfer) } -// dhwTransferSize defines the size (bytes) of a USB standard transfer packet. -const dhwTransferSize = 32 // bytes +// ============================================================================= +// Transfer Descriptor +// ============================================================================= // dhwTransfer describes the size and location of data to be transferred to or // from a USB endpoint. @@ -725,6 +768,9 @@ type dhwTransfer struct { param uint32 } +// dhwTransferSize defines the size (bytes) of a USB standard transfer packet. +const dhwTransferSize = 32 // bytes + // dhwTransferEOL is a sentinel value used to indicate the final node in a // linked list of transfer descriptors. var dhwTransferEOL = (*dhwTransfer)(unsafe.Pointer(uintptr(1))) @@ -744,6 +790,8 @@ func (d *dhw) transferControl() (dat, ack *dhwTransfer) { switch d.cc.id { case classDeviceCDCACM: return descCDCACM[d.cc.config-1].cd, descCDCACM[d.cc.config-1].ad + case classDeviceHID: + return descHID[d.cc.config-1].cd, descHID[d.cc.config-1].ad default: return nil, nil } @@ -753,7 +801,9 @@ func (d *dhw) transferPrepare( transfer *dhwTransfer, data *uint8, size uint16, param uint32) { transfer.next = dhwTransferEOL - transfer.token = (uint32(size) << 16) | (1 << 7) + + // Set 15-bit packet size and transfer active bit 7. + transfer.token = (uint32(size&0x7FFF) << 16) | (1 << 7) addr := uintptr(unsafe.Pointer(data)) for i := range transfer.pointer { transfer.pointer[i] = addr + uintptr(i)*4096 @@ -794,6 +844,10 @@ endTransfer: arm.EnableInterrupts(ivm) } +// ============================================================================= +// General-Purpose (GP) Timer +// ============================================================================= + func (d *dhw) timerConfigure(timer int, usec uint32, fn func()) { if timer < 0 || timer >= len(d.timerInterrupt) { return @@ -831,9 +885,15 @@ func (d *dhw) timerStop(timer int) { } } +// ============================================================================= +// [CDC-ACM] Serial UART (Virtual COM Port) +// ============================================================================= + func (d *dhw) uartConfigure() { + acm := &descCDCACM[d.cc.config-1] - switch d.controlBusSpeed() { + + switch d.speed() { case descDeviceSpeedHigh: acm.rxSize = descCDCACMDataRxHSPacketSize acm.txSize = descCDCACMDataTxHSPacketSize @@ -843,6 +903,7 @@ func (d *dhw) uartConfigure() { } acm.txHead = 0 acm.txFree = 0 + acm.txPrev = false acm.rxHead = 0 acm.rxTail = 0 acm.rxFree = 0 @@ -855,7 +916,7 @@ func (d *dhw) uartConfigure() { false, descCDCACMConfigAttrDataTx) d.endpointConfigureTx(descCDCACMEndpointStatus, - acm.cxSize, false, nil) + acm.sxSize, false, nil) d.endpointConfigureRx(descCDCACMEndpointDataRx, acm.rxSize, false, d.uartNotify) d.endpointConfigureTx(descCDCACMEndpointDataTx, @@ -866,6 +927,16 @@ func (d *dhw) uartConfigure() { d.timerConfigure(0, descCDCACMTxSyncUs, d.uartSync) } +func (d *dhw) uartSetLineState(dtr, rts bool) { + // TBD: does the PHY need to handle on iMXRT1062 (e.g., Teensyduino Loader)? +} + +func (d *dhw) uartSetLineCoding(coding descCDCACMLineCoding) { + if 134 == coding.baud { + d.enableSOF(true, descCDCACMInterfaceCount) + } +} + func (d *dhw) uartReceive(endpoint uint8) { acm := &descCDCACM[d.cc.config-1] num := uint16(endpoint) & descEndptAddrNumberMsk @@ -1071,3 +1142,263 @@ func (d *dhw) uartSync() { } acm.txFree = 0 } + +// ============================================================================= +// [HID] Serial +// ============================================================================= + +func (d *dhw) serialConfigure() { + + hid := &descHID[d.cc.config-1] + + switch d.speed() { + case descDeviceSpeedHigh: + hid.rxSerialSize = descHIDSerialRxHSPacketSize + hid.txSerialSize = descHIDSerialTxHSPacketSize + default: + hid.rxSerialSize = descHIDSerialRxFSPacketSize + hid.txSerialSize = descHIDSerialTxFSPacketSize + } + + // Rx and Tx are on same endpoint + d.endpointEnable(descHIDEndpointSerialRx, + false, descHIDConfigAttrSerial) + + d.endpointConfigureRx(descHIDEndpointSerialRx, + hid.rxSerialSize, false, d.serialNotify) + d.endpointConfigureTx(descHIDEndpointSerialTx, + hid.txSerialSize, false, nil) + for i := range hid.rdSerial { + d.serialReceive(uint8(i)) + } + d.timerConfigure(0, descHIDSerialTxSyncUs, d.serialSync) +} + +func (d *dhw) serialReceive(endpoint uint8) { + hid := &descHID[d.cc.config-1] + num := uint16(endpoint) & descEndptAddrNumberMsk + buf := &hid.rxSerial[num*descHIDSerialRxSize] + d.enableInterrupts(false) + d.transferPrepare(&hid.rdSerial[num], buf, hid.rxSerialSize, uint32(endpoint)) + deleteCache(uintptr(unsafe.Pointer(buf)), uintptr(hid.rxSerialSize)) + d.endpointReceive(descHIDEndpointSerialRx, &hid.rdSerial[num]) + d.enableInterrupts(true) +} + +func (d *dhw) serialTransmit() { + hid := &descHID[d.cc.config-1] + xfer := &hid.tdSerial[hid.txSerialHead] + buff := &hid.txSerial[uint16(hid.txSerialHead)*descHIDSerialTxSize] + d.transferPrepare(xfer, buff, hid.txSerialSize, 0) + flushCache(uintptr(unsafe.Pointer(buff)), uintptr(hid.txSerialSize)) + d.endpointTransmit(descHIDEndpointSerialTx, xfer) + hid.txSerialHead += 1 + if hid.txSerialHead >= descHIDSerialTDCount { + hid.txSerialHead = 0 + } +} + +func (d *dhw) serialNotify(transfer *dhwTransfer) { + hid := &descHID[d.cc.config-1] + len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF) + p := transfer.param + if len == hid.rxSerialSize && 0 != hid.rxSerial[p*uint32(hid.rxSerialSize)] { + // data packet + hid.rxSerialIndex[p] = 0 + h := hid.rxSerialHead + 1 + if h > descHIDSerialRDCount { // should be >= + h = 0 + } + hid.rxSerialQueue[h] = uint16(p) + hid.rxSerialHead = h + hid.rxSerialFree += len + } else { + // short packet + d.serialReceive(uint8(p)) + } +} + +// serialFlush discards all buffered input (Rx) data. +func (d *dhw) serialFlush() { + hid := &descHID[d.cc.config-1] + tail := hid.rxSerialTail + for tail != hid.rxSerialHead { + tail += 1 + if tail > descHIDSerialRDCount { + tail = 0 + } + i := hid.rxSerialQueue[tail] + d.serialReceive(uint8(i)) + hid.rxSerialTail = tail + } +} + +func (d *dhw) serialSync() { + const autoFlushTx = true + if !autoFlushTx { + return + } + hid := &descHID[d.cc.config-1] + if 0 == hid.txSerialFree { + return + } + xfer := &hid.tdSerial[hid.txSerialHead] + buff := &hid.txSerial[uint16(hid.txSerialHead)*descHIDSerialTxSize] + size := descHIDSerialTxSize - hid.txSerialFree + d.transferPrepare(xfer, buff, size, 0) + flushCache(uintptr(unsafe.Pointer(buff)), uintptr(size)) + d.endpointTransmit(descHIDEndpointSerialTx, xfer) + hid.txSerialHead += 1 + if hid.txSerialHead >= descHIDSerialTDCount { + hid.txSerialHead = 0 + } + hid.txSerialFree = 0 +} + +// ============================================================================= +// [HID] Keyboard +// ============================================================================= + +func (d *dhw) keyboard() *Keyboard { return descHID[d.cc.config-1].keyboard } + +func (d *dhw) keyboardConfigure() { + + hid := &descHID[d.cc.config-1] + + // Initialize keyboard + hid.keyboard.configure(d.dcd, hid) + + switch d.speed() { + case descDeviceSpeedHigh: + hid.txKeyboardSize = descHIDKeyboardTxHSPacketSize + default: + hid.txKeyboardSize = descHIDKeyboardTxFSPacketSize + } + + d.endpointEnable(descHIDEndpointKeyboard, + false, descHIDConfigAttrKeyboard) + d.endpointEnable(descHIDEndpointMediaKey, + false, descHIDConfigAttrMediaKey) + + d.endpointConfigureTx(descHIDEndpointKeyboard, + hid.txKeyboardSize, false, nil) + d.endpointConfigureTx(descHIDEndpointMediaKey, + hid.txKeyboardSize, false, nil) +} + +func (d *dhw) keyboardSendKeys(consumer bool) bool { + + hid := &descHID[d.cc.config-1] + + if !consumer { + + hid.tpKeyboard[0] = hid.keyboard.mod + hid.tpKeyboard[1] = 0 + hid.tpKeyboard[2] = hid.keyboard.key[0] + hid.tpKeyboard[3] = hid.keyboard.key[1] + hid.tpKeyboard[4] = hid.keyboard.key[2] + hid.tpKeyboard[5] = hid.keyboard.key[3] + hid.tpKeyboard[6] = hid.keyboard.key[4] + hid.tpKeyboard[7] = hid.keyboard.key[5] + + return d.keyboardWrite(descHIDEndpointKeyboard, hid.tpKeyboard[:]) + + } else { + + // 44444444 44333333 33332222 22222211 11111111 [ word ] + // 98765432 10987654 32109876 54321098 76543210 [ index ] (right-to-left) + + hid.tpKeyboard[1] = uint8((hid.keyboard.con[1] << 2) | ((hid.keyboard.con[0] >> 8) & 0x03)) + hid.tpKeyboard[2] = uint8((hid.keyboard.con[2] << 4) | ((hid.keyboard.con[1] >> 6) & 0x0F)) + hid.tpKeyboard[3] = uint8((hid.keyboard.con[3] << 6) | ((hid.keyboard.con[2] >> 4) & 0x3F)) + hid.tpKeyboard[4] = uint8(hid.keyboard.con[3] >> 2) + hid.tpKeyboard[5] = hid.keyboard.sys[0] + hid.tpKeyboard[6] = hid.keyboard.sys[1] + hid.tpKeyboard[7] = hid.keyboard.sys[2] + + return d.keyboardWrite(descHIDEndpointMediaKey, hid.tpKeyboard[:]) + + } +} + +func (d *dhw) keyboardWrite(endpoint uint8, data []uint8) bool { + + hid := &descHID[d.cc.config-1] + + size := uint16(len(data)) + xfer := &hid.tdKeyboard[hid.txKeyboardHead] + when := ticks() + for { + if 0 == xfer.token&0x80 { + if 0 != xfer.token&0x68 { + // TODO: token contains error, how to handle? + } + hid.txKeyboardPrev = false + break + } + if hid.txKeyboardPrev { + return false + } + if ticks()-when > descHIDKeyboardTxTimeoutMs { + // Waited too long, assume host connection dropped + hid.txKeyboardPrev = true + return false + } + } + // Without this delay, the order packets are transmitted is seriously screwy. + udelay(60) + buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:] + _ = copy(buff, data) + d.transferPrepare(xfer, &buff[0], size, 0) + flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize) + d.endpointTransmit(endpoint, xfer) + hid.txKeyboardHead += 1 + if hid.txKeyboardHead >= descHIDKeyboardTDCount { + hid.txKeyboardHead = 0 + } + return true +} + +// ============================================================================= +// [HID] Mouse +// ============================================================================= + +func (d *dhw) mouseConfigure() { + + hid := &descHID[d.cc.config-1] + + switch d.speed() { + case descDeviceSpeedHigh: + hid.txMouseSize = descHIDMouseTxHSPacketSize + default: + hid.txMouseSize = descHIDMouseTxFSPacketSize + } + + d.endpointEnable(descHIDEndpointMouse, + false, descHIDConfigAttrMouse) + + d.endpointConfigureTx(descHIDEndpointMouse, + hid.txMouseSize, false, nil) +} + +// ============================================================================= +// [HID] Joystick +// ============================================================================= + +func (d *dhw) joystickConfigure() { + + hid := &descHID[d.cc.config-1] + + switch d.speed() { + case descDeviceSpeedHigh: + hid.txJoystickSize = descHIDJoystickTxHSPacketSize + default: + hid.txJoystickSize = descHIDJoystickTxFSPacketSize + } + + d.endpointEnable(descHIDEndpointJoystick, + false, descHIDConfigAttrJoystick) + + d.endpointConfigureTx(descHIDEndpointJoystick, + hid.txJoystickSize, false, nil) +} diff --git a/src/machine/usb/hid.go b/src/machine/usb/hid.go new file mode 100644 index 000000000..6b2dcc9ab --- /dev/null +++ b/src/machine/usb/hid.go @@ -0,0 +1,42 @@ +package usb + +import ( + "errors" +) + +var ( + ErrHIDInvalidPort = errors.New("invalid USB port") + ErrHIDInvalidCore = errors.New("invalid USB core") + ErrHIDReportTransfer = errors.New("failed to transfer HID report") +) + +type HID struct { + Port int + core *core +} + +type HIDConfig struct { + // Port is the MCU's native USB core number. If in doubt, leave it + // uninitialized for default (0). + Port int +} + +func (hid *HID) Configure(config HIDConfig) error { + + if config.Port >= CoreCount || config.Port >= dcdCount { + return ErrHIDInvalidPort + } + hid.Port = config.Port + + // verify we have a free USB port and take ownership of it + var st status + hid.core, st = initCore(hid.Port, class{id: classDeviceHID, config: 1}) + if !st.ok() { + return ErrHIDInvalidPort + } + return nil +} + +func (hid *HID) Keyboard() *Keyboard { + return hid.core.dc.keyboard() +} diff --git a/src/machine/usb/keyboard.go b/src/machine/usb/keyboard.go new file mode 100644 index 000000000..b0988f78d --- /dev/null +++ b/src/machine/usb/keyboard.go @@ -0,0 +1,1055 @@ +package usb + +import "errors" + +var ( + ErrInvalidCodepoint = errors.New("invalid Unicode codepoint") + ErrInvalidKeycode = errors.New("invalid keyboard keycode") + ErrInvalidUTF8 = errors.New("invalid UTF-8 encoding") + ErrKeypressMaximum = errors.New("maximum keypresses exceeded") +) + +// Keyboard represents a USB HID keyboard device with support for international +// layouts and various control, system, multimedia, and consumer keycodes. +// +// Keyboard implements the io.Writer interface that translates UTF-8 encoded +// byte strings into sequences of keypress events. +type Keyboard struct { + dc *dcd + hc *descHIDClass + + // led holds the current state of all keyboard LEDs: + // 1=NumLock 2=CapsLock 4=ScrollLock 8=Compose 16=Kana + led uint8 + + // mod holds the current state of all keyboard modifier keys: + // 1=LeftCtrl 2=LeftShift 4=LeftAlt 8=LeftGUI + // 16=RightCtrl 32=RightShift 64=RightAlt 128=RightGUI + mod uint8 + + // key holds a list of all keyboard keys currently pressed. + key *[hidKeyboardKeyCount]uint8 + con *[hidKeyboardConCount]uint16 + sys *[hidKeyboardSysCount]uint8 + + // decode holds the current state of the UTF-8 decoder. + decode decodeState + + // wideChar holds high bits for the UTF-8 decoder. + wideChar uint16 +} + +// decodeState represents a state in the UTF-8 decode state machine. +type decodeState uint8 + +// Constant enumerated values of type decodeState. +const ( + decodeReset decodeState = iota + decodeByte1 + decodeByte2 + decodeByte3 +) + +// configure initializes the receiver Keyboard by associating it with the given +// USB device controller driver and HID class configuration. +func (kb *Keyboard) configure(dc *dcd, hc *descHIDClass) { + kb.dc = dc + kb.hc = hc +} + +// Write transmits press-and-release key sequences for each Keycode translated +// from the given UTF-8 byte string. Write implements the io.Writer interface +// and conforms to all documented conventions for arguments and return values. +func (kb *Keyboard) Write(b []byte) (n int, err error) { + for _, c := range b { + if err = kb.WriteByte(c); nil != err { + break + } + n += 1 + } + return +} + +// WriteByte processes a single byte from a UTF-8 byte string. This method is a +// stateful method with respect to the receiver Keyboard, meaning that its exact +// behavior will depend on the current state of its UTF-8 decode state machine: +// +// (a) If the given byte is a valid ASCII encoding (0-127), then a keypress +// sequence is immediately transmitted for the respective Keycode. +// +// (b) If the given byte represents the final byte in a multi-byte codepoint, +// then a keypress sequence is immediately transmitted by translating the +// multi-byte codepoint to its respective Keycode. +// +// (c) If the given byte appears to represent high bits for a multi-byte +// codepoint, then the bits are copied to the receiver's internal state +// machine buffer for use by a subsequent call to WriteByte() (or Write()) +// that completes the codepoint. +// +// (d) If the given byte is out of range, or contains illegal bits for the +// current state of the UTF-8 decoder, then the UTF-8 decode state machine +// is reset to its initial state. +// +// In cases (c) and (d), a keypress sequence is not generated and no data is +// transmitted. In case (c), additional bytes must be received via WriteByte() +// (or Write()) to complete or discard the current codepoint. +func (kb *Keyboard) WriteByte(b byte) error { + switch { + case b < 0x80: + // 1-byte encoding (0x00-0x7F) + kb.decode = decodeByte1 + return kb.write(uint16(b)) + + case b < 0xC0: + // 2nd, 3rd, or 4th byte (0x80-0xBF) + b = Keycode(b).key() + switch kb.decode { + case decodeByte2: + kb.decode = decodeByte1 + return kb.write(kb.wideChar | uint16(b)) + case decodeByte3: + kb.decode = decodeByte2 + kb.wideChar |= uint16(b) << 6 + } + + case b < 0xE0: + // 2-byte encoding (0xC2-0xDF), or illegal byte 2 (0xC0-0xC1) + kb.decode = decodeByte2 + kb.wideChar = uint16(b&0x1F) << 6 + + case b < 0xF0: + // 3-byte encoding (0xE0-0xEF) + kb.decode = decodeByte3 + kb.wideChar = uint16(b&0x0F) << 12 + + default: + // 4-byte encoding unsupported (0xF0-0xF4), or illegal byte 4 (0xF5-0xFF) + kb.decode = decodeReset + return ErrInvalidUTF8 + } + return nil +} + +func (kb *Keyboard) write(p uint16) error { + c := keycode(p) + if 0 == c { + return ErrInvalidCodepoint + } + if d := deadkey(c); 0 != d { + if err := kb.writeKeycode(d); nil != err { + return err + } + } + return kb.writeKeycode(c) +} + +func (kb *Keyboard) writeKeycode(c Keycode) error { + kb.mod = c.mod() + kb.key[0] = c.key() + kb.key[1] = 0 + kb.key[2] = 0 + kb.key[3] = 0 + kb.key[4] = 0 + kb.key[5] = 0 + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + kb.mod = 0 + kb.key[0] = 0 + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + return nil +} + +// Press transmits a press-and-release sequence for the given Keycode, which +// simulates a discrete keypress event. +// +// The following values of Keycode are supported: +// +// 0x0020 - 0x007F ASCII (U+0020 to U+007F) [USES LAYOUT] +// 0x0080 - 0xC1FF Unicode (U+0080 to U+C1FF) [USES LAYOUT] +// 0xC200 - 0xDFFF UTF-8 packed (U+0080 to U+07FF) [USES LAYOUT] +// 0xE000 - 0xE0FF Modifier key (bitmap, 8 keys, Shift/Ctrl/Alt/GUI) +// 0xE200 - 0xE2FF System key (HID usage code, page 1) +// 0xE400 - 0xE7FF Media/Consumer key (HID usage code, page 12) +// 0xF000 - 0xFFFF Normal key (HID usage code, page 7) +func (kb *Keyboard) Press(c Keycode) error { + if err := kb.Down(c); nil != err { + return err + } + return kb.Up(c) +} + +// Down transmits a press (key-down) event for the given Keycode. +// +// The host will interpret the key as being held down continuously until a +// corresponding release (key-up) event is transmitted, e.g., via method Up(). +// +// See godoc comment on method Press() for details on what input is accepted and +// how it is interpreted. +func (kb *Keyboard) Down(c Keycode) error { + var res uint8 + msb := c >> 8 + if msb >= 0xC2 { + if msb < 0xE0 { + c = ((msb & 0x1F) << 6) | Keycode(c.key()) + } else { + switch msb { + case 0xF0: + return kb.down(uint8(c), 0) + + case 0xE0: + return kb.down(0, uint8(c)) + + case 0xE2: + return kb.downSys(uint8(c)) + + default: + if 0xE4 <= msb && msb <= 0xE7 { + return kb.downCon(uint16(c & 0x03FF)) + } + return ErrInvalidKeycode + } + } + } + c = keycode(uint16(c)) + if 0 == c { + return ErrInvalidCodepoint + } + if d := deadkey(c); 0 != d { + res = kb.mod + if 0 != res { + kb.mod = 0 + kb.dc.keyboardSendKeys(false) + } + kb.down(d.key(), d.mod()) + kb.up(d.key(), d.mod()) + } + return kb.down(c.key(), c.mod()|res) +} + +func (kb *Keyboard) down(key uint8, mod uint8) error { + send := false + if 0 != mod { + if kb.mod&mod != mod { + kb.mod |= mod + send = true + } + } + if 0 != key { + for _, k := range kb.key { + if k == key { + goto end + } + } + for i, k := range kb.key { + if 0 == k { + kb.key[i] = key + send = true + goto end + } + } + return ErrKeypressMaximum + } +end: + if send { + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + } + return nil +} + +func (kb *Keyboard) downCon(key uint16) error { + if 0 == key { + return ErrInvalidKeycode + } + for _, k := range kb.con { + if key == k { + return nil // already pressed + } + } + for i, k := range kb.con { + if 0 == k { + kb.con[i] = key + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return ErrKeypressMaximum +} + +func (kb *Keyboard) downSys(key uint8) error { + if 0 == key { + return ErrInvalidKeycode + } + for _, k := range kb.sys { + if key == k { + return nil // already pressed + } + } + for i, k := range kb.sys { + if 0 == k { + kb.sys[i] = key + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return ErrKeypressMaximum +} + +// Release transmits a release (key-up) event for the given Keycode. +// +// See godoc comment on method Press() for details on what input is accepted and +// how it is interpreted. +func (kb *Keyboard) Up(c Keycode) error { + msb := c >> 8 + if msb >= 0xC2 { + if msb < 0xE0 { + c = ((msb & 0x1F) << 6) | Keycode(c.key()) + } else { + switch msb { + case 0xF0: + return kb.up(uint8(c), 0) + + case 0xE0: + return kb.up(0, uint8(c)) + + case 0xE2: + return kb.upSys(uint8(c)) + + default: + if 0xE4 <= msb && msb <= 0xE7 { + return kb.upCon(uint16(c & 0x03FF)) + } + return ErrInvalidKeycode + } + } + } + c = keycode(uint16(c)) + if 0 == c { + return ErrInvalidCodepoint + } + return kb.up(c.key(), c.mod()) +} + +func (kb *Keyboard) Release() error { + + bits := uint16(kb.mod) + kb.mod = 0 + for i, k := range kb.key { + bits |= uint16(k) + kb.key[i] = 0 + } + if 0 != bits { + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + } + bits = 0 + for i, k := range kb.con { + bits |= k + kb.con[i] = 0 + } + for i, k := range kb.sys { + bits |= uint16(k) + kb.sys[i] = 0 + } + if 0 != bits { + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + } + return nil +} + +func (kb *Keyboard) up(key uint8, mod uint8) error { + send := false + if 0 != mod { + if kb.mod&mod != 0 { + kb.mod &^= mod + send = true + } + } + if 0 != key { + for i, k := range kb.key { + if key == k { + kb.key[i] = 0 + send = true + } + } + } + if send { + if !kb.dc.keyboardSendKeys(false) { + return ErrHIDReportTransfer + } + } + return nil +} + +func (kb *Keyboard) upCon(key uint16) error { + if 0 == key { + return ErrInvalidKeycode + } + for i, k := range kb.con { + if key == k { + kb.con[i] = 0 + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return nil +} + +func (kb *Keyboard) upSys(key uint8) error { + if 0 == key { + return ErrInvalidKeycode + } + for i, k := range kb.sys { + if key == k { + kb.sys[i] = 0 + if !kb.dc.keyboardSendKeys(true) { + return ErrHIDReportTransfer + } + return nil + } + } + return nil +} + +// Keycode is a package-defined bitmap used to encode the value of a given key. +type Keycode uint16 + +// keycode returns the given Unicode codepoint translated to a Keycode sequence. +// Unicode codepoints greater than U+FFFF are unsupported. +//go:inline +func keycode(p uint16) Keycode { + if p < 0x80 { + return ascii[p] + } else if p >= 0xA0 && p < 0x0100 { + return iso88591[p-0xA0] + } else if uint16(UNICODEEXTRA00) == p { + return KEYCODEEXTRA00.mask() + } + return 0 +} + +//go:inline +func deadkey(c Keycode) Keycode { + switch c & deadkeysMask { + case acuteAccentBits: + return deadkeyAcuteAccent + case circumflexBits: + return deadkeyCircumflex + case diaeresisBits: + return deadkeyDiaeresis + case graveAccentBits: + return deadkeyGraveAccent + case tildeBits: + return deadkeyTilde + } + return 0 +} + +//go:inline +func (c Keycode) mask() Keycode { return c & keycodeMask } + +//go:inline +func (c Keycode) key() uint8 { return uint8(c & keyMask) } + +//go:inline +func (c Keycode) mod() uint8 { + var m Keycode + if 0 != c&shiftMask { + m |= KeyModifierShift + } + if 0 != c&altgrMask { + m |= KeyModifierRightAlt + } + return uint8(m) +} + +//go:inline +func (c Keycode) Shift() Keycode { return c | KeyModifierShift } + +const ( + hidKeyboardKeyCount = 6 // Max number of simultaneous keypresses + hidKeyboardSysCount = 3 + hidKeyboardConCount = 4 +) + +// Keycodes common to all Keyboard layouts +const ( + KeyModifierCtrl Keycode = (0x01 | 0xE000) + KeyModifierShift Keycode = (0x02 | 0xE000) + KeyModifierAlt Keycode = (0x04 | 0xE000) + KeyModifierGUI Keycode = (0x08 | 0xE000) + KeyModifierLeftCtrl Keycode = (0x01 | 0xE000) + KeyModifierLeftShift Keycode = (0x02 | 0xE000) + KeyModifierLeftAlt Keycode = (0x04 | 0xE000) + KeyModifierLeftGUI Keycode = (0x08 | 0xE000) + KeyModifierRightCtrl Keycode = (0x10 | 0xE000) + KeyModifierRightShift Keycode = (0x20 | 0xE000) + KeyModifierRightAlt Keycode = (0x40 | 0xE000) + KeyModifierRightGUI Keycode = (0x80 | 0xE000) + + KeySystemPowerDown Keycode = (0x81 | 0xE200) + KeySystemSleep Keycode = (0x82 | 0xE200) + KeySystemWakeUp Keycode = (0x83 | 0xE200) + + KeyMediaPlay Keycode = (0xB0 | 0xE400) + KeyMediaPause Keycode = (0xB1 | 0xE400) + KeyMediaRecord Keycode = (0xB2 | 0xE400) + KeyMediaFastForward Keycode = (0xB3 | 0xE400) + KeyMediaRewind Keycode = (0xB4 | 0xE400) + KeyMediaNextTrack Keycode = (0xB5 | 0xE400) + KeyMediaPrevTrack Keycode = (0xB6 | 0xE400) + KeyMediaStop Keycode = (0xB7 | 0xE400) + KeyMediaEject Keycode = (0xB8 | 0xE400) + KeyMediaRandomPlay Keycode = (0xB9 | 0xE400) + KeyMediaPlayPause Keycode = (0xCD | 0xE400) + KeyMediaPlaySkip Keycode = (0xCE | 0xE400) + KeyMediaMute Keycode = (0xE2 | 0xE400) + KeyMediaVolumeInc Keycode = (0xE9 | 0xE400) + KeyMediaVolumeDec Keycode = (0xEA | 0xE400) + + KeyA Keycode = (4 | 0xF000) + KeyB Keycode = (5 | 0xF000) + KeyC Keycode = (6 | 0xF000) + KeyD Keycode = (7 | 0xF000) + KeyE Keycode = (8 | 0xF000) + KeyF Keycode = (9 | 0xF000) + KeyG Keycode = (10 | 0xF000) + KeyH Keycode = (11 | 0xF000) + KeyI Keycode = (12 | 0xF000) + KeyJ Keycode = (13 | 0xF000) + KeyK Keycode = (14 | 0xF000) + KeyL Keycode = (15 | 0xF000) + KeyM Keycode = (16 | 0xF000) + KeyN Keycode = (17 | 0xF000) + KeyO Keycode = (18 | 0xF000) + KeyP Keycode = (19 | 0xF000) + KeyQ Keycode = (20 | 0xF000) + KeyR Keycode = (21 | 0xF000) + KeyS Keycode = (22 | 0xF000) + KeyT Keycode = (23 | 0xF000) + KeyU Keycode = (24 | 0xF000) + KeyV Keycode = (25 | 0xF000) + KeyW Keycode = (26 | 0xF000) + KeyX Keycode = (27 | 0xF000) + KeyY Keycode = (28 | 0xF000) + KeyZ Keycode = (29 | 0xF000) + Key1 Keycode = (30 | 0xF000) + Key2 Keycode = (31 | 0xF000) + Key3 Keycode = (32 | 0xF000) + Key4 Keycode = (33 | 0xF000) + Key5 Keycode = (34 | 0xF000) + Key6 Keycode = (35 | 0xF000) + Key7 Keycode = (36 | 0xF000) + Key8 Keycode = (37 | 0xF000) + Key9 Keycode = (38 | 0xF000) + Key0 Keycode = (39 | 0xF000) + KeyEnter Keycode = (40 | 0xF000) + KeyEsc Keycode = (41 | 0xF000) + KeyBackspace Keycode = (42 | 0xF000) + KeyTab Keycode = (43 | 0xF000) + KeySpace Keycode = (44 | 0xF000) + KeyMinus Keycode = (45 | 0xF000) + KeyEqual Keycode = (46 | 0xF000) + KeyLeftBrace Keycode = (47 | 0xF000) + KeyRightBrace Keycode = (48 | 0xF000) + KeyBackslash Keycode = (49 | 0xF000) + KeyNonUsNum Keycode = (50 | 0xF000) + KeySemicolon Keycode = (51 | 0xF000) + KeyQuote Keycode = (52 | 0xF000) + KeyTilde Keycode = (53 | 0xF000) + KeyComma Keycode = (54 | 0xF000) + KeyPeriod Keycode = (55 | 0xF000) + KeySlash Keycode = (56 | 0xF000) + KeyCapsLock Keycode = (57 | 0xF000) + KeyF1 Keycode = (58 | 0xF000) + KeyF2 Keycode = (59 | 0xF000) + KeyF3 Keycode = (60 | 0xF000) + KeyF4 Keycode = (61 | 0xF000) + KeyF5 Keycode = (62 | 0xF000) + KeyF6 Keycode = (63 | 0xF000) + KeyF7 Keycode = (64 | 0xF000) + KeyF8 Keycode = (65 | 0xF000) + KeyF9 Keycode = (66 | 0xF000) + KeyF10 Keycode = (67 | 0xF000) + KeyF11 Keycode = (68 | 0xF000) + KeyF12 Keycode = (69 | 0xF000) + KeyPrintscreen Keycode = (70 | 0xF000) + KeyScrollLock Keycode = (71 | 0xF000) + KeyPause Keycode = (72 | 0xF000) + KeyInsert Keycode = (73 | 0xF000) + KeyHome Keycode = (74 | 0xF000) + KeyPageUp Keycode = (75 | 0xF000) + KeyDelete Keycode = (76 | 0xF000) + KeyEnd Keycode = (77 | 0xF000) + KeyPageDown Keycode = (78 | 0xF000) + KeyRight Keycode = (79 | 0xF000) + KeyLeft Keycode = (80 | 0xF000) + KeyDown Keycode = (81 | 0xF000) + KeyUp Keycode = (82 | 0xF000) + KeyNumLock Keycode = (83 | 0xF000) + KeypadSlash Keycode = (84 | 0xF000) + KeypadAsterisk Keycode = (85 | 0xF000) + KeypadMinus Keycode = (86 | 0xF000) + KeypadPlus Keycode = (87 | 0xF000) + KeypadEnter Keycode = (88 | 0xF000) + Keypad1 Keycode = (89 | 0xF000) + Keypad2 Keycode = (90 | 0xF000) + Keypad3 Keycode = (91 | 0xF000) + Keypad4 Keycode = (92 | 0xF000) + Keypad5 Keycode = (93 | 0xF000) + Keypad6 Keycode = (94 | 0xF000) + Keypad7 Keycode = (95 | 0xF000) + Keypad8 Keycode = (96 | 0xF000) + Keypad9 Keycode = (97 | 0xF000) + Keypad0 Keycode = (98 | 0xF000) + KeypadPeriod Keycode = (99 | 0xF000) + KeyNonUSBS Keycode = (100 | 0xF000) + KeyMenu Keycode = (101 | 0xF000) + KeyF13 Keycode = (104 | 0xF000) + KeyF14 Keycode = (105 | 0xF000) + KeyF15 Keycode = (106 | 0xF000) + KeyF16 Keycode = (107 | 0xF000) + KeyF17 Keycode = (108 | 0xF000) + KeyF18 Keycode = (109 | 0xF000) + KeyF19 Keycode = (110 | 0xF000) + KeyF20 Keycode = (111 | 0xF000) + KeyF21 Keycode = (112 | 0xF000) + KeyF22 Keycode = (113 | 0xF000) + KeyF23 Keycode = (114 | 0xF000) + KeyF24 Keycode = (115 | 0xF000) + + KeyUpArrow Keycode = KeyUp + KeyDownArrow Keycode = KeyDown + KeyLeftArrow Keycode = KeyLeft + KeyRightArrow Keycode = KeyRight + KeyReturn Keycode = KeyEnter + KeyLeftCtrl Keycode = KeyModifierLeftCtrl + KeyLeftShift Keycode = KeyModifierLeftShift + KeyLeftAlt Keycode = KeyModifierLeftAlt + KeyLeftGUI Keycode = KeyModifierLeftGUI + KeyRightCtrl Keycode = KeyModifierRightCtrl + KeyRightShift Keycode = KeyModifierRightShift + KeyRightAlt Keycode = KeyModifierRightAlt + KeyRightGUI Keycode = KeyModifierRightGUI +) + +// Keycodes for layout US English (0x0904) +const ( + keycodeMask Keycode = 0x07FF + keyMask Keycode = 0x003F + + shiftMask Keycode = 0x0040 + altgrMask Keycode = 0x0080 + deadkeysMask Keycode = 0x0700 + circumflexBits Keycode = 0x0100 + acuteAccentBits Keycode = 0x0200 + graveAccentBits Keycode = 0x0300 + tildeBits Keycode = 0x0400 + diaeresisBits Keycode = 0x0500 + deadkeyCircumflex Keycode = Key6 + shiftMask + deadkeyAcuteAccent Keycode = KeyQuote + deadkeyGraveAccent Keycode = KeyTilde + deadkeyTilde Keycode = KeyTilde + shiftMask + deadkeyDiaeresis Keycode = KeyQuote + shiftMask + + ASCII00 Keycode = 0 // 0 NUL + ASCII01 Keycode = 0 // 1 SOH + ASCII02 Keycode = 0 // 2 STX + ASCII03 Keycode = 0 // 3 ETX + ASCII04 Keycode = 0 // 4 EOT + ASCII05 Keycode = 0 // 5 ENQ + ASCII06 Keycode = 0 // 6 ACK + ASCII07 Keycode = 0 // 7 BEL + ASCII08 Keycode = KeyBackspace // 8 BS + ASCII09 Keycode = KeyTab // 9 TAB + ASCII0A Keycode = KeyEnter // 10 LF + ASCII0B Keycode = 0 // 11 VT + ASCII0C Keycode = 0 // 12 FF + ASCII0D Keycode = 0 // 13 CR + ASCII0E Keycode = 0 // 14 SO + ASCII0F Keycode = 0 // 15 SI + ASCII10 Keycode = 0 // 16 DEL + ASCII11 Keycode = 0 // 17 DC1 + ASCII12 Keycode = 0 // 18 DC2 + ASCII13 Keycode = 0 // 19 DC3 + ASCII14 Keycode = 0 // 20 DC4 + ASCII15 Keycode = 0 // 21 NAK + ASCII16 Keycode = 0 // 22 SYN + ASCII17 Keycode = 0 // 23 ETB + ASCII18 Keycode = 0 // 24 CAN + ASCII19 Keycode = 0 // 25 EM + ASCII1A Keycode = 0 // 26 SUB + ASCII1B Keycode = 0 // 27 ESC + ASCII1C Keycode = 0 // 28 FS + ASCII1D Keycode = 0 // 29 GS + ASCII1E Keycode = 0 // 30 RS + ASCII1F Keycode = 0 // 31 US + + ASCII20 Keycode = KeySpace // 32 SPACE + ASCII21 Keycode = Key1 + shiftMask // 33 ! + ASCII22 Keycode = diaeresisBits + KeySpace // 34 " + ASCII23 Keycode = Key3 + shiftMask // 35 # + ASCII24 Keycode = Key4 + shiftMask // 36 $ + ASCII25 Keycode = Key5 + shiftMask // 37 % + ASCII26 Keycode = Key7 + shiftMask // 38 & + ASCII27 Keycode = acuteAccentBits + KeySpace // 39 ' + ASCII28 Keycode = Key9 + shiftMask // 40 ( + ASCII29 Keycode = Key0 + shiftMask // 41 ) + ASCII2A Keycode = Key8 + shiftMask // 42 * + ASCII2B Keycode = KeyEqual + shiftMask // 43 + + ASCII2C Keycode = KeyComma // 44 , + ASCII2D Keycode = KeyMinus // 45 - + ASCII2E Keycode = KeyPeriod // 46 . + ASCII2F Keycode = KeySlash // 47 / + ASCII30 Keycode = Key0 // 48 0 + ASCII31 Keycode = Key1 // 49 1 + ASCII32 Keycode = Key2 // 50 2 + ASCII33 Keycode = Key3 // 51 3 + ASCII34 Keycode = Key4 // 52 4 + ASCII35 Keycode = Key5 // 53 5 + ASCII36 Keycode = Key6 // 54 6 + ASCII37 Keycode = Key7 // 55 7 + ASCII38 Keycode = Key8 // 55 8 + ASCII39 Keycode = Key9 // 57 9 + ASCII3A Keycode = KeySemicolon + shiftMask // 58 : + ASCII3B Keycode = KeySemicolon // 59 ; + ASCII3C Keycode = KeyComma + shiftMask // 60 < + ASCII3D Keycode = KeyEqual // 61 = + ASCII3E Keycode = KeyPeriod + shiftMask // 62 > + ASCII3F Keycode = KeySlash + shiftMask // 63 ? + ASCII40 Keycode = Key2 + shiftMask // 64 @ + ASCII41 Keycode = KeyA + shiftMask // 65 A + ASCII42 Keycode = KeyB + shiftMask // 66 B + ASCII43 Keycode = KeyC + shiftMask // 67 C + ASCII44 Keycode = KeyD + shiftMask // 68 D + ASCII45 Keycode = KeyE + shiftMask // 69 E + ASCII46 Keycode = KeyF + shiftMask // 70 F + ASCII47 Keycode = KeyG + shiftMask // 71 G + ASCII48 Keycode = KeyH + shiftMask // 72 H + ASCII49 Keycode = KeyI + shiftMask // 73 I + ASCII4A Keycode = KeyJ + shiftMask // 74 J + ASCII4B Keycode = KeyK + shiftMask // 75 K + ASCII4C Keycode = KeyL + shiftMask // 76 L + ASCII4D Keycode = KeyM + shiftMask // 77 M + ASCII4E Keycode = KeyN + shiftMask // 78 N + ASCII4F Keycode = KeyO + shiftMask // 79 O + ASCII50 Keycode = KeyP + shiftMask // 80 P + ASCII51 Keycode = KeyQ + shiftMask // 81 Q + ASCII52 Keycode = KeyR + shiftMask // 82 R + ASCII53 Keycode = KeyS + shiftMask // 83 S + ASCII54 Keycode = KeyT + shiftMask // 84 T + ASCII55 Keycode = KeyU + shiftMask // 85 U + ASCII56 Keycode = KeyV + shiftMask // 86 V + ASCII57 Keycode = KeyW + shiftMask // 87 W + ASCII58 Keycode = KeyX + shiftMask // 88 X + ASCII59 Keycode = KeyY + shiftMask // 89 Y + ASCII5A Keycode = KeyZ + shiftMask // 90 Z + ASCII5B Keycode = KeyLeftBrace // 91 [ + ASCII5C Keycode = KeyBackslash // 92 \ + ASCII5D Keycode = KeyRightBrace // 93 ] + ASCII5E Keycode = circumflexBits + KeySpace // 94 ^ + ASCII5F Keycode = KeyMinus + shiftMask // 95 + ASCII60 Keycode = graveAccentBits + KeySpace // 96 ` + ASCII61 Keycode = KeyA // 97 a + ASCII62 Keycode = KeyB // 98 b + ASCII63 Keycode = KeyC // 99 c + ASCII64 Keycode = KeyD // 100 d + ASCII65 Keycode = KeyE // 101 e + ASCII66 Keycode = KeyF // 102 f + ASCII67 Keycode = KeyG // 103 g + ASCII68 Keycode = KeyH // 104 h + ASCII69 Keycode = KeyI // 105 i + ASCII6A Keycode = KeyJ // 106 j + ASCII6B Keycode = KeyK // 107 k + ASCII6C Keycode = KeyL // 108 l + ASCII6D Keycode = KeyM // 109 m + ASCII6E Keycode = KeyN // 110 n + ASCII6F Keycode = KeyO // 111 o + ASCII70 Keycode = KeyP // 112 p + ASCII71 Keycode = KeyQ // 113 q + ASCII72 Keycode = KeyR // 114 r + ASCII73 Keycode = KeyS // 115 s + ASCII74 Keycode = KeyT // 116 t + ASCII75 Keycode = KeyU // 117 u + ASCII76 Keycode = KeyV // 118 v + ASCII77 Keycode = KeyW // 119 w + ASCII78 Keycode = KeyX // 120 x + ASCII79 Keycode = KeyY // 121 y + ASCII7A Keycode = KeyZ // 122 z + ASCII7B Keycode = KeyLeftBrace + shiftMask // 123 { + ASCII7C Keycode = KeyBackslash + shiftMask // 124 | + ASCII7D Keycode = KeyRightBrace + shiftMask // 125 } + ASCII7E Keycode = tildeBits + KeySpace // 126 ~ + ASCII7F Keycode = KeyBackspace // 127 DEL + ISO88591A0 Keycode = KeySpace // 160 Nonbreakng Space + ISO88591A1 Keycode = Key1 + altgrMask // 161 ¡ Inverted Exclamation + ISO88591A2 Keycode = KeyC + altgrMask + shiftMask // 162 ¢ Cent SIGN + ISO88591A3 Keycode = Key4 + altgrMask + shiftMask // 163 £ Pound Sign + ISO88591A4 Keycode = Key4 + altgrMask // 164 ¤ Currency or Euro Sign + ISO88591A5 Keycode = KeyMinus + altgrMask // 165 ¥ YEN SIGN + ISO88591A6 Keycode = KeyBackslash + altgrMask + shiftMask // 166 ¦ BROKEN BAR ?? + ISO88591A7 Keycode = KeyS + altgrMask + shiftMask // 167 § SECTION SIGN + ISO88591A8 Keycode = KeyQuote + altgrMask + shiftMask // 168 ¨ DIAERESIS + ISO88591A9 Keycode = KeyC + altgrMask // 169 © COPYRIGHT SIGN + ISO88591AA Keycode = 0 // 170 ª FEMININE ORDINAL + ISO88591AB Keycode = KeyLeftBrace + altgrMask // 171 « LEFT DOUBLE ANGLE QUOTE + ISO88591AC Keycode = KeyBackslash + altgrMask // 172 ¬ NOT SIGN ?? + ISO88591AD Keycode = 0 // 173 SOFT HYPHEN + ISO88591AE Keycode = KeyR + altgrMask // 174 ® REGISTERED SIGN + ISO88591AF Keycode = 0 // 175 ¯ MACRON + ISO88591B0 Keycode = KeySemicolon + altgrMask + shiftMask // 176 ° DEGREE SIGN + ISO88591B1 Keycode = 0 // 177 ± PLUS-MINUS SIGN + ISO88591B2 Keycode = Key2 + altgrMask // 178 ² SUPERSCRIPT TWO + ISO88591B3 Keycode = Key3 + altgrMask // 179 ³ SUPERSCRIPT THREE + ISO88591B4 Keycode = KeyQuote + altgrMask // 180 ´ ACUTE ACCENT + ISO88591B5 Keycode = KeyM + altgrMask // 181 µ MICRO SIGN + ISO88591B6 Keycode = KeySemicolon + altgrMask // 182 ¶ PILCROW SIGN + ISO88591B7 Keycode = 0 // 183 · MIDDLE DOT + ISO88591B8 Keycode = 0 // 184 ¸ CEDILLA + ISO88591B9 Keycode = Key1 + altgrMask + shiftMask // 185 ¹ SUPERSCRIPT ONE + ISO88591BA Keycode = 0 // 186 º MASCULINE ORDINAL + ISO88591BB Keycode = KeyRightBrace + altgrMask // 187 » RIGHT DOUBLE ANGLE QUOTE + ISO88591BC Keycode = Key6 + altgrMask // 188 ¼ FRACTION ONE QUARTER + ISO88591BD Keycode = Key7 + altgrMask // 189 ½ FRACTION ONE HALF + ISO88591BE Keycode = Key8 + altgrMask // 190 ¾ FRACTION THREE QUARTERS + ISO88591BF Keycode = KeySlash + altgrMask // 191 ¿ INVERTED QUESTION MARK + ISO88591C0 Keycode = graveAccentBits + KeyA + shiftMask // 192 À A GRAVE + ISO88591C1 Keycode = KeyA + altgrMask + shiftMask // 193 Á A ACUTE + ISO88591C2 Keycode = circumflexBits + KeyA + shiftMask // 194  A CIRCUMFLEX + ISO88591C3 Keycode = tildeBits + KeyA + shiftMask // 195 à A TILDE + ISO88591C4 Keycode = KeyQ + altgrMask + shiftMask // 196 Ä A DIAERESIS + ISO88591C5 Keycode = KeyW + altgrMask + shiftMask // 197 Å A RING ABOVE + ISO88591C6 Keycode = KeyZ + altgrMask + shiftMask // 198 Æ AE + ISO88591C7 Keycode = KeyComma + altgrMask + shiftMask // 199 Ç C CEDILLA + ISO88591C8 Keycode = graveAccentBits + KeyE + shiftMask // 200 È E GRAVE + ISO88591C9 Keycode = KeyE + altgrMask + shiftMask // 201 É E ACUTE + ISO88591CA Keycode = circumflexBits + KeyE + shiftMask // 202 Ê E CIRCUMFLEX + ISO88591CB Keycode = diaeresisBits + KeyE + shiftMask // 203 Ë E DIAERESIS + ISO88591CC Keycode = graveAccentBits + KeyI + shiftMask // 204 Ì I GRAVE + ISO88591CD Keycode = KeyI + altgrMask + shiftMask // 205 Í I ACUTE + ISO88591CE Keycode = circumflexBits + KeyI + shiftMask // 206 Î I CIRCUMFLEX + ISO88591CF Keycode = diaeresisBits + KeyI + shiftMask // 207 Ï I DIAERESIS + ISO88591D0 Keycode = KeyD + altgrMask + shiftMask // 208 Ð ETH + ISO88591D1 Keycode = KeyN + altgrMask + shiftMask // 209 Ñ N TILDE + ISO88591D2 Keycode = graveAccentBits + KeyO + shiftMask // 210 Ò O GRAVE + ISO88591D3 Keycode = KeyO + altgrMask + shiftMask // 211 Ó O ACUTE + ISO88591D4 Keycode = circumflexBits + KeyO + shiftMask // 212 Ô O CIRCUMFLEX + ISO88591D5 Keycode = tildeBits + KeyO + shiftMask // 213 Õ O TILDE + ISO88591D6 Keycode = KeyP + altgrMask + shiftMask // 214 Ö O DIAERESIS + ISO88591D7 Keycode = KeyEqual + altgrMask // 215 × MULTIPLICATION + ISO88591D8 Keycode = KeyL + altgrMask + shiftMask // 216 Ø O STROKE + ISO88591D9 Keycode = graveAccentBits + KeyU + shiftMask // 217 Ù U GRAVE + ISO88591DA Keycode = KeyU + altgrMask + shiftMask // 218 Ú U ACUTE + ISO88591DB Keycode = circumflexBits + KeyU + shiftMask // 219 Û U CIRCUMFLEX + ISO88591DC Keycode = KeyY + altgrMask + shiftMask // 220 Ü U DIAERESIS + ISO88591DD Keycode = acuteAccentBits + KeyY + shiftMask // 221 Ý Y ACUTE + ISO88591DE Keycode = KeyT + altgrMask + shiftMask // 222 Þ THORN + ISO88591DF Keycode = KeyS + altgrMask // 223 ß SHARP S + ISO88591E0 Keycode = graveAccentBits + KeyA // 224 à a GRAVE + ISO88591E1 Keycode = KeyA + altgrMask // 225 á a ACUTE + ISO88591E2 Keycode = circumflexBits + KeyA // 226 â a CIRCUMFLEX + ISO88591E3 Keycode = tildeBits + KeyA // 227 ã a TILDE + ISO88591E4 Keycode = diaeresisBits + KeyA // 228 ä a DIAERESIS + ISO88591E5 Keycode = KeyW + altgrMask // 229 å a RING ABOVE + ISO88591E6 Keycode = KeyZ + altgrMask // 230 æ ae + ISO88591E7 Keycode = KeyComma + altgrMask // 231 ç c CEDILLA + ISO88591E8 Keycode = graveAccentBits + KeyE // 232 è e GRAVE + ISO88591E9 Keycode = acuteAccentBits + KeyE // 233 é e ACUTE + ISO88591EA Keycode = circumflexBits + KeyE // 234 ê e CIRCUMFLEX + ISO88591EB Keycode = diaeresisBits + KeyE // 235 ë e DIAERESIS + ISO88591EC Keycode = graveAccentBits + KeyI // 236 ì i GRAVE + ISO88591ED Keycode = KeyI + altgrMask // 237 í i ACUTE + ISO88591EE Keycode = circumflexBits + KeyI // 238 î i CIRCUMFLEX + ISO88591EF Keycode = diaeresisBits + KeyI // 239 ï i DIAERESIS + ISO88591F0 Keycode = KeyD + altgrMask // 240 ð ETH + ISO88591F1 Keycode = KeyN + altgrMask // 241 ñ n TILDE + ISO88591F2 Keycode = graveAccentBits + KeyO // 242 ò o GRAVE + ISO88591F3 Keycode = KeyO + altgrMask // 243 ó o ACUTE + ISO88591F4 Keycode = circumflexBits + KeyO // 244 ô o CIRCUMFLEX + ISO88591F5 Keycode = tildeBits + KeyO // 245 õ o TILDE + ISO88591F6 Keycode = KeyP + altgrMask // 246 ö o DIAERESIS + ISO88591F7 Keycode = KeyEqual + altgrMask + shiftMask // 247 ÷ DIVISION + ISO88591F8 Keycode = KeyL + altgrMask // 248 ø o STROKE + ISO88591F9 Keycode = graveAccentBits + KeyU // 249 ù u GRAVE + ISO88591FA Keycode = KeyU + altgrMask // 250 ú u ACUTE + ISO88591FB Keycode = circumflexBits + KeyU // 251 û u CIRCUMFLEX + ISO88591FC Keycode = KeyY + altgrMask // 252 ü u DIAERESIS + ISO88591FD Keycode = acuteAccentBits + KeyY // 253 ý y ACUTE + ISO88591FE Keycode = KeyT + altgrMask // 254 þ THORN + ISO88591FF Keycode = diaeresisBits + KeyY // 255 ÿ y DIAERESIS + UNICODE20AC Keycode = Key5 + altgrMask // € Euro Sign + UNICODEEXTRA00 Keycode = 0x20AC + KEYCODEEXTRA00 Keycode = Key5 + altgrMask // 20AC € Euro Sign + + // ASCII20 Keycode = KeySpace // 32 SPACE + // ASCII21 Keycode = Key1 + shiftMask // 33 ! + // ASCII22 Keycode = KeyQuote + shiftMask // 34 " + // ASCII23 Keycode = Key3 + shiftMask // 35 # + // ASCII24 Keycode = Key4 + shiftMask // 36 $ + // ASCII25 Keycode = Key5 + shiftMask // 37 % + // ASCII26 Keycode = Key7 + shiftMask // 38 & + // ASCII27 Keycode = KeyQuote // 39 ' + // ASCII28 Keycode = Key9 + shiftMask // 40 ( + // ASCII29 Keycode = Key0 + shiftMask // 41 ) + // ASCII2A Keycode = Key8 + shiftMask // 42 * + // ASCII2B Keycode = KeyEqual + shiftMask // 43 + + // ASCII2C Keycode = KeyComma // 44 , + // ASCII2D Keycode = KeyMinus // 45 - + // ASCII2E Keycode = KeyPeriod // 46 . + // ASCII2F Keycode = KeySlash // 47 / + // ASCII30 Keycode = Key0 // 48 0 + // ASCII31 Keycode = Key1 // 49 1 + // ASCII32 Keycode = Key2 // 50 2 + // ASCII33 Keycode = Key3 // 51 3 + // ASCII34 Keycode = Key4 // 52 4 + // ASCII35 Keycode = Key5 // 53 5 + // ASCII36 Keycode = Key6 // 54 6 + // ASCII37 Keycode = Key7 // 55 7 + // ASCII38 Keycode = Key8 // 55 8 + // ASCII39 Keycode = Key9 // 57 9 + // ASCII3A Keycode = KeySemicolon + shiftMask // 58 : + // ASCII3B Keycode = KeySemicolon // 59 ; + // ASCII3C Keycode = KeyComma + shiftMask // 60 < + // ASCII3D Keycode = KeyEqual // 61 = + // ASCII3E Keycode = KeyPeriod + shiftMask // 62 > + // ASCII3F Keycode = KeySlash + shiftMask // 63 ? + // ASCII40 Keycode = Key2 + shiftMask // 64 @ + // ASCII41 Keycode = KeyA + shiftMask // 65 A + // ASCII42 Keycode = KeyB + shiftMask // 66 B + // ASCII43 Keycode = KeyC + shiftMask // 67 C + // ASCII44 Keycode = KeyD + shiftMask // 68 D + // ASCII45 Keycode = KeyE + shiftMask // 69 E + // ASCII46 Keycode = KeyF + shiftMask // 70 F + // ASCII47 Keycode = KeyG + shiftMask // 71 G + // ASCII48 Keycode = KeyH + shiftMask // 72 H + // ASCII49 Keycode = KeyI + shiftMask // 73 I + // ASCII4A Keycode = KeyJ + shiftMask // 74 J + // ASCII4B Keycode = KeyK + shiftMask // 75 K + // ASCII4C Keycode = KeyL + shiftMask // 76 L + // ASCII4D Keycode = KeyM + shiftMask // 77 M + // ASCII4E Keycode = KeyN + shiftMask // 78 N + // ASCII4F Keycode = KeyO + shiftMask // 79 O + // ASCII50 Keycode = KeyP + shiftMask // 80 P + // ASCII51 Keycode = KeyQ + shiftMask // 81 Q + // ASCII52 Keycode = KeyR + shiftMask // 82 R + // ASCII53 Keycode = KeyS + shiftMask // 83 S + // ASCII54 Keycode = KeyT + shiftMask // 84 T + // ASCII55 Keycode = KeyU + shiftMask // 85 U + // ASCII56 Keycode = KeyV + shiftMask // 86 V + // ASCII57 Keycode = KeyW + shiftMask // 87 W + // ASCII58 Keycode = KeyX + shiftMask // 88 X + // ASCII59 Keycode = KeyY + shiftMask // 89 Y + // ASCII5A Keycode = KeyZ + shiftMask // 90 Z + // ASCII5B Keycode = KeyLeftBrace // 91 [ + // ASCII5C Keycode = KeyBackslash // 92 \ + // ASCII5D Keycode = KeyRightBrace // 93 ] + // ASCII5E Keycode = Key6 + shiftMask // 94 ^ + // ASCII5F Keycode = KeyMinus + shiftMask // 95 + // ASCII60 Keycode = KeyTilde // 96 ` + // ASCII61 Keycode = KeyA // 97 a + // ASCII62 Keycode = KeyB // 98 b + // ASCII63 Keycode = KeyC // 99 c + // ASCII64 Keycode = KeyD // 100 d + // ASCII65 Keycode = KeyE // 101 e + // ASCII66 Keycode = KeyF // 102 f + // ASCII67 Keycode = KeyG // 103 g + // ASCII68 Keycode = KeyH // 104 h + // ASCII69 Keycode = KeyI // 105 i + // ASCII6A Keycode = KeyJ // 106 j + // ASCII6B Keycode = KeyK // 107 k + // ASCII6C Keycode = KeyL // 108 l + // ASCII6D Keycode = KeyM // 109 m + // ASCII6E Keycode = KeyN // 110 n + // ASCII6F Keycode = KeyO // 111 o + // ASCII70 Keycode = KeyP // 112 p + // ASCII71 Keycode = KeyQ // 113 q + // ASCII72 Keycode = KeyR // 114 r + // ASCII73 Keycode = KeyS // 115 s + // ASCII74 Keycode = KeyT // 116 t + // ASCII75 Keycode = KeyU // 117 u + // ASCII76 Keycode = KeyV // 118 v + // ASCII77 Keycode = KeyW // 119 w + // ASCII78 Keycode = KeyX // 120 x + // ASCII79 Keycode = KeyY // 121 y + // ASCII7A Keycode = KeyZ // 122 z + // ASCII7B Keycode = KeyLeftBrace + shiftMask // 123 { + // ASCII7C Keycode = KeyBackslash + shiftMask // 124 | + // ASCII7D Keycode = KeyRightBrace + shiftMask // 125 } + // ASCII7E Keycode = KeyTilde + shiftMask // 126 ~ + // ASCII7F Keycode = KeyBackspace // 127 DEL +) + +var ascii = [...]Keycode{ + ASCII00.mask(), ASCII01.mask(), ASCII02.mask(), ASCII03.mask(), + ASCII04.mask(), ASCII05.mask(), ASCII06.mask(), ASCII07.mask(), + ASCII08.mask(), ASCII09.mask(), ASCII0A.mask(), ASCII0B.mask(), + ASCII0C.mask(), ASCII0D.mask(), ASCII0E.mask(), ASCII0F.mask(), + ASCII10.mask(), ASCII11.mask(), ASCII12.mask(), ASCII13.mask(), + ASCII14.mask(), ASCII15.mask(), ASCII16.mask(), ASCII17.mask(), + ASCII18.mask(), ASCII19.mask(), ASCII1A.mask(), ASCII1B.mask(), + ASCII1C.mask(), ASCII1D.mask(), ASCII1E.mask(), ASCII1F.mask(), + ASCII20.mask(), ASCII21.mask(), ASCII22.mask(), ASCII23.mask(), + ASCII24.mask(), ASCII25.mask(), ASCII26.mask(), ASCII27.mask(), + ASCII28.mask(), ASCII29.mask(), ASCII2A.mask(), ASCII2B.mask(), + ASCII2C.mask(), ASCII2D.mask(), ASCII2E.mask(), ASCII2F.mask(), + ASCII30.mask(), ASCII31.mask(), ASCII32.mask(), ASCII33.mask(), + ASCII34.mask(), ASCII35.mask(), ASCII36.mask(), ASCII37.mask(), + ASCII38.mask(), ASCII39.mask(), ASCII3A.mask(), ASCII3B.mask(), + ASCII3C.mask(), ASCII3D.mask(), ASCII3E.mask(), ASCII3F.mask(), + ASCII40.mask(), ASCII41.mask(), ASCII42.mask(), ASCII43.mask(), + ASCII44.mask(), ASCII45.mask(), ASCII46.mask(), ASCII47.mask(), + ASCII48.mask(), ASCII49.mask(), ASCII4A.mask(), ASCII4B.mask(), + ASCII4C.mask(), ASCII4D.mask(), ASCII4E.mask(), ASCII4F.mask(), + ASCII50.mask(), ASCII51.mask(), ASCII52.mask(), ASCII53.mask(), + ASCII54.mask(), ASCII55.mask(), ASCII56.mask(), ASCII57.mask(), + ASCII58.mask(), ASCII59.mask(), ASCII5A.mask(), ASCII5B.mask(), + ASCII5C.mask(), ASCII5D.mask(), ASCII5E.mask(), ASCII5F.mask(), + ASCII60.mask(), ASCII61.mask(), ASCII62.mask(), ASCII63.mask(), + ASCII64.mask(), ASCII65.mask(), ASCII66.mask(), ASCII67.mask(), + ASCII68.mask(), ASCII69.mask(), ASCII6A.mask(), ASCII6B.mask(), + ASCII6C.mask(), ASCII6D.mask(), ASCII6E.mask(), ASCII6F.mask(), + ASCII70.mask(), ASCII71.mask(), ASCII72.mask(), ASCII73.mask(), + ASCII74.mask(), ASCII75.mask(), ASCII76.mask(), ASCII77.mask(), + ASCII78.mask(), ASCII79.mask(), ASCII7A.mask(), ASCII7B.mask(), + ASCII7C.mask(), ASCII7D.mask(), ASCII7E.mask(), ASCII7F.mask(), +} + +var iso88591 = [...]Keycode{ + ISO88591A0.mask(), ISO88591A1.mask(), ISO88591A2.mask(), ISO88591A3.mask(), + ISO88591A4.mask(), ISO88591A5.mask(), ISO88591A6.mask(), ISO88591A7.mask(), + ISO88591A8.mask(), ISO88591A9.mask(), ISO88591AA.mask(), ISO88591AB.mask(), + ISO88591AC.mask(), ISO88591AD.mask(), ISO88591AE.mask(), ISO88591AF.mask(), + ISO88591B0.mask(), ISO88591B1.mask(), ISO88591B2.mask(), ISO88591B3.mask(), + ISO88591B4.mask(), ISO88591B5.mask(), ISO88591B6.mask(), ISO88591B7.mask(), + ISO88591B8.mask(), ISO88591B9.mask(), ISO88591BA.mask(), ISO88591BB.mask(), + ISO88591BC.mask(), ISO88591BD.mask(), ISO88591BE.mask(), ISO88591BF.mask(), + ISO88591C0.mask(), ISO88591C1.mask(), ISO88591C2.mask(), ISO88591C3.mask(), + ISO88591C4.mask(), ISO88591C5.mask(), ISO88591C6.mask(), ISO88591C7.mask(), + ISO88591C8.mask(), ISO88591C9.mask(), ISO88591CA.mask(), ISO88591CB.mask(), + ISO88591CC.mask(), ISO88591CD.mask(), ISO88591CE.mask(), ISO88591CF.mask(), + ISO88591D0.mask(), ISO88591D1.mask(), ISO88591D2.mask(), ISO88591D3.mask(), + ISO88591D4.mask(), ISO88591D5.mask(), ISO88591D6.mask(), ISO88591D7.mask(), + ISO88591D8.mask(), ISO88591D9.mask(), ISO88591DA.mask(), ISO88591DB.mask(), + ISO88591DC.mask(), ISO88591DD.mask(), ISO88591DE.mask(), ISO88591DF.mask(), + ISO88591E0.mask(), ISO88591E1.mask(), ISO88591E2.mask(), ISO88591E3.mask(), + ISO88591E4.mask(), ISO88591E5.mask(), ISO88591E6.mask(), ISO88591E7.mask(), + ISO88591E8.mask(), ISO88591E9.mask(), ISO88591EA.mask(), ISO88591EB.mask(), + ISO88591EC.mask(), ISO88591ED.mask(), ISO88591EE.mask(), ISO88591EF.mask(), + ISO88591F0.mask(), ISO88591F1.mask(), ISO88591F2.mask(), ISO88591F3.mask(), + ISO88591F4.mask(), ISO88591F5.mask(), ISO88591F6.mask(), ISO88591F7.mask(), + ISO88591F8.mask(), ISO88591F9.mask(), ISO88591FA.mask(), ISO88591FB.mask(), + ISO88591FC.mask(), ISO88591FD.mask(), ISO88591FE.mask(), ISO88591FF.mask(), +} diff --git a/src/machine/usb/uart.go b/src/machine/usb/uart.go index 7c1ae2e90..f7e8f3aef 100644 --- a/src/machine/usb/uart.go +++ b/src/machine/usb/uart.go @@ -11,28 +11,29 @@ var ( ErrUARTWriteFailed = errors.New("USB write failure") ) -type ( - UARTConfig struct { - BaudRate uint32 - } +// UART represents a virtual serial (UART) device emulation using the USB +// CDC-ACM device class driver. +type UART struct { + Port int + core *core +} - // UART represents a virtual serial (UART) device emulation using the USB - // CDC-ACM device class driver. - UART struct { - port int // USB port (core index, e.g., 0-1) - core *core - } -) +type UARTConfig struct { + // Port is the MCU's native USB core number. If in doubt, leave it + // uninitialized for default (0). + Port int +} func (uart *UART) Configure(config UARTConfig) error { - if uart.port >= CoreCount || uart.port >= dcdCount { + if config.Port >= CoreCount || config.Port >= dcdCount { return ErrUARTInvalidPort } + uart.Port = config.Port // verify we have a free USB port and take ownership of it var st status - uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1}) + uart.core, st = initCore(uart.Port, class{id: classDeviceCDCACM, config: 1}) if !st.ok() { return ErrUARTInvalidPort } diff --git a/src/machine/usb/usb.go b/src/machine/usb/usb.go index 40d325ddb..5d4c4e4ca 100644 --- a/src/machine/usb/usb.go +++ b/src/machine/usb/usb.go @@ -106,14 +106,15 @@ type class struct { // Enumerated constants for all host/device class configurations. const ( - classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) + classDeviceCDCACM = 0 + classDeviceHID = 1 ) // mode returns the USB core operating mode of the receiver class c. //go:inline func (c class) mode() int { switch c.id { - case classDeviceCDCACM: + case classDeviceCDCACM, classDeviceHID: return modeDevice default: return modeIdle diff --git a/src/machine/usb/util.go b/src/machine/usb/util.go index 94dfff99d..5b88f7a50 100644 --- a/src/machine/usb/util.go +++ b/src/machine/usb/util.go @@ -1,5 +1,8 @@ package usb +//go:linkname ticks runtime.ticks +func ticks() int64 + // leU64 returns a slice containing 8 bytes from the given uint64 u. // // The returned bytes have little-endian ordering; that is, the first element @@ -214,3 +217,55 @@ func endpointIndex(address uint8) uint8 { return ((address & descEndptAddrNumberMsk) << 1) | ((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos) } + +// The following buffLo and buffHi are helper methods for slice definitions from +// potentially zero-length arrays (depending on compile-time constants). +// +// For example, if we have an array containing a 5-element buffer for three +// instances of some device class (15 total elements), partitioned as follows, +// then we compute the indices for instance 2 as usual: +// +// Index: 01234 56789 ABCDE +// Array: [ 1 | 2 | 3 ] +// +// Lo: (n-1) * size => (2-1) * 5 => 5 +// Hi: (n) * size => (2) * 5 => 10 (0xA) +// +// However, if we have specified (via const definition) that 0 instances of some +// device class be allocated, then the associated device class buffer arrays +// will all be zero-length arrays, and the arithmetic to compute the slice +// indices used above will result in out-of-bounds indices: +// +// Index: +// Array: [] +// +// Lo: (n-1) * size => (2-1) * 5 => 5 [Error!] +// Hi: (n) * size => (2) * 5 => 10 (0xA) [Error!] +// +// +// I couldn't figure out a straight-forward way to resolve these slice indices +// using only arithmetic, so I've resorted to simple conditionals. If the number +// of instances for some given class is zero (count=0), defined via compile-time +// constant, then just use the empty slice range [0:0]. + +// buffLo returns the starting array slice index for the n'th region of size +// elements from an array containing count regions of size elements. +// Regions are specified using a 1-based index (n > 0). Returns 0 if any given +// argument equals 0. +func buffLo(n, count, size uint16) uint16 { + if 0 == n || 0 == count || 0 == size { + return 0 + } + return (n - 1) * size +} + +// buffHi returns the ending array slice index for the n'th region of size +// elements from an array containing count regions of size elements. +// Regions are specified using a 1-based index (n > 0). Returns 0 if any given +// argument equals 0. +func buffHi(n, count, size uint16) uint16 { + if 0 == n || 0 == count || 0 == size { + return 0 + } + return n * size +} diff --git a/src/machine/usb/util_arm.go b/src/machine/usb/util_arm.go index 750629357..f40112fea 100644 --- a/src/machine/usb/util_arm.go +++ b/src/machine/usb/util_arm.go @@ -4,9 +4,6 @@ package usb import "device/arm" -//go:linkname ticks runtime.ticks -func ticks() int64 - // udelay waits for the given number of microseconds before returning. // We cannot use the sleep timer from this context (import cycle), but we need // an approximate method to spin CPU cycles for short periods of time. diff --git a/src/runtime/runtime_mimxrt1062.go b/src/runtime/runtime_mimxrt1062.go index 490627e40..21a2a754a 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" ) @@ -125,12 +126,13 @@ func initUART() { } func initUSB() { - machine.UART0.Configure(machine.UARTConfig{}) + machine.HID0.Configure(usb.HIDConfig{}) + // machine.UART0.Configure(usb.UARTConfig{}) } func putchar(c byte) { - machine.UART0.WriteByte(c) // print to USB UART - // machine.UART1.WriteByte(c) // print to hardware UART + // machine.UART0.WriteByte(c) // print to USB UART + machine.UART1.WriteByte(c) // print to hardware UART } func abort() { From 025f2fe7e44dcccdb6ea852eb7b3a6bc1e18de00 Mon Sep 17 00:00:00 2001 From: ardnew Date: Sat, 26 Jun 2021 18:21:57 -0500 Subject: [PATCH 9/9] add Serial var to Adafruit Matrix Portal M4 --- src/machine/board_matrixportal-m4_baremetal.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/machine/board_matrixportal-m4_baremetal.go b/src/machine/board_matrixportal-m4_baremetal.go index 8e0cd8543..5c57fbd35 100644 --- a/src/machine/board_matrixportal-m4_baremetal.go +++ b/src/machine/board_matrixportal-m4_baremetal.go @@ -9,6 +9,7 @@ import ( // UART on the MatrixPortal M4 var ( + Serial = UART1 UART1 = &_UART1 _UART1 = UART{ Buffer: NewRingBuffer(),