mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 10:43:29 +00:00
begin USB refactor with package machine/usb2
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
package nxp
|
package nxp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"device/arm"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -378,30 +379,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
|
// PLL configuration for ARM
|
||||||
type ClockConfigArmPll struct {
|
type ClockConfigArmPll struct {
|
||||||
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
||||||
@@ -472,59 +449,178 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
|||||||
setSysPfd(pfd...)
|
setSysPfd(pfd...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PLL configuration for USB
|
func setSysPfd(value ...uint32) {
|
||||||
type ClockConfigUsbPll struct {
|
for i, val := range value {
|
||||||
Instance uint8 // USB PLL number (1 or 2)
|
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
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 {
|
switch cfg.Instance {
|
||||||
case 1:
|
case 1:
|
||||||
|
usb = USB1 // Select USB1 HS PHY/PLL
|
||||||
// bypass PLL first
|
phy = USBPHY1 //
|
||||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
||||||
CCM_ANALOG.PLL_USB1.Set(
|
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
||||||
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...)
|
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
// bypass PLL first
|
usb = USB2 // Select USB2 HS PHY/PLL
|
||||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
phy = USBPHY2 //
|
||||||
CCM_ANALOG.PLL_USB2.Set(
|
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
||||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
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)
|
// Configure and enable USB PLL clocks
|
||||||
CCM_ANALOG.PLL_USB2.Set(
|
cfg.PllConfig.Configure()
|
||||||
(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)
|
|
||||||
|
|
||||||
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:
|
default:
|
||||||
panic("nxp: invalid USB PLL")
|
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`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,21 +56,21 @@ const (
|
|||||||
|
|
||||||
// Analog pins
|
// Analog pins
|
||||||
const (
|
const (
|
||||||
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
// = Pin // Dig [Pad] {ADC1/ADC2}
|
||||||
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
|
||||||
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
|
||||||
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
|
||||||
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
|
||||||
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
|
||||||
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
|
||||||
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
|
||||||
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
|
||||||
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
|
||||||
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
|
||||||
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
|
||||||
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
|
||||||
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
|
||||||
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default peripheral pins
|
// Default peripheral pins
|
||||||
@@ -105,6 +105,18 @@ func init() {
|
|||||||
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
_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 |
|
// | UART |
|
||||||
// #===========#===========#=============#===============#
|
// #===========#===========#=============#===============#
|
||||||
|
|||||||
@@ -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})
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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{}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package usb2
|
||||||
|
|
||||||
|
type dci interface {
|
||||||
|
init() status
|
||||||
|
enable(enable bool) status
|
||||||
|
critical(enter bool) status
|
||||||
|
interrupt()
|
||||||
|
udelay(micros uint32)
|
||||||
|
}
|
||||||
@@ -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`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package usb2
|
||||||
|
|
||||||
|
type hci interface {
|
||||||
|
init() status
|
||||||
|
enable(enable bool) status
|
||||||
|
critical(enter bool) status
|
||||||
|
interrupt()
|
||||||
|
udelay(micros uint32)
|
||||||
|
}
|
||||||
@@ -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`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ func handleHardFault(sp *interruptStack) {
|
|||||||
if fault.Mem().WhileUnstackingException() {
|
if fault.Mem().WhileUnstackingException() {
|
||||||
print(" while unstacking exception")
|
print(" while unstacking exception")
|
||||||
}
|
}
|
||||||
if fault.Mem().WileStackingException() {
|
if fault.Mem().WhileStackingException() {
|
||||||
print(" while stacking exception")
|
print(" while stacking exception")
|
||||||
}
|
}
|
||||||
if fault.Mem().DuringFPLazyStatePres() {
|
if fault.Mem().DuringFPLazyStatePres() {
|
||||||
@@ -162,13 +162,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
|
|||||||
return fs&arm.SCB_CFSR_MUNSTKERR != 0
|
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
|
// access violations
|
||||||
//
|
//
|
||||||
// "When this bit is 1, the SP is still adjusted but the values in the context
|
// "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
|
// area on the stack might be incorrect. The processor has not written a fault
|
||||||
// address to the MMAR."
|
// address to the MMAR."
|
||||||
func (fs MemFaultStatus) WileStackingException() bool {
|
func (fs MemFaultStatus) WhileStackingException() bool {
|
||||||
return fs&arm.SCB_CFSR_MSTKERR != 0
|
return fs&arm.SCB_CFSR_MSTKERR != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func main() {
|
|||||||
// initialize cache and MPU
|
// initialize cache and MPU
|
||||||
initCache()
|
initCache()
|
||||||
|
|
||||||
// enable SysTick, GPIO, and peripherals
|
// enable SysTick, GPIO, USB, and other peripherals
|
||||||
initPeripherals()
|
initPeripherals()
|
||||||
|
|
||||||
// reenable interrupts
|
// reenable interrupts
|
||||||
@@ -107,7 +107,8 @@ func initPeripherals() {
|
|||||||
initPins() // configure GPIO
|
initPins() // configure GPIO
|
||||||
|
|
||||||
enablePeripheralClocks() // activate peripheral clock gates
|
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() {
|
func initPins() {
|
||||||
@@ -122,6 +123,10 @@ func initUART() {
|
|||||||
machine.Serial.Configure(machine.UARTConfig{})
|
machine.Serial.Configure(machine.UARTConfig{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initUSB() {
|
||||||
|
machine.USBCDC0.Configure(machine.UARTConfig{})
|
||||||
|
}
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
machine.Serial.WriteByte(c)
|
machine.Serial.WriteByte(c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,31 @@ var (
|
|||||||
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
||||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||||
}
|
}
|
||||||
Usb1PllConfig = nxp.ClockConfigUsbPll{
|
Usb1PhyConfig = nxp.ClockConfigUsbPhy{
|
||||||
Instance: 1, // USB PLL instance
|
Instance: 1, // USB PHY number (1 or 2)
|
||||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
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{
|
Usb2PhyConfig = nxp.ClockConfigUsbPhy{
|
||||||
Instance: 2, // USB PLL instance
|
Instance: 2, // USB PHY number (1 or 2)
|
||||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
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)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,7 +102,7 @@ func initClocks() {
|
|||||||
|
|
||||||
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
|
// 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)) |
|
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
|
// wait until DCDC_STS_DC_OK bit is asserted
|
||||||
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
||||||
@@ -105,6 +121,8 @@ func initClocks() {
|
|||||||
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
||||||
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
||||||
|
|
||||||
|
nxp.ClockIpUsbOh3.Enable(false) // disable USB
|
||||||
|
|
||||||
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
||||||
nxp.ClockIpGpt1S.Enable(false) //
|
nxp.ClockIpGpt1S.Enable(false) //
|
||||||
nxp.ClockIpGpt2.Enable(false) //
|
nxp.ClockIpGpt2.Enable(false) //
|
||||||
@@ -113,6 +131,11 @@ func initClocks() {
|
|||||||
|
|
||||||
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
|
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.ClockIpUsdhc1.Enable(false) // disable USDHC1
|
||||||
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
||||||
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
||||||
@@ -121,9 +144,9 @@ func initClocks() {
|
|||||||
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
||||||
|
|
||||||
nxp.ClockIpSemc.Enable(false) // disable SEMC
|
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.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 {
|
if false {
|
||||||
// TODO: external flash is on this bus, configured via DCD block
|
// TODO: external flash is on this bus, configured via DCD block
|
||||||
@@ -192,7 +215,7 @@ func initClocks() {
|
|||||||
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
||||||
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
||||||
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
|
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.ClockIpSpdif.Enable(false) // disable SPDIF
|
||||||
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
||||||
@@ -210,43 +233,43 @@ func initClocks() {
|
|||||||
|
|
||||||
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
|
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
|
ArmPllConfig.Configure() // init ARM PLL
|
||||||
// SYS PLL (PLL2) @ 528 MHz
|
// SYS PLL (PLL2) @ 528 MHz
|
||||||
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
||||||
// PFD1 = 594 MHz -> (currently unused)
|
// PFD1 = 594 MHz -> (currently unused)
|
||||||
// PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz
|
// PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz
|
||||||
// PFD3 = 454.73 MHz -> (currently unused)
|
// PFD3 = 594 MHz -> (currently unused)
|
||||||
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
||||||
|
|
||||||
// USB1 PLL (PLL3) @ 480 MHz
|
Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL
|
||||||
// PFD0 -> (currently unused)
|
Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL
|
||||||
// PFD1 -> (currently unused)
|
|
||||||
// PFD2 -> (currently unused)
|
|
||||||
// PFD3 -> (currently unused)
|
|
||||||
Usb1PllConfig.Configure() // init USB1 PLL and PFDs
|
|
||||||
Usb2PllConfig.Configure() // init USB2 PLL
|
|
||||||
|
|
||||||
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
||||||
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
||||||
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
||||||
nxp.MuxIpPerclk.Mux(1) // PERCLK 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)) |
|
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))
|
((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)) |
|
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))
|
((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)) |
|
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))
|
((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)) |
|
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))
|
((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)) |
|
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.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
|
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
|
||||||
@@ -254,15 +277,10 @@ func initClocks() {
|
|||||||
|
|
||||||
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
||||||
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
|
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)
|
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.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() {
|
func enableTimerClocks() {
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ func timerSleep(cycles uint32) bool {
|
|||||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts
|
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
|
nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
|
||||||
for {
|
for {
|
||||||
//arm.Asm("wfi") // TODO: causes hardfault! why?
|
waitForEvents()
|
||||||
if pitActive.Get() == 0 {
|
if pitActive.Get() == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user