mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 02:57:46 +00:00
begin USB refactor with package machine/usb2
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
package nxp
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"runtime/volatile"
|
||||
"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
|
||||
type ClockConfigArmPll struct {
|
||||
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...)
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
||||
// PHY configuration for USB HS
|
||||
type ClockConfigUsbPhy struct {
|
||||
Instance uint8 // USB PHY number (1 or 2)
|
||||
XtalFreq uint32 // External reference clock frequency (Hz)
|
||||
DCal uint32 // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig ClockConfigUsbPll
|
||||
}
|
||||
|
||||
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
|
||||
// the USB +3V regulator (PMU), for use as either USB host or device.
|
||||
func (cfg ClockConfigUsbPhy) Configure() {
|
||||
|
||||
var (
|
||||
usb *USB_Type
|
||||
phy *USBPHY_Type
|
||||
chrgDetectReg *volatile.Register32
|
||||
chrgDetectMsk uint32
|
||||
)
|
||||
|
||||
// Select appropriate peripherals based on receiver Instance
|
||||
switch cfg.Instance {
|
||||
case 1:
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB1_SET.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
||||
|
||||
// update PFDs after update
|
||||
setUsb1Pfd(pfd...)
|
||||
|
||||
usb = USB1 // Select USB1 HS PHY/PLL
|
||||
phy = USBPHY1 //
|
||||
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
||||
case 2:
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
||||
usb = USB2 // Select USB2 HS PHY/PLL
|
||||
phy = USBPHY2 //
|
||||
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
|
||||
default:
|
||||
panic("nxp: invalid USB PHY")
|
||||
}
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
||||
// Configure and enable USB PLL clocks
|
||||
cfg.PllConfig.Configure()
|
||||
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
||||
// Release PHY from reset
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
|
||||
|
||||
// Enable power to USB PHY
|
||||
phy.PWD.Set(0)
|
||||
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
|
||||
// enable support for low-speed device connection, direct and indirect (hub)
|
||||
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
|
||||
|
||||
// Enable USB HS clocks gate
|
||||
ClockIpUsbOh3.Enable(true)
|
||||
// Reset USB peripheral
|
||||
usb.USBCMD.SetBits(USB_USBCMD_RST)
|
||||
|
||||
// Add a delay after RST to ensure there is a USB D+ pullup sequence
|
||||
nopDelay(400000)
|
||||
|
||||
// Enable USB LDO
|
||||
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
|
||||
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
|
||||
|
||||
// check whether we are connected to USB charger
|
||||
chrgDetectReg.Set(chrgDetectMsk)
|
||||
|
||||
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
|
||||
phy.TX.Set((phy.TX.Get() &
|
||||
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
|
||||
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
|
||||
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
|
||||
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure() {
|
||||
// select USB peripheral registers based on receiver's Instance
|
||||
switch cfg.Instance {
|
||||
case 1: // USB1 PLL
|
||||
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
|
||||
|
||||
// update PFDs (if provided)
|
||||
if nil != cfg.Pfd {
|
||||
setUsb1Pfd(cfg.Pfd...)
|
||||
}
|
||||
}
|
||||
case 2: // USB2 PLL
|
||||
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
|
||||
|
||||
default:
|
||||
panic("nxp: invalid USB PLL")
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
// go:inline
|
||||
func nopDelay(cycles uint32) {
|
||||
for i := uint32(0); i < cycles; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,21 +56,21 @@ const (
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
||||
// = Pin // Dig [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
|
||||
)
|
||||
|
||||
// Default peripheral pins
|
||||
@@ -105,6 +105,18 @@ func init() {
|
||||
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
||||
}
|
||||
|
||||
// #=====================================================#
|
||||
// | USB |
|
||||
// #=====================================================#
|
||||
var (
|
||||
// USBCDC is a legacy class being retained here as temporary wrapper.
|
||||
// See godoc comments on type USBCDC struct definition for details.
|
||||
USBCDC0 = USBCDC{
|
||||
port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0)
|
||||
buff: NewRingBuffer(),
|
||||
}
|
||||
)
|
||||
|
||||
// #=====================================================#
|
||||
// | UART |
|
||||
// #===========#===========#=============#===============#
|
||||
|
||||
@@ -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() {
|
||||
print(" while unstacking exception")
|
||||
}
|
||||
if fault.Mem().WileStackingException() {
|
||||
if fault.Mem().WhileStackingException() {
|
||||
print(" while stacking exception")
|
||||
}
|
||||
if fault.Mem().DuringFPLazyStatePres() {
|
||||
@@ -162,13 +162,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
|
||||
return fs&arm.SCB_CFSR_MUNSTKERR != 0
|
||||
}
|
||||
|
||||
// WileStackingException: stacking for an exception entry has caused one or more
|
||||
// WhileStackingException: stacking for an exception entry has caused one or more
|
||||
// access violations
|
||||
//
|
||||
// "When this bit is 1, the SP is still adjusted but the values in the context
|
||||
// area on the stack might be incorrect. The processor has not written a fault
|
||||
// address to the MMAR."
|
||||
func (fs MemFaultStatus) WileStackingException() bool {
|
||||
func (fs MemFaultStatus) WhileStackingException() bool {
|
||||
return fs&arm.SCB_CFSR_MSTKERR != 0
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func main() {
|
||||
// initialize cache and MPU
|
||||
initCache()
|
||||
|
||||
// enable SysTick, GPIO, and peripherals
|
||||
// enable SysTick, GPIO, USB, and other peripherals
|
||||
initPeripherals()
|
||||
|
||||
// reenable interrupts
|
||||
@@ -107,7 +107,8 @@ func initPeripherals() {
|
||||
initPins() // configure GPIO
|
||||
|
||||
enablePeripheralClocks() // activate peripheral clock gates
|
||||
initUART() // configure UART (initialized first for debugging)
|
||||
initUSB() // configure USB CDC-ACM (UART0)
|
||||
initUART() // configure hardware UART (UART1)
|
||||
}
|
||||
|
||||
func initPins() {
|
||||
@@ -122,6 +123,10 @@ func initUART() {
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
func initUSB() {
|
||||
machine.USBCDC0.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
@@ -36,15 +36,31 @@ var (
|
||||
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
}
|
||||
Usb1PllConfig = nxp.ClockConfigUsbPll{
|
||||
Instance: 1, // USB PLL instance
|
||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
Usb1PhyConfig = nxp.ClockConfigUsbPhy{
|
||||
Instance: 1, // USB PHY number (1 or 2)
|
||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||
DCal: 0xC, // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig: nxp.ClockConfigUsbPll{
|
||||
Instance: 1, // USB PLL number (1 or 2)
|
||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
||||
},
|
||||
}
|
||||
Usb2PllConfig = nxp.ClockConfigUsbPll{
|
||||
Instance: 2, // USB PLL instance
|
||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
Usb2PhyConfig = nxp.ClockConfigUsbPhy{
|
||||
Instance: 2, // USB PHY number (1 or 2)
|
||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||
DCal: 0xC, // Decode to trim the nominal 17.78mA current source
|
||||
TxCal45DP: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig: nxp.ClockConfigUsbPll{
|
||||
Instance: 2, // USB PLL number (1 or 2)
|
||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -86,7 +102,7 @@ func initClocks() {
|
||||
|
||||
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
|
||||
nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) |
|
||||
((13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||
((0x13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||
|
||||
// wait until DCDC_STS_DC_OK bit is asserted
|
||||
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
||||
@@ -105,6 +121,8 @@ func initClocks() {
|
||||
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
||||
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
||||
|
||||
nxp.ClockIpUsbOh3.Enable(false) // disable USB
|
||||
|
||||
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
||||
nxp.ClockIpGpt1S.Enable(false) //
|
||||
nxp.ClockIpGpt2.Enable(false) //
|
||||
@@ -113,6 +131,11 @@ func initClocks() {
|
||||
|
||||
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
|
||||
|
||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||
nxp.ClockIpGpio2.Enable(false) //
|
||||
nxp.ClockIpGpio3.Enable(false) //
|
||||
nxp.ClockIpGpio4.Enable(false) //
|
||||
|
||||
nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1
|
||||
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
||||
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
||||
@@ -121,9 +144,9 @@ func initClocks() {
|
||||
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
||||
|
||||
nxp.ClockIpSemc.Enable(false) // disable SEMC
|
||||
nxp.DivIpSemc.Div(1) // divide SEMC_PODF (DIV2)
|
||||
nxp.DivIpSemc.Div(7) // divide SEMC_PODF (DIV8)
|
||||
nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2
|
||||
nxp.MuxIpSemc.Mux(1) // SEMC select SEMC_ALT
|
||||
nxp.MuxIpSemc.Mux(0) // SEMC select PERIPH_CLK
|
||||
|
||||
if false {
|
||||
// TODO: external flash is on this bus, configured via DCD block
|
||||
@@ -192,7 +215,7 @@ func initClocks() {
|
||||
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
||||
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
||||
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
|
||||
nxp.MuxIpLcdifPre.Mux(5) // LCDIF_PRE select PLL3_PFD1
|
||||
nxp.MuxIpLcdifPre.Mux(4) // LCDIF_PRE select PLL2_PFD1
|
||||
|
||||
nxp.ClockIpSpdif.Enable(false) // disable SPDIF
|
||||
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
||||
@@ -210,43 +233,43 @@ func initClocks() {
|
||||
|
||||
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
|
||||
|
||||
// Disable Audio/Video/Ethernet PLLs
|
||||
nxp.CCM_ANALOG.PLL_AUDIO.Set(nxp.CCM_ANALOG_PLL_AUDIO_POWERDOWN_Msk)
|
||||
nxp.CCM_ANALOG.PLL_VIDEO.Set(nxp.CCM_ANALOG_PLL_VIDEO_POWERDOWN_Msk)
|
||||
nxp.CCM_ANALOG.PLL_ENET.Set(nxp.CCM_ANALOG_PLL_ENET_POWERDOWN_Msk)
|
||||
|
||||
ArmPllConfig.Configure() // init ARM PLL
|
||||
// SYS PLL (PLL2) @ 528 MHz
|
||||
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
||||
// PFD1 = 594 MHz -> (currently unused)
|
||||
// PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz
|
||||
// PFD3 = 454.73 MHz -> (currently unused)
|
||||
// PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz
|
||||
// PFD3 = 594 MHz -> (currently unused)
|
||||
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
||||
|
||||
// USB1 PLL (PLL3) @ 480 MHz
|
||||
// PFD0 -> (currently unused)
|
||||
// PFD1 -> (currently unused)
|
||||
// PFD2 -> (currently unused)
|
||||
// PFD3 -> (currently unused)
|
||||
Usb1PllConfig.Configure() // init USB1 PLL and PFDs
|
||||
Usb2PllConfig.Configure() // init USB2 PLL
|
||||
Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL
|
||||
Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL
|
||||
|
||||
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
||||
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
||||
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
||||
nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC
|
||||
|
||||
// set LVDS1 clock source
|
||||
// set LVDS1 clock source (ARM_PLL)
|
||||
nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) |
|
||||
((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk))
|
||||
|
||||
// set CLOCK_OUT1 divider
|
||||
// set CLOCK_OUT1 divider (DIV1)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) |
|
||||
((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk))
|
||||
// set CLOCK_OUT1 source
|
||||
// set CLOCK_OUT1 source (PLL2/DIV2)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) |
|
||||
((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk))
|
||||
// set CLOCK_OUT2 divider
|
||||
// set CLOCK_OUT2 divider (DIV1)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) |
|
||||
((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk))
|
||||
// set CLOCK_OUT2 source
|
||||
// set CLOCK_OUT2 source (SPDIF0)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) |
|
||||
((18 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||
((29 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||
|
||||
nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT
|
||||
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
|
||||
@@ -254,15 +277,10 @@ func initClocks() {
|
||||
|
||||
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
||||
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
|
||||
// set GPT1 High frequency reference clock source
|
||||
// set GPT1 High frequency reference clock source (PERCLK)
|
||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk)
|
||||
// set GPT2 High frequency reference clock source
|
||||
// set GPT2 High frequency reference clock source (PERCLK)
|
||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk)
|
||||
|
||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||
nxp.ClockIpGpio2.Enable(false) //
|
||||
nxp.ClockIpGpio3.Enable(false) //
|
||||
nxp.ClockIpGpio4.Enable(false) //
|
||||
}
|
||||
|
||||
func enableTimerClocks() {
|
||||
|
||||
@@ -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.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
|
||||
for {
|
||||
//arm.Asm("wfi") // TODO: causes hardfault! why?
|
||||
waitForEvents()
|
||||
if pitActive.Get() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user