mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
Merge remote-tracking branch 'ardnew/feature/usb-common' into feature/usb-common
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
package nxp
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -377,30 +378,6 @@ func (clk Clock) setCcm(value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// PLL configuration for ARM
|
||||
type ClockConfigArmPll struct {
|
||||
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
||||
@@ -471,59 +448,178 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
||||
setSysPfd(pfd...)
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
||||
// PHY configuration for USB HS
|
||||
type ClockConfigUsbPhy struct {
|
||||
Instance uint8 // USB PHY number (1 or 2)
|
||||
XtalFreq uint32 // External reference clock frequency (Hz)
|
||||
DCal uint32 // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig ClockConfigUsbPll
|
||||
}
|
||||
|
||||
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
|
||||
// the USB +3V regulator (PMU), for use as either USB host or device.
|
||||
func (cfg ClockConfigUsbPhy) Configure() {
|
||||
|
||||
var (
|
||||
usb *USB_Type
|
||||
phy *USBPHY_Type
|
||||
chrgDetectReg *volatile.Register32
|
||||
chrgDetectMsk uint32
|
||||
)
|
||||
|
||||
// Select appropriate peripherals based on receiver Instance
|
||||
switch cfg.Instance {
|
||||
case 1:
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB1_SET.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
||||
|
||||
// update PFDs after update
|
||||
setUsb1Pfd(pfd...)
|
||||
|
||||
usb = USB1 // Select USB1 HS PHY/PLL
|
||||
phy = USBPHY1 //
|
||||
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
||||
case 2:
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
||||
usb = USB2 // Select USB2 HS PHY/PLL
|
||||
phy = USBPHY2 //
|
||||
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
|
||||
default:
|
||||
panic("nxp: invalid USB PHY")
|
||||
}
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
||||
// Configure and enable USB PLL clocks
|
||||
cfg.PllConfig.Configure()
|
||||
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
||||
// Release PHY from reset
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
|
||||
|
||||
// Enable power to USB PHY
|
||||
phy.PWD.Set(0)
|
||||
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
|
||||
// enable support for low-speed device connection, direct and indirect (hub)
|
||||
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
|
||||
|
||||
// Enable USB HS clocks gate
|
||||
ClockIpUsbOh3.Enable(true)
|
||||
// Reset USB peripheral
|
||||
usb.USBCMD.SetBits(USB_USBCMD_RST)
|
||||
|
||||
// Add a delay after RST to ensure there is a USB D+ pullup sequence
|
||||
nopDelay(400000)
|
||||
|
||||
// Enable USB LDO
|
||||
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
|
||||
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
|
||||
|
||||
// check whether we are connected to USB charger
|
||||
chrgDetectReg.Set(chrgDetectMsk)
|
||||
|
||||
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
|
||||
phy.TX.Set((phy.TX.Get() &
|
||||
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
|
||||
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
|
||||
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
|
||||
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure() {
|
||||
// select USB peripheral registers based on receiver's Instance
|
||||
switch cfg.Instance {
|
||||
case 1: // USB1 PLL
|
||||
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
|
||||
|
||||
// update PFDs (if provided)
|
||||
if nil != cfg.Pfd {
|
||||
setUsb1Pfd(cfg.Pfd...)
|
||||
}
|
||||
}
|
||||
case 2: // USB2 PLL
|
||||
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
|
||||
|
||||
default:
|
||||
panic("nxp: invalid USB PLL")
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
// go:inline
|
||||
func nopDelay(cycles uint32) {
|
||||
for i := uint32(0); i < cycles; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,3 +278,83 @@ func enableDcache(enable bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FlushDcache flushes data from cache to memory
|
||||
//
|
||||
// Normally FlushDcache is used when metadata written to memory will be used by
|
||||
// a DMA or a bus-controller peripheral. Any data in the cache is written to
|
||||
// memory. A copy remains in the cache, so this is typically used with special
|
||||
// fields you will want to quickly access in the future. For data transmission,
|
||||
// use FlushDeleteDcache.
|
||||
//go:inline
|
||||
func FlushDcache(addr, size uintptr) {
|
||||
location := addr & 0xFFFFFFE0
|
||||
endAddr := addr + size
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
for {
|
||||
SystemControl.DCCMVAC.Set(uint32(location))
|
||||
location += 32
|
||||
if location >= endAddr {
|
||||
break
|
||||
}
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
|
||||
// DeleteDcache deletes data from the cache, without touching memory.
|
||||
//
|
||||
// Normally DeleteDcache is used before receiving data via DMA or from
|
||||
// bus-controller peripherals which write to memory. You want to delete anything
|
||||
// the cache may have stored, so your next read is certain to access the
|
||||
// physical memory.
|
||||
//go:inline
|
||||
func DeleteDcache(addr, size uintptr) {
|
||||
location := addr & 0xFFFFFFE0
|
||||
endAddr := addr + size
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
for {
|
||||
SystemControl.DCIMVAC.Set(uint32(location))
|
||||
location += 32
|
||||
if location >= endAddr {
|
||||
break
|
||||
}
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
|
||||
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
|
||||
// cache
|
||||
//
|
||||
// Normally FlushDeleteDcache is used when transmitting data via DMA or
|
||||
// bus-controller peripherals which read from memory. You want any cached data
|
||||
// written to memory, and then removed from the cache, because you no longer
|
||||
// need to access the data after transmission.
|
||||
//go:inline
|
||||
func FlushDeleteDcache(addr, size uintptr) {
|
||||
location := addr & 0xFFFFFFE0
|
||||
endAddr := addr + size
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
for {
|
||||
SystemControl.DCCIMVAC.Set(uint32(location))
|
||||
location += 32
|
||||
if location >= endAddr {
|
||||
break
|
||||
}
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"time"
|
||||
)
|
||||
|
||||
var keyboard = machine.HID0.Keyboard()
|
||||
|
||||
func main() {
|
||||
|
||||
println("USB HID keyboard demo")
|
||||
|
||||
for {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Open a new text editor
|
||||
keyboard.Down(usb.KeyModifierAlt)
|
||||
keyboard.Press(usb.KeySpace)
|
||||
keyboard.Up(usb.KeyModifierAlt)
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Write([]byte("kate"))
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Use the io.Writer interface
|
||||
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Or manually specify keycodes and Unicode codepoints
|
||||
testKeys([]Key{
|
||||
// Print alphabet out-of-order
|
||||
{Press: usb.KeyX},
|
||||
{Press: usb.KeyY},
|
||||
{Press: usb.KeyZ},
|
||||
{Press: usb.KeyG},
|
||||
{Press: usb.KeyH},
|
||||
{Press: usb.KeyI},
|
||||
{Press: usb.KeyJ},
|
||||
{Press: usb.KeyK},
|
||||
{Press: usb.KeyL},
|
||||
{Press: usb.KeyM},
|
||||
{Press: usb.KeyN},
|
||||
{Press: usb.KeyO},
|
||||
{Press: usb.KeyP},
|
||||
{Press: usb.KeyQ},
|
||||
{Press: usb.KeyR},
|
||||
{Press: usb.KeyS},
|
||||
{Press: usb.KeyT},
|
||||
{Press: usb.KeyA},
|
||||
{Press: usb.KeyB},
|
||||
{Press: usb.KeyC},
|
||||
{Press: usb.KeyD},
|
||||
{Press: usb.KeyE},
|
||||
{Press: usb.KeyF},
|
||||
{Press: usb.KeyU},
|
||||
{Press: usb.KeyV},
|
||||
{Press: usb.KeyW},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Move cursor left x3
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Highlight 6 symbols to the left
|
||||
{Down: usb.KeyModifierShift},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Up: usb.KeyModifierShift},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Use Ctrl-X to cut
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Move to beginning of line
|
||||
{Press: usb.KeyHome},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Use Ctrl-V to paste
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Highlight 3 symbols to the right
|
||||
{Down: usb.KeyModifierShift},
|
||||
{Press: usb.KeyRight},
|
||||
{Press: usb.KeyRight},
|
||||
{Press: usb.KeyRight},
|
||||
{Up: usb.KeyModifierShift},
|
||||
// Use Ctrl-X to cut
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Move to end of line
|
||||
{Press: usb.KeyEnd},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Use Ctrl-V to paste
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Newline
|
||||
{Press: usb.KeyEnter},
|
||||
{Press: usb.KeyEnter},
|
||||
}, 150*time.Millisecond)
|
||||
|
||||
// Highlight all text and delete
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
keyboard.Press(usb.KeyA)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyDelete)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Close window
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
keyboard.Press(usb.KeyQ)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(2 * time.Second)
|
||||
// Confirm discard file
|
||||
keyboard.Down(usb.KeyModifierAlt)
|
||||
keyboard.Press(usb.KeyD)
|
||||
keyboard.Up(usb.KeyModifierAlt)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Open a new terminal
|
||||
keyboard.Down(usb.KeyModifierAlt)
|
||||
keyboard.Press(usb.KeySpace)
|
||||
keyboard.Up(usb.KeyModifierAlt)
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Write([]byte("konsole"))
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Open serial connection
|
||||
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(4 * time.Second)
|
||||
|
||||
// Write to UART
|
||||
keyboard.Write([]byte("hello!"))
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Write([]byte("NO U"))
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Close serial connection
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
keyboard.Press(usb.KeyX)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyBackslash)
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyY)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Close terminal
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
keyboard.Press(usb.KeyD)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
|
||||
time.Sleep(25 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
Press usb.Keycode
|
||||
Down usb.Keycode
|
||||
Up usb.Keycode
|
||||
Time time.Duration
|
||||
}
|
||||
|
||||
func testKeys(key []Key, delay time.Duration) {
|
||||
for _, k := range key {
|
||||
if 0 != k.Down {
|
||||
keyboard.Down(k.Down)
|
||||
}
|
||||
if 0 != k.Press {
|
||||
keyboard.Press(k.Press)
|
||||
}
|
||||
if 0 != k.Up {
|
||||
keyboard.Up(k.Up)
|
||||
}
|
||||
if 0 != k.Time {
|
||||
time.Sleep(k.Time)
|
||||
} else {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInternationalLayout() {
|
||||
// International keyboard layouts also supported
|
||||
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Numbers: 0123456789\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬®¯°±\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Cedilla: Çç\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Ring Above: Åå\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("AE: Ææ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Thorn: Þþ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Sharp S: ß\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("O-Stroke: Øø\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Eth: Ðð\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Euro: €\n"))
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// This is a echo console running on the device UART.
|
||||
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart := machine.UART0
|
||||
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
|
||||
|
||||
input := make([]byte, 4096)
|
||||
i := 0
|
||||
for {
|
||||
if uart.Buffered() > 0 {
|
||||
data, _ := uart.ReadByte()
|
||||
|
||||
switch data {
|
||||
case 13:
|
||||
// return key
|
||||
uart.Write([]byte("\r\n"))
|
||||
uart.Write([]byte("You typed: "))
|
||||
uart.Write(input[:i])
|
||||
uart.Write([]byte("\r\n"))
|
||||
i = 0
|
||||
default:
|
||||
// just echo the character
|
||||
uart.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// +build sam,atsamd51,matrixportal_m4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART on the MatrixPortal M4
|
||||
var (
|
||||
Serial = UART1
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM1_USART_INT,
|
||||
SERCOM: 1,
|
||||
}
|
||||
|
||||
UART2 = &_UART2
|
||||
_UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART_INT,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, _UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, _UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the MatrixPortal M4
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the MatrixPortal M4
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPIM,
|
||||
SERCOM: 3, // BUG: SDO on SERCOM1!
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM0_SPIM,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"machine/usb"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
@@ -55,21 +56,21 @@ const (
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
||||
// = Pin // Dig [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
|
||||
)
|
||||
|
||||
// Default peripheral pins
|
||||
@@ -104,6 +105,15 @@ func init() {
|
||||
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
||||
}
|
||||
|
||||
// #=====================================================#
|
||||
// | USB |
|
||||
// #=====================================================#
|
||||
var (
|
||||
// UART0 = usb.UART{Port: 0}
|
||||
HID0 = usb.HID{Port: 0}
|
||||
UART0 = &UART1
|
||||
)
|
||||
|
||||
// #=====================================================#
|
||||
// | UART |
|
||||
// #===========#===========#=============#===============#
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// Peripheral abstraction layer for the MIMXRT1062
|
||||
|
||||
//go:inline
|
||||
func CPUFrequency() uint32 {
|
||||
return 600000000
|
||||
}
|
||||
|
||||
@@ -0,0 +1,935 @@
|
||||
package usb
|
||||
|
||||
// Implementation of 32-bit target-agnostic USB device controller driver (dcd).
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||
panic("USB device controller is only supported on 32-bit systems")
|
||||
}
|
||||
}
|
||||
|
||||
// dcdCount defines the number of USB cores to configure for device mode. It is
|
||||
// computed as the sum of all declared device configuration descriptors.
|
||||
const dcdCount = descCDCACMCount + descHIDCount
|
||||
|
||||
// dcdInstance provides statically-allocated instances of each USB device
|
||||
// controller configured on this platform.
|
||||
var dcdInstance [dcdCount]dcd
|
||||
|
||||
// dhwInstance provides statically-allocated instances of each USB hardware
|
||||
// abstraction for ports configured as device on this platform.
|
||||
var dhwInstance [dcdCount]dhw
|
||||
|
||||
// dcd implements a generic USB device controller driver (dcd) for 32-bit ARM
|
||||
// targets.
|
||||
type dcd struct {
|
||||
*dhw // USB hardware abstraction layer
|
||||
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
cc class // USB device class
|
||||
id int // USB device controller index
|
||||
}
|
||||
|
||||
// initDCD initializes and assigns a free device controller instance to the
|
||||
// given USB port. Returns the initialized device controller or nil if no free
|
||||
// device controller instances remain.
|
||||
func initDCD(port int, class class) (*dcd, status) {
|
||||
if 0 == dcdCount {
|
||||
return nil, statusInvalid // Must have defined device controllers
|
||||
}
|
||||
switch class.id {
|
||||
case classDeviceCDCACM:
|
||||
if 0 == class.config || class.config > descCDCACMCount {
|
||||
return nil, statusInvalid // Must have defined descriptors
|
||||
}
|
||||
default:
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range dcdInstance {
|
||||
if nil == dcdInstance[i].core {
|
||||
// Initialize device controller.
|
||||
dcdInstance[i].dhw = allocDHW(port, i, &dcdInstance[i])
|
||||
dcdInstance[i].core = &coreInstance[port]
|
||||
dcdInstance[i].port = port
|
||||
dcdInstance[i].cc = class
|
||||
dcdInstance[i].id = i
|
||||
return &dcdInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free device controller instances available.
|
||||
}
|
||||
|
||||
// class returns the receiver's current device class configuration.
|
||||
func (d *dcd) class() class { return d.cc }
|
||||
|
||||
// dcdSetupSize defines the size (bytes) of a USB standard setup packet.
|
||||
const dcdSetupSize = 8 // bytes
|
||||
|
||||
// dcdSetup contains the USB standard setup packet used to configure a device.
|
||||
type dcdSetup struct {
|
||||
bmRequestType uint8
|
||||
bRequest uint8
|
||||
wValue uint16
|
||||
wIndex uint16
|
||||
wLength uint16
|
||||
}
|
||||
|
||||
// pack returns the receiver setup packet encoded as uint64.
|
||||
func (s dcdSetup) pack() uint64 {
|
||||
return ((uint64(s.bmRequestType) & 0xFF) << 0) |
|
||||
((uint64(s.bRequest) & 0xFF) << 8) |
|
||||
((uint64(s.wValue) & 0xFFFF) << 16) |
|
||||
((uint64(s.wIndex) & 0xFFFF) << 32) |
|
||||
((uint64(s.wLength) & 0xFFFF) << 48)
|
||||
}
|
||||
|
||||
// dcdEvent is used to describe virtual interrupts on the USB bus to a device
|
||||
// controller.
|
||||
//
|
||||
// Since the device controller software is intended for use with multiple TinyGo
|
||||
// targets, all of which may not have exactly the same USB bus interrupts, a
|
||||
// "virtual interrupt" is defined that is common to all targets. The target's
|
||||
// hardware implementation (type dhw) is responsible for translating real system
|
||||
// interrupts it receives into the appropriate virtual interrupt code, defined
|
||||
// below, and notifying the device controller via method (*dcd).event(dcdEvent).
|
||||
type dcdEvent struct {
|
||||
id uint8
|
||||
setup dcdSetup
|
||||
mask uint32
|
||||
}
|
||||
|
||||
// Enumerated constants for all possible USB device controller interrupt codes.
|
||||
const (
|
||||
dcdEventInvalid uint8 = iota // Invalid interrupt
|
||||
dcdEventStatusReset // USB reset received
|
||||
dcdEventStatusRun // USB controller entered run state
|
||||
dcdEventStatusSuspend // USB suspend received
|
||||
dcdEventStatusError // USB error condition detected on bus
|
||||
dcdEventControlSetup // USB setup received
|
||||
dcdEventPeripheralReady // USB PHY powered and ready to _go_
|
||||
dcdEventTransactComplete // USB transaction complete
|
||||
dcdEventTimer // USB (system) timer
|
||||
)
|
||||
|
||||
func (d *dcd) event(ev dcdEvent) {
|
||||
|
||||
switch ev.id {
|
||||
case dcdEventInvalid:
|
||||
case dcdEventStatusReset:
|
||||
d.endpointMask = 0
|
||||
|
||||
case dcdEventPeripheralReady:
|
||||
// Configure and enable control endpoint 0
|
||||
d.endpointEnable(0, true, 0)
|
||||
|
||||
case dcdEventStatusRun:
|
||||
case dcdEventStatusSuspend:
|
||||
case dcdEventStatusError:
|
||||
case dcdEventControlSetup:
|
||||
// On control endpoint 0 setup events, the ev.setup field will be defined
|
||||
switch d.controlSetup(ev.setup) {
|
||||
case dcdStageSetup:
|
||||
case dcdStageData:
|
||||
case dcdStageStatus:
|
||||
case dcdStageStall:
|
||||
d.controlStall()
|
||||
}
|
||||
|
||||
case dcdEventTransactComplete:
|
||||
case dcdEventTimer:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// dcdStage represents the USB transaction stage of a control request.
|
||||
type dcdStage uint8
|
||||
|
||||
// Enumerated constants for all possible USB transaction stages.
|
||||
const (
|
||||
dcdStageSetup dcdStage = iota // Indicates no stage transition required
|
||||
dcdStageData // IN/OUT data transfer
|
||||
dcdStageStatus // Setup request complete
|
||||
dcdStageStall // Unhandled or invalid request
|
||||
)
|
||||
|
||||
// controlSetup handles setup messages on control endpoint 0.
|
||||
func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
|
||||
|
||||
// Reset endpoint 0 notify mask
|
||||
d.controlMask = 0
|
||||
|
||||
// First, switch on the type of request (standard, class, or vendor)
|
||||
switch sup.bmRequestType & descRequestTypeTypeMsk {
|
||||
|
||||
// === STANDARD REQUEST ===
|
||||
case descRequestTypeTypeStandard:
|
||||
|
||||
// Switch on the recepient and direction of the request
|
||||
switch sup.bmRequestType &
|
||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
||||
|
||||
// --- DEVICE Rx (OUT) ---
|
||||
case descRequestTypeRecipientDevice | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// SET ADDRESS (0x05):
|
||||
case descRequestStandardSetAddress:
|
||||
d.setDeviceAddress(sup.wValue)
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
// SET CONFIGURATION (0x09):
|
||||
case descRequestStandardSetConfiguration:
|
||||
d.cc.config = int(sup.wValue)
|
||||
if 0 == d.cc.config || d.cc.config > dcdCount {
|
||||
// Use default if invalid index received
|
||||
d.cc.config = 1
|
||||
}
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
d.uartConfigure()
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
d.serialConfigure()
|
||||
d.keyboardConfigure()
|
||||
d.mouseConfigure()
|
||||
d.joystickConfigure()
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- DEVICE Tx (IN) ---
|
||||
case descRequestTypeRecipientDevice | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET STATUS (0x00):
|
||||
case descRequestStandardGetStatus:
|
||||
d.controlReply[0] = 0
|
||||
d.controlReply[1] = 0
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
||||
return dcdStageSetup
|
||||
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
d.controlDescriptorCDCACM(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
d.controlDescriptorHID(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// GET CONFIGURATION (0x08):
|
||||
case descRequestStandardGetConfiguration:
|
||||
d.controlReply[0] = uint8(d.cc.config)
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 1, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- INTERFACE Tx (IN) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
d.controlDescriptorCDCACM(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
d.controlDescriptorHID(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descHIDRequestGetReport:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
d.controlDescriptorHID(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- ENDPOINT Rx (OUT) ---
|
||||
case descRequestTypeRecipientEndpoint | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// CLEAR FEATURE (0x01):
|
||||
case descRequestStandardClearFeature:
|
||||
d.endpointClearFeature(uint8(sup.wIndex))
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
// SET FEATURE (0x03):
|
||||
case descRequestStandardSetFeature:
|
||||
d.endpointSetFeature(uint8(sup.wIndex))
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- ENDPOINT Tx (IN) ---
|
||||
case descRequestTypeRecipientEndpoint | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET STATUS (0x00):
|
||||
case descRequestStandardGetStatus:
|
||||
status := d.endpointStatus(uint8(sup.wIndex))
|
||||
d.controlReply[0] = uint8(status)
|
||||
d.controlReply[1] = uint8(status >> 8)
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
// === CLASS REQUEST ===
|
||||
case descRequestTypeTypeClass:
|
||||
|
||||
// Switch on the recepient and direction of the request
|
||||
switch sup.bmRequestType &
|
||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
||||
|
||||
// --- INTERFACE Rx (OUT) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// CDC | SET LINE CODING (0x20):
|
||||
case descCDCRequestSetLineCoding:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
// line coding must contain exactly 7 bytes
|
||||
if descCDCACMCodingSize == sup.wLength {
|
||||
d.setup = sup
|
||||
d.controlReceive(
|
||||
uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])),
|
||||
descCDCACMCodingSize, true)
|
||||
// CDC Line Coding packet receipt handling occurs in method
|
||||
// controlComplete().
|
||||
return dcdStageSetup
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// CDC | SET CONTROL LINE STATE (0x22):
|
||||
case descCDCRequestSetControlLineState:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch sup.wIndex {
|
||||
|
||||
// Control/status interface:
|
||||
case descCDCACMInterfaceCtrl:
|
||||
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
|
||||
d.uartSetLineState(0 != sup.wValue&0x01, 0 != sup.wValue&0x02)
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// CDC | SEND BREAK (0x23):
|
||||
case descCDCRequestSendBreak:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// HID | SET REPORT (0x09)
|
||||
case descHIDRequestSetReport:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
if sup.wLength <= descHIDCxCount {
|
||||
d.setup = sup
|
||||
descHID[d.cc.config-1].cx[0] = 0xE9
|
||||
d.controlReceive(
|
||||
uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])),
|
||||
uint32(sup.wLength), true)
|
||||
return dcdStageSetup
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// HID | SET IDLE (0x0A)
|
||||
case descHIDRequestSetIdle:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
idleRate := sup.wValue >> 8
|
||||
// TBD: do we need to handle this request? wIndex contains the target
|
||||
// interface of the request.
|
||||
_ = idleRate
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- INTERFACE Tx (IN) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// HID | GET REPORT (0x01)
|
||||
case descHIDRequestGetReport:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
reportType := uint8(sup.wValue >> 8)
|
||||
reportID := uint8(sup.wValue)
|
||||
// TBD: do we need to handle this request? wIndex contains the target
|
||||
// interface of the request.
|
||||
_, _ = reportType, reportID
|
||||
d.controlReply[0] = 0
|
||||
d.controlReply[1] = 0
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
case descRequestTypeTypeVendor:
|
||||
default:
|
||||
// Unhandled request type
|
||||
}
|
||||
|
||||
// All successful requests return early. If we reach this point, the request
|
||||
// was invalid or unhandled. Stall the endpoint.
|
||||
return dcdStageStall
|
||||
}
|
||||
|
||||
// controlComplete handles the setup completion of control endpoint 0.
|
||||
func (d *dcd) controlComplete(status uint32) {
|
||||
|
||||
// Reset endpoint 0 notify mask
|
||||
d.controlMask = 0
|
||||
|
||||
// First, switch on the type of request (standard, class, or vendor)
|
||||
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
|
||||
|
||||
// === CLASS REQUEST ===
|
||||
case descRequestTypeTypeClass:
|
||||
|
||||
// Switch on the recepient and direction of the request
|
||||
switch d.setup.bmRequestType &
|
||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
||||
|
||||
// --- INTERFACE Rx (OUT) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch d.setup.bRequest {
|
||||
|
||||
// CDC | SET LINE CODING (0x20):
|
||||
case descCDCRequestSetLineCoding:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch d.setup.wIndex {
|
||||
|
||||
// CDC-ACM Control Interface:
|
||||
case descCDCACMInterfaceCtrl:
|
||||
// Notify PHY to handle triggers like special baud rates, which
|
||||
// signal to reboot into bootloader or begin receiving OTA updates
|
||||
d.uartSetLineCoding(descCDCACMLineCoding{
|
||||
baud: packU32(acm.cx[:]),
|
||||
stopBits: acm.cx[4],
|
||||
parity: acm.cx[5],
|
||||
numBits: acm.cx[6],
|
||||
})
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// HID | SET REPORT (0x09)
|
||||
case descHIDRequestSetReport:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
hid := &descHID[d.cc.config-1]
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch d.setup.wIndex {
|
||||
|
||||
// HID Keyboard Interface
|
||||
case descHIDInterfaceKeyboard:
|
||||
|
||||
// Determine the type of descriptor being requested
|
||||
switch d.setup.wValue >> 8 {
|
||||
|
||||
// Configuration descriptor
|
||||
case descTypeConfigure:
|
||||
if 1 == d.setup.wLength {
|
||||
hid.keyboard.led = hid.cx[0]
|
||||
d.controlTransmit(uintptr(0), 0, false)
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled descriptor type
|
||||
}
|
||||
|
||||
// HID Serial Interface
|
||||
case descHIDInterfaceSerial:
|
||||
|
||||
// Determine the type of descriptor being requested
|
||||
switch d.setup.wValue >> 8 {
|
||||
|
||||
// String descriptor
|
||||
case descTypeString:
|
||||
if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) {
|
||||
d.enableSOF(true, descHIDInterfaceCount)
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled descriptor type
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled recepient or direction
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request type
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) {
|
||||
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
dxn := uint8(0)
|
||||
|
||||
// Determine the type of descriptor being requested
|
||||
switch sup.wValue >> 8 {
|
||||
|
||||
// Device descriptor
|
||||
case descTypeDevice:
|
||||
dxn = descLengthDevice
|
||||
_ = copy(acm.dx[:], acm.device[:dxn])
|
||||
|
||||
// Configuration descriptor
|
||||
case descTypeConfigure:
|
||||
dxn = uint8(descCDCACMConfigSize)
|
||||
_ = copy(acm.dx[:], acm.config[:dxn])
|
||||
|
||||
// String descriptor
|
||||
case descTypeString:
|
||||
if 0 == len(acm.locale) {
|
||||
break // No string descriptors defined!
|
||||
}
|
||||
var sd []uint8
|
||||
if 0 == uint8(sup.wValue) {
|
||||
|
||||
// setup.wIndex contains an arbitrary index referring to a collection of
|
||||
// strings in some given language. This case (setup.wValue = [0x03]00)
|
||||
// is a string request from the host to determine what that language is.
|
||||
//
|
||||
// In subsequent string requests, the host will populate setup.wIndex
|
||||
// with the language code we return here in this string descriptor.
|
||||
//
|
||||
// This way all strings returned to the host are in the same language,
|
||||
// whatever language that may be.
|
||||
code := int(sup.wIndex)
|
||||
if code >= len(acm.locale) {
|
||||
code = 0
|
||||
}
|
||||
sd = acm.locale[code].descriptor[sup.wValue&0xFF][:]
|
||||
|
||||
} else {
|
||||
|
||||
// setup.wIndex now contains a language code, which we specified in a
|
||||
// previous request (above: setup.wValue = [0x03]00). We need to locate
|
||||
// the set of strings whose language matches the language code given in
|
||||
// this new setup.wIndex.
|
||||
for code := range acm.locale {
|
||||
if sup.wIndex == acm.locale[code].language {
|
||||
// Found language, check if string descriptor at given index exists
|
||||
if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) {
|
||||
|
||||
// Found language with a string defined at the requested index.
|
||||
//
|
||||
// TODO: Add API methods to device controller that allows the user
|
||||
// to provide these strings at/before driver initialization.
|
||||
//
|
||||
// For now, we just always use the descCommon* strings.
|
||||
var s string
|
||||
switch uint8(sup.wValue) {
|
||||
case 1:
|
||||
s = descCommonManufacturer
|
||||
case 2:
|
||||
s = descCommonProduct + " CDC-ACM"
|
||||
case 3:
|
||||
s = descCommonSerialNumber
|
||||
}
|
||||
|
||||
// Construct a string descriptor dynamically to be transmitted on
|
||||
// the serial bus.
|
||||
sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:]
|
||||
// String descriptor format is 2-byte header + 2-bytes per rune
|
||||
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
|
||||
sd[1] = descTypeString // header[1] = descriptor type
|
||||
// Copy UTF-8 string into string descriptor as UTF-16
|
||||
for n, c := range s {
|
||||
if 2+2*n >= len(sd) {
|
||||
break
|
||||
}
|
||||
sd[2+2*n] = uint8(c)
|
||||
sd[3+2*n] = 0
|
||||
}
|
||||
break // end search for matching language code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy string descriptor into descriptor transmit buffer
|
||||
if nil != sd && len(sd) >= 0 {
|
||||
dxn = sd[0]
|
||||
_ = copy(acm.dx[:], sd[:dxn])
|
||||
}
|
||||
|
||||
// Device qualification descriptor
|
||||
case descTypeQualification:
|
||||
dxn = descLengthQualification
|
||||
_ = copy(acm.dx[:], acm.qualif[:dxn])
|
||||
|
||||
// Alternate configuration descriptor
|
||||
case descTypeOtherSpeedConfiguration:
|
||||
// TODO
|
||||
|
||||
default:
|
||||
// Unhandled descriptor type
|
||||
}
|
||||
|
||||
if dxn > 0 {
|
||||
if dxn > uint8(sup.wLength) {
|
||||
dxn = uint8(sup.wLength)
|
||||
}
|
||||
flushCache(
|
||||
uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn))
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dcd) controlDescriptorHID(sup dcdSetup) {
|
||||
|
||||
hid := &descHID[d.cc.config-1]
|
||||
dxn := uint8(0)
|
||||
pos := uint8(0)
|
||||
|
||||
// Determine the type of descriptor being requested
|
||||
switch sup.wValue >> 8 {
|
||||
|
||||
// Device descriptor
|
||||
case descTypeDevice:
|
||||
dxn = descLengthDevice
|
||||
_ = copy(hid.dx[:], hid.device[:dxn])
|
||||
|
||||
// Configuration descriptor
|
||||
case descTypeConfigure:
|
||||
dxn = uint8(descHIDConfigSize)
|
||||
_ = copy(hid.dx[:], hid.config[:dxn])
|
||||
|
||||
// String descriptor
|
||||
case descTypeString:
|
||||
if 0 == len(hid.locale) {
|
||||
break // No string descriptors defined!
|
||||
}
|
||||
var sd []uint8
|
||||
if 0 == uint8(sup.wValue) {
|
||||
|
||||
// setup.wIndex contains an arbitrary index referring to a collection of
|
||||
// strings in some given language. This case (setup.wValue = [0x03]00)
|
||||
// is a string request from the host to determine what that language is.
|
||||
//
|
||||
// In subsequent string requests, the host will populate setup.wIndex
|
||||
// with the language code we return here in this string descriptor.
|
||||
//
|
||||
// This way all strings returned to the host are in the same language,
|
||||
// whatever language that may be.
|
||||
code := int(sup.wIndex)
|
||||
if code >= len(hid.locale) {
|
||||
code = 0
|
||||
}
|
||||
sd = hid.locale[code].descriptor[sup.wValue&0xFF][:]
|
||||
|
||||
} else {
|
||||
|
||||
// setup.wIndex now contains a language code, which we specified in a
|
||||
// previous request (above: setup.wValue = [0x03]00). We need to locate
|
||||
// the set of strings whose language matches the language code given in
|
||||
// this new setup.wIndex.
|
||||
for code := range hid.locale {
|
||||
if sup.wIndex == hid.locale[code].language {
|
||||
// Found language, check if string descriptor at given index exists
|
||||
if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) {
|
||||
|
||||
// Found language with a string defined at the requested index.
|
||||
//
|
||||
// TODO: Add API methods to device controller that allows the user
|
||||
// to provide these strings at/before driver initialization.
|
||||
//
|
||||
// For now, we just always use the descCommon* strings.
|
||||
var s string
|
||||
switch uint8(sup.wValue) {
|
||||
case 1:
|
||||
s = descCommonManufacturer
|
||||
case 2:
|
||||
s = descCommonProduct + " HID"
|
||||
case 3:
|
||||
s = descCommonSerialNumber
|
||||
}
|
||||
|
||||
// Construct a string descriptor dynamically to be transmitted on
|
||||
// the serial bus.
|
||||
sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:]
|
||||
// String descriptor format is 2-byte header + 2-bytes per rune
|
||||
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
|
||||
sd[1] = descTypeString // header[1] = descriptor type
|
||||
// Copy UTF-8 string into string descriptor as UTF-16
|
||||
for n, c := range s {
|
||||
if 2+2*n >= len(sd) {
|
||||
break
|
||||
}
|
||||
sd[2+2*n] = uint8(c)
|
||||
sd[3+2*n] = 0
|
||||
}
|
||||
break // end search for matching language code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy string descriptor into descriptor transmit buffer
|
||||
if nil != sd && len(sd) >= 0 {
|
||||
dxn = sd[0]
|
||||
_ = copy(hid.dx[:], sd[:dxn])
|
||||
}
|
||||
|
||||
// Device qualification descriptor
|
||||
case descTypeQualification:
|
||||
dxn = descLengthQualification
|
||||
_ = copy(hid.dx[:], hid.qualif[:dxn])
|
||||
|
||||
// Alternate configuration descriptor
|
||||
case descTypeOtherSpeedConfiguration:
|
||||
// TODO
|
||||
|
||||
// HID descriptor
|
||||
case descTypeHID:
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch sup.wIndex {
|
||||
case descHIDInterfaceKeyboard:
|
||||
pos = descHIDConfigKeyboardPos
|
||||
|
||||
case descHIDInterfaceMouse:
|
||||
pos = descHIDConfigMousePos
|
||||
|
||||
case descHIDInterfaceSerial:
|
||||
pos = descHIDConfigSerialPos
|
||||
|
||||
case descHIDInterfaceJoystick:
|
||||
pos = descHIDConfigJoystickPos
|
||||
|
||||
case descHIDInterfaceMediaKey:
|
||||
pos = descHIDConfigMediaKeyPos
|
||||
|
||||
default:
|
||||
// Unhandled HID interface
|
||||
}
|
||||
|
||||
if 0 != pos {
|
||||
dxn = descLengthInterface
|
||||
_ = copy(hid.dx[:], hid.config[pos:pos+dxn])
|
||||
}
|
||||
|
||||
// HID report descriptor
|
||||
case descTypeHIDReport:
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch sup.wIndex {
|
||||
case descHIDInterfaceKeyboard:
|
||||
dxn = uint8(len(descHIDReportKeyboard))
|
||||
_ = copy(hid.dx[:], descHIDReportKeyboard[:])
|
||||
|
||||
case descHIDInterfaceMouse:
|
||||
dxn = uint8(len(descHIDReportMouse))
|
||||
_ = copy(hid.dx[:], descHIDReportMouse[:])
|
||||
|
||||
case descHIDInterfaceSerial:
|
||||
dxn = uint8(len(descHIDReportSerial))
|
||||
_ = copy(hid.dx[:], descHIDReportSerial[:])
|
||||
|
||||
case descHIDInterfaceJoystick:
|
||||
dxn = uint8(len(descHIDReportJoystick))
|
||||
_ = copy(hid.dx[:], descHIDReportJoystick[:])
|
||||
|
||||
case descHIDInterfaceMediaKey:
|
||||
dxn = uint8(len(descHIDReportMediaKey))
|
||||
_ = copy(hid.dx[:], descHIDReportMediaKey[:])
|
||||
|
||||
default:
|
||||
// Unhandled HID interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled descriptor type
|
||||
}
|
||||
|
||||
if dxn > 0 {
|
||||
if dxn > uint8(sup.wLength) {
|
||||
dxn = uint8(sup.wLength)
|
||||
}
|
||||
flushCache(
|
||||
uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn))
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,604 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// descCPUFrequencyHz defines the target CPU frequency (Hz).
|
||||
const descCPUFrequencyHz = 600000000
|
||||
|
||||
// descCDCACMCount defines the number of USB cores that may be configured as
|
||||
// CDC-ACM (single) devices.
|
||||
const descCDCACMCount = 0
|
||||
|
||||
// descHIDCount defines the number of USB cores that may be configured as a
|
||||
// composite (keyboard + mouse + joystick) human interface device (HID).
|
||||
const descHIDCount = 1
|
||||
|
||||
// General USB device identification constants.
|
||||
const (
|
||||
descCommonVendorID = 0x16C0
|
||||
descCommonProductID = 0x0483
|
||||
descCommonReleaseID = 0x0101 // BCD (1.1)
|
||||
|
||||
descCommonLanguage = descLanguageEnglish
|
||||
descCommonManufacturer = "TinyGo"
|
||||
descCommonProduct = "USB"
|
||||
descCommonSerialNumber = "00000"
|
||||
)
|
||||
|
||||
// Constants for USB CDC-ACM device classes.
|
||||
const (
|
||||
descCDCACMMaxPower = 50 // 100 mA
|
||||
|
||||
// CDC-ACM Control Buffers
|
||||
|
||||
descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1)
|
||||
descCDCACMCxCount = 8
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
descCDCACMRDCount = 2 * descCDCACMEndpointCount
|
||||
descCDCACMRxSize = descCDCACMDataRxPacketSize
|
||||
descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount
|
||||
|
||||
descCDCACMTDCount = descCDCACMEndpointCount
|
||||
descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize
|
||||
descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount
|
||||
|
||||
descCDCACMTxTimeoutMs = 120 // millisec
|
||||
descCDCACMTxSyncUs = 75 // microsec
|
||||
|
||||
// Default CDC-ACM Endpoint Configurations (High-Speed)
|
||||
|
||||
descCDCACMStatusInterval = descCDCACMStatusHSInterval // Status
|
||||
descCDCACMStatusPacketSize = descCDCACMStatusHSPacketSize //
|
||||
|
||||
descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // Data Rx
|
||||
|
||||
descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // Data Tx
|
||||
|
||||
// CDC-ACM Endpoint Configurations for Full-Speed Device
|
||||
|
||||
descCDCACMStatusFSInterval = 5 // Status
|
||||
descCDCACMStatusFSPacketSize = 16 // (full-speed)
|
||||
|
||||
descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed)
|
||||
|
||||
descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed)
|
||||
|
||||
// CDC-ACM Endpoint Configurations for High-Speed Device
|
||||
|
||||
descCDCACMStatusHSInterval = 5 // Status
|
||||
descCDCACMStatusHSPacketSize = 16 // (high-speed)
|
||||
|
||||
descCDCACMDataRxHSPacketSize = 512 // Data Rx (high-speed)
|
||||
|
||||
descCDCACMDataTxHSPacketSize = 512 // Data Tx (high-speed)
|
||||
)
|
||||
|
||||
// Constants for USB HID (keyboard, mouse, joystick) device classes.
|
||||
const (
|
||||
descHIDMaxPower = 50 // 100 mA
|
||||
|
||||
// HID Control Buffers
|
||||
|
||||
descHIDQHCount = 2 * (descHIDEndpointCount + 1)
|
||||
descHIDCxCount = 8
|
||||
|
||||
// HID Serial Buffers
|
||||
|
||||
descHIDSerialRDCount = 8
|
||||
descHIDSerialRxSize = descHIDSerialRxPacketSize
|
||||
descHIDSerialRxCount = descHIDSerialRxSize * descHIDSerialRDCount
|
||||
|
||||
descHIDSerialTDCount = 12
|
||||
descHIDSerialTxSize = descHIDSerialTxPacketSize
|
||||
descHIDSerialTxCount = descHIDSerialTxSize * descHIDSerialTDCount
|
||||
|
||||
descHIDSerialTxTimeoutMs = 50 // millisec
|
||||
descHIDSerialTxSyncUs = 75 // microsec
|
||||
|
||||
// HID Keyboard Buffers
|
||||
|
||||
descHIDKeyboardTDCount = 12
|
||||
descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize
|
||||
descHIDKeyboardTxCount = descHIDKeyboardTxSize * descHIDKeyboardTDCount
|
||||
|
||||
descHIDKeyboardTxTimeoutMs = 50 // millisec
|
||||
|
||||
// HID Mouse Buffers
|
||||
|
||||
descHIDMouseTDCount = 4
|
||||
descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize
|
||||
descHIDMouseTxCount = descHIDMouseTxSize * descHIDMouseTDCount
|
||||
|
||||
descHIDMouseTxTimeoutMs = 30 // millisec
|
||||
|
||||
// HID Joystick Buffers
|
||||
|
||||
descHIDJoystickTDCount = 4
|
||||
descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize
|
||||
descHIDJoystickTxCount = descHIDJoystickTxSize * descHIDJoystickTDCount
|
||||
|
||||
descHIDJoystickTxTimeoutMs = 30 // millisec
|
||||
|
||||
// Default HID Endpoint Configurations (High-Speed)
|
||||
|
||||
descHIDSerialRxInterval = descHIDSerialRxHSInterval // Serial Rx
|
||||
descHIDSerialRxPacketSize = descHIDSerialRxHSPacketSize //
|
||||
|
||||
descHIDSerialTxInterval = descHIDSerialTxHSInterval // Serial Tx
|
||||
descHIDSerialTxPacketSize = descHIDSerialTxHSPacketSize //
|
||||
|
||||
descHIDKeyboardTxInterval = descHIDKeyboardTxHSInterval // Keyboard
|
||||
descHIDKeyboardTxPacketSize = descHIDKeyboardTxHSPacketSize //
|
||||
|
||||
descHIDMediaKeyTxInterval = descHIDMediaKeyTxHSInterval // Keyboard Media Keys
|
||||
descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxHSPacketSize //
|
||||
|
||||
descHIDMouseTxInterval = descHIDMouseTxHSInterval // Mouse
|
||||
descHIDMouseTxPacketSize = descHIDMouseTxHSPacketSize //
|
||||
|
||||
descHIDJoystickTxInterval = descHIDJoystickTxHSInterval // Joystick
|
||||
descHIDJoystickTxPacketSize = descHIDJoystickTxHSPacketSize //
|
||||
|
||||
// HID Endpoint Configurations for Full-Speed Device
|
||||
|
||||
descHIDSerialRxFSInterval = 2 // Serial Rx
|
||||
descHIDSerialRxFSPacketSize = 8 // (full-speed)
|
||||
|
||||
descHIDSerialTxFSInterval = 1 // Serial Tx
|
||||
descHIDSerialTxFSPacketSize = 16 // (full-speed)
|
||||
|
||||
descHIDKeyboardTxFSInterval = 4 // Keyboard
|
||||
descHIDKeyboardTxFSPacketSize = 8 // (full-speed)
|
||||
|
||||
descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys
|
||||
descHIDMediaKeyTxFSPacketSize = 8 // (full-speed)
|
||||
|
||||
descHIDMouseTxFSInterval = 4 // Mouse
|
||||
descHIDMouseTxFSPacketSize = 8 // (full-speed)
|
||||
|
||||
descHIDJoystickTxFSInterval = 4 // Joystick
|
||||
descHIDJoystickTxFSPacketSize = 12 // (full-speed)
|
||||
|
||||
// HID Endpoint Configurations for High-Speed Device
|
||||
|
||||
descHIDSerialRxHSInterval = 2 // Serial
|
||||
descHIDSerialRxHSPacketSize = 32 // (high-speed)
|
||||
|
||||
descHIDSerialTxHSInterval = 1 // Serial Tx
|
||||
descHIDSerialTxHSPacketSize = 64 // (high-speed)
|
||||
|
||||
descHIDKeyboardTxHSInterval = 1 // Keyboard
|
||||
descHIDKeyboardTxHSPacketSize = 8 // (high-speed)
|
||||
|
||||
descHIDMediaKeyTxHSInterval = 4 // Keyboard Media Keys
|
||||
descHIDMediaKeyTxHSPacketSize = 8 // (high-speed)
|
||||
|
||||
descHIDMouseTxHSInterval = 1 // Mouse
|
||||
descHIDMouseTxHSPacketSize = 8 // (high-speed)
|
||||
|
||||
descHIDJoystickTxHSInterval = 2 // Joystick
|
||||
descHIDJoystickTxHSPacketSize = 12 // (high-speed)
|
||||
)
|
||||
|
||||
// descCDCACM0QH is an array of endpoint queue heads, which is where all
|
||||
// transfers for a given endpoint are managed, for the default CDC-ACM (single)
|
||||
// device class configuration (index 1).
|
||||
//
|
||||
// From the iMXRT1062 Reference Manual:
|
||||
//
|
||||
// Software must ensure that no interface data structure reachable
|
||||
// by the Device Controller spans a 4K-page boundary.
|
||||
//
|
||||
// The [queue head] is a 48-byte data structure, but must be aligned on
|
||||
// 64-byte boundaries.
|
||||
//
|
||||
// Endpoint queue heads are arranged in an array in a continuous area of
|
||||
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
|
||||
// device queue heads in the list support receive endpoints (OUT/SETUP) and
|
||||
// the odd-numbered queue heads in the list are used for transmit endpoints
|
||||
// (IN/INTERRUPT). The device controller will index into this array based upon
|
||||
// the endpoint number received from the USB bus. All information necessary to
|
||||
// respond to transactions for all primed transfers is contained in this list
|
||||
// so the Device Controller can readily respond to incoming requests without
|
||||
// having to traverse a linked list.
|
||||
//go:align 4096
|
||||
var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint
|
||||
|
||||
// descCDCACM0CD is the transfer descriptor for messages transmitted or received
|
||||
// on the status/control endpoint 0 for the default CDC-ACM (single) device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0CD dhwTransfer
|
||||
|
||||
// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of
|
||||
// the default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0Cx [descCDCACMCxCount]uint8
|
||||
|
||||
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
|
||||
// transmitted or received on the status/control endpoint 0 for the default
|
||||
// CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0AD dhwTransfer
|
||||
|
||||
// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0
|
||||
// for the default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0Dx [descCDCACMConfigSize]uint8
|
||||
|
||||
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
|
||||
// which describe to the device controller the location and quantity of data
|
||||
// being received for a given transfer, for the default CDC-ACM (single) device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0RD [descCDCACMRDCount]dhwTransfer
|
||||
|
||||
// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0Rx [descCDCACMRxCount]uint8
|
||||
|
||||
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
|
||||
// which describe to the device controller the location and quantity of data
|
||||
// being transmitted for a given transfer, for the default CDC-ACM (single)
|
||||
// device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0TD [descCDCACMTDCount]dhwTransfer
|
||||
|
||||
// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0Tx [descCDCACMTxCount]uint8
|
||||
|
||||
var descCDCACM0RDNum [descCDCACMRDCount]uint16
|
||||
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
|
||||
var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16
|
||||
|
||||
// descCDCACMClassData holds the buffers and control states for all CDC-ACM
|
||||
// (single) device class configurations, ordered by index (offset by -1), for
|
||||
// iMXRT1062 targets only.
|
||||
//
|
||||
// Instances of this type (elements of descCDCACMData) are embedded in elements
|
||||
// of the common/target-agnostic CDC-ACM class configurations (descCDCACM).
|
||||
// Methods defined on this type implement target-specific functionality, and
|
||||
// some of these methods are required by the common device controller driver.
|
||||
// Thus, this type functions as a hardware abstraction layer (HAL).
|
||||
type descCDCACMClassData struct {
|
||||
|
||||
// CDC-ACM Control Buffers
|
||||
|
||||
qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads
|
||||
|
||||
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
|
||||
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
||||
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
||||
dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors
|
||||
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
||||
td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors
|
||||
tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer
|
||||
|
||||
rxCount *[descCDCACMRDCount]uint16
|
||||
rxIndex *[descCDCACMRDCount]uint16
|
||||
rxQueue *[(descCDCACMRDCount + 1)]uint16
|
||||
|
||||
sxSize uint16
|
||||
rxSize uint16
|
||||
txSize uint16
|
||||
|
||||
txHead uint8
|
||||
txFree uint16
|
||||
txPrev bool
|
||||
|
||||
rxHead uint8
|
||||
rxTail uint8
|
||||
rxFree uint16
|
||||
}
|
||||
|
||||
// descCDCACMData holds statically-allocated instances for each of the target-
|
||||
// specific (iMXRT1062) CDC-ACM (single) device class configurations' control
|
||||
// and data structures, ordered by configuration index (offset by -1). Each
|
||||
// element is embedded in a corresponding element of descCDCACM.
|
||||
//go:align 64
|
||||
var descCDCACMData = [dcdCount]descCDCACMClassData{
|
||||
|
||||
{ // -- CDC-ACM (single) Class Configuration Index 1 --
|
||||
|
||||
// CDC-ACM Control Buffers
|
||||
|
||||
qh: &descCDCACM0QH,
|
||||
|
||||
cd: &descCDCACM0CD,
|
||||
cx: &descCDCACM0Cx,
|
||||
ad: &descCDCACM0AD,
|
||||
dx: &descCDCACM0Dx,
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
rd: &descCDCACM0RD,
|
||||
rx: &descCDCACM0Rx,
|
||||
td: &descCDCACM0TD,
|
||||
tx: &descCDCACM0Tx,
|
||||
|
||||
rxCount: &descCDCACM0RDNum,
|
||||
rxIndex: &descCDCACM0RDIdx,
|
||||
rxQueue: &descCDCACM0RDQue,
|
||||
|
||||
sxSize: descCDCACMStatusPacketSize,
|
||||
rxSize: descCDCACMDataRxPacketSize,
|
||||
txSize: descCDCACMDataTxPacketSize,
|
||||
},
|
||||
}
|
||||
|
||||
// descHID0QH is an array of endpoint queue heads, which is where all transfers
|
||||
// for a given endpoint are managed, for the default HID device class
|
||||
// configuration (index 1).
|
||||
//
|
||||
// From the iMXRT1062 Reference Manual:
|
||||
//
|
||||
// Software must ensure that no interface data structure reachable
|
||||
// by the Device Controller spans a 4K-page boundary.
|
||||
//
|
||||
// The [queue head] is a 48-byte data structure, but must be aligned on
|
||||
// 64-byte boundaries.
|
||||
//
|
||||
// Endpoint queue heads are arranged in an array in a continuous area of
|
||||
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
|
||||
// device queue heads in the list support receive endpoints (OUT/SETUP) and
|
||||
// the odd-numbered queue heads in the list are used for transmit endpoints
|
||||
// (IN/INTERRUPT). The device controller will index into this array based upon
|
||||
// the endpoint number received from the USB bus. All information necessary to
|
||||
// respond to transactions for all primed transfers is contained in this list
|
||||
// so the Device Controller can readily respond to incoming requests without
|
||||
// having to traverse a linked list.
|
||||
//go:align 4096
|
||||
var descHID0QH [descHIDQHCount]dhwEndpoint
|
||||
|
||||
// descHID0CD is the transfer descriptor for messages transmitted or received on
|
||||
// the status/control endpoint 0 for the default HID device class configuration
|
||||
// (index 1).
|
||||
//go:align 32
|
||||
var descHID0CD dhwTransfer
|
||||
|
||||
// descHID0Cx is the buffer for control/status data received on endpoint 0 of
|
||||
// the default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0Cx [descHIDCxCount]uint8
|
||||
|
||||
// descHID0AD is the transfer descriptor for ackowledgement (ACK) messages
|
||||
// transmitted or received on the status/control endpoint 0 for the default HID
|
||||
// device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0AD dhwTransfer
|
||||
|
||||
// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for
|
||||
// the default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0Dx [descHIDConfigSize]uint8
|
||||
|
||||
// descHID0SerialRD is an array of transfer descriptors for serial Rx (OUT)
|
||||
// transfers, which describe to the device controller the location and quantity
|
||||
// of data being received for a given transfer, for the default HID device class
|
||||
// configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0SerialRD [descHIDSerialRDCount]dhwTransfer
|
||||
|
||||
// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default
|
||||
// HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0SerialRx [descHIDSerialRxCount]uint8
|
||||
|
||||
// descHID0SerialTD is an array of transfer descriptors for serial Tx (IN)
|
||||
// transfers, which describe to the device controller the location and quantity
|
||||
// of data being transmitted for a given transfer, for the default HID device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0SerialTD [descHIDSerialTDCount]dhwTransfer
|
||||
|
||||
// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default
|
||||
// HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0SerialTx [descHIDSerialTxCount]uint8
|
||||
|
||||
var descHID0SerialRDIdx [descHIDSerialRDCount]uint16
|
||||
var descHID0SerialRDQue [(descHIDSerialRDCount + 1)]uint16
|
||||
|
||||
// descHID0KeyboardTD is an array of transfer descriptors for keyboard Tx (IN)
|
||||
// transfers, which describe to the device controller the location and quantity
|
||||
// of data being transmitted for a given transfer, for the default HID device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0KeyboardTD [descHIDKeyboardTDCount]dhwTransfer
|
||||
|
||||
// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the
|
||||
// default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0KeyboardTx [descHIDKeyboardTxCount]uint8
|
||||
|
||||
// descHID0KeyboardTp is the keyboard HID report transmit (Tx) transfer buffer
|
||||
// for the default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0KeyboardTp [descHIDKeyboardTxPacketSize]uint8
|
||||
|
||||
//go:align 32
|
||||
var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8
|
||||
|
||||
//go:align 32
|
||||
var descHID0KeyboardTxCon [hidKeyboardConCount]uint16
|
||||
|
||||
//go:align 32
|
||||
var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8
|
||||
|
||||
// descHID0MouseTD is an array of transfer descriptors for mouse Tx (IN)
|
||||
// transfers, which describe to the device controller the location and quantity
|
||||
// of data being transmitted for a given transfer, for the default HID device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0MouseTD [descHIDMouseTDCount]dhwTransfer
|
||||
|
||||
// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default
|
||||
// HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0MouseTx [descHIDMouseTxCount]uint8
|
||||
|
||||
// descHID0JoystickTD is an array of transfer descriptors for joystick Tx (IN)
|
||||
// transfers, which describe to the device controller the location and quantity
|
||||
// of data being transmitted for a given transfer, for the default HID device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0JoystickTD [descHIDJoystickTDCount]dhwTransfer
|
||||
|
||||
// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the
|
||||
// default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0JoystickTx [descHIDJoystickTxCount]uint8
|
||||
|
||||
// descHID0Keyboard is the Keyboard instance with which the user may interact
|
||||
// when using the default HID device class configuration (index 1).
|
||||
//go:align 64
|
||||
var descHID0Keyboard = Keyboard{
|
||||
key: &descHID0KeyboardTxKey,
|
||||
con: &descHID0KeyboardTxCon,
|
||||
sys: &descHID0KeyboardTxSys,
|
||||
}
|
||||
|
||||
// descHIDClassData holds the buffers and control states for all of the HID
|
||||
// device class configurations, ordered by index (offset by -1), for iMXRT1062
|
||||
// targets only.
|
||||
//
|
||||
// Instances of this type (elements of descHIDData) are embedded in elements
|
||||
// of the common/target-agnostic HID class configurations (descHID).
|
||||
// Methods defined on this type implement target-specific functionality, and
|
||||
// some of these methods are required by the common device controller driver.
|
||||
// Thus, this type functions as a hardware abstraction layer (HAL).
|
||||
type descHIDClassData struct {
|
||||
|
||||
// HID Control Buffers
|
||||
|
||||
qh *[descHIDQHCount]dhwEndpoint // endpoint queue heads
|
||||
|
||||
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
|
||||
cx *[descHIDCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
||||
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
||||
dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
|
||||
|
||||
// HID Serial Buffers
|
||||
|
||||
rdSerial *[descHIDSerialRDCount]dhwTransfer // interrupt endpoint serial Rx (OUT) transfer descriptors
|
||||
rxSerial *[descHIDSerialRxCount]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer
|
||||
tdSerial *[descHIDSerialTDCount]dhwTransfer // interrupt endpoint serial Tx (IN) transfer descriptors
|
||||
txSerial *[descHIDSerialTxCount]uint8 // interrupt endpoint serial Tx (IN) transfer buffer
|
||||
|
||||
rxSerialIndex *[descHIDSerialRDCount]uint16
|
||||
rxSerialQueue *[(descHIDSerialRDCount + 1)]uint16
|
||||
|
||||
rxSerialSize uint16
|
||||
txSerialSize uint16
|
||||
|
||||
txSerialHead uint8
|
||||
txSerialFree uint16
|
||||
txSerialPrev bool
|
||||
|
||||
rxSerialHead uint8
|
||||
rxSerialTail uint8
|
||||
rxSerialFree uint16
|
||||
|
||||
// HID Keyboard Buffers
|
||||
|
||||
tdKeyboard *[descHIDKeyboardTDCount]dhwTransfer // interrupt endpoint keyboard Tx (IN) transfer descriptors
|
||||
txKeyboard *[descHIDKeyboardTxCount]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer
|
||||
tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer
|
||||
|
||||
txKeyboardSize uint16
|
||||
|
||||
txKeyboardHead uint8
|
||||
txKeyboardPrev bool
|
||||
|
||||
// HID Mouse Buffers
|
||||
|
||||
tdMouse *[descHIDMouseTDCount]dhwTransfer // interrupt endpoint mouse Tx (IN) transfer descriptors
|
||||
txMouse *[descHIDMouseTxCount]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer
|
||||
|
||||
txMouseSize uint16
|
||||
|
||||
txMouseHead uint8
|
||||
txMousePrev bool
|
||||
|
||||
// HID Joystick Buffers
|
||||
|
||||
tdJoystick *[descHIDJoystickTDCount]dhwTransfer // interrupt endpoint joystick Tx (IN) transfer descriptors
|
||||
txJoystick *[descHIDJoystickTxCount]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer
|
||||
|
||||
txJoystickSize uint16
|
||||
|
||||
txJoystickHead uint8
|
||||
txJoystickPrev bool
|
||||
|
||||
// HID Device Instances
|
||||
|
||||
keyboard *Keyboard
|
||||
}
|
||||
|
||||
// descHIDData holds statically-allocated instances for each of the target-
|
||||
// specific (iMXRT1062) HID device class configurations' control and data
|
||||
// structures, ordered by configuration index (offset by -1). Each element is
|
||||
// embedded in a corresponding element of descHID.
|
||||
//go:align 64
|
||||
var descHIDData = [dcdCount]descHIDClassData{
|
||||
|
||||
{ // -- HID Class Configuration Index 1 --
|
||||
|
||||
// HID Control Buffers
|
||||
|
||||
qh: &descHID0QH,
|
||||
|
||||
cd: &descHID0CD,
|
||||
cx: &descHID0Cx,
|
||||
ad: &descHID0AD,
|
||||
dx: &descHID0Dx,
|
||||
|
||||
// HID Serial Buffers
|
||||
|
||||
rdSerial: &descHID0SerialRD,
|
||||
rxSerial: &descHID0SerialRx,
|
||||
tdSerial: &descHID0SerialTD,
|
||||
txSerial: &descHID0SerialTx,
|
||||
|
||||
rxSerialIndex: &descHID0SerialRDIdx,
|
||||
rxSerialQueue: &descHID0SerialRDQue,
|
||||
|
||||
rxSerialSize: descHIDSerialRxPacketSize,
|
||||
txSerialSize: descHIDSerialTxPacketSize,
|
||||
|
||||
// HID Keyboard Buffers
|
||||
|
||||
tdKeyboard: &descHID0KeyboardTD,
|
||||
txKeyboard: &descHID0KeyboardTx,
|
||||
tpKeyboard: &descHID0KeyboardTp,
|
||||
|
||||
txKeyboardSize: descHIDKeyboardTxPacketSize,
|
||||
|
||||
// HID Mouse Buffers
|
||||
|
||||
tdMouse: &descHID0MouseTD,
|
||||
txMouse: &descHID0MouseTx,
|
||||
|
||||
txMouseSize: descHIDMouseTxPacketSize,
|
||||
|
||||
// HID Joystick Buffers
|
||||
|
||||
tdJoystick: &descHID0JoystickTD,
|
||||
txJoystick: &descHID0JoystickTx,
|
||||
|
||||
txJoystickSize: descHIDJoystickTxPacketSize,
|
||||
|
||||
// HID Device Instances
|
||||
|
||||
keyboard: &descHID0Keyboard,
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
package usb
|
||||
|
||||
// Implementation of 32-bit target-agnostic USB host controller driver (hcd).
|
||||
|
||||
// hcdCount defines the number of USB cores to configure for host mode. It is
|
||||
// computed as the sum of all declared host configuration descriptors.
|
||||
const hcdCount = 0 // + ...
|
||||
|
||||
// hcdInstance provides statically-allocated instances of each USB host
|
||||
// controller configured on this platform.
|
||||
var hcdInstance [hcdCount]hcd
|
||||
|
||||
// hhwInstance provides statically-allocated instances of each USB hardware
|
||||
// abstraction for ports configured as host on this platform.
|
||||
var hhwInstance [hcdCount]hhw
|
||||
|
||||
// hcd implements USB host controller driver (hcd) interface.
|
||||
type hcd struct {
|
||||
*hhw // USB hardware abstraction layer
|
||||
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
cc class // USB host class
|
||||
id int // USB host controller index
|
||||
}
|
||||
|
||||
// initHCD initializes and assigns a free host controller instance to the given
|
||||
// USB port. Returns the initialized host controller or nil if no free host
|
||||
// controller instances remain.
|
||||
func initHCD(port int, class class) (*hcd, status) {
|
||||
if 0 == hcdCount {
|
||||
return nil, statusInvalid // Must have defined host controllers
|
||||
}
|
||||
switch class.id {
|
||||
default:
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range hcdInstance {
|
||||
if nil == hcdInstance[i].core {
|
||||
// Initialize host controller.
|
||||
hcdInstance[i].hhw = allocHHW(port, i, &hcdInstance[i])
|
||||
hcdInstance[i].core = &coreInstance[port]
|
||||
hcdInstance[i].port = port
|
||||
hcdInstance[i].cc = class
|
||||
hcdInstance[i].id = i
|
||||
return &hcdInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free host controller instances available.
|
||||
}
|
||||
|
||||
// class returns the receiver's current host class configuration.
|
||||
func (h *hcd) class() class { return h.cc }
|
||||
@@ -0,0 +1,59 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// hcdInterruptPriority defines the priority for all USB host interrupts.
|
||||
const hcdInterruptPriority = 3
|
||||
|
||||
// hcd implements USB host controller driver (hcd) interface.
|
||||
type hhw struct {
|
||||
*hcd // USB host controller driver
|
||||
|
||||
bus *nxp.USB_Type // USB core register
|
||||
phy *nxp.USBPHY_Type // USB PHY register
|
||||
irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062
|
||||
}
|
||||
|
||||
// allocHHW returns a reference to the USB hardware abstraction for the given
|
||||
// host controller driver. Should be called only one time and during host
|
||||
// controller initialization.
|
||||
func allocHHW(port, instance int, hc *hcd) *hhw {
|
||||
switch port {
|
||||
case 0:
|
||||
hhwInstance[instance].hcd = hc
|
||||
hhwInstance[instance].bus = nxp.USB1
|
||||
hhwInstance[instance].phy = nxp.USBPHY1
|
||||
|
||||
case 1:
|
||||
hhwInstance[instance].hcd = hc
|
||||
hhwInstance[instance].bus = nxp.USB2
|
||||
hhwInstance[instance].phy = nxp.USBPHY2
|
||||
}
|
||||
|
||||
return &hhwInstance[instance]
|
||||
}
|
||||
|
||||
// init configures the USB port for host mode operation by initializing all
|
||||
// endpoint and transfer descriptor data structures, initializing core registers
|
||||
// and interrupts, resetting the USB PHY, and enabling power on the bust.
|
||||
func (h *hhw) init() status {
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
// enable causes the USB core to enter (or exit) the normal run state and
|
||||
// enables/disables all interrupts on the receiver's USB port.
|
||||
func (h *hhw) enable(enable bool) {
|
||||
if enable {
|
||||
h.irq.Enable() // Enable USB interrupts
|
||||
} else {
|
||||
h.irq.Disable() // Disable USB interrupts
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package usb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHIDInvalidPort = errors.New("invalid USB port")
|
||||
ErrHIDInvalidCore = errors.New("invalid USB core")
|
||||
ErrHIDReportTransfer = errors.New("failed to transfer HID report")
|
||||
)
|
||||
|
||||
type HID struct {
|
||||
Port int
|
||||
core *core
|
||||
}
|
||||
|
||||
type HIDConfig struct {
|
||||
// Port is the MCU's native USB core number. If in doubt, leave it
|
||||
// uninitialized for default (0).
|
||||
Port int
|
||||
}
|
||||
|
||||
func (hid *HID) Configure(config HIDConfig) error {
|
||||
|
||||
if config.Port >= CoreCount || config.Port >= dcdCount {
|
||||
return ErrHIDInvalidPort
|
||||
}
|
||||
hid.Port = config.Port
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
var st status
|
||||
hid.core, st = initCore(hid.Port, class{id: classDeviceHID, config: 1})
|
||||
if !st.ok() {
|
||||
return ErrHIDInvalidPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hid *HID) Keyboard() *Keyboard {
|
||||
return hid.core.dc.keyboard()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
package usb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUARTInvalidPort = errors.New("invalid USB port")
|
||||
ErrUARTInvalidCore = errors.New("invalid USB core")
|
||||
ErrUARTEmptyBuffer = errors.New("USB receive buffer empty")
|
||||
ErrUARTWriteFailed = errors.New("USB write failure")
|
||||
)
|
||||
|
||||
// UART represents a virtual serial (UART) device emulation using the USB
|
||||
// CDC-ACM device class driver.
|
||||
type UART struct {
|
||||
Port int
|
||||
core *core
|
||||
}
|
||||
|
||||
type UARTConfig struct {
|
||||
// Port is the MCU's native USB core number. If in doubt, leave it
|
||||
// uninitialized for default (0).
|
||||
Port int
|
||||
}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
|
||||
if config.Port >= CoreCount || config.Port >= dcdCount {
|
||||
return ErrUARTInvalidPort
|
||||
}
|
||||
uart.Port = config.Port
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
var st status
|
||||
uart.core, st = initCore(uart.Port, class{id: classDeviceCDCACM, config: 1})
|
||||
if !st.ok() {
|
||||
return ErrUARTInvalidPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (uart UART) Buffered() int {
|
||||
return uart.core.dc.uartAvailable()
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (uart UART) ReadByte() (byte, error) {
|
||||
n, ok := uart.core.dc.uartReadByte()
|
||||
if !ok {
|
||||
return 0, ErrUARTEmptyBuffer
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (uart UART) Read(data []byte) (n int, err error) {
|
||||
return uart.core.dc.uartRead(data), nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte of data to the UART interface.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
if !uart.core.dc.uartWriteByte(c) {
|
||||
return ErrUARTWriteFailed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write data to the UART.
|
||||
func (uart UART) Write(data []byte) (n int, err error) {
|
||||
return uart.core.dc.uartWrite(data), nil
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package usb
|
||||
|
||||
// Hardware abstraction for USB ports configured as either host or device.
|
||||
|
||||
// CoreCount defines the total number of USB cores to configure in device or
|
||||
// host mode.
|
||||
const CoreCount = dcdCount + hcdCount
|
||||
|
||||
// coreInstance provides statically-allocated instances of each USB core
|
||||
// configured on this platform.
|
||||
var coreInstance [CoreCount]core
|
||||
|
||||
// core represents the core of a USB port configured as either host or device.
|
||||
type core struct {
|
||||
port int
|
||||
mode int
|
||||
dc *dcd
|
||||
hc *hcd
|
||||
}
|
||||
|
||||
// Constant definitions for USB core operating modes.
|
||||
const (
|
||||
modeIdle = 0 // USB port has not been configured
|
||||
modeDevice = 1
|
||||
modeHost = 2
|
||||
)
|
||||
|
||||
// initCore initializes a free USB core with given operating mode on the USB
|
||||
// port at given index, if available. Returns a reference to the initialized
|
||||
// core or nil if the core is unavailable.
|
||||
func initCore(port int, class class) (*core, status) {
|
||||
|
||||
iv := disableInterrupts()
|
||||
defer enableInterrupts(iv)
|
||||
|
||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
||||
return nil, statusInvalid
|
||||
}
|
||||
|
||||
if modeIdle != coreInstance[port].mode {
|
||||
// Check if requested port is already configured as requested class. If so,
|
||||
// just return a reference to the existing core instead of an error.
|
||||
//
|
||||
// This will allow, for instance, TinyGo examples that try to reconfigure
|
||||
// the USB (CDC-ACM) UART port (which is already configured by the runtime)
|
||||
// to continue without error.
|
||||
if coreInstance[port].mode == class.mode() {
|
||||
switch class.mode() {
|
||||
case modeDevice:
|
||||
if coreInstance[port].dc.class().equals(class) {
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
case modeHost:
|
||||
if coreInstance[port].hc.class().equals(class) {
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, statusBusy
|
||||
}
|
||||
|
||||
switch class.mode() {
|
||||
case modeDevice:
|
||||
// Allocate a free device controller and install interrupts
|
||||
dc, st := initDCD(port, class)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = dc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = modeDevice
|
||||
coreInstance[port].dc = dc
|
||||
dc.enable(true) // Enable interrupts and enter runtime
|
||||
|
||||
case modeHost:
|
||||
// Allocate a free host controller and install interrupts
|
||||
hc, st := initHCD(port, class)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = hc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = modeHost
|
||||
coreInstance[port].hc = hc
|
||||
hc.enable(true) // Enable interrupts and enter runtime
|
||||
|
||||
default:
|
||||
return nil, statusInvalid
|
||||
}
|
||||
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
|
||||
// class represents the type of a host/device and its class configuration index.
|
||||
// The first valid configuration index is 1. Index 0 is reserved and invalid.
|
||||
type class struct {
|
||||
id int
|
||||
config int
|
||||
}
|
||||
|
||||
// Enumerated constants for all host/device class configurations.
|
||||
const (
|
||||
classDeviceCDCACM = 0
|
||||
classDeviceHID = 1
|
||||
)
|
||||
|
||||
// mode returns the USB core operating mode of the receiver class c.
|
||||
//go:inline
|
||||
func (c class) mode() int {
|
||||
switch c.id {
|
||||
case classDeviceCDCACM, classDeviceHID:
|
||||
return modeDevice
|
||||
default:
|
||||
return modeIdle
|
||||
}
|
||||
}
|
||||
|
||||
// equals returns true if and only if all fields of the given class are equal to
|
||||
// those of the receiver c.
|
||||
//go:inline
|
||||
func (c class) equals(class class) bool {
|
||||
return c.id == class.id && c.config == class.config
|
||||
}
|
||||
|
||||
// status represents the return code of a subroutine.
|
||||
type status uint8
|
||||
|
||||
// Constant definitions for all status codes used within the package.
|
||||
const (
|
||||
statusOK status = iota // Success
|
||||
statusBusy // Busy
|
||||
statusInvalid // Invalid argument
|
||||
)
|
||||
|
||||
// ok returns true if and only if the receiver st equals statusOK.
|
||||
//go:inline
|
||||
func (s status) ok() bool { return statusOK == s }
|
||||
@@ -0,0 +1,271 @@
|
||||
package usb
|
||||
|
||||
//go:linkname ticks runtime.ticks
|
||||
func ticks() int64
|
||||
|
||||
// leU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 7 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU64(u uint64) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
||||
uint8(u >> 32), uint8(u >> 40), uint8(u >> 48), uint8(u >> 56),
|
||||
}
|
||||
}
|
||||
|
||||
// leU32 returns a slice containing 4 bytes from the given uint32 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 3 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU32(u uint32) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
||||
}
|
||||
}
|
||||
|
||||
// leU16 returns a slice containing 2 bytes from the given uint16 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 1 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU16(u uint16) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8),
|
||||
}
|
||||
}
|
||||
|
||||
// beU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 7 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU64(u uint64) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 56), uint8(u >> 48), uint8(u >> 40), uint8(u >> 32),
|
||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// beU32 returns a slice containing 4 bytes from the given uint32 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 3 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU32(u uint32) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// beU16 returns a slice containing 2 bytes from the given uint16 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 1 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU16(u uint16) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// revU64 returns the given uint64 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU64(u uint64) uint64 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x00000000000000FF) << 56) |
|
||||
((u & 0x000000000000FF00) << 40) |
|
||||
((u & 0x0000000000FF0000) << 24) |
|
||||
((u & 0x00000000FF000000) << 8) |
|
||||
((u & 0x000000FF00000000) >> 8) |
|
||||
((u & 0x0000FF0000000000) >> 24) |
|
||||
((u & 0x00FF000000000000) >> 40) |
|
||||
((u & 0xFF00000000000000) >> 56)
|
||||
}
|
||||
|
||||
// revU32 returns the given uint32 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU32(u uint32) uint32 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) |
|
||||
((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24)
|
||||
}
|
||||
|
||||
// revU16 returns the given uint16 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU16(u uint16) uint16 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8)
|
||||
}
|
||||
|
||||
// packU64 returns a uint64 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 7, if given. If fewer
|
||||
// than 8 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU64(b []uint8) (u uint64) {
|
||||
for i := 0; i < 8 && i < len(b); i++ {
|
||||
u |= uint64(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// packU32 returns a uint32 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 3, if given. If fewer
|
||||
// than 4 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU32(b []uint8) (u uint32) {
|
||||
for i := 0; i < 4 && i < len(b); i++ {
|
||||
u |= uint32(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// packU16 returns a uint16 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 1, if given. If fewer
|
||||
// than 2 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU16(b []uint8) (u uint16) {
|
||||
for i := 0; i < 2 && i < len(b); i++ {
|
||||
u |= uint16(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// msU8 returns the most-significant byte of u.
|
||||
//go:inline
|
||||
func msU8(u uint16) uint8 { return uint8(u >> 8) }
|
||||
|
||||
// lsU8 returns the least-significant byte of u.
|
||||
//go:inline
|
||||
func lsU8(u uint16) uint8 { return uint8(u) }
|
||||
|
||||
// cycles converts the given number of microseconds to CPU cycles for a CPU with
|
||||
// given frequency.
|
||||
//go:inline
|
||||
func cycles(microsec, cpuFreqHz uint32) uint32 {
|
||||
return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func unpackEndpoint(address uint8) (number, direction uint8) {
|
||||
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos,
|
||||
(address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func rxEndpoint(number uint8) uint8 {
|
||||
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func txEndpoint(number uint8) uint8 {
|
||||
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionIn
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func endpointIndex(address uint8) uint8 {
|
||||
return ((address & descEndptAddrNumberMsk) << 1) |
|
||||
((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos)
|
||||
}
|
||||
|
||||
// The following buffLo and buffHi are helper methods for slice definitions from
|
||||
// potentially zero-length arrays (depending on compile-time constants).
|
||||
//
|
||||
// For example, if we have an array containing a 5-element buffer for three
|
||||
// instances of some device class (15 total elements), partitioned as follows,
|
||||
// then we compute the indices for instance 2 as usual:
|
||||
//
|
||||
// Index: 01234 56789 ABCDE
|
||||
// Array: [ 1 | 2 | 3 ]
|
||||
//
|
||||
// Lo: (n-1) * size => (2-1) * 5 => 5
|
||||
// Hi: (n) * size => (2) * 5 => 10 (0xA)
|
||||
//
|
||||
// However, if we have specified (via const definition) that 0 instances of some
|
||||
// device class be allocated, then the associated device class buffer arrays
|
||||
// will all be zero-length arrays, and the arithmetic to compute the slice
|
||||
// indices used above will result in out-of-bounds indices:
|
||||
//
|
||||
// Index:
|
||||
// Array: []
|
||||
//
|
||||
// Lo: (n-1) * size => (2-1) * 5 => 5 [Error!]
|
||||
// Hi: (n) * size => (2) * 5 => 10 (0xA) [Error!]
|
||||
//
|
||||
//
|
||||
// I couldn't figure out a straight-forward way to resolve these slice indices
|
||||
// using only arithmetic, so I've resorted to simple conditionals. If the number
|
||||
// of instances for some given class is zero (count=0), defined via compile-time
|
||||
// constant, then just use the empty slice range [0:0].
|
||||
|
||||
// buffLo returns the starting array slice index for the n'th region of size
|
||||
// elements from an array containing count regions of size elements.
|
||||
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
|
||||
// argument equals 0.
|
||||
func buffLo(n, count, size uint16) uint16 {
|
||||
if 0 == n || 0 == count || 0 == size {
|
||||
return 0
|
||||
}
|
||||
return (n - 1) * size
|
||||
}
|
||||
|
||||
// buffHi returns the ending array slice index for the n'th region of size
|
||||
// elements from an array containing count regions of size elements.
|
||||
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
|
||||
// argument equals 0.
|
||||
func buffHi(n, count, size uint16) uint16 {
|
||||
if 0 == n || 0 == count || 0 == size {
|
||||
return 0
|
||||
}
|
||||
return n * size
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// +build arm
|
||||
|
||||
package usb
|
||||
|
||||
import "device/arm"
|
||||
|
||||
// udelay waits for the given number of microseconds before returning.
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
//go:inline
|
||||
func udelay(microsec uint32) {
|
||||
n := cycles(microsec, descCPUFrequencyHz)
|
||||
for i := uint32(0); i < n; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
func disableInterrupts() uintptr {
|
||||
return arm.DisableInterrupts()
|
||||
}
|
||||
|
||||
func enableInterrupts(mask uintptr) {
|
||||
arm.EnableInterrupts(mask)
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func handleHardFault(sp *interruptStack) {
|
||||
if fault.Mem().WhileUnstackingException() {
|
||||
print(" while unstacking exception")
|
||||
}
|
||||
if fault.Mem().WileStackingException() {
|
||||
if fault.Mem().WhileStackingException() {
|
||||
print(" while stacking exception")
|
||||
}
|
||||
if fault.Mem().DuringFPLazyStatePres() {
|
||||
@@ -161,13 +161,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
|
||||
return fs&arm.SCB_CFSR_MUNSTKERR != 0
|
||||
}
|
||||
|
||||
// WileStackingException: stacking for an exception entry has caused one or more
|
||||
// WhileStackingException: stacking for an exception entry has caused one or more
|
||||
// access violations
|
||||
//
|
||||
// "When this bit is 1, the SP is still adjusted but the values in the context
|
||||
// area on the stack might be incorrect. The processor has not written a fault
|
||||
// address to the MMAR."
|
||||
func (fs MemFaultStatus) WileStackingException() bool {
|
||||
func (fs MemFaultStatus) WhileStackingException() bool {
|
||||
return fs&arm.SCB_CFSR_MSTKERR != 0
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -36,7 +37,7 @@ func main() {
|
||||
// initialize cache and MPU
|
||||
initCache()
|
||||
|
||||
// enable SysTick, GPIO, and peripherals
|
||||
// enable SysTick, GPIO, USB, and other peripherals
|
||||
initPeripherals()
|
||||
|
||||
// reenable interrupts
|
||||
@@ -108,7 +109,8 @@ func initPeripherals() {
|
||||
initPins() // configure GPIO
|
||||
|
||||
enablePeripheralClocks() // activate peripheral clock gates
|
||||
initUART() // configure UART (initialized first for debugging)
|
||||
initUSB() // configure USB CDC-ACM (UART0)
|
||||
initUART() // configure hardware UART (UART1)
|
||||
}
|
||||
|
||||
func initPins() {
|
||||
@@ -123,8 +125,14 @@ func initUART() {
|
||||
machine.UART1.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
func initUSB() {
|
||||
machine.HID0.Configure(usb.HIDConfig{})
|
||||
// machine.UART0.Configure(usb.UARTConfig{})
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.UART1.WriteByte(c)
|
||||
// machine.UART0.WriteByte(c) // print to USB UART
|
||||
machine.UART1.WriteByte(c) // print to hardware UART
|
||||
}
|
||||
|
||||
func exit(code int) {
|
||||
|
||||
@@ -35,15 +35,31 @@ var (
|
||||
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
}
|
||||
Usb1PllConfig = nxp.ClockConfigUsbPll{
|
||||
Instance: 1, // USB PLL instance
|
||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
Usb1PhyConfig = nxp.ClockConfigUsbPhy{
|
||||
Instance: 1, // USB PHY number (1 or 2)
|
||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||
DCal: 0xC, // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig: nxp.ClockConfigUsbPll{
|
||||
Instance: 1, // USB PLL number (1 or 2)
|
||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
||||
},
|
||||
}
|
||||
Usb2PllConfig = nxp.ClockConfigUsbPll{
|
||||
Instance: 2, // USB PLL instance
|
||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
Usb2PhyConfig = nxp.ClockConfigUsbPhy{
|
||||
Instance: 2, // USB PHY number (1 or 2)
|
||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||
DCal: 0xC, // Decode to trim the nominal 17.78mA current source
|
||||
TxCal45DP: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig: nxp.ClockConfigUsbPll{
|
||||
Instance: 2, // USB PLL number (1 or 2)
|
||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -85,7 +101,7 @@ func initClocks() {
|
||||
|
||||
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
|
||||
nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) |
|
||||
((13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||
((0x13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||
|
||||
// wait until DCDC_STS_DC_OK bit is asserted
|
||||
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
||||
@@ -104,6 +120,8 @@ func initClocks() {
|
||||
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
||||
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
||||
|
||||
nxp.ClockIpUsbOh3.Enable(false) // disable USB
|
||||
|
||||
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
||||
nxp.ClockIpGpt1S.Enable(false) //
|
||||
nxp.ClockIpGpt2.Enable(false) //
|
||||
@@ -112,6 +130,11 @@ func initClocks() {
|
||||
|
||||
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
|
||||
|
||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||
nxp.ClockIpGpio2.Enable(false) //
|
||||
nxp.ClockIpGpio3.Enable(false) //
|
||||
nxp.ClockIpGpio4.Enable(false) //
|
||||
|
||||
nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1
|
||||
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
||||
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
||||
@@ -120,9 +143,9 @@ func initClocks() {
|
||||
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
||||
|
||||
nxp.ClockIpSemc.Enable(false) // disable SEMC
|
||||
nxp.DivIpSemc.Div(1) // divide SEMC_PODF (DIV2)
|
||||
nxp.DivIpSemc.Div(7) // divide SEMC_PODF (DIV8)
|
||||
nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2
|
||||
nxp.MuxIpSemc.Mux(1) // SEMC select SEMC_ALT
|
||||
nxp.MuxIpSemc.Mux(0) // SEMC select PERIPH_CLK
|
||||
|
||||
if false {
|
||||
// TODO: external flash is on this bus, configured via DCD block
|
||||
@@ -191,7 +214,7 @@ func initClocks() {
|
||||
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
||||
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
||||
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
|
||||
nxp.MuxIpLcdifPre.Mux(5) // LCDIF_PRE select PLL3_PFD1
|
||||
nxp.MuxIpLcdifPre.Mux(4) // LCDIF_PRE select PLL2_PFD1
|
||||
|
||||
nxp.ClockIpSpdif.Enable(false) // disable SPDIF
|
||||
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
||||
@@ -209,43 +232,43 @@ func initClocks() {
|
||||
|
||||
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
|
||||
|
||||
// Disable Audio/Video/Ethernet PLLs
|
||||
nxp.CCM_ANALOG.PLL_AUDIO.Set(nxp.CCM_ANALOG_PLL_AUDIO_POWERDOWN_Msk)
|
||||
nxp.CCM_ANALOG.PLL_VIDEO.Set(nxp.CCM_ANALOG_PLL_VIDEO_POWERDOWN_Msk)
|
||||
nxp.CCM_ANALOG.PLL_ENET.Set(nxp.CCM_ANALOG_PLL_ENET_POWERDOWN_Msk)
|
||||
|
||||
ArmPllConfig.Configure() // init ARM PLL
|
||||
// SYS PLL (PLL2) @ 528 MHz
|
||||
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
||||
// PFD1 = 594 MHz -> (currently unused)
|
||||
// PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz
|
||||
// PFD3 = 454.73 MHz -> (currently unused)
|
||||
// PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz
|
||||
// PFD3 = 594 MHz -> (currently unused)
|
||||
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
||||
|
||||
// USB1 PLL (PLL3) @ 480 MHz
|
||||
// PFD0 -> (currently unused)
|
||||
// PFD1 -> (currently unused)
|
||||
// PFD2 -> (currently unused)
|
||||
// PFD3 -> (currently unused)
|
||||
Usb1PllConfig.Configure() // init USB1 PLL and PFDs
|
||||
Usb2PllConfig.Configure() // init USB2 PLL
|
||||
Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL
|
||||
Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL
|
||||
|
||||
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
||||
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
||||
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
||||
nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC
|
||||
|
||||
// set LVDS1 clock source
|
||||
// set LVDS1 clock source (ARM_PLL)
|
||||
nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) |
|
||||
((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk))
|
||||
|
||||
// set CLOCK_OUT1 divider
|
||||
// set CLOCK_OUT1 divider (DIV1)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) |
|
||||
((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk))
|
||||
// set CLOCK_OUT1 source
|
||||
// set CLOCK_OUT1 source (PLL2/DIV2)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) |
|
||||
((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk))
|
||||
// set CLOCK_OUT2 divider
|
||||
// set CLOCK_OUT2 divider (DIV1)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) |
|
||||
((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk))
|
||||
// set CLOCK_OUT2 source
|
||||
// set CLOCK_OUT2 source (SPDIF0)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) |
|
||||
((18 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||
((29 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||
|
||||
nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT
|
||||
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
|
||||
@@ -253,15 +276,10 @@ func initClocks() {
|
||||
|
||||
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
||||
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
|
||||
// set GPT1 High frequency reference clock source
|
||||
// set GPT1 High frequency reference clock source (PERCLK)
|
||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk)
|
||||
// set GPT2 High frequency reference clock source
|
||||
// set GPT2 High frequency reference clock source (PERCLK)
|
||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk)
|
||||
|
||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||
nxp.ClockIpGpio2.Enable(false) //
|
||||
nxp.ClockIpGpio3.Enable(false) //
|
||||
nxp.ClockIpGpio4.Enable(false) //
|
||||
}
|
||||
|
||||
func enableTimerClocks() {
|
||||
|
||||
@@ -141,7 +141,7 @@ func timerSleep(cycles uint32) bool {
|
||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts
|
||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
|
||||
for {
|
||||
//arm.Asm("wfi") // TODO: causes hardfault! why?
|
||||
waitForEvents()
|
||||
if pitActive.Get() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ MEMORY
|
||||
ENTRY(Reset_Handler);
|
||||
|
||||
_stack_size = 4K;
|
||||
_heap_size = 512K;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user