mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27a9e5d764 | |||
| 297d3cbcc2 | |||
| 2651b1d8bd | |||
| 319b49e81c | |||
| 7e493642eb | |||
| 674b3b966f | |||
| d9d29d3c68 | |||
| e94ab78926 | |||
| 8fa58fdc6d | |||
| 4b81985d2b | |||
| 3b892cbe41 | |||
| 5f6489d3cf | |||
| 2f50d24fec | |||
| 6e29c17a8b | |||
| 67e68e484f | |||
| 4b8b4243ec | |||
| d2a1835ffc | |||
| 34b931b6f3 | |||
| 07964818d9 | |||
| 079eace344 | |||
| 69e8257e7b | |||
| 8103b3c3c8 | |||
| a041a7866d | |||
| 6583ec1448 |
@@ -8,6 +8,7 @@
|
||||
package nxp
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -378,30 +379,6 @@ func (clk Clock) setCcm(value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// PLL configuration for ARM
|
||||
type ClockConfigArmPll struct {
|
||||
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
||||
@@ -472,59 +449,178 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
||||
setSysPfd(pfd...)
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
||||
// PHY configuration for USB HS
|
||||
type ClockConfigUsbPhy struct {
|
||||
Instance uint8 // USB PHY number (1 or 2)
|
||||
XtalFreq uint32 // External reference clock frequency (Hz)
|
||||
DCal uint32 // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig ClockConfigUsbPll
|
||||
}
|
||||
|
||||
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
|
||||
// the USB +3V regulator (PMU), for use as either USB host or device.
|
||||
func (cfg ClockConfigUsbPhy) Configure() {
|
||||
|
||||
var (
|
||||
usb *USB_Type
|
||||
phy *USBPHY_Type
|
||||
chrgDetectReg *volatile.Register32
|
||||
chrgDetectMsk uint32
|
||||
)
|
||||
|
||||
// Select appropriate peripherals based on receiver Instance
|
||||
switch cfg.Instance {
|
||||
case 1:
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB1_SET.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
||||
|
||||
// update PFDs after update
|
||||
setUsb1Pfd(pfd...)
|
||||
|
||||
usb = USB1 // Select USB1 HS PHY/PLL
|
||||
phy = USBPHY1 //
|
||||
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
||||
case 2:
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
||||
usb = USB2 // Select USB2 HS PHY/PLL
|
||||
phy = USBPHY2 //
|
||||
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
|
||||
default:
|
||||
panic("nxp: invalid USB PHY")
|
||||
}
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
||||
// Configure and enable USB PLL clocks
|
||||
cfg.PllConfig.Configure()
|
||||
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
||||
// Release PHY from reset
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
|
||||
|
||||
// Enable power to USB PHY
|
||||
phy.PWD.Set(0)
|
||||
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
|
||||
// enable support for low-speed device connection, direct and indirect (hub)
|
||||
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
|
||||
|
||||
// Enable USB HS clocks gate
|
||||
ClockIpUsbOh3.Enable(true)
|
||||
// Reset USB peripheral
|
||||
usb.USBCMD.SetBits(USB_USBCMD_RST)
|
||||
|
||||
// Add a delay after RST to ensure there is a USB D+ pullup sequence
|
||||
nopDelay(400000)
|
||||
|
||||
// Enable USB LDO
|
||||
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
|
||||
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
|
||||
|
||||
// check whether we are connected to USB charger
|
||||
chrgDetectReg.Set(chrgDetectMsk)
|
||||
|
||||
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
|
||||
phy.TX.Set((phy.TX.Get() &
|
||||
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
|
||||
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
|
||||
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
|
||||
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure() {
|
||||
// select USB peripheral registers based on receiver's Instance
|
||||
switch cfg.Instance {
|
||||
case 1: // USB1 PLL
|
||||
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
|
||||
|
||||
// update PFDs (if provided)
|
||||
if nil != cfg.Pfd {
|
||||
setUsb1Pfd(cfg.Pfd...)
|
||||
}
|
||||
}
|
||||
case 2: // USB2 PLL
|
||||
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
|
||||
|
||||
default:
|
||||
panic("nxp: invalid USB PLL")
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
// go:inline
|
||||
func nopDelay(cycles uint32) {
|
||||
for i := uint32(0); i < cycles; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,3 +252,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,258 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"time"
|
||||
)
|
||||
|
||||
var keyboard = machine.USB.Keyboard()
|
||||
|
||||
func main() {
|
||||
|
||||
for !machine.USB.Ready() {
|
||||
}
|
||||
|
||||
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 (GNU screen)
|
||||
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 (GNU screen)
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
// Ctrl-X is the prefix sequence in my GNU screen configuration
|
||||
keyboard.Press(usb.KeyX)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(time.Second)
|
||||
// Backslash (Prefix-\) is the GNU screen command to kill window
|
||||
keyboard.Press(usb.KeyBackslash)
|
||||
time.Sleep(time.Second)
|
||||
// Confirm "Kill window (Y/N)?" prompt
|
||||
keyboard.Press(usb.KeyY)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Close terminal
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
// Ctrl-D exits the shell (sends a resemblance of EOF, I believe?)
|
||||
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.Serial
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"machine/usb"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
@@ -56,21 +57,21 @@ const (
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
||||
// = Pin // Dig [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
|
||||
)
|
||||
|
||||
// Default peripheral pins
|
||||
@@ -105,6 +106,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 |
|
||||
// #===========#===========#=============#===============#
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -1975,719 +1974,10 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
}
|
||||
|
||||
var (
|
||||
// USB is a USB CDC interface.
|
||||
USB = &USBCDC{Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
|
||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
const (
|
||||
// these are SAMD51 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
var (
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
func initUSB() {
|
||||
// Configure USB D+/D- pins.
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ at highest priority
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
|
||||
usbcdc.configured = true
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
USB.Flush()
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
handleEndpoint(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN {
|
||||
USB.waitTxc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
// TODO: actually check if the endpoint in question is currently halted
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
|
||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
// Enable interrupt for CDC data messages from host
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
||||
}
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
|
||||
@@ -5,3 +5,6 @@ package machine
|
||||
|
||||
// Serial is a null device: writes to it are ignored.
|
||||
var Serial = NullSerial{}
|
||||
|
||||
func InitSerial() {
|
||||
}
|
||||
|
||||
@@ -5,3 +5,7 @@ package machine
|
||||
|
||||
// Serial is implemented via the default (usually the first) UART on the chip.
|
||||
var Serial = DefaultUART
|
||||
|
||||
func InitSerial() {
|
||||
Serial.Configure(UARTConfig{})
|
||||
}
|
||||
|
||||
@@ -5,3 +5,6 @@ package machine
|
||||
|
||||
// Serial is implemented via USB (USB-CDC).
|
||||
var Serial = USB
|
||||
|
||||
func InitSerial() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build usb.cdc
|
||||
// +build usb.cdc
|
||||
|
||||
package machine
|
||||
|
||||
import "machine/usb"
|
||||
|
||||
var USB = &usb.CDC{}
|
||||
|
||||
func InitUSB() {
|
||||
initUSB()
|
||||
USB.Configure(usb.CDCConfig{})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build usb.hid
|
||||
// +build usb.hid
|
||||
|
||||
package machine
|
||||
|
||||
import "machine/usb"
|
||||
|
||||
var USB = &usb.HID{}
|
||||
|
||||
func InitUSB() {
|
||||
initUSB()
|
||||
USB.Configure(usb.HIDConfig{})
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
//go:build sam || nrf52840
|
||||
// +build sam nrf52840
|
||||
//go:build nrf52840
|
||||
// +build nrf52840
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
//go:build usb.cdc
|
||||
// +build usb.cdc
|
||||
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:inline
|
||||
func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 {
|
||||
switch endpointNumber(endpoint) {
|
||||
case descCDCEndpointCtrl:
|
||||
return descControlPacketSize
|
||||
case descCDCEndpointStatus:
|
||||
return descCDCStatusPacketSize
|
||||
case descCDCEndpointDataRx:
|
||||
return descCDCDataRxPacketSize
|
||||
case descCDCEndpointDataTx:
|
||||
return descCDCDataTxPacketSize
|
||||
}
|
||||
return descControlPacketSize
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dcd) controlEndpoint() uint8 {
|
||||
return descCDCEndpointCtrl
|
||||
}
|
||||
|
||||
func (d *dcd) controlSetConfiguration() {
|
||||
d.cdcConfigure()
|
||||
}
|
||||
|
||||
func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage {
|
||||
|
||||
// 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:
|
||||
// line coding must contain exactly 7 bytes
|
||||
if uint16(descCDCLineCodingSize) == sup.wLength {
|
||||
d.controlReceive(
|
||||
uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].cx[0])),
|
||||
uint32(descCDCLineCodingSize), true)
|
||||
// CDC Line Coding packet receipt handling occurs in method
|
||||
// controlComplete().
|
||||
return dcdStageDataOut
|
||||
}
|
||||
|
||||
// CDC | SET CONTROL LINE STATE (0x22):
|
||||
case descCDCRequestSetControlLineState:
|
||||
// Determine interface destination of the request
|
||||
switch sup.wIndex {
|
||||
// Control/status interface:
|
||||
case descCDCInterfaceCtrl:
|
||||
// CDC Control Line State packet receipt handling occurs in method
|
||||
// controlComplete().
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageStatusOut
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
// CDC | SEND BREAK (0x23):
|
||||
case descCDCRequestSendBreak:
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageStatusOut
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
return dcdStageStall
|
||||
}
|
||||
|
||||
func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool {
|
||||
switch sup.bRequest {
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
d.controlGetDescriptor(sup)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *dcd) controlGetDescriptor(sup dcdSetup) {
|
||||
|
||||
acm := &descCDC[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(descCDCConfigSize)
|
||||
_ = 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)
|
||||
}
|
||||
}
|
||||
|
||||
// controlComplete handles the setup completion of control endpoint 0.
|
||||
func (d *dcd) controlComplete() {
|
||||
|
||||
// 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:
|
||||
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch d.setup.wIndex {
|
||||
|
||||
// CDC-ACM Control Interface:
|
||||
case descCDCInterfaceCtrl:
|
||||
// Notify PHY to handle triggers like special baud rates, which
|
||||
// signal to reboot into bootloader or begin receiving OTA updates
|
||||
d.cdcSetLineCoding(acm.cx[:])
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
// CDC | SET CONTROL LINE STATE (0x22):
|
||||
case descCDCRequestSetControlLineState:
|
||||
|
||||
// Determine interface destination of the request
|
||||
switch d.setup.wIndex {
|
||||
|
||||
// Control/status interface:
|
||||
case descCDCInterfaceCtrl:
|
||||
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
|
||||
d.cdcSetLineState(d.setup.wValue)
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled recepient or direction
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request type
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
//go:build usb.hid
|
||||
// +build usb.hid
|
||||
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:inline
|
||||
func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 {
|
||||
switch endpointNumber(endpoint) {
|
||||
case descHIDEndpointCtrl:
|
||||
return descControlPacketSize
|
||||
case descHIDEndpointKeyboard:
|
||||
return descHIDKeyboardTxPacketSize
|
||||
case descHIDEndpointMouse:
|
||||
return descHIDMouseTxPacketSize
|
||||
case descHIDEndpointSerialRx: // == descHIDEndpointSerialTx
|
||||
switch endpoint {
|
||||
case rxEndpoint(endpoint):
|
||||
return descHIDSerialRxPacketSize
|
||||
case txEndpoint(endpoint):
|
||||
return descHIDSerialTxPacketSize
|
||||
}
|
||||
case descHIDEndpointJoystick:
|
||||
return descHIDJoystickTxPacketSize
|
||||
case descHIDEndpointMediaKey:
|
||||
return descHIDMediaKeyTxPacketSize
|
||||
}
|
||||
return descControlPacketSize
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dcd) controlEndpoint() uint8 {
|
||||
return descHIDEndpointCtrl
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dcd) controlSetConfiguration() {
|
||||
d.serialConfigure()
|
||||
d.keyboardConfigure()
|
||||
d.mouseConfigure()
|
||||
d.joystickConfigure()
|
||||
}
|
||||
|
||||
func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage {
|
||||
|
||||
// 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 {
|
||||
|
||||
// HID | SET REPORT (0x09)
|
||||
case descHIDRequestSetReport:
|
||||
if sup.wLength <= descHIDSxSize {
|
||||
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 dcdStageDataOut
|
||||
}
|
||||
|
||||
// HID | SET IDLE (0x0A)
|
||||
case descHIDRequestSetIdle:
|
||||
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 dcdStageStatusOut
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- INTERFACE Tx (IN) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// HID | GET REPORT (0x01)
|
||||
case descHIDRequestGetReport:
|
||||
|
||||
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.controlTransmit(
|
||||
d.controlStatusBuffer([]uint8{0, 0}),
|
||||
2, false)
|
||||
return dcdStageDataIn
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
return dcdStageStall
|
||||
}
|
||||
|
||||
func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool {
|
||||
switch sup.bRequest {
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
d.controlGetDescriptor(sup)
|
||||
return true
|
||||
// GET HID REPORT (0x01):
|
||||
case descHIDRequestGetReport:
|
||||
d.controlGetDescriptor(sup)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *dcd) controlGetDescriptor(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)
|
||||
}
|
||||
}
|
||||
|
||||
// controlComplete handles the setup completion of control endpoint 0.
|
||||
func (d *dcd) controlComplete() {
|
||||
|
||||
// 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 {
|
||||
|
||||
// HID | SET REPORT (0x09)
|
||||
case descHIDRequestSetReport:
|
||||
|
||||
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 request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled recepient or direction
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request type
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package usb
|
||||
|
||||
// Implementation of target-agnostic USB device controller driver (dcd).
|
||||
//
|
||||
// The types, constants, and methods defined in this unit are applicable to all
|
||||
// targets. It was designed to complement the device hardware abstraction (dhw)
|
||||
// implemented for each target, providing common/shared functionality and
|
||||
// defining a standard interface with which the dhw must adhere.
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// dcdCount defines the number of USB cores to configure for device mode. It is
|
||||
// computed as the sum of all declared device configuration descriptors.
|
||||
const dcdCount = descCDCCount + 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 all 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
|
||||
|
||||
st volatile.Register8 // USB device state
|
||||
}
|
||||
|
||||
// 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, speed Speed, class class) (*dcd, status) {
|
||||
if 0 == dcdCount {
|
||||
return nil, statusInvalid // Must have defined device controllers
|
||||
}
|
||||
switch class.id {
|
||||
case classDeviceCDC:
|
||||
if 0 == class.config || class.config > descCDCCount {
|
||||
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, speed, &dcdInstance[i])
|
||||
dcdInstance[i].core = &coreInstance[port]
|
||||
dcdInstance[i].port = port
|
||||
dcdInstance[i].cc = class
|
||||
dcdInstance[i].id = i
|
||||
dcdInstance[i].setState(dcdStateNotReady)
|
||||
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 = unsafe.Sizeof(dcdSetup{}) // 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
|
||||
}
|
||||
|
||||
// setupFrom decodes and returns a USB standard setup packet located at the
|
||||
// memory address pointed to by addr.
|
||||
func setupFrom(addr uintptr) (s dcdSetup) {
|
||||
var u uint64
|
||||
for i := uintptr(0); i < dcdSetupSize; i++ {
|
||||
u |= uint64(*(*uint8)(unsafe.Pointer(addr + i))) << (i << 3)
|
||||
}
|
||||
s.set(u)
|
||||
return
|
||||
}
|
||||
|
||||
// setup decodes and returns a USB standard setup packet stored in the given
|
||||
// byte slice b.
|
||||
func setup(b []uint8) dcdSetup {
|
||||
if len(b) >= int(dcdSetupSize) {
|
||||
return dcdSetup{
|
||||
bmRequestType: b[0],
|
||||
bRequest: b[1],
|
||||
wValue: packU16(b[2:]),
|
||||
wIndex: packU16(b[4:]),
|
||||
wLength: packU16(b[6:]),
|
||||
}
|
||||
}
|
||||
return dcdSetup{}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (s *dcdSetup) set(u uint64) {
|
||||
s.bmRequestType = uint8(u & 0xFF)
|
||||
s.bRequest = uint8((u & 0xFF00) >> 8)
|
||||
s.wValue = uint16((u & 0xFFFF0000) >> 16)
|
||||
s.wIndex = uint16((u & 0xFFFF00000000) >> 32)
|
||||
s.wLength = uint16((u & 0xFFFF000000000000) >> 48)
|
||||
}
|
||||
|
||||
// pack returns the receiver USB standard setup packet s encoded as uint64.
|
||||
//go:inline
|
||||
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)
|
||||
}
|
||||
|
||||
// direction parses the direction bit from the bmRequestType field of a SETUP
|
||||
// packet, returning 0 for OUT (Rx) and 1 for IN (Tx) requests.
|
||||
//go:inline
|
||||
func (s dcdSetup) direction() uint8 {
|
||||
return (s.bmRequestType & descRequestTypeDirMsk) >> descRequestTypeDirPos
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (s dcdSetup) equals(t dcdSetup) bool {
|
||||
return s.bmRequestType == t.bmRequestType && s.bRequest == t.bRequest &&
|
||||
s.wValue == t.wValue && s.wIndex == t.wIndex && s.wLength == t.wLength
|
||||
}
|
||||
|
||||
// dcdState defines the current state of the device class driver.
|
||||
type dcdState uint8
|
||||
|
||||
const (
|
||||
dcdStateNotReady dcdState = iota // initial state, before END_OF_RESET
|
||||
dcdStateDefault // after END_OF_RESET, before SET_ADDRESS
|
||||
dcdStateAddressed // after SET_ADDRESS, before SET_CONFIGURATION
|
||||
dcdStateConfigured // after SET_CONFIGURATION, operational state
|
||||
dcdStateSuspended // while operational, after SUSPEND
|
||||
)
|
||||
|
||||
func (d *dcd) state() dcdState { return dcdState(d.st.Get()) }
|
||||
|
||||
func (d *dcd) setState(state dcdState) (ok bool) {
|
||||
curr := d.state()
|
||||
switch state {
|
||||
case dcdStateNotReady:
|
||||
ok = true
|
||||
case dcdStateDefault:
|
||||
ok = curr == dcdStateNotReady || curr == dcdStateDefault
|
||||
case dcdStateAddressed:
|
||||
ok = curr == dcdStateDefault
|
||||
case dcdStateConfigured:
|
||||
ok = curr == dcdStateAddressed || curr == dcdStateConfigured || curr == dcdStateSuspended
|
||||
case dcdStateSuspended:
|
||||
ok = curr == dcdStateAddressed || curr == dcdStateConfigured || curr == dcdStateSuspended
|
||||
default:
|
||||
ok = false
|
||||
}
|
||||
if ok {
|
||||
d.st.Set(uint8(state))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
dcdEventStatusResume // USB RESUME condition
|
||||
dcdEventStatusSuspend // USB SUSPEND received
|
||||
dcdEventStatusError // USB error condition detected on bus
|
||||
dcdEventDeviceReady // USB PHY powered and ready to _go_
|
||||
dcdEventDeviceAddress // USB device SET_ADDRESS complete
|
||||
dcdEventDeviceConfiguration // USB device SET_CONFIGURATION complete
|
||||
dcdEventControlSetup // USB SETUP received
|
||||
dcdEventControlComplete // USB control request complete
|
||||
dcdEventTransferComplete // USB data transfer complete
|
||||
dcdEventTimer // USB (system) timer
|
||||
)
|
||||
|
||||
func (d *dcd) event(ev dcdEvent) {
|
||||
|
||||
switch ev.id {
|
||||
|
||||
case dcdEventStatusReset:
|
||||
d.setState(dcdStateNotReady)
|
||||
|
||||
case dcdEventStatusResume:
|
||||
d.setState(dcdStateConfigured)
|
||||
|
||||
case dcdEventStatusSuspend:
|
||||
d.setState(dcdStateSuspended)
|
||||
|
||||
case dcdEventDeviceReady:
|
||||
if d.setState(dcdStateDefault) {
|
||||
// Configure and enable control endpoint 0
|
||||
d.endpointEnable(0, true, 0)
|
||||
}
|
||||
|
||||
case dcdEventDeviceAddress:
|
||||
// -- ** IMPORTANT ** --
|
||||
// dcdEventDeviceAddress must be triggered by the target driver, because
|
||||
// different MCUs require setting the device address at different times
|
||||
// during the enumeration process.
|
||||
d.setState(dcdStateAddressed)
|
||||
|
||||
case dcdEventDeviceConfiguration:
|
||||
d.setState(dcdStateConfigured)
|
||||
|
||||
case dcdEventControlSetup:
|
||||
// On control endpoint 0 setup events, the ev.setup field will be defined.
|
||||
// We overwrite the receiver's setup field, leaving it unmodified throughout
|
||||
// all transactions of a control transfer. It is only cleared once the
|
||||
// completion event dcdEventControlComplete has been called and finished
|
||||
// processing, or if its initial processing fails due to error.
|
||||
d.setup = ev.setup
|
||||
d.stage = d.controlSetup(ev.setup)
|
||||
switch d.stage {
|
||||
case dcdStageDataIn, dcdStageDataOut:
|
||||
// TBD: control endpoint data transfer
|
||||
|
||||
case dcdStageStatusIn, dcdStageStatusOut:
|
||||
// TBD: control endpoint status transfer
|
||||
|
||||
case dcdStageStall:
|
||||
d.controlStall(true, ev.setup.direction())
|
||||
|
||||
case dcdStageSetup:
|
||||
fallthrough
|
||||
default:
|
||||
// TBD: no stage transition occurred
|
||||
}
|
||||
|
||||
case dcdEventControlComplete:
|
||||
d.controlComplete()
|
||||
// clear the active SETUP packet once the control transfer completes.
|
||||
d.setup = dcdSetup{}
|
||||
|
||||
case dcdEventTransferComplete:
|
||||
// TBD: data endpoint transfer complete
|
||||
|
||||
case dcdEventInvalid, dcdEventStatusError, dcdEventTimer:
|
||||
fallthrough
|
||||
default:
|
||||
// TBD: unhandled events
|
||||
}
|
||||
}
|
||||
|
||||
// dcdStage represents the stage of a USB control transfer.
|
||||
type dcdStage uint8
|
||||
|
||||
// Enumerated constants for all possible USB control transfer stages.
|
||||
const (
|
||||
dcdStageSetup dcdStage = iota // Indicates no stage transition required
|
||||
dcdStageDataIn // IN data transfer
|
||||
dcdStageDataOut // OUT data transfer
|
||||
dcdStageStatusIn // IN status request
|
||||
dcdStageStatusOut // OUT status request
|
||||
dcdStageStall // Unhandled or invalid request
|
||||
)
|
||||
|
||||
// controlSetup handles setup messages on control endpoint 0.
|
||||
func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
|
||||
|
||||
// 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 dcdStageStatusOut
|
||||
|
||||
// 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
|
||||
}
|
||||
d.event(dcdEvent{id: dcdEventDeviceConfiguration})
|
||||
d.controlSetConfiguration()
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageStatusOut
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- DEVICE Tx (IN) ---
|
||||
case descRequestTypeRecipientDevice | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET STATUS (0x00):
|
||||
case descRequestStandardGetStatus:
|
||||
d.controlTransmit(
|
||||
d.controlStatusBuffer([]uint8{0, 0}),
|
||||
2, false)
|
||||
return dcdStageDataIn
|
||||
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
d.controlGetDescriptor(sup)
|
||||
return dcdStageDataIn
|
||||
|
||||
// GET CONFIGURATION (0x08):
|
||||
case descRequestStandardGetConfiguration:
|
||||
d.controlTransmit(
|
||||
d.controlStatusBuffer([]uint8{
|
||||
uint8(d.cc.config),
|
||||
}),
|
||||
1, false)
|
||||
return dcdStageDataIn
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- INTERFACE Tx (IN) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
||||
if d.controlGetInterfaceDescriptor(sup) {
|
||||
return dcdStageDataIn
|
||||
}
|
||||
|
||||
// --- 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 dcdStageStatusOut
|
||||
|
||||
// SET FEATURE (0x03):
|
||||
case descRequestStandardSetFeature:
|
||||
d.endpointSetFeature(uint8(sup.wIndex))
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageStatusOut
|
||||
|
||||
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.controlTransmit(
|
||||
d.controlStatusBuffer([]uint8{
|
||||
uint8(status),
|
||||
uint8(status >> 8),
|
||||
}),
|
||||
2, false)
|
||||
return dcdStageDataIn
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
// === CLASS REQUEST ===
|
||||
case descRequestTypeTypeClass:
|
||||
|
||||
// Forward all class requests to the device class implementation.
|
||||
return d.controlClassRequest(sup)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
//go:build usb.cdc
|
||||
// +build usb.cdc
|
||||
|
||||
package usb
|
||||
|
||||
// descHIDCount defines the number of USB cores that may be configured as a
|
||||
// composite (keyboard + mouse + joystick) human interface device (HID).
|
||||
const descHIDCount = 0
|
||||
|
||||
// USB CDC constants defined per specification.
|
||||
const (
|
||||
|
||||
// Device class
|
||||
descCDCTypeComm = 0x02 // communication/control
|
||||
descCDCTypeData = 0x0A // data
|
||||
|
||||
// Communication/control subclass
|
||||
descCDCSubNone = 0x00
|
||||
descCDCSubDirectLineControl = 0x01
|
||||
descCDCSubAbstractControl = 0x02
|
||||
descCDCSubTelephoneControl = 0x03
|
||||
descCDCSubMultiChannelControl = 0x04
|
||||
descCDCSubCAPIControl = 0x05
|
||||
descCDCSubEthernetNetworkingControl = 0x06
|
||||
descCDCSubATMNetworkingControl = 0x07
|
||||
descCDCSubWirelessHandsetControl = 0x08
|
||||
descCDCSubDeviceManagement = 0x09
|
||||
descCDCSubMobileDirectLine = 0x0A
|
||||
descCDCSubOBEX = 0x0B
|
||||
descCDCSubEthernetEmulation = 0x0C
|
||||
|
||||
// Communication/control protocol
|
||||
descCDCProtoNone = 0x00 // also for data class
|
||||
descCDCProtoAT250 = 0x01
|
||||
descCDCProtoATPCCA101 = 0x02
|
||||
descCDCProtoATPCCA101AnnexO = 0x03
|
||||
descCDCProtoATGSM707 = 0x04
|
||||
descCDCProtoAT3GPP27007 = 0x05
|
||||
descCDCProtoATTIACDMA = 0x06
|
||||
descCDCProtoEthernetEmulation = 0x07
|
||||
descCDCProtoExternal = 0xFE
|
||||
descCDCProtoVendorSpecific = 0xFF // also for data class
|
||||
|
||||
// Data protocol
|
||||
descCDCProtoPyhsicalInterface = 0x30
|
||||
descCDCProtoHDLC = 0x31
|
||||
descCDCProtoTransparent = 0x32
|
||||
descCDCProtoManagement = 0x50
|
||||
descCDCProtoDataLinkQ931 = 0x51
|
||||
descCDCProtoDataLinkQ921 = 0x52
|
||||
descCDCProtoDataCompressionV42BIS = 0x90
|
||||
descCDCProtoEuroISDN = 0x91
|
||||
descCDCProtoRateAdaptionISDNV24 = 0x92
|
||||
descCDCProtoCAPICommands = 0x93
|
||||
descCDCProtoHostBasedDriver = 0xFD
|
||||
descCDCProtoUnitFunctional = 0xFE
|
||||
|
||||
// Functional descriptor length
|
||||
descCDCFuncLengthHeader = 5
|
||||
descCDCFuncLengthCallManagement = 5
|
||||
descCDCFuncLengthAbstractControl = 4
|
||||
descCDCFuncLengthUnion = 5
|
||||
|
||||
// Functional descriptor type
|
||||
descCDCFuncTypeHeader = 0x00
|
||||
descCDCFuncTypeCallManagement = 0x01
|
||||
descCDCFuncTypeAbstractControl = 0x02
|
||||
descCDCFuncTypeDirectLine = 0x03
|
||||
descCDCFuncTypeTelephoneRinger = 0x04
|
||||
descCDCFuncTypeTelephoneReport = 0x05
|
||||
descCDCFuncTypeUnion = 0x06
|
||||
descCDCFuncTypeCountrySelect = 0x07
|
||||
descCDCFuncTypeTelephoneModes = 0x08
|
||||
descCDCFuncTypeTerminal = 0x09
|
||||
descCDCFuncTypeNetworkChannel = 0x0A
|
||||
descCDCFuncTypeProtocolUnit = 0x0B
|
||||
descCDCFuncTypeExtensionUnit = 0x0C
|
||||
descCDCFuncTypeMultiChannel = 0x0D
|
||||
descCDCFuncTypeCAPIControl = 0x0E
|
||||
descCDCFuncTypeEthernetNetworking = 0x0F
|
||||
descCDCFuncTypeATMNetworking = 0x10
|
||||
descCDCFuncTypeWirelessControl = 0x11
|
||||
descCDCFuncTypeMobileDirectLine = 0x12
|
||||
descCDCFuncTypeMDLMDetail = 0x13
|
||||
descCDCFuncTypeDeviceManagement = 0x14
|
||||
descCDCFuncTypeOBEX = 0x15
|
||||
descCDCFuncTypeCommandSet = 0x16
|
||||
descCDCFuncTypeCommandSetDetail = 0x17
|
||||
descCDCFuncTypeTelephoneControl = 0x18
|
||||
descCDCFuncTypeOBEXServiceID = 0x19
|
||||
|
||||
// Standard request
|
||||
descCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND
|
||||
descCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE
|
||||
descCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE
|
||||
descCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE
|
||||
descCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE
|
||||
descCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE
|
||||
descCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE
|
||||
descCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP
|
||||
descCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE
|
||||
descCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME
|
||||
descCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK
|
||||
descCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING
|
||||
descCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING
|
||||
descCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE
|
||||
descCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK
|
||||
descCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS
|
||||
descCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS
|
||||
descCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM
|
||||
descCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM
|
||||
descCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS
|
||||
descCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS
|
||||
descCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS
|
||||
descCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER
|
||||
descCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER
|
||||
descCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER
|
||||
descCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS
|
||||
descCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER
|
||||
descCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER
|
||||
descCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER
|
||||
descCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC
|
||||
descCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT
|
||||
descCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS
|
||||
descCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC
|
||||
descCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS
|
||||
descCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK
|
||||
|
||||
// Notification type
|
||||
descCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION
|
||||
descCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL
|
||||
descCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE
|
||||
descCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT
|
||||
descCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE
|
||||
descCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE
|
||||
descCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE
|
||||
descCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE
|
||||
|
||||
// Feature select
|
||||
descCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE
|
||||
descCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING
|
||||
|
||||
// Control signal
|
||||
descCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION
|
||||
descCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE
|
||||
|
||||
// UART emulated state
|
||||
descCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER
|
||||
descCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER
|
||||
descCDCUARTStateBreak = 0x04 // UART state BREAK
|
||||
descCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL
|
||||
descCDCUARTStateFraming = 0x10 // UART state FRAMING
|
||||
descCDCUARTStateParity = 0x20 // UART state PARITY
|
||||
descCDCUARTStateOverrun = 0x40 // UART state OVERRUN
|
||||
)
|
||||
|
||||
const (
|
||||
// Size of all CDC-ACM configuration descriptors.
|
||||
descCDCConfigSize = uint16(
|
||||
descLengthConfigure + // Configuration Header
|
||||
descLengthInterface + // CDC Interface Descriptor
|
||||
descLengthInterfaceAssociation + // IAD
|
||||
descCDCFuncLengthHeader + // CDC Header
|
||||
descCDCFuncLengthCallManagement + // CDC Call Management Func Descriptor
|
||||
descCDCFuncLengthAbstractControl + // CDC Abstract Control Func Descriptor
|
||||
descCDCFuncLengthUnion + // CDC Union Functional Descriptor
|
||||
descLengthEndpoint + // CDC Status IN Endpoint Descriptor
|
||||
descLengthInterface + // CDC Data Interface Descriptor
|
||||
descLengthEndpoint + // CDC Data IN Endpoint Descriptor
|
||||
descLengthEndpoint) // CDC Data OUT Endpoint Descriptor
|
||||
)
|
||||
|
||||
// descCDCLineCodingSize defines the length of a CDC-ACM UART line coding
|
||||
// buffer. Note that the actual buffer may be padded for alignment; but for
|
||||
// Rx/Tx transfer purposes, descCDCLineCodingSize defines the number of bytes
|
||||
// that are transferred following a control SETUP request.
|
||||
const descCDCLineCodingSize = 7
|
||||
|
||||
// descCDCLineCoding represents an emulated UART's line configuration.
|
||||
//
|
||||
// Use descCDCLineCodingSize instead of unsafe.Sizeof(descCDCLineCoding)
|
||||
// in any transfer requests, because the actual struct is padded for alignment.
|
||||
type descCDCLineCoding struct {
|
||||
baud uint32
|
||||
stopBits uint8
|
||||
parity uint8
|
||||
numBits uint8
|
||||
_ uint8
|
||||
}
|
||||
|
||||
// parse initializes the receiver descCDCLineCoding from the given []uint8 v.
|
||||
// Argument v is a Rx transfer buffer, filled following the completion of a
|
||||
// control transfer from a CDC SET_LINE_CODING (0x20) request
|
||||
func (s *descCDCLineCoding) parse(v []uint8) bool {
|
||||
if len(v) >= descCDCLineCodingSize {
|
||||
s.baud = packU32(v[:])
|
||||
s.stopBits = v[4]
|
||||
s.parity = v[5]
|
||||
s.numBits = v[6]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// descCDCLineState represents an emulated UART's line state.
|
||||
type descCDCLineState struct {
|
||||
// dataTerminalReady indicates if DTE is present or not.
|
||||
// Corresponds to V.24 signal 108/2 and RS-232 signal DTR.
|
||||
dataTerminalReady bool // DTR
|
||||
// requestToSend is the carrier control for half-duplex modems.
|
||||
// Corresponds to V.24 signal 105 and RS-232 signal RTS.
|
||||
requestToSend bool // RTS
|
||||
}
|
||||
|
||||
// parse initializes the receiver descCDCLineState from the given uint16 v.
|
||||
// Argument v corresponds to the wValue field in a control SETUP packet, which
|
||||
// carries the line state from a CDC SET_CONTROL_LINE_STATE (0x22) request.
|
||||
func (s *descCDCLineState) parse(v uint16) bool {
|
||||
s.dataTerminalReady = 0 != v&0x1
|
||||
s.requestToSend = 0 != v&0x2
|
||||
return true
|
||||
}
|
||||
|
||||
// Common configuration constants for the USB CDC-ACM (single) device class.
|
||||
const (
|
||||
descCDCLanguageCount = 1 // String descriptor languages available
|
||||
|
||||
descCDCInterfaceCount = 2 // Interfaces for all CDC-ACM configurations.
|
||||
descCDCEndpointCount = 4 // Endpoints for all CDC-ACM configurations.
|
||||
|
||||
descCDCEndpointCtrl = 0 // CDC-ACM Control Endpoint 0
|
||||
|
||||
descCDCInterfaceCtrl = 0 // CDC-ACM Control Interface
|
||||
descCDCEndpointStatus = 1 // CDC-ACM Interrupt IN Endpoint
|
||||
descCDCConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt
|
||||
|
||||
descCDCInterfaceData = 1 // CDC-ACM Data Interface
|
||||
descCDCEndpointDataRx = 2 // CDC-ACM Bulk Data OUT (Rx) Endpoint
|
||||
descCDCConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused
|
||||
descCDCEndpointDataTx = 3 // CDC-ACM Bulk Data IN (Tx) Endpoint
|
||||
descCDCConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk
|
||||
)
|
||||
|
||||
// descCDCClass holds references to all descriptors, buffers, and control
|
||||
// structures for the USB CDC-ACM (single) device class.
|
||||
type descCDCClass struct {
|
||||
*descCDCClassData // Target-defined, class-specific data
|
||||
|
||||
locale *[descCDCLanguageCount]descStringLanguage // string descriptors
|
||||
device *[descLengthDevice]uint8 // device descriptor
|
||||
qualif *[descLengthQualification]uint8 // device qualification descriptor
|
||||
config *[descCDCConfigSize]uint8 // configuration descriptor
|
||||
}
|
||||
|
||||
// descCDC holds statically-allocated instances for each of the CDC-ACM
|
||||
// (single) device class configurations, ordered by index (offset by -1).
|
||||
var descCDC = [dcdCount]descCDCClass{
|
||||
|
||||
{ // CDC-ACM (single) class configuration index 1
|
||||
descCDCClassData: &descCDCData[0],
|
||||
|
||||
locale: &[descCDCLanguageCount]descStringLanguage{
|
||||
|
||||
{ // [0x0409] US English
|
||||
language: descLanguageEnglish,
|
||||
descriptor: descStringIndex{
|
||||
{ /* [0] Language */
|
||||
4,
|
||||
descTypeString,
|
||||
lsU8(descLanguageEnglish),
|
||||
msU8(descLanguageEnglish),
|
||||
},
|
||||
// Actual string descriptors (index > 0) are copied into here at runtime!
|
||||
// This allows for application- or even user-defined string descriptors.
|
||||
{ /* [1] Manufacturer */ },
|
||||
{ /* [2] Product */ },
|
||||
{ /* [3] Serial Number */ },
|
||||
},
|
||||
},
|
||||
},
|
||||
device: &[descLengthDevice]uint8{
|
||||
descLengthDevice, // Size of this descriptor in bytes
|
||||
descTypeDevice, // Descriptor Type
|
||||
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
|
||||
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
|
||||
descDeviceClassCodeMisc, // Class code (assigned by the USB-IF).
|
||||
descDeviceSubClassCommon, // Subclass code (assigned by the USB-IF).
|
||||
descDeviceProtocolIAD, // Protocol code (assigned by the USB-IF).
|
||||
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
|
||||
lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF)
|
||||
msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF)
|
||||
lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer)
|
||||
msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer)
|
||||
lsU8(descCommonReleaseID), // Device release number in BCD (low)
|
||||
msU8(descCommonReleaseID), // Device release number in BCD (high)
|
||||
1, // Index of string descriptor describing manufacturer
|
||||
2, // Index of string descriptor describing product
|
||||
3, // Index of string descriptor describing the device's serial number
|
||||
descCDCCount, // Number of possible configurations
|
||||
},
|
||||
qualif: &[descLengthQualification]uint8{
|
||||
descLengthQualification, // Size of this descriptor in bytes
|
||||
descTypeQualification, // Descriptor Type
|
||||
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
|
||||
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
|
||||
0, // Class code (assigned by the USB-IF).
|
||||
0, // Subclass code (assigned by the USB-IF).
|
||||
0, // Protocol code (assigned by the USB-IF).
|
||||
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
|
||||
descCDCCount, // Number of possible configurations
|
||||
0, // Reserved
|
||||
},
|
||||
config: &[descCDCConfigSize]uint8{
|
||||
descLengthConfigure, // Size of this descriptor in bytes
|
||||
descTypeConfigure, // Descriptor Type
|
||||
lsU8(descCDCConfigSize), // Total length of data returned for this configuration (low)
|
||||
msU8(descCDCConfigSize), // Total length of data returned for this configuration (high)
|
||||
descCDCInterfaceCount, // Number of interfaces supported by this configuration
|
||||
1, // Value to use to select this configuration (1 = CDC-ACM[0])
|
||||
0, // Index of string descriptor describing this configuration
|
||||
descEndptConfigAttr, // Configuration attributes
|
||||
descCDCMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units)
|
||||
|
||||
// Interface Association Descriptor
|
||||
descLengthInterfaceAssociation, // Size of this descriptor in bytes
|
||||
descTypeInterfaceAssociation, // Descriptor Type
|
||||
0x00, // bFirstInterface
|
||||
descCDCInterfaceCount, // bInterfaceCount
|
||||
0x02, // bFunctionClass : Communications and CDC Control (0x02)
|
||||
0x02, // bFunctionSubClass
|
||||
0x00, // bFunctionProtocol
|
||||
0x00, // iFunction
|
||||
|
||||
// Communication/Control Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descCDCInterfaceCtrl, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
descCDCTypeComm, // Class code
|
||||
descCDCSubAbstractControl, // Subclass code
|
||||
descCDCProtoAT250, // Protocol code (NOTE: Teensyduino & Arduino-Mbed define this as 1 [AT V.250])
|
||||
0, // Interface Description String Index
|
||||
|
||||
// CDC Header Functional Descriptor
|
||||
descCDCFuncLengthHeader, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCFuncTypeHeader, // Descriptor Subtype
|
||||
0x10, // USB CDC specification version 1.10 (low)
|
||||
0x01, // USB CDC specification version 1.10 (high)
|
||||
|
||||
// CDC Call Management Functional Descriptor
|
||||
descCDCFuncLengthCallManagement, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCFuncTypeCallManagement, // Descriptor Subtype
|
||||
0x01, // Capabilities
|
||||
descCDCInterfaceData, // Data Interface
|
||||
|
||||
// CDC Abstract Control Management Functional Descriptor
|
||||
descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCFuncTypeAbstractControl, // Descriptor Subtype
|
||||
0x06, // Capabilities
|
||||
|
||||
// CDC Union Functional Descriptor
|
||||
descCDCFuncLengthUnion, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCFuncTypeUnion, // Descriptor Subtype
|
||||
descCDCInterfaceCtrl, // Controlling interface index
|
||||
descCDCInterfaceData, // Controlled interface index
|
||||
|
||||
// Communication/Control Notification Endpoint descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descCDCEndpointStatus | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descCDCStatusPacketSize), // Max packet size (low)
|
||||
msU8(descCDCStatusPacketSize), // Max packet size (high)
|
||||
descCDCStatusInterval, // Polling Interval
|
||||
|
||||
// Data Interface Descriptor
|
||||
descLengthInterface, // Interface length
|
||||
descTypeInterface, // Interface type
|
||||
descCDCInterfaceData, // Interface index
|
||||
0, // Alternate setting
|
||||
2, // Number of endpoints
|
||||
descCDCTypeData, // Class code
|
||||
descCDCSubNone, // Subclass code
|
||||
descCDCProtoNone, // Protocol code
|
||||
0, // Interface Description String Index
|
||||
|
||||
// Data Bulk Rx Endpoint descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descCDCEndpointDataRx | // Endpoint address
|
||||
descEndptAddrDirectionOut,
|
||||
descEndptTypeBulk, // Attributes
|
||||
lsU8(descCDCDataRxPacketSize), // Max packet size (low)
|
||||
msU8(descCDCDataRxPacketSize), // Max packet size (high)
|
||||
0, // Polling Interval
|
||||
|
||||
// Data Bulk Tx Endpoint descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descCDCEndpointDataTx | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeBulk, // Attributes
|
||||
lsU8(descCDCDataTxPacketSize), // Max packet size (low)
|
||||
msU8(descCDCDataTxPacketSize), // Max packet size (high)
|
||||
0, // Polling Interval
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//go:build usb.cdc && (atsamd51 || atsame5x)
|
||||
// +build usb.cdc
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package usb
|
||||
|
||||
import "runtime/volatile"
|
||||
|
||||
// descCDCCount defines the number of USB cores that may be configured as
|
||||
// CDC-ACM (single) devices.
|
||||
const descCDCCount = 1
|
||||
|
||||
// Constants for USB CDC-ACM device classes.
|
||||
const (
|
||||
|
||||
// USB Bus Configuration Attributes
|
||||
|
||||
descCDCMaxPowerMa = 100 // Maximum current (mA) requested from host
|
||||
|
||||
// CDC-ACM Endpoint Descriptor Buffers
|
||||
|
||||
descCDCEDCount = descMaxEndpoints
|
||||
|
||||
// Setup packet is only 8 bytes in length. However, under certain scenarios,
|
||||
// USB DMA controller may decide to overwrite/overflow the buffer with 2 extra
|
||||
// bytes of CRC. From datasheet's "Management of SETUP Transactions" section:
|
||||
// | If the number of received data bytes is the maximum data payload
|
||||
// | specified by PCKSIZE.SIZE minus one, only the first CRC data is written
|
||||
// | to the data buffer. If the number of received data is equal or less
|
||||
// | than the data payload specified by PCKSIZE.SIZE minus two, both CRC
|
||||
// | data bytes are written to the data buffer.
|
||||
// Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT).
|
||||
descCDCSxSize = 8 + 2
|
||||
descCDCCxSize = descControlPacketSize
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
descCDCRxSize = descCDCDataRxPacketSize
|
||||
descCDCTxSize = descCDCDataTxPacketSize
|
||||
|
||||
descCDCTxTimeoutMs = 120 // millisec
|
||||
descCDCTxSyncUs = 75 // microsec
|
||||
|
||||
// Default CDC-ACM Endpoint Configurations (Full-Speed)
|
||||
|
||||
descCDCStatusInterval = descCDCStatusFSInterval // Status
|
||||
descCDCStatusPacketSize = descCDCStatusFSPacketSize //
|
||||
|
||||
descCDCDataRxPacketSize = descCDCDataRxFSPacketSize // Data Rx
|
||||
descCDCDataTxPacketSize = descCDCDataTxFSPacketSize // Data Tx
|
||||
|
||||
// CDC-ACM Endpoint Configurations for Full-Speed Device
|
||||
|
||||
descCDCStatusFSInterval = 5 // Status
|
||||
descCDCStatusFSPacketSize = 64 // (full-speed)
|
||||
|
||||
descCDCDataRxFSPacketSize = 64 // Data Rx (full-speed)
|
||||
descCDCDataTxFSPacketSize = 64 // Data Tx (full-speed)
|
||||
|
||||
// CDC-ACM Endpoint Configurations for High-Speed Device
|
||||
|
||||
// - N/A, SAMx51 only has a full-speed PHY
|
||||
)
|
||||
|
||||
// descCDC0ED is an array of endpoint descriptors, which describes to the USB
|
||||
// DMA controller the buffer and transfer properties for each endpoint, for the
|
||||
// default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0ED [descCDCEDCount]dhwEPAddrDesc
|
||||
|
||||
// descCDC0Sx is the receive (Rx) buffer for setup packets on control endpoint
|
||||
// 0 of the default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0Sx [descCDCSxSize]uint8
|
||||
|
||||
// descCDC0Cx is the transmit (Tx) buffer for control/status packets on control
|
||||
// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0Cx [descCDCCxSize]uint8
|
||||
|
||||
// descCDC0Dx 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 descCDC0Dx [descCDCConfigSize]uint8
|
||||
|
||||
// descCDC0Rx is the receive (Rx) transfer buffer for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0Rx [descCDCRxSize]uint8
|
||||
|
||||
// descCDC0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0Tx [descCDCTxSize]uint8
|
||||
|
||||
// descCDC0Rq is the receive (Rx) transfer buffer for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0Rq [descCDCRxSize]uint8
|
||||
|
||||
// descCDC0Tq is the transmit (Tx) transfer buffer for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0Tq [descCDCTxSize]uint8
|
||||
|
||||
// descCDC0LC is the emulated UART's line coding configuration for the
|
||||
// default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0LC descCDCLineCoding
|
||||
|
||||
// descCDC0LS is the emulated UART's line state for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDC0LS descCDCLineState
|
||||
|
||||
// descCDCState defines the state of the CDC-ACM handshake initialization.
|
||||
//
|
||||
// Many USB hosts will send a default SET_LINE_CODING prior to SET_LINE_STATE,
|
||||
// and then another SET_LINE_CODING containing the actual terminal settings.
|
||||
//
|
||||
// We do not want to start UART Rx/Tx transactions until after we have
|
||||
// received the final SET_LINE_CODING with the intended terminal settings.
|
||||
// Otherwise, the host may cancel any data transfers occurring during a change
|
||||
// in line state or line coding.
|
||||
//
|
||||
// The "set" method on type descCDCState defines this incremental state
|
||||
// machine, with the UART's current state stored in the volatile.Register8
|
||||
// field "st" of descCDCClassData.
|
||||
type descCDCState uint8
|
||||
|
||||
// set implements the state transition logic described in the godoc comment on
|
||||
// type descCDCState. Returns the value of the resulting state.
|
||||
//go:inline
|
||||
func (s *descCDCState) set(state descCDCState) descCDCState {
|
||||
if state > *s {
|
||||
// state must be incremented in-order. Otherwise, reset to initial state.
|
||||
if state == *s+1 {
|
||||
*s = state
|
||||
} else {
|
||||
var init descCDCState // Reset to zero-value of type.
|
||||
*s = init
|
||||
}
|
||||
}
|
||||
// Return a value for safely chaining the result.
|
||||
// (Not a pointer to the object we just modified.)
|
||||
return *s
|
||||
}
|
||||
|
||||
const (
|
||||
descCDCStateConfigured descCDCState = iota // Received SET_CONFIGURATION class request
|
||||
descCDCStateLineState // Received SET_LINE_STATE after Configured state
|
||||
descCDCStateLineCoding // Received SET_LINE_CODING after LineState state
|
||||
)
|
||||
|
||||
// descCDCClassData holds the buffers and control states for all CDC-ACM
|
||||
// (single) device class configurations, ordered by index (offset by -1), for
|
||||
// SAMx51 targets only.
|
||||
//
|
||||
// Instances of this type (elements of descCDCData) are embedded in elements
|
||||
// of the common/target-agnostic CDC-ACM class configurations (descCDC).
|
||||
// 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 descCDCClassData struct {
|
||||
|
||||
// CDC-ACM Control Buffers
|
||||
|
||||
ed *[descCDCEDCount]dhwEPAddrDesc // endpoint descriptors
|
||||
|
||||
sx *[descCDCSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets
|
||||
cx *[descCDCCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets
|
||||
dx *[descCDCConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
rx *[descCDCRxSize]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
||||
tx *[descCDCTxSize]uint8 // bulk data endpoint Tx (IN) transfer buffer
|
||||
|
||||
rxq *[descCDCRxSize]uint8 // CDC-ACM UART Rx FIFO
|
||||
txq *[descCDCTxSize]uint8 // CDC-ACM UART Tx FIFO
|
||||
|
||||
rq *Queue // CDC-ACM UART Rx Queue (backed by FIFO rxq)
|
||||
tq *Queue // CDC-ACM UART Tx Queue (backed by FIFO txq)
|
||||
|
||||
lc *descCDCLineCoding // UART line coding
|
||||
ls *descCDCLineState // UART line state
|
||||
|
||||
st volatile.Register8
|
||||
|
||||
sxSize uint32
|
||||
rxSize uint32
|
||||
txSize uint32
|
||||
}
|
||||
|
||||
// setState is a wrapper for converting and storing the given descCDCState
|
||||
// value as a uint8 in the receiver's volatile.Register8 field st.
|
||||
//go:inline
|
||||
func (c *descCDCClassData) setState(state descCDCState) {
|
||||
s := descCDCState(c.st.Get())
|
||||
c.st.Set(uint8(s.set(state)))
|
||||
}
|
||||
|
||||
// state is a wrapper for retrieving and converting the receiver's
|
||||
// volatile.Register8 field st from uint8 to descCDCState.
|
||||
//go:inline
|
||||
func (c *descCDCClassData) state() descCDCState {
|
||||
return descCDCState(c.st.Get())
|
||||
}
|
||||
|
||||
// descCDCData holds statically-allocated instances for each of the target-
|
||||
// specific (SAMx51) 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 descCDC.
|
||||
var descCDCData = [dcdCount]descCDCClassData{
|
||||
|
||||
{ // -- CDC-ACM (single) Class Configuration Index 1 --
|
||||
|
||||
// CDC-ACM Control Buffers
|
||||
|
||||
ed: &descCDC0ED,
|
||||
|
||||
sx: &descCDC0Sx,
|
||||
cx: &descCDC0Cx,
|
||||
dx: &descCDC0Dx,
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
rx: &descCDC0Rx,
|
||||
tx: &descCDC0Tx,
|
||||
|
||||
rxq: &descCDC0Rq,
|
||||
txq: &descCDC0Tq,
|
||||
|
||||
rq: &Queue{},
|
||||
tq: &Queue{},
|
||||
|
||||
lc: &descCDC0LC,
|
||||
ls: &descCDC0LS,
|
||||
|
||||
sxSize: descCDCStatusPacketSize,
|
||||
rxSize: descCDCDataRxPacketSize,
|
||||
txSize: descCDCDataTxPacketSize,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
//go:build usb.hid
|
||||
// +build usb.hid
|
||||
|
||||
package usb
|
||||
|
||||
// descCDCCount defines the number of USB cores that may be configured as
|
||||
// CDC-ACM (single) devices.
|
||||
const descCDCCount = 0
|
||||
|
||||
// USB HID constants defined per specification
|
||||
const (
|
||||
// HID class
|
||||
descHIDType = 0x03
|
||||
|
||||
// HID subclass
|
||||
descHIDSubNone = 0x00
|
||||
descHIDSubBoot = 0x01
|
||||
|
||||
// HID protocol
|
||||
descHIDProtoNone = 0x00
|
||||
descHIDProtoKeyboard = 0x01
|
||||
descHIDProtoMouse = 0x02
|
||||
|
||||
descHIDRequestGetReport = 0x01 // HID request GET_REPORT
|
||||
descHIDRequestGetReportTypeInput = 0x01 // HID request GET_REPORT type INPUT
|
||||
descHIDRequestGetReportTypeOutput = 0x02 // HID request GET_REPORT type OUTPUT
|
||||
descHIDRequestGetReportTypeFeature = 0x03 // HID request GET_REPORT type FEATURE
|
||||
descHIDRequestGetIdle = 0x02 // HID request GET_IDLE
|
||||
descHIDRequestGetProtocol = 0x03 // HID request GET_PROTOCOL
|
||||
descHIDRequestSetReport = 0x09 // HID request SET_REPORT
|
||||
descHIDRequestSetIdle = 0x0A // HID request SET_IDLE
|
||||
descHIDRequestSetProtocol = 0x0B // HID request SET_PROTOCOL
|
||||
)
|
||||
|
||||
const (
|
||||
// Size of all HID configuration descriptors.
|
||||
descHIDConfigSize = uint16(
|
||||
descLengthConfigure + // Configuration Header
|
||||
descLengthInterface + // Keyboard Interface Descriptor
|
||||
descLengthInterface + // Keyboard HID Interface Descriptor
|
||||
descLengthEndpoint + // Keyboard Endpoint Descriptor
|
||||
descLengthInterface + // Mouse Interface Descriptor
|
||||
descLengthInterface + // Mouse HID Interface Descriptor
|
||||
descLengthEndpoint + // Mouse Endpoint Descriptor
|
||||
descLengthInterface + // Serial Interface Descriptor
|
||||
descLengthInterface + // Serial HID Interface Descriptor
|
||||
descLengthEndpoint + // Serial Tx Endpoint Descriptor
|
||||
descLengthEndpoint + // Serial Rx Endpoint Descriptor
|
||||
descLengthInterface + // Joystick Interface Descriptor
|
||||
descLengthInterface + // Joystick HID Interface Descriptor
|
||||
descLengthEndpoint + // Joystick Endpoint Descriptor
|
||||
descLengthInterface + // Keyboard Media Keys Interface Descriptor
|
||||
descLengthInterface + // Keyboard Media Keys HID Interface Descriptor
|
||||
descLengthEndpoint) // Keyboard Media Keys Endpoint Descriptor
|
||||
|
||||
// Position of each HID interface descriptor as offsets into the configuration
|
||||
// descriptor. See comments in the configuration descriptor definition for the
|
||||
// incremental tally that computes these.
|
||||
descHIDConfigKeyboardPos = 18
|
||||
descHIDConfigMousePos = 43
|
||||
descHIDConfigSerialPos = 68
|
||||
descHIDConfigJoystickPos = 100
|
||||
descHIDConfigMediaKeyPos = 125
|
||||
)
|
||||
|
||||
// Common configuration constants for the USB HID device class.
|
||||
const (
|
||||
descHIDLanguageCount = 1 // String descriptor languages available
|
||||
|
||||
descHIDInterfaceCount = 5 // Interfaces for all HID configurations.
|
||||
descHIDEndpointCount = 6 // Endpoints for all HID configurations.
|
||||
|
||||
descHIDEndpointCtrl = 0 // HID Control Endpoint 0
|
||||
|
||||
descHIDInterfaceKeyboard = 0 // HID Keyboard Interface
|
||||
descHIDEndpointKeyboard = 3 // HID Keyboard IN Endpoint
|
||||
descHIDConfigAttrKeyboard = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
|
||||
|
||||
descHIDInterfaceMouse = 1 // HID Mouse Interface
|
||||
descHIDEndpointMouse = 5 // HID Mouse IN Endpoint
|
||||
descHIDConfigAttrMouse = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
|
||||
|
||||
descHIDInterfaceSerial = 2 // HID Serial (UART emulation) Interface
|
||||
descHIDEndpointSerialRx = 2 // HID Serial OUT (Rx) Endpoint
|
||||
descHIDEndpointSerialTx = 2 // HID Serial IN (Tx) Endpoint
|
||||
descHIDConfigAttrSerial = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxInterrupt
|
||||
|
||||
descHIDInterfaceJoystick = 3 // HID Joystick Interface
|
||||
descHIDEndpointJoystick = 6 // HID Joystick IN Endpoint
|
||||
descHIDConfigAttrJoystick = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
|
||||
|
||||
descHIDInterfaceMediaKey = 4 // HID Keyboard Media Keys Interface
|
||||
descHIDEndpointMediaKey = 4 // HID Keyboard Media Keys IN Endpoint
|
||||
descHIDConfigAttrMediaKey = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
|
||||
)
|
||||
|
||||
// descHIDClass holds references to all descriptors, buffers, and control
|
||||
// structures for the USB HID device class.
|
||||
type descHIDClass struct {
|
||||
*descHIDClassData // Target-defined, class-specific data
|
||||
|
||||
locale *[descHIDLanguageCount]descStringLanguage // string descriptors
|
||||
device *[descLengthDevice]uint8 // device descriptor
|
||||
qualif *[descLengthQualification]uint8 // device qualification descriptor
|
||||
config *[descHIDConfigSize]uint8 // configuration descriptor
|
||||
}
|
||||
|
||||
// descHID holds statically-allocated instances for each of the HID device class
|
||||
// configurations, ordered by index (offset by -1).
|
||||
var descHID = [dcdCount]descHIDClass{
|
||||
|
||||
{ // HID class configuration index 1
|
||||
descHIDClassData: &descHIDData[0],
|
||||
|
||||
locale: &[descHIDLanguageCount]descStringLanguage{
|
||||
|
||||
{ // [0x0409] US English
|
||||
language: descLanguageEnglish,
|
||||
descriptor: descStringIndex{
|
||||
{ /* [0] Language */
|
||||
4,
|
||||
descTypeString,
|
||||
lsU8(descLanguageEnglish),
|
||||
msU8(descLanguageEnglish),
|
||||
},
|
||||
// Actual string descriptors (index > 0) are copied into here at runtime!
|
||||
// This allows for application- or even user-defined string descriptors.
|
||||
{ /* [1] Manufacturer */ },
|
||||
{ /* [2] Product */ },
|
||||
{ /* [3] Serial Number */ },
|
||||
},
|
||||
},
|
||||
},
|
||||
device: &[descLengthDevice]uint8{
|
||||
descLengthDevice, // Size of this descriptor in bytes
|
||||
descTypeDevice, // Descriptor Type
|
||||
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
|
||||
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
|
||||
0, // Class code (assigned by the USB-IF).
|
||||
0, // Subclass code (assigned by the USB-IF).
|
||||
0, // Protocol code (assigned by the USB-IF).
|
||||
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
|
||||
lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF)
|
||||
msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF)
|
||||
lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer)
|
||||
msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer)
|
||||
lsU8(descCommonReleaseID), // Device release number in BCD (low)
|
||||
msU8(descCommonReleaseID), // Device release number in BCD (high)
|
||||
1, // Index of string descriptor describing manufacturer
|
||||
2, // Index of string descriptor describing product
|
||||
3, // Index of string descriptor describing the device's serial number
|
||||
descHIDCount, // Number of possible configurations
|
||||
},
|
||||
qualif: &[descLengthQualification]uint8{
|
||||
descLengthQualification, // Size of this descriptor in bytes
|
||||
descTypeQualification, // Descriptor Type
|
||||
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
|
||||
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
|
||||
0, // Class code (assigned by the USB-IF).
|
||||
0, // Subclass code (assigned by the USB-IF).
|
||||
0, // Protocol code (assigned by the USB-IF).
|
||||
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
|
||||
descHIDCount, // Number of possible configurations
|
||||
0, // Reserved
|
||||
},
|
||||
config: &[descHIDConfigSize]uint8{
|
||||
// [0+9]
|
||||
descLengthConfigure, // Size of this descriptor in bytes
|
||||
descTypeConfigure, // Descriptor Type
|
||||
lsU8(descHIDConfigSize), // Total length of data returned for this configuration (low)
|
||||
msU8(descHIDConfigSize), // Total length of data returned for this configuration (high)
|
||||
descHIDInterfaceCount, // Number of interfaces supported by this configuration
|
||||
1, // Value to use to select this configuration (1 = CDC-ACM[0])
|
||||
0, // Index of string descriptor describing this configuration
|
||||
descEndptConfigAttr, // Configuration attributes
|
||||
descHIDMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units)
|
||||
|
||||
// [9+9] Keyboard Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descHIDInterfaceKeyboard, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
descHIDType, // Class code (HID = 0x03)
|
||||
descHIDSubBoot, // Subclass code (Boot = 0x01)
|
||||
descHIDProtoKeyboard, // Protocol code (Keyboard = 0x01)
|
||||
0, // Interface Description String Index
|
||||
|
||||
// [18+9] Keyboard HID Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeHID, // Descriptor type
|
||||
0x11, // HID BCD (low)
|
||||
0x01, // HID BCD (high)
|
||||
0, // Country code
|
||||
1, // Number of descriptors
|
||||
descTypeHIDReport, // Descriptor type
|
||||
lsU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (low)
|
||||
msU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (high)
|
||||
|
||||
// [27+7] Keyboard Endpoint Descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descHIDEndpointKeyboard | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descHIDKeyboardTxPacketSize), // Max packet size (low)
|
||||
msU8(descHIDKeyboardTxPacketSize), // Max packet size (high)
|
||||
descHIDKeyboardTxInterval, // Polling Interval
|
||||
|
||||
// [34+9] Mouse Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descHIDInterfaceMouse, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
descHIDType, // Class code (HID = 0x03)
|
||||
descHIDSubBoot, // Subclass code (Boot = 0x01)
|
||||
descHIDProtoMouse, // Protocol code (Mouse = 0x02)
|
||||
0, // Interface Description String Index
|
||||
|
||||
// [43+9] Mouse HID Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeHID, // Descriptor type
|
||||
0x11, // HID BCD (low)
|
||||
0x01, // HID BCD (high)
|
||||
0, // Country code
|
||||
1, // Number of descriptors
|
||||
descTypeHIDReport, // Descriptor type
|
||||
lsU8(uint16(len(descHIDReportMouse))), // Descriptor length (low)
|
||||
msU8(uint16(len(descHIDReportMouse))), // Descriptor length (high)
|
||||
|
||||
// [52+7] Mouse Endpoint Descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descHIDEndpointMouse | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descHIDMouseTxPacketSize), // Max packet size (low)
|
||||
msU8(descHIDMouseTxPacketSize), // Max packet size (high)
|
||||
descHIDMouseTxInterval, // Polling Interval
|
||||
|
||||
// [59+9] Serial Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descHIDInterfaceSerial, // Interface index
|
||||
0, // Alternate setting
|
||||
2, // Number of endpoints
|
||||
descHIDType, // Class code (HID = 0x03)
|
||||
descHIDSubNone, // Subclass code
|
||||
descHIDProtoNone, // Protocol code
|
||||
0, // Interface Description String Index
|
||||
|
||||
// [68+9] Serial HID Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeHID, // Descriptor type
|
||||
0x11, // HID BCD (low)
|
||||
0x01, // HID BCD (high)
|
||||
0, // Country code
|
||||
1, // Number of descriptors
|
||||
descTypeHIDReport, // Descriptor type
|
||||
lsU8(uint16(len(descHIDReportSerial))), // Descriptor length (low)
|
||||
msU8(uint16(len(descHIDReportSerial))), // Descriptor length (high)
|
||||
|
||||
// [77+7] Serial Tx Endpoint Descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descHIDEndpointSerialTx | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descHIDSerialTxPacketSize), // Max packet size (low)
|
||||
msU8(descHIDSerialTxPacketSize), // Max packet size (high)
|
||||
descHIDSerialTxInterval, // Polling Interval
|
||||
|
||||
// [84+7] Serial Rx Endpoint Descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descHIDEndpointSerialRx | // Endpoint address
|
||||
descEndptAddrDirectionOut,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descHIDSerialRxPacketSize), // Max packet size (low)
|
||||
msU8(descHIDSerialRxPacketSize), // Max packet size (high)
|
||||
descHIDSerialRxInterval, // Polling Interval
|
||||
|
||||
// [91+9] Joystick Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descHIDInterfaceJoystick, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
descHIDType, // Class code (HID = 0x03)
|
||||
descHIDSubNone, // Subclass code
|
||||
descHIDProtoNone, // Protocol code
|
||||
0, // Interface Description String Index
|
||||
|
||||
// [100+9] Joystick HID Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeHID, // Descriptor type
|
||||
0x11, // HID BCD (low)
|
||||
0x01, // HID BCD (high)
|
||||
0, // Country code
|
||||
1, // Number of descriptors
|
||||
descTypeHIDReport, // Descriptor type
|
||||
lsU8(uint16(len(descHIDReportJoystick))), // Descriptor length (low)
|
||||
msU8(uint16(len(descHIDReportJoystick))), // Descriptor length (high)
|
||||
|
||||
// [109+7] Joystick Endpoint Descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descHIDEndpointJoystick | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descHIDJoystickTxPacketSize), // Max packet size (low)
|
||||
msU8(descHIDJoystickTxPacketSize), // Max packet size (high)
|
||||
descHIDJoystickTxInterval, // Polling Interval
|
||||
|
||||
// [116+9] Keyboard Media Keys Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descHIDInterfaceMediaKey, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
descHIDType, // Class code (HID = 0x03)
|
||||
descHIDSubNone, // Subclass code
|
||||
descHIDProtoNone, // Protocol code
|
||||
0, // Interface Description String Index
|
||||
|
||||
// [125+9] Keyboard Media Keys HID Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeHID, // Descriptor type
|
||||
0x11, // HID BCD (low)
|
||||
0x01, // HID BCD (high)
|
||||
0, // Country code
|
||||
1, // Number of descriptors
|
||||
descTypeHIDReport, // Descriptor type
|
||||
lsU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (low)
|
||||
msU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (high)
|
||||
|
||||
// [134+7] Keyboard Media Keys Endpoint Descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descHIDEndpointMediaKey | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descHIDMediaKeyTxPacketSize), // Max packet size (low)
|
||||
msU8(descHIDMediaKeyTxPacketSize), // Max packet size (high)
|
||||
descHIDMediaKeyTxInterval, // Polling Interval
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var descHIDReportSerial = [...]uint8{
|
||||
0x06, 0xC9, 0xFF, // Usage Page 0xFFC9 (vendor defined)
|
||||
0x09, 0x04, // Usage 0x04
|
||||
0xA1, 0x5C, // Collection 0x5C
|
||||
0x75, 0x08, // report size = 8 bits (global)
|
||||
0x15, 0x00, // logical minimum = 0 (global)
|
||||
0x26, 0xFF, 0x00, // logical maximum = 255 (global)
|
||||
0x95, descHIDSerialTxPacketSize, // report count (global)
|
||||
0x09, 0x75, // usage (local)
|
||||
0x81, 0x02, // Input
|
||||
0x95, descHIDSerialRxPacketSize, // report count (global)
|
||||
0x09, 0x76, // usage (local)
|
||||
0x91, 0x02, // Output
|
||||
0x95, 0x04, // report count (global)
|
||||
0x09, 0x76, // usage (local)
|
||||
0xB1, 0x02, // Feature
|
||||
0xC0, // end collection
|
||||
}
|
||||
|
||||
var descHIDReportKeyboard = [...]uint8{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x05, 0x07, // Usage Page (Key Codes)
|
||||
0x19, 0xE0, // Usage Minimum (224)
|
||||
0x29, 0xE7, // Usage Maximum (231)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute) [Modifier keys]
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x81, 0x03, // Input (Constant) [Reserved byte]
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x08, // Usage Page (LEDs)
|
||||
0x19, 0x01, // Usage Minimum (1)
|
||||
0x29, 0x05, // Usage Maximum (5)
|
||||
0x91, 0x02, // Output (Data, Variable, Absolute) [LED report]
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x91, 0x03, // Output (Constant) [LED report padding]
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x7F, // Logical Maximum(104)
|
||||
0x05, 0x07, // Usage Page (Key Codes)
|
||||
0x19, 0x00, // Usage Minimum (0)
|
||||
0x29, 0x7F, // Usage Maximum (104)
|
||||
0x81, 0x00, // Input (Data, Array) [Normal keys]
|
||||
0xC0, // End Collection
|
||||
}
|
||||
|
||||
var descHIDReportMediaKey = [...]uint8{
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Controls)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x75, 0x0A, // Report Size (10)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x19, 0x00, // Usage Minimum (0)
|
||||
0x2A, 0x9C, 0x02, // Usage Maximum (0x29C)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0x9C, 0x02, // Logical Maximum (0x29C)
|
||||
0x81, 0x00, // Input (Data, Array)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x19, 0x00, // Usage Minimum (0)
|
||||
0x29, 0xB7, // Usage Maximum (0xB7)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xB7, 0x00, // Logical Maximum (0xB7)
|
||||
0x81, 0x00, // Input (Data, Array)
|
||||
0xC0, // End Collection
|
||||
}
|
||||
|
||||
var descHIDReportMouse = [...]uint8{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x02, // Usage (Mouse)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // REPORT_ID (1)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (Button #1)
|
||||
0x29, 0x08, // Usage Maximum (Button #8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x38, // Usage (Wheel)
|
||||
0x15, 0x81, // Logical Minimum (-127)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8),
|
||||
0x95, 0x03, // Report Count (3),
|
||||
0x81, 0x06, // Input (Data, Variable, Relative)
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x0A, 0x38, 0x02, // Usage (AC Pan)
|
||||
0x15, 0x81, // Logical Minimum (-127)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8),
|
||||
0x95, 0x01, // Report Count (1),
|
||||
0x81, 0x06, // Input (Data, Variable, Relative)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x02, // Usage (Mouse)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x02, // REPORT_ID (2)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
|
||||
0x75, 0x10, // Report Size (16),
|
||||
0x95, 0x02, // Report Count (2),
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||
0xC0, // End Collection
|
||||
}
|
||||
|
||||
var descHIDReportJoystick = [...]uint8{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x04, // Usage (Joystick)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x20, // Report Count (32)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (Button #1)
|
||||
0x29, 0x20, // Usage Maximum (Button #32)
|
||||
0x81, 0x02, // Input (variable,absolute)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x07, // Logical Maximum (7)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x65, 0x14, // Unit (20)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x39, // Usage (Hat switch)
|
||||
0x81, 0x42, // Input (variable,absolute,null_state)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection ()
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
0x75, 0x0A, // Report Size (10)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x81, 0x02, // Input (variable,absolute)
|
||||
0xC0, // End Collection
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
0x75, 0x0A, // Report Size (10)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x09, 0x36, // Usage (Slider)
|
||||
0x09, 0x36, // Usage (Slider)
|
||||
0x81, 0x02, // Input (variable,absolute)
|
||||
0xC0, // End Collection
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
//go:build usb.hid && (atsamd51 || atsame5x)
|
||||
// +build usb.hid
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package usb
|
||||
|
||||
// descHIDCount defines the number of USB cores that may be configured as a
|
||||
// composite (keyboard + mouse + joystick) human interface device (HID).
|
||||
const descHIDCount = 1
|
||||
|
||||
// Constants for USB HID (keyboard, mouse, joystick) device classes.
|
||||
const (
|
||||
|
||||
// USB Bus Configuration Attributes
|
||||
|
||||
descHIDMaxPowerMa = 100 // Maximum current (mA) requested from host
|
||||
|
||||
// HID Endpoint Descriptor Buffers
|
||||
|
||||
descHIDEDCount = descMaxEndpoints
|
||||
|
||||
// Setup packet is only 8 bytes in length. However, under certain scenarios,
|
||||
// USB DMA controller may decide to overwrite/overflow the buffer with 2 extra
|
||||
// bytes of CRC. From datasheet's "Management of SETUP Transactions" section:
|
||||
// | If the number of received data bytes is the maximum data payload
|
||||
// | specified by PCKSIZE.SIZE minus one, only the first CRC data is written
|
||||
// | to the data buffer. If the number of received data is equal or less
|
||||
// | than the data payload specified by PCKSIZE.SIZE minus two, both CRC
|
||||
// | data bytes are written to the data buffer.
|
||||
// Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT).
|
||||
descHIDSxSize = 8 + 2
|
||||
descHIDCxSize = descControlPacketSize
|
||||
|
||||
// HID Serial Buffers
|
||||
|
||||
descHIDSerialRxSize = descHIDSerialRxPacketSize
|
||||
descHIDSerialTxSize = descHIDSerialTxPacketSize
|
||||
|
||||
descHIDSerialTxTimeoutMs = 50 // millisec
|
||||
descHIDSerialTxSyncUs = 75 // microsec
|
||||
|
||||
// HID Keyboard Buffers
|
||||
|
||||
descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize
|
||||
|
||||
descHIDKeyboardTxTimeoutMs = 50 // millisec
|
||||
|
||||
// HID Mouse Buffers
|
||||
|
||||
descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize
|
||||
|
||||
descHIDMouseTxTimeoutMs = 30 // millisec
|
||||
|
||||
// HID Joystick Buffers
|
||||
|
||||
descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize
|
||||
|
||||
descHIDJoystickTxTimeoutMs = 30 // millisec
|
||||
|
||||
// Default HID Endpoint Configurations (Full-Speed)
|
||||
|
||||
descHIDSerialRxInterval = descHIDSerialRxFSInterval // Serial Rx
|
||||
descHIDSerialRxPacketSize = descHIDSerialRxFSPacketSize //
|
||||
|
||||
descHIDSerialTxInterval = descHIDSerialTxFSInterval // Serial Tx
|
||||
descHIDSerialTxPacketSize = descHIDSerialTxFSPacketSize //
|
||||
|
||||
descHIDKeyboardTxInterval = descHIDKeyboardTxFSInterval // Keyboard
|
||||
descHIDKeyboardTxPacketSize = descHIDKeyboardTxFSPacketSize //
|
||||
|
||||
descHIDMediaKeyTxInterval = descHIDMediaKeyTxFSInterval // Keyboard Media Keys
|
||||
descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxFSPacketSize //
|
||||
|
||||
descHIDMouseTxInterval = descHIDMouseTxFSInterval // Mouse
|
||||
descHIDMouseTxPacketSize = descHIDMouseTxFSPacketSize //
|
||||
|
||||
descHIDJoystickTxInterval = descHIDJoystickTxFSInterval // Joystick
|
||||
descHIDJoystickTxPacketSize = descHIDJoystickTxFSPacketSize //
|
||||
|
||||
// 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
|
||||
|
||||
// - N/A, SAMx51 only has a full-speed PHY
|
||||
)
|
||||
|
||||
// descHID0ED is an array of endpoint descriptors, which describes to the USB
|
||||
// DMA controller the buffer and transfer properties for each endpoint, for the
|
||||
// default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0ED [descHIDEDCount]dhwEPAddrDesc
|
||||
|
||||
// descHID0Sx is the receive (Rx) buffer for setup packets on control endpoint 0
|
||||
// of the default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0Sx [descHIDSxSize]uint8
|
||||
|
||||
// descHID0Cx is the transmit (Tx) buffer for control/status packets on control
|
||||
// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0Cx [descHIDCxSize]uint8
|
||||
|
||||
// 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
|
||||
|
||||
// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default
|
||||
// HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
// var descHID0SerialRx [descHIDSerialRxSize]uint8
|
||||
|
||||
// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default
|
||||
// HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
// var descHID0SerialTx [descHIDSerialTxSize]uint8
|
||||
|
||||
// descHID0KeyboardTx is the keyboard HID report transmit (Tx) transfer buffer
|
||||
// for the default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descHID0KeyboardTx [descHIDKeyboardTxPacketSize]uint8
|
||||
|
||||
// descHID0KeyboardTq is the keyboard transmit (Tx) transfer buffer for the
|
||||
// default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
// var descHID0KeyboardTq [descHIDKeyboardTxSize]uint8
|
||||
|
||||
// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default
|
||||
// HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
// var descHID0MouseTx [descHIDMouseTxSize]uint8
|
||||
|
||||
// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the
|
||||
// default HID device class configuration (index 1).
|
||||
//go:align 32
|
||||
// var descHID0JoystickTx [descHIDJoystickTxSize]uint8
|
||||
|
||||
var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8
|
||||
var descHID0KeyboardTxCon [hidKeyboardConCount]uint16
|
||||
var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8
|
||||
|
||||
// descHID0Keyboard is the Keyboard instance with which the user may interact
|
||||
// when using the default HID device class configuration (index 1).
|
||||
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 SAMx51
|
||||
// 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
|
||||
|
||||
ed *[descHIDEDCount]dhwEPAddrDesc // endpoint descriptors
|
||||
|
||||
sx *[descHIDSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets
|
||||
cx *[descHIDCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets
|
||||
dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
|
||||
|
||||
// HID Serial Buffers
|
||||
|
||||
// rxSerial *[descHIDSerialRxSize]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer
|
||||
// txSerial *[descHIDSerialTxSize]uint8 // interrupt endpoint serial Tx (IN) transfer buffer
|
||||
|
||||
// rxSerialSize uint16
|
||||
// txSerialSize uint16
|
||||
|
||||
// HID Keyboard Buffers
|
||||
|
||||
txKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report buffer
|
||||
// txqKeyboard *[descHIDKeyboardTxSize]uint8 // interrupt endpoint keyboard Tx (IN) transfer FIFO
|
||||
// tqKeyboard *Queue
|
||||
|
||||
txKeyboardSize uint16
|
||||
|
||||
// HID Mouse Buffers
|
||||
|
||||
// txMouse *[descHIDMouseTxSize]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer
|
||||
|
||||
// txMouseSize uint16
|
||||
|
||||
// HID Joystick Buffers
|
||||
|
||||
// txJoystick *[descHIDJoystickTxSize]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer
|
||||
|
||||
// txJoystickSize uint16
|
||||
|
||||
// HID Device Instances
|
||||
|
||||
//serial *Serial
|
||||
keyboard *Keyboard
|
||||
//mouse *Mouse
|
||||
//joystick *Joystick
|
||||
}
|
||||
|
||||
// descHIDData holds statically-allocated instances for each of the target-
|
||||
// specific (SAMx51) 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.
|
||||
var descHIDData = [dcdCount]descHIDClassData{
|
||||
|
||||
{ // -- HID Class Configuration Index 1 --
|
||||
|
||||
// HID Control Buffers
|
||||
|
||||
ed: &descHID0ED,
|
||||
|
||||
sx: &descHID0Sx,
|
||||
cx: &descHID0Cx,
|
||||
dx: &descHID0Dx,
|
||||
|
||||
// HID Serial Buffers
|
||||
|
||||
// rxSerial: &descHID0SerialRx,
|
||||
// txSerial: &descHID0SerialTx,
|
||||
|
||||
// rxSerialSize: descHIDSerialRxPacketSize,
|
||||
// txSerialSize: descHIDSerialTxPacketSize,
|
||||
|
||||
// HID Keyboard Buffers
|
||||
|
||||
txKeyboard: &descHID0KeyboardTx,
|
||||
// txqKeyboard: &descHID0KeyboardTq,
|
||||
// tqKeyboard: &Queue{},
|
||||
|
||||
txKeyboardSize: descHIDKeyboardTxPacketSize,
|
||||
|
||||
// HID Mouse Buffers
|
||||
|
||||
// txMouse: &descHID0MouseTx,
|
||||
|
||||
// txMouseSize: descHIDMouseTxPacketSize,
|
||||
|
||||
// HID Joystick Buffers
|
||||
|
||||
// txJoystick: &descHID0JoystickTx,
|
||||
|
||||
// txJoystickSize: descHIDJoystickTxPacketSize,
|
||||
|
||||
// HID Device Instances
|
||||
|
||||
//serial: &descHID0Serial,
|
||||
keyboard: &descHID0Keyboard,
|
||||
//mouse: &descHID0Mouse,
|
||||
//joystick: &descHID0Joystick,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package usb
|
||||
|
||||
const descUSBSpecVersion = uint16(0x0200) // USB 2.0
|
||||
|
||||
const descLanguageEnglish = uint16(0x0409) // (US) English
|
||||
|
||||
// USB constants defined per specification.
|
||||
const (
|
||||
|
||||
// Descriptor length
|
||||
descLengthDevice = 18
|
||||
descLengthConfigure = 9
|
||||
descLengthInterface = 9
|
||||
descLengthInterfaceAssociation = 8
|
||||
descLengthEndpoint = 7
|
||||
descLengthQualification = 10
|
||||
descLengthOTG = 5
|
||||
descLengthBOS = 5
|
||||
descLengthEndpointCompanion = 6
|
||||
descLengthUSB20Extension = 7
|
||||
descLengthSuperspeed = 10
|
||||
|
||||
// Descriptor type
|
||||
descTypeDevice = 0x01
|
||||
descTypeConfigure = 0x02
|
||||
descTypeString = 0x03
|
||||
descTypeInterface = 0x04
|
||||
descTypeEndpoint = 0x05
|
||||
descTypeQualification = 0x06
|
||||
descTypeOtherSpeedConfiguration = 0x07
|
||||
descTypeInterfacePower = 0x08
|
||||
descTypeOTG = 0x09
|
||||
descTypeInterfaceAssociation = 0x0B
|
||||
descTypeBOS = 0x0F
|
||||
descTypeDeviceCapability = 0x10
|
||||
descTypeHID = 0x21
|
||||
descTypeHIDReport = 0x22
|
||||
descTypeHIDPhysical = 0x23
|
||||
descTypeCDCInterface = 0x24
|
||||
descTypeCDCEndpoint = 0x25
|
||||
descTypeEndpointCompanion = 0x30
|
||||
|
||||
// Standard request type
|
||||
descRequestTypeDirMsk = 0x80
|
||||
descRequestTypeDirPos = 7
|
||||
descRequestTypeDirOut = 0x00
|
||||
descRequestTypeDirIn = 0x80
|
||||
descRequestTypeTypeMsk = 0x60
|
||||
descRequestTypeTypePos = 5
|
||||
descRequestTypeTypeStandard = 0
|
||||
descRequestTypeTypeClass = 0x20
|
||||
descRequestTypeTypeVendor = 0x40
|
||||
descRequestTypeRecipientMsk = 0x1F
|
||||
descRequestTypeRecipientPos = 0
|
||||
descRequestTypeRecipientDevice = 0x00
|
||||
descRequestTypeRecipientInterface = 0x01
|
||||
descRequestTypeRecipientEndpoint = 0x02
|
||||
descRequestTypeRecipientOther = 0x03
|
||||
|
||||
// Standard request
|
||||
descRequestStandardGetStatus = 0x00
|
||||
descRequestStandardClearFeature = 0x01
|
||||
descRequestStandardSetFeature = 0x03
|
||||
descRequestStandardSetAddress = 0x05
|
||||
descRequestStandardGetDescriptor = 0x06
|
||||
descRequestStandardSetDescriptor = 0x07
|
||||
descRequestStandardGetConfiguration = 0x08
|
||||
descRequestStandardSetConfiguration = 0x09
|
||||
descRequestStandardGetInterface = 0x0A
|
||||
descRequestStandardSetInterface = 0x0B
|
||||
descRequestStandardSynchFrame = 0x0C
|
||||
|
||||
// Configuration attributes
|
||||
descConfigAttrD7Msk = 0x80
|
||||
descConfigAttrD7Pos = 7
|
||||
descConfigAttrSelfPoweredMsk = 0x40
|
||||
descConfigAttrSelfPoweredPos = 6
|
||||
descConfigAttrRemoteWakeupMsk = 0x20
|
||||
descConfigAttrRemoteWakeupPos = 5
|
||||
|
||||
// Endpoint type
|
||||
descEndptTypeControl = 0x00
|
||||
descEndptTypeIsochronous = 0x01
|
||||
descEndptTypeBulk = 0x02
|
||||
descEndptTypeInterrupt = 0x03
|
||||
|
||||
// Endpoint address
|
||||
descEndptAddrNumberMsk = 0x0F
|
||||
descEndptAddrNumberPos = 0
|
||||
descEndptAddrDirectionMsk = 0x80
|
||||
descEndptAddrDirectionPos = 7
|
||||
descEndptAddrDirectionOut = 0
|
||||
descEndptAddrDirectionIn = 0x80
|
||||
|
||||
// Endpoint attributes
|
||||
descEndptAttrTypeMsk = 0x03
|
||||
descEndptAttrNumberPos = 0
|
||||
descEndptAttrSyncTypeMsk = 0x0C
|
||||
descEndptAttrSyncTypePos = 2
|
||||
descEndptAttrSyncTypeNoSync = 0x00
|
||||
descEndptAttrSyncTypeAsync = 0x04
|
||||
descEndptAttrSyncTypeAdaptive = 0x08
|
||||
descEndptAttrSyncTypeSync = 0x0C
|
||||
descEndptAttrUsageTypeMsk = 0x30
|
||||
descEndptAttrUsageTypePos = 4
|
||||
descEndptAttrUsageTypeData = 0x00
|
||||
descEndptAttrUsageTypeFeed = 0x10
|
||||
descEndptAttrUsageTypeFeedData = 0x20
|
||||
|
||||
// Endpoint max packet size
|
||||
descEndptMaxPktSizeMsk = 0x07FF
|
||||
descEndptMaxPktSize = 64
|
||||
descEndptMaxPktSizeMultMsk = 0x1800
|
||||
descEndptMaxPktSizeMultPos = 11
|
||||
|
||||
// OTG attributes
|
||||
descOTGAttrSRPMsk = 0x01
|
||||
descOTGAttrHNPMsk = 0x02
|
||||
descOTGAttrADPMsk = 0x04
|
||||
|
||||
// Device bus speed
|
||||
descDeviceSpeedFull = 0x00
|
||||
descDeviceSpeedLow = 0x01
|
||||
descDeviceSpeedHigh = 0x02
|
||||
descDeviceSpeedSuper = 0x04
|
||||
|
||||
// Device capability type
|
||||
descDeviceCapTypeWireless = 0x01
|
||||
descDeviceCapTypeUSB20Extension = 0x02
|
||||
descDeviceCapTypeSuperspeed = 0x03
|
||||
|
||||
// Device capability attributes (USB 2.0 extension)
|
||||
descDeviceCapExtAttrLPMMsk = 0x02
|
||||
descDeviceCapExtAttrLPMPos = 1
|
||||
descDeviceCapExtAttrBESLMsk = 0x04
|
||||
descDeviceCapExtAttrBESLPos = 2
|
||||
|
||||
// Device class
|
||||
descDeviceClassCodeMisc = 0xEF
|
||||
descDeviceSubClassCommon = 0x02
|
||||
descDeviceProtocolIAD = 0x01
|
||||
)
|
||||
|
||||
// descEndpointInvalid represents an invalid endpoint address.
|
||||
const descEndpointInvalid = ^uint8(descEndptAddrNumberMsk | descEndptAddrDirectionMsk)
|
||||
|
||||
const (
|
||||
descDirOut = descRequestTypeDirOut >> descRequestTypeDirPos
|
||||
descDirIn = descRequestTypeDirIn >> descRequestTypeDirPos
|
||||
|
||||
descDirRx = descDirOut // "IN" and "OUT" terms are from host's perspective,
|
||||
descDirTx = descDirIn // which is opposite from USB device. Kinda awkward.
|
||||
)
|
||||
|
||||
// device returns the enumerated device descriptor value, defined per USB
|
||||
// specification, for the receiver Speed s.
|
||||
func (s Speed) device() uint32 {
|
||||
switch s {
|
||||
case LowSpeed:
|
||||
return descDeviceSpeedLow
|
||||
case FullSpeed:
|
||||
return descDeviceSpeedFull
|
||||
case HighSpeed:
|
||||
return descDeviceSpeedHigh
|
||||
case SuperSpeed, DualSuperSpeed:
|
||||
return descDeviceSpeedSuper
|
||||
default: // unrecognized Speed defaults to full-speed
|
||||
return descDeviceSpeedFull
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// Common attributes for all endpoint descriptor configurations.
|
||||
descEndptConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1)
|
||||
(0 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered
|
||||
(0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup
|
||||
0 // Bits 0-4: reserved (0)
|
||||
|
||||
descEndptConfigAttrRxPos = 0
|
||||
descEndptConfigAttrTxPos = 16
|
||||
descEndptConfigAttrRxMsk = (descEndptAttrSyncTypeMsk | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxMsk = (descEndptAttrSyncTypeMsk | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
|
||||
descEndptConfigAttrRxUnused = 0x02 << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxUnused = 0x02 << descEndptConfigAttrTxPos
|
||||
descEndptConfigAttrRxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
descEndptConfigAttrRxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
descEndptConfigAttrRxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
)
|
||||
|
||||
type (
|
||||
// descString is the actual byte array used to hold string descriptors. The
|
||||
// first two bytes are a USB-specified header (0=length, 1=type), and the
|
||||
// remaining bytes are UTF-16 code points, ordered low byte-first. If you just
|
||||
// want to use UTF-8 (or even ASCII), you still need to reserve 2 bytes for
|
||||
// each symbol, but you can set all of their high bytes 0.
|
||||
descString [descStringSize]uint8
|
||||
// descStringIndex defines an indexed collection of string descriptors for a
|
||||
// given language.
|
||||
descStringIndex [descStringIndexCount]descString
|
||||
// descStringLanguage contains a language code and an indexed collection of
|
||||
// string descriptors encoded in that language.
|
||||
descStringLanguage struct {
|
||||
language uint16
|
||||
descriptor descStringIndex
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
descStringIndexCount = 4 // Language, Manufacturer, Product, Serial Number
|
||||
descStringSize = 64 // (64-2)/2 = 31 chars each (UTF-16 code points)
|
||||
// The maximum allowable string descriptor size is 255, or (255-2)/2 = 126
|
||||
// available UTF-16 code points. Considering we are allocating this storage at
|
||||
// compile-time, it seems like an awful waste of space (255*4 = ~1 KiB) just
|
||||
// to store four strings, which, in all likelihood, will not be modified by
|
||||
// anyone other than TinyGo devs; 64*4 = 256 B (i.e., 31 UTF-16 code points
|
||||
// for each string) seems a good compromise.
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
//go:build atsamd51 || atsame5x
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package usb
|
||||
|
||||
// descCPUFrequencyHz defines the target CPU frequency (Hz).
|
||||
const descCPUFrequencyHz = 120000000
|
||||
|
||||
// descCoreCount defines the number of USB PHY cores available on this platform,
|
||||
// independent of the number of cores which shall be configured as TinyGo USB
|
||||
// host/device controller instances.
|
||||
const descCoreCount = 1 // SAMx51 has a single, full-speed USB PHY
|
||||
|
||||
// General USB device identification constants.
|
||||
const (
|
||||
descCommonVendorID = 0x03EB
|
||||
descCommonProductID = 0x2421
|
||||
descCommonReleaseID = 0x0101 // BCD (1.1)
|
||||
|
||||
descCommonLanguage = descLanguageEnglish
|
||||
descCommonManufacturer = "TinyGo"
|
||||
descCommonProduct = "USB"
|
||||
descCommonSerialNumber = "00000"
|
||||
)
|
||||
|
||||
// Constants for all USB device classes.
|
||||
const (
|
||||
|
||||
// USB endpoints parameters
|
||||
|
||||
descMaxEndpoints = 8 // SAMx51 maximum number of endpoints
|
||||
|
||||
descControlPacketSize = 64
|
||||
)
|
||||
@@ -0,0 +1,622 @@
|
||||
//go:build mimxrt1062
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// descCPUFrequencyHz defines the target CPU frequency (Hz).
|
||||
const descCPUFrequencyHz = 600000000
|
||||
|
||||
// descCoreCount defines the number of USB PHY cores available on this platform,
|
||||
// independent of the number of cores which shall be configured as TinyGo USB
|
||||
// host/device controller instances.
|
||||
const descCoreCount = 2
|
||||
|
||||
// descCDCACMCount defines the number of USB cores that may be configured as
|
||||
// CDC-ACM (single) devices.
|
||||
const descCDCACMCount = 1
|
||||
|
||||
// descHIDCount defines the number of USB cores that may be configured as a
|
||||
// composite (keyboard + mouse + joystick) human interface device (HID).
|
||||
const descHIDCount = 0
|
||||
|
||||
// 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 (
|
||||
|
||||
// USB bus configuration attributes
|
||||
|
||||
descCDCACMMaxPowerMa = 100 // Maximum current (mA) requested from host
|
||||
|
||||
// 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 (
|
||||
|
||||
// USB bus configuration attributes
|
||||
|
||||
descHIDMaxPowerMa = 100 // Maximum current (mA) requested from host
|
||||
|
||||
// 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
|
||||
|
||||
//serial *Serial
|
||||
keyboard *Keyboard
|
||||
//mouse *Mouse
|
||||
//joystick *Joystick
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
//serial: &descHID0Serial,
|
||||
keyboard: &descHID0Keyboard,
|
||||
//mouse: &descHID0Mouse,
|
||||
//joystick: &descHID0Joystick,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
//go:build usb.cdc && (atsamd51 || atsame5x)
|
||||
// +build usb.cdc
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:inline
|
||||
func (d *dhw) descriptorTable() uintptr {
|
||||
return uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].ed[0]))
|
||||
}
|
||||
|
||||
// endpointDescriptor returns the endpoint descriptor for the given endpoint
|
||||
// address, encoded as direction D and endpoint number N with the 8-bit mask
|
||||
// D000NNNN.
|
||||
//go:inline
|
||||
func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc {
|
||||
num, dir := unpackEndpoint(endpoint)
|
||||
return &descCDC[d.cc.config-1].ed[num][dir]
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dhw) controlSetupBuffer() uintptr {
|
||||
return uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].sx[0]))
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dhw) controlStatusBuffer(data []uint8) uintptr {
|
||||
// reference to class configuration data
|
||||
c := descCDC[d.cc.config-1]
|
||||
for i := range c.cx {
|
||||
c.cx[i] = 0 // zero out the control reply buffer
|
||||
}
|
||||
// copy the given data into control reply buffer
|
||||
copy(c.cx[:], data)
|
||||
return uintptr(unsafe.Pointer(&c.cx[0]))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// [CDC-ACM] Serial UART (Virtual COM Port)
|
||||
// =============================================================================
|
||||
|
||||
func (d *dhw) cdcConfigure() {
|
||||
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
|
||||
acm.setState(descCDCStateConfigured)
|
||||
|
||||
// SAMx51 only supports USB full-speed (FS) operation
|
||||
acm.sxSize = descCDCStatusFSPacketSize
|
||||
acm.rxSize = descCDCDataRxFSPacketSize
|
||||
acm.txSize = descCDCDataTxFSPacketSize
|
||||
|
||||
rq := acm.rxq[:]
|
||||
tq := acm.txq[:]
|
||||
|
||||
// Rx gives priority to incoming data, Tx gives priority to outgoing data
|
||||
acm.rq.Init(&rq, int(acm.rxSize), QueueFullDiscardFirst)
|
||||
acm.tq.Init(&tq, int(acm.txSize), QueueFullDiscardLast)
|
||||
|
||||
d.endpointEnable(txEndpoint(descCDCEndpointStatus),
|
||||
false, descCDCConfigAttrStatus)
|
||||
d.endpointEnable(rxEndpoint(descCDCEndpointDataRx),
|
||||
false, descCDCConfigAttrDataRx)
|
||||
d.endpointEnable(txEndpoint(descCDCEndpointDataTx),
|
||||
false, descCDCConfigAttrDataTx)
|
||||
|
||||
d.endpointConfigure(txEndpoint(descCDCEndpointStatus),
|
||||
nil)
|
||||
d.endpointConfigure(rxEndpoint(descCDCEndpointDataRx),
|
||||
d.cdcReceiveComplete)
|
||||
d.endpointConfigure(txEndpoint(descCDCEndpointDataTx),
|
||||
d.cdcTransmitComplete)
|
||||
|
||||
d.cdcReceiveStart(rxEndpoint(descCDCEndpointDataRx))
|
||||
}
|
||||
|
||||
func (d *dhw) cdcSetLineState(state uint16) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
acm.setState(descCDCStateLineState)
|
||||
if acm.ls.parse(state) {
|
||||
// TBD: respond to changes in line state?
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) cdcSetLineCoding(coding []uint8) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
acm.setState(descCDCStateLineCoding)
|
||||
if acm.lc.parse(coding) {
|
||||
switch acm.lc.baud {
|
||||
case 1200:
|
||||
if acm.ls.dataTerminalReady {
|
||||
// reboot CPU
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) cdcReady() bool {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
// Ensure we have received SET_CONFIGURATION class request, and then both
|
||||
// SET_LINE_STATE and SET_LINE_CODING CDC requests (in that order).
|
||||
return d.state() == dcdStateConfigured &&
|
||||
acm.st.Get() == uint8(descCDCStateLineCoding)
|
||||
}
|
||||
|
||||
func (d *dhw) cdcReceiveStart(endpoint uint8) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
|
||||
ready, _ := d.ep[num][descDirRx].scheduleTransfer(
|
||||
uintptr(unsafe.Pointer(&acm.rx[0])), acm.rxSize)
|
||||
if ready {
|
||||
if xfer, ok := d.ep[num][descDirRx].pendingTransfer(); ok {
|
||||
// Update the active transfer descriptor on the corresponding endpoint.
|
||||
d.ep[num][descDirRx].setActiveTransfer(xfer)
|
||||
d.endpointTransfer(endpoint, xfer.data, xfer.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) cdcReceiveComplete(endpoint uint8, size uint32) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
|
||||
if xfer, ok := d.ep[num][descDirRx].activeTransfer(); ok {
|
||||
for ptr := xfer.data; ptr < xfer.data+uintptr(size); ptr++ {
|
||||
acm.rq.Enq(*(*uint8)(unsafe.Pointer(ptr)))
|
||||
}
|
||||
}
|
||||
d.ep[num][descDirRx].setActiveTransfer(nil)
|
||||
d.cdcReceiveStart(endpoint)
|
||||
}
|
||||
|
||||
func (d *dhw) cdcTransmitStart(endpoint uint8) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
|
||||
// BULK data endpoints can simply use a single time slot in the schedule, and
|
||||
// repeatedly transfer from the same transmit buffer (acm.tx) as soon as the
|
||||
// transaction complete callback has been called for a prior transaction.
|
||||
|
||||
// Do not schedule another transfer if one is already active, or if our Tx
|
||||
// FIFO is currently empty.
|
||||
if d.ep[num][descDirTx].hasActiveTransfer() || acm.tq.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if send, err := acm.tq.Read(acm.tx[:]); err == nil && send > 0 {
|
||||
ready, _ := d.ep[num][descDirTx].scheduleTransfer(
|
||||
uintptr(unsafe.Pointer(&acm.tx[0])), uint32(send))
|
||||
if ready {
|
||||
if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok {
|
||||
d.ep[num][descDirTx].setActiveTransfer(xfer)
|
||||
d.endpointTransfer(endpoint, xfer.data, xfer.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) cdcTransmitComplete(endpoint uint8, size uint32) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
if size > 0 && size%acm.txSize == 0 {
|
||||
// Send ZLP if transfer length is a non-zero multiple of max packet size.
|
||||
d.endpointTransfer(endpoint, 0, 0)
|
||||
}
|
||||
d.ep[num][descDirTx].setActiveTransfer(nil)
|
||||
d.cdcTransmitStart(endpoint)
|
||||
}
|
||||
|
||||
// cdcFlush discards all buffered input (Rx) data.
|
||||
func (d *dhw) cdcFlush() {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
acm.rq.Reset(int(acm.rxSize))
|
||||
}
|
||||
|
||||
func (d *dhw) cdcAvailable() int {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
return acm.rq.Len()
|
||||
}
|
||||
|
||||
func (d *dhw) cdcPeek() (uint8, bool) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
return acm.rq.Front()
|
||||
}
|
||||
|
||||
func (d *dhw) cdcReadByte() (uint8, bool) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
return acm.rq.Deq()
|
||||
}
|
||||
|
||||
func (d *dhw) cdcRead(data []uint8) (int, error) {
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
return acm.rq.Read(data)
|
||||
}
|
||||
|
||||
func (d *dhw) cdcWriteByte(c uint8) error {
|
||||
_, err := d.cdcWrite([]uint8{c})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *dhw) cdcWrite(data []uint8) (int, error) {
|
||||
|
||||
acm := &descCDC[d.cc.config-1]
|
||||
num := uint16(descCDCEndpointDataTx) & descEndptAddrNumberMsk
|
||||
|
||||
var sent int
|
||||
var werr error
|
||||
for off := 0; off < len(data); off += int(acm.txSize) {
|
||||
|
||||
cnt := len(data[off:])
|
||||
if cnt > int(acm.txSize) {
|
||||
cnt = int(acm.txSize)
|
||||
}
|
||||
|
||||
// Block until we have room in the Tx FIFO. Space will become available once
|
||||
// the endpoint transaction complete interrupt is raised for the Tx BULK data
|
||||
// endpoint, and then the uartTransmitComplete callback has dequeued data from
|
||||
// the Tx FIFO (acm.tq) into the Tx transmit buffer (acm.tx).
|
||||
for acm.tq.Rem() < cnt {
|
||||
}
|
||||
|
||||
// Add data to Tx FIFO
|
||||
add, err := acm.tq.Write(data[off : off+cnt])
|
||||
if err != nil {
|
||||
werr = err
|
||||
break
|
||||
}
|
||||
sent += add
|
||||
|
||||
if d.ep[num][descDirTx].hasActiveTransfer() {
|
||||
// If there is already a transmit in-progress, wait for its callback to
|
||||
// detect new data in the FIFO and continue the transfer automatically.
|
||||
} else {
|
||||
// Otherwise, initiate a new data transfer.
|
||||
d.cdcTransmitStart(txEndpoint(descCDCEndpointDataTx))
|
||||
}
|
||||
}
|
||||
|
||||
return sent, werr
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
//go:build usb.hid && (atsamd51 || atsame5x)
|
||||
// +build usb.hid
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:inline
|
||||
func (d *dhw) descriptorTable() uintptr {
|
||||
return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].ed[0]))
|
||||
}
|
||||
|
||||
// endpointDescriptor returns the endpoint descriptor for the given endpoint
|
||||
// address, encoded as direction D and endpoint number N with the 8-bit mask
|
||||
// D000NNNN.
|
||||
//go:inline
|
||||
func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc {
|
||||
num, dir := unpackEndpoint(endpoint)
|
||||
return &descHID[d.cc.config-1].ed[num][dir]
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dhw) controlSetupBuffer() uintptr {
|
||||
return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].sx[0]))
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *dhw) controlStatusBuffer(data []uint8) uintptr {
|
||||
// reference to class configuration data
|
||||
c := descHID[d.cc.config-1]
|
||||
for i := range c.cx {
|
||||
c.cx[i] = 0 // zero out the control reply buffer
|
||||
}
|
||||
// copy the given data into control reply buffer
|
||||
copy(c.cx[:], data)
|
||||
return uintptr(unsafe.Pointer(&c.cx[0]))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// [HID] Serial
|
||||
// =============================================================================
|
||||
|
||||
func (d *dhw) serialConfigure() {
|
||||
|
||||
// hid := &descHID[d.cc.config-1]
|
||||
|
||||
// SAMx51 only supports USB full-speed (FS) operation
|
||||
// hid.rxSerialSize = descHIDSerialRxFSPacketSize
|
||||
// hid.txSerialSize = descHIDSerialTxFSPacketSize
|
||||
|
||||
// Rx and Tx are on same endpoint
|
||||
d.endpointEnable(descHIDEndpointSerialRx,
|
||||
false, descHIDConfigAttrSerial)
|
||||
|
||||
// d.endpointConfigureRx(descHIDEndpointSerialRx,
|
||||
// hid.rxSerialSize, false, d.serialNotify)
|
||||
// d.endpointConfigureTx(descHIDEndpointSerialTx,
|
||||
// hid.txSerialSize, false, nil)
|
||||
|
||||
// for i := range hid.rdSerial {
|
||||
// d.serialReceive(uint8(i))
|
||||
// }
|
||||
|
||||
// d.timerConfigure(0, descHIDSerialTxSyncUs, d.serialSync)
|
||||
}
|
||||
|
||||
func (d *dhw) serialReceive(endpoint uint8) {
|
||||
hid := &descHID[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
_, _ = hid, num // TODO(ardnew): elaborate stub
|
||||
}
|
||||
|
||||
func (d *dhw) serialTransmit() {
|
||||
hid := &descHID[d.cc.config-1]
|
||||
_ = hid // TODO(ardnew): elaborate stub
|
||||
}
|
||||
|
||||
func (d *dhw) serialNotify( /* transfer *dhwTransfer */ ) {
|
||||
// hid := &descHID[d.cc.config-1]
|
||||
// len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF)
|
||||
// _ = len // TODO(ardnew): elaborate stub
|
||||
}
|
||||
|
||||
// serialFlush discards all buffered input (Rx) data.
|
||||
func (d *dhw) serialFlush() {
|
||||
hid := &descHID[d.cc.config-1]
|
||||
_ = hid
|
||||
}
|
||||
|
||||
func (d *dhw) serialSync() {
|
||||
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// [HID] Keyboard
|
||||
// =============================================================================
|
||||
|
||||
func (d *dhw) keyboard() *Keyboard { return descHID[d.cc.config-1].keyboard }
|
||||
|
||||
func (d *dhw) keyboardConfigure() {
|
||||
|
||||
hid := &descHID[d.cc.config-1]
|
||||
|
||||
// Initialize keyboard
|
||||
hid.keyboard.configure(d.dcd, hid)
|
||||
|
||||
// SAMx51 only supports USB full-speed (FS) operation
|
||||
hid.txKeyboardSize = descHIDKeyboardTxPacketSize
|
||||
|
||||
// tq := hid.txqKeyboard[:]
|
||||
|
||||
// hid.tqKeyboard.Init(&tq, len(hid.txqKeyboard), QueueFullDiscardFirst)
|
||||
|
||||
d.endpointEnable(txEndpoint(descHIDEndpointKeyboard),
|
||||
false, descHIDConfigAttrKeyboard)
|
||||
d.endpointEnable(txEndpoint(descHIDEndpointMediaKey),
|
||||
false, descHIDConfigAttrMediaKey)
|
||||
|
||||
d.endpointConfigure(txEndpoint(descHIDEndpointKeyboard),
|
||||
d.keyboardWriteComplete)
|
||||
d.endpointConfigure(txEndpoint(descHIDEndpointMediaKey),
|
||||
d.keyboardWriteComplete)
|
||||
}
|
||||
|
||||
func (d *dhw) keyboardSendKeys(consumer bool) bool {
|
||||
|
||||
hid := &descHID[d.cc.config-1]
|
||||
data := [8]uint8{}
|
||||
|
||||
if !consumer {
|
||||
|
||||
data[0] = hid.keyboard.mod
|
||||
data[1] = 0
|
||||
data[2] = hid.keyboard.key[0]
|
||||
data[3] = hid.keyboard.key[1]
|
||||
data[4] = hid.keyboard.key[2]
|
||||
data[5] = hid.keyboard.key[3]
|
||||
data[6] = hid.keyboard.key[4]
|
||||
data[7] = hid.keyboard.key[5]
|
||||
|
||||
return d.keyboardWrite(txEndpoint(descHIDEndpointKeyboard), data[:])
|
||||
|
||||
} else {
|
||||
|
||||
// 44444444 44333333 33332222 22222211 11111111 [ word ]
|
||||
// 98765432 10987654 32109876 54321098 76543210 [ index ] (right-to-left)
|
||||
|
||||
data[1] = uint8((hid.keyboard.con[1] << 2) | ((hid.keyboard.con[0] >> 8) & 0x03))
|
||||
data[2] = uint8((hid.keyboard.con[2] << 4) | ((hid.keyboard.con[1] >> 6) & 0x0F))
|
||||
data[3] = uint8((hid.keyboard.con[3] << 6) | ((hid.keyboard.con[2] >> 4) & 0x3F))
|
||||
data[4] = uint8(hid.keyboard.con[3] >> 2)
|
||||
data[5] = hid.keyboard.sys[0]
|
||||
data[6] = hid.keyboard.sys[1]
|
||||
data[7] = hid.keyboard.sys[2]
|
||||
|
||||
return d.keyboardWrite(txEndpoint(descHIDEndpointMediaKey), data[:])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) keyboardWriteComplete(endpoint uint8, size uint32) {
|
||||
hid := &descHID[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
if size > 0 && size%uint32(hid.txKeyboardSize) == 0 {
|
||||
// Send ZLP if transfer length is a non-zero multiple of max packet size.
|
||||
d.endpointTransfer(endpoint, 0, 0)
|
||||
}
|
||||
d.ep[num][descDirTx].setActiveTransfer(nil)
|
||||
}
|
||||
|
||||
func (d *dhw) keyboardWrite(endpoint uint8, data []uint8) bool {
|
||||
|
||||
hid := &descHID[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
|
||||
for off := 0; off < len(data); off += int(hid.txKeyboardSize) {
|
||||
|
||||
cnt := len(data[off:])
|
||||
if cnt > int(hid.txKeyboardSize) {
|
||||
cnt = int(hid.txKeyboardSize)
|
||||
}
|
||||
|
||||
for d.ep[num][descDirTx].hasActiveTransfer() {
|
||||
}
|
||||
|
||||
ready, _ := d.ep[num][descDirTx].scheduleTransfer(
|
||||
uintptr(unsafe.Pointer(&data[0])), uint32(cnt))
|
||||
if ready {
|
||||
if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok {
|
||||
d.ep[num][descDirTx].setActiveTransfer(xfer)
|
||||
d.endpointTransfer(endpoint, xfer.data, xfer.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// size := uint16(len(data))
|
||||
// xfer := &hid.tdKeyboard[hid.txKeyboardHead]
|
||||
// when := ticks()
|
||||
// for {
|
||||
// if 0 == xfer.token&0x80 {
|
||||
// if 0 != xfer.token&0x68 {
|
||||
// // TODO: token contains error, how to handle?
|
||||
// }
|
||||
// hid.txKeyboardPrev = false
|
||||
// break
|
||||
// }
|
||||
// if hid.txKeyboardPrev {
|
||||
// return false
|
||||
// }
|
||||
// if ticks()-when > descHIDKeyboardTxTimeoutMs {
|
||||
// // Waited too long, assume host connection dropped
|
||||
// hid.txKeyboardPrev = true
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// // Without this delay, the order packets are transmitted is seriously screwy.
|
||||
// udelay(60)
|
||||
// buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:]
|
||||
// _ = copy(buff, data)
|
||||
// d.transferPrepare(xfer, &buff[0], size, 0)
|
||||
// flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize)
|
||||
// d.endpointTransmit(endpoint, xfer)
|
||||
// hid.txKeyboardHead += 1
|
||||
// if hid.txKeyboardHead >= descHIDKeyboardTDCount {
|
||||
// hid.txKeyboardHead = 0
|
||||
// }
|
||||
return true
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// [HID] Mouse
|
||||
// =============================================================================
|
||||
|
||||
func (d *dhw) mouseConfigure() {
|
||||
|
||||
// hid := &descHID[d.cc.config-1]
|
||||
|
||||
// SAMx51 only supports USB full-speed (FS) operation
|
||||
// hid.txMouseSize = descHIDMouseTxFSPacketSize
|
||||
|
||||
d.endpointEnable(descHIDEndpointMouse,
|
||||
false, descHIDConfigAttrMouse)
|
||||
|
||||
// d.endpointConfigureTx(descHIDEndpointMouse,
|
||||
// hid.txMouseSize, false, nil)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// [HID] Joystick
|
||||
// =============================================================================
|
||||
|
||||
func (d *dhw) joystickConfigure() {
|
||||
|
||||
// hid := &descHID[d.cc.config-1]
|
||||
|
||||
// SAMx51 only supports USB full-speed (FS) operation
|
||||
// hid.txJoystickSize = descHIDJoystickTxFSPacketSize
|
||||
|
||||
d.endpointEnable(descHIDEndpointJoystick,
|
||||
false, descHIDConfigAttrJoystick)
|
||||
|
||||
// d.endpointConfigureTx(descHIDEndpointJoystick,
|
||||
// hid.txJoystickSize, false, nil)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
package usb
|
||||
|
||||
// Implementation of 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 a generic USB host controller driver (hcd) for all targets.
|
||||
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, speed Speed, 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, speed, &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,61 @@
|
||||
//go:build atsamd51 || atsame5x
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package usb
|
||||
|
||||
// Implementation of USB host controller driver (hcd) for Microchip SAMD51.
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// hhwInterruptPriority defines the priority for all USB host interrupts.
|
||||
const hhwInterruptPriority = 3
|
||||
|
||||
// hhw implements USB host controller hardware abstraction interface.
|
||||
type hhw struct {
|
||||
*hcd // USB host controller driver
|
||||
|
||||
bus *sam.USB_HOST_Type // USB core registers
|
||||
irq interrupt.Interrupt // USB IRQ, only a single interrupt on SAMx51
|
||||
|
||||
speed Speed
|
||||
}
|
||||
|
||||
// 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, speed Speed, hc *hcd) *hhw {
|
||||
switch port {
|
||||
case 0:
|
||||
hhwInstance[instance].hcd = hc
|
||||
hhwInstance[instance].bus = sam.USB_HOST
|
||||
}
|
||||
|
||||
// Port defaults to full-speed (12 Mbit/sec) on SAMx51
|
||||
if 0 == speed {
|
||||
speed = HighSpeed
|
||||
}
|
||||
hhwInstance[instance].speed = speed
|
||||
|
||||
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 bus.
|
||||
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,67 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// hhwInterruptPriority defines the priority for all USB host interrupts.
|
||||
const hhwInterruptPriority = 3
|
||||
|
||||
// hhw implements USB host controller hardware abstraction 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
|
||||
|
||||
speed Speed
|
||||
}
|
||||
|
||||
// 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, speed Speed, 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
|
||||
}
|
||||
|
||||
// Both ports default to high-speed (480 Mbit/sec) on Teensy 4.x
|
||||
if 0 == speed {
|
||||
speed = HighSpeed
|
||||
}
|
||||
hhwInstance[instance].speed = speed
|
||||
|
||||
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,293 @@
|
||||
package usb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
type QueueFullDiscardMode uint8
|
||||
|
||||
const (
|
||||
QueueFullDiscardLast QueueFullDiscardMode = iota // Drop incoming data
|
||||
QueueFullDiscardFirst // Drop outgoing data
|
||||
)
|
||||
|
||||
type Queue struct {
|
||||
mode QueueFullDiscardMode
|
||||
size volatile.Register32
|
||||
fifo *[]uint8
|
||||
tail volatile.Register32 // New elements are enqueued at index tail
|
||||
head volatile.Register32 // Oldest element in queue is at index head
|
||||
}
|
||||
|
||||
var (
|
||||
ErrQueueReadZero = errors.New("copy into zero-length buffer")
|
||||
ErrQueueWriteZero = errors.New("copy from zero-length buffer")
|
||||
ErrQueueEmpty = errors.New("buffer empty") // Read underrun
|
||||
ErrQueueFull = errors.New("buffer full") // Write overrun
|
||||
ErrQueueDiscardMode = errors.New("unknown discard mode")
|
||||
)
|
||||
|
||||
// Init initializes the receiver queue's backing data store with the given byte
|
||||
// slice fifo and logical capacity size. If size is greater than the slice's
|
||||
// physical length, uses the slice's physical length.
|
||||
func (q *Queue) Init(fifo *[]uint8, size int, mode QueueFullDiscardMode) {
|
||||
q.mode = mode
|
||||
q.fifo = fifo
|
||||
q.Reset(size)
|
||||
}
|
||||
|
||||
// Reset discards all buffered data and sets the FIFO logical capacity.
|
||||
// If size is less than 0 or greater than FIFO physical length, uses FIFO
|
||||
// physical length.
|
||||
//go:inline
|
||||
func (q *Queue) Reset(size int) {
|
||||
if phy := len(*q.fifo); size < 0 || size > phy {
|
||||
size = phy
|
||||
}
|
||||
q.size.Set(uint32(size))
|
||||
q.tail.Set(0)
|
||||
q.head.Set(0)
|
||||
}
|
||||
|
||||
// Cap returns the logical capacity of the receiver FIFO.
|
||||
//go:inline
|
||||
func (q *Queue) Cap() int {
|
||||
return int(q.size.Get())
|
||||
}
|
||||
|
||||
// Len returns the number of elements enqueued in the receiver FIFO.
|
||||
//go:inline
|
||||
func (q *Queue) Len() int {
|
||||
return int(q.tail.Get() - q.head.Get())
|
||||
}
|
||||
|
||||
// Rem returns the number of elements not enqueued in the receiver FIFO.
|
||||
//go:inline
|
||||
func (q *Queue) Rem() int {
|
||||
return q.Cap() - q.Len()
|
||||
}
|
||||
|
||||
// Deq dequeues and returns the element at the front of the receiver FIFO and true.
|
||||
// If the FIFO is empty and no element was dequeued, returns 0 and false.
|
||||
func (q *Queue) Deq() (uint8, bool) {
|
||||
|
||||
head := q.head.Get()
|
||||
if head == q.tail.Get() {
|
||||
return 0, false
|
||||
} // empty queue
|
||||
|
||||
data := (*q.fifo)[head%q.size.Get()]
|
||||
q.head.Set(head + 1)
|
||||
|
||||
return data, true
|
||||
}
|
||||
|
||||
// Enq enqueues the given element data at the back of the receiver FIFO and
|
||||
// returns true.
|
||||
// If the FIFO is full and no element can be enqueued, returns false.
|
||||
//
|
||||
// TODO(ardnew): Document both operations based on receiver's QueueFullMode.
|
||||
func (q *Queue) Enq(data uint8) bool {
|
||||
|
||||
tail := q.tail.Get()
|
||||
head := q.head.Get()
|
||||
if tail-head == q.size.Get() {
|
||||
switch q.mode {
|
||||
case QueueFullDiscardLast:
|
||||
// drop incoming data
|
||||
return false
|
||||
case QueueFullDiscardFirst:
|
||||
// drop outgoing data
|
||||
q.head.Set(head + 1)
|
||||
}
|
||||
} // full queue
|
||||
|
||||
(*q.fifo)[tail%q.size.Get()] = data
|
||||
q.tail.Set(tail + 1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface. It dequeues min(q.Len(), len(data))
|
||||
// elements from the receiver FIFO into the given slice data.
|
||||
// If len(data) equals 0, returns 0 and ErrReadBuffer.
|
||||
// Otherwise, if q.Len() equals 0, returns 0 and ErrQueueEmpty.
|
||||
func (q *Queue) Read(data []uint8) (int, error) {
|
||||
|
||||
less := uint32(len(data))
|
||||
if less == 0 {
|
||||
return 0, ErrQueueReadZero
|
||||
} // nothing to copy into
|
||||
|
||||
head := q.head.Get()
|
||||
used := q.tail.Get() - head
|
||||
|
||||
if used == 0 {
|
||||
return 0, ErrQueueEmpty
|
||||
} // empty queue
|
||||
|
||||
if less > used {
|
||||
less = used
|
||||
} // only get from used space
|
||||
|
||||
for i := uint32(0); i < less; i++ {
|
||||
data[i] = (*q.fifo)[head%q.size.Get()]
|
||||
head++
|
||||
}
|
||||
q.head.Set(head)
|
||||
|
||||
return int(less), nil
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface. It enqueues min(q.Rem(), len(data))
|
||||
// elements from the given slice data into the receiver FIFO.
|
||||
// If len(data) equals 0, returns 0 and ErrWriteBuffer.
|
||||
// Otherwise, if q.Rem() equals 0, returns 0 and ErrQueueFull.
|
||||
//
|
||||
// TODO(ardnew): Document both operations based on receiver's QueueFullMode.
|
||||
func (q *Queue) Write(data []uint8) (int, error) {
|
||||
|
||||
more := uint32(len(data))
|
||||
|
||||
// Nothing to copy from is an error regardless of mode.
|
||||
if more == 0 {
|
||||
return 0, ErrQueueWriteZero
|
||||
}
|
||||
|
||||
switch q.mode {
|
||||
case QueueFullDiscardLast:
|
||||
// drop incoming data
|
||||
|
||||
tail := q.tail.Get()
|
||||
used := tail - q.head.Get()
|
||||
|
||||
// Full queue, cannot add any data.
|
||||
if used == q.size.Get() {
|
||||
return 0, ErrQueueFull
|
||||
}
|
||||
|
||||
// Only put to unused space.
|
||||
if used+more > q.size.Get() {
|
||||
more = q.size.Get() - used
|
||||
}
|
||||
|
||||
// Copy a potentially-limited number of elements from data, depending on the
|
||||
// current length of FIFO.
|
||||
for i := uint32(0); i < more; i++ {
|
||||
(*q.fifo)[tail%q.size.Get()] = data[i]
|
||||
tail++
|
||||
}
|
||||
q.tail.Set(tail)
|
||||
|
||||
return int(more), nil
|
||||
|
||||
case QueueFullDiscardFirst:
|
||||
// drop outgoing data
|
||||
|
||||
// Trying to write more data than the FIFO will hold will simply overwrite
|
||||
// some of the given data, so there is no point writing that data.
|
||||
from := uint32(0)
|
||||
if more >= q.size.Get() {
|
||||
// Begin copying only the data that will be kept.
|
||||
from = more - q.size.Get()
|
||||
// We can fill the entire FIFO.
|
||||
more = q.size.Get()
|
||||
// Reset the indices
|
||||
q.head.Set(0)
|
||||
q.tail.Set(0)
|
||||
}
|
||||
|
||||
tail := q.tail.Get()
|
||||
used := tail - q.head.Get()
|
||||
|
||||
// Make space for incoming data by discarding only as many FIFO elements as
|
||||
// is necessary to store incoming data.
|
||||
if used+more > q.size.Get() {
|
||||
q.head.Set(tail + more - q.size.Get())
|
||||
}
|
||||
|
||||
// Copy a potentially-limited number of elements from data, depending on the
|
||||
// current length of FIFO.
|
||||
for i := uint32(0); i < more; i++ {
|
||||
(*q.fifo)[tail%q.size.Get()] = data[from+i]
|
||||
tail++
|
||||
}
|
||||
q.tail.Set(tail)
|
||||
|
||||
return int(more), nil
|
||||
}
|
||||
|
||||
return 0, ErrQueueDiscardMode
|
||||
}
|
||||
|
||||
// Front returns the next element that would be dequeued from the receiver FIFO
|
||||
// and true.
|
||||
// If the FIFO is empty and no element would be dequeued, returns 0 and false.
|
||||
func (q *Queue) Front() (uint8, bool) {
|
||||
|
||||
head := q.head.Get()
|
||||
if head == q.tail.Get() {
|
||||
return 0, false
|
||||
} // empty queue
|
||||
|
||||
return (*q.fifo)[head%q.size.Get()], true
|
||||
}
|
||||
|
||||
// Back returns the last element that would be dequeued from the receiver FIFO
|
||||
// and true.
|
||||
// If the FIFO is empty and no element would be dequeued, returns 0 and false.
|
||||
func (q *Queue) Back() (uint8, bool) {
|
||||
|
||||
tail := q.tail.Get()
|
||||
if tail == q.head.Get() {
|
||||
return 0, false
|
||||
} // empty queue
|
||||
|
||||
return (*q.fifo)[(tail-1)%q.size.Get()], true
|
||||
}
|
||||
|
||||
// index returns an index into the receiver FIFO based on sign and magnitude of i:
|
||||
// 1. If i is greater than or equal to zero and less then q.Len(), returns the
|
||||
// (i+1)'th element that would be dequeued from the receiver FIFO and true.
|
||||
// 2. Otherwise, if i is negative and -i is less than or equal to q.Len(), returns
|
||||
// the -(i+1)'th from the last element that would be dequeued from the receiver
|
||||
// FIFO and true.
|
||||
// 3. Otherwise, returns 0 and false.
|
||||
func (q *Queue) index(i int) (int, bool) {
|
||||
if n := q.Len(); i < 0 {
|
||||
if -i <= n {
|
||||
return (int(q.tail.Get()) + i) % int(q.size.Get()), true
|
||||
}
|
||||
} else {
|
||||
if i < n {
|
||||
return (int(q.head.Get()) + i) % int(q.size.Get()), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Get returns the value of an element in the receiver FIFO, offset by i from the
|
||||
// front of the queue if i is positive, or from the back of the queue if i is
|
||||
// negative. For example:
|
||||
// Get(0) == Get(-Len()) == Front(), and
|
||||
// Get(-1) == Get(Len()-1) == Back().
|
||||
// If the offset is beyond queue boundaries, returns 0 and false.
|
||||
func (q *Queue) Get(i int) (uint8, bool) {
|
||||
|
||||
if n, ok := q.index(i); ok {
|
||||
return (*q.fifo)[n], true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Set modifies the value of an element in the receiver FIFO.
|
||||
// Set uses the same logic as Get to select an element in the FIFO.
|
||||
func (q *Queue) Set(i int, data uint8) bool {
|
||||
|
||||
if n, ok := q.index(i); ok {
|
||||
(*q.fifo)[n] = data
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//go:build usb.cdc
|
||||
// +build usb.cdc
|
||||
|
||||
package usb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCDCInvalidPort = errors.New("invalid port")
|
||||
ErrCDCEmptyBuffer = errors.New("buffer empty")
|
||||
)
|
||||
|
||||
// CDC represents a virtual UART serial device emulation using the USB
|
||||
// CDC-ACM device class driver.
|
||||
type CDC struct {
|
||||
// Port is the MCU's native USB core number. If in doubt, leave it
|
||||
// uninitialized for default (0).
|
||||
Port int
|
||||
core *core
|
||||
}
|
||||
|
||||
type CDCConfig struct {
|
||||
BusSpeed Speed
|
||||
}
|
||||
|
||||
func (cdc *CDC) Configure(config CDCConfig) error {
|
||||
|
||||
c := class{id: classDeviceCDC, config: 1}
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
var st status
|
||||
cdc.core, st = initCore(cdc.Port, config.BusSpeed, c)
|
||||
if !st.ok() {
|
||||
return ErrCDCInvalidPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cdc *CDC) Ready() bool {
|
||||
return cdc.core.dc.cdcReady()
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the Rx buffer.
|
||||
func (cdc *CDC) Buffered() int {
|
||||
for !cdc.Ready() {
|
||||
}
|
||||
return cdc.core.dc.cdcAvailable()
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the Rx buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (cdc *CDC) ReadByte() (byte, error) {
|
||||
for !cdc.Ready() {
|
||||
}
|
||||
n, ok := cdc.core.dc.cdcReadByte()
|
||||
if !ok {
|
||||
return 0, ErrCDCEmptyBuffer
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Read from the Rx buffer.
|
||||
func (cdc *CDC) Read(data []byte) (n int, err error) {
|
||||
for !cdc.Ready() {
|
||||
}
|
||||
return cdc.core.dc.cdcRead(data)
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte of data to the virtual UART interface.
|
||||
func (cdc *CDC) WriteByte(c byte) error {
|
||||
for !cdc.Ready() {
|
||||
}
|
||||
return cdc.core.dc.cdcWriteByte(c)
|
||||
}
|
||||
|
||||
// Write data to the virtual UART.
|
||||
func (cdc *CDC) Write(data []byte) (n int, err error) {
|
||||
for !cdc.Ready() {
|
||||
}
|
||||
return cdc.core.dc.cdcWrite(data)
|
||||
}
|
||||
@@ -0,0 +1,965 @@
|
||||
//go:build usb.hid
|
||||
// +build usb.hid
|
||||
|
||||
package usb
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidCodepoint = errors.New("invalid Unicode codepoint")
|
||||
ErrInvalidKeycode = errors.New("invalid keyboard keycode")
|
||||
ErrInvalidUTF8 = errors.New("invalid UTF-8 encoding")
|
||||
ErrKeypressMaximum = errors.New("maximum keypresses exceeded")
|
||||
)
|
||||
|
||||
// Keyboard represents a USB HID keyboard device with support for international
|
||||
// layouts and various control, system, multimedia, and consumer keycodes.
|
||||
//
|
||||
// Keyboard implements the io.Writer interface that translates UTF-8 encoded
|
||||
// byte strings into sequences of keypress events.
|
||||
type Keyboard struct {
|
||||
dc *dcd
|
||||
hc *descHIDClass
|
||||
|
||||
// led holds the current state of all keyboard LEDs:
|
||||
// 1=NumLock 2=CapsLock 4=ScrollLock 8=Compose 16=Kana
|
||||
led uint8
|
||||
|
||||
// mod holds the current state of all keyboard modifier keys:
|
||||
// 1=LeftCtrl 2=LeftShift 4=LeftAlt 8=LeftGUI
|
||||
// 16=RightCtrl 32=RightShift 64=RightAlt 128=RightGUI
|
||||
mod uint8
|
||||
|
||||
// key holds a list of all keyboard keys currently pressed.
|
||||
key *[hidKeyboardKeyCount]uint8
|
||||
con *[hidKeyboardConCount]uint16
|
||||
sys *[hidKeyboardSysCount]uint8
|
||||
|
||||
// decode holds the current state of the UTF-8 decoder.
|
||||
decode decodeState
|
||||
|
||||
// wideChar holds high bits for the UTF-8 decoder.
|
||||
wideChar uint16
|
||||
}
|
||||
|
||||
// decodeState represents a state in the UTF-8 decode state machine.
|
||||
type decodeState uint8
|
||||
|
||||
// Constant enumerated values of type decodeState.
|
||||
const (
|
||||
decodeReset decodeState = iota
|
||||
decodeByte1
|
||||
decodeByte2
|
||||
decodeByte3
|
||||
)
|
||||
|
||||
// configure initializes the receiver Keyboard by associating it with the given
|
||||
// USB device controller driver and HID class configuration.
|
||||
func (kb *Keyboard) configure(dc *dcd, hc *descHIDClass) {
|
||||
kb.dc = dc
|
||||
kb.hc = hc
|
||||
}
|
||||
|
||||
func (kb *Keyboard) ready() bool {
|
||||
return kb.dc != nil && kb.hc != nil
|
||||
}
|
||||
|
||||
// Write transmits press-and-release key sequences for each Keycode translated
|
||||
// from the given UTF-8 byte string. Write implements the io.Writer interface
|
||||
// and conforms to all documented conventions for arguments and return values.
|
||||
func (kb *Keyboard) Write(b []byte) (n int, err error) {
|
||||
for _, c := range b {
|
||||
if err = kb.WriteByte(c); nil != err {
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteByte processes a single byte from a UTF-8 byte string. This method is a
|
||||
// stateful method with respect to the receiver Keyboard, meaning that its exact
|
||||
// behavior will depend on the current state of its UTF-8 decode state machine:
|
||||
//
|
||||
// (a) If the given byte is a valid ASCII encoding (0-127), then a keypress
|
||||
// sequence is immediately transmitted for the respective Keycode.
|
||||
//
|
||||
// (b) If the given byte represents the final byte in a multi-byte codepoint,
|
||||
// then a keypress sequence is immediately transmitted by translating the
|
||||
// multi-byte codepoint to its respective Keycode.
|
||||
//
|
||||
// (c) If the given byte appears to represent high bits for a multi-byte
|
||||
// codepoint, then the bits are copied to the receiver's internal state
|
||||
// machine buffer for use by a subsequent call to WriteByte() (or Write())
|
||||
// that completes the codepoint.
|
||||
//
|
||||
// (d) If the given byte is out of range, or contains illegal bits for the
|
||||
// current state of the UTF-8 decoder, then the UTF-8 decode state machine
|
||||
// is reset to its initial state.
|
||||
//
|
||||
// In cases (c) and (d), a keypress sequence is not generated and no data is
|
||||
// transmitted. In case (c), additional bytes must be received via WriteByte()
|
||||
// (or Write()) to complete or discard the current codepoint.
|
||||
func (kb *Keyboard) WriteByte(b byte) error {
|
||||
switch {
|
||||
case b < 0x80:
|
||||
// 1-byte encoding (0x00-0x7F)
|
||||
kb.decode = decodeByte1
|
||||
return kb.write(uint16(b))
|
||||
|
||||
case b < 0xC0:
|
||||
// 2nd, 3rd, or 4th byte (0x80-0xBF)
|
||||
b = Keycode(b).key()
|
||||
switch kb.decode {
|
||||
case decodeByte2:
|
||||
kb.decode = decodeByte1
|
||||
return kb.write(kb.wideChar | uint16(b))
|
||||
case decodeByte3:
|
||||
kb.decode = decodeByte2
|
||||
kb.wideChar |= uint16(b) << 6
|
||||
}
|
||||
|
||||
case b < 0xE0:
|
||||
// 2-byte encoding (0xC2-0xDF), or illegal byte 2 (0xC0-0xC1)
|
||||
kb.decode = decodeByte2
|
||||
kb.wideChar = uint16(b&0x1F) << 6
|
||||
|
||||
case b < 0xF0:
|
||||
// 3-byte encoding (0xE0-0xEF)
|
||||
kb.decode = decodeByte3
|
||||
kb.wideChar = uint16(b&0x0F) << 12
|
||||
|
||||
default:
|
||||
// 4-byte encoding unsupported (0xF0-0xF4), or illegal byte 4 (0xF5-0xFF)
|
||||
kb.decode = decodeReset
|
||||
return ErrInvalidUTF8
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *Keyboard) write(p uint16) error {
|
||||
c := keycode(p)
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
if d := deadkey(c); 0 != d {
|
||||
if err := kb.writeKeycode(d); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return kb.writeKeycode(c)
|
||||
}
|
||||
|
||||
func (kb *Keyboard) writeKeycode(c Keycode) error {
|
||||
kb.mod = c.mod()
|
||||
kb.key[0] = c.key()
|
||||
kb.key[1] = 0
|
||||
kb.key[2] = 0
|
||||
kb.key[3] = 0
|
||||
kb.key[4] = 0
|
||||
kb.key[5] = 0
|
||||
if !kb.dc.keyboardSendKeys(false) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
kb.mod = 0
|
||||
kb.key[0] = 0
|
||||
if !kb.dc.keyboardSendKeys(false) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Press transmits a press-and-release sequence for the given Keycode, which
|
||||
// simulates a discrete keypress event.
|
||||
//
|
||||
// The following values of Keycode are supported:
|
||||
//
|
||||
// 0x0020 - 0x007F ASCII (U+0020 to U+007F) [USES LAYOUT]
|
||||
// 0x0080 - 0xC1FF Unicode (U+0080 to U+C1FF) [USES LAYOUT]
|
||||
// 0xC200 - 0xDFFF UTF-8 packed (U+0080 to U+07FF) [USES LAYOUT]
|
||||
// 0xE000 - 0xE0FF Modifier key (bitmap, 8 keys, Shift/Ctrl/Alt/GUI)
|
||||
// 0xE200 - 0xE2FF System key (HID usage code, page 1)
|
||||
// 0xE400 - 0xE7FF Media/Consumer key (HID usage code, page 12)
|
||||
// 0xF000 - 0xFFFF Normal key (HID usage code, page 7)
|
||||
func (kb *Keyboard) Press(c Keycode) error {
|
||||
if err := kb.Down(c); nil != err {
|
||||
return err
|
||||
}
|
||||
return kb.Up(c)
|
||||
}
|
||||
|
||||
// Down transmits a key-down event for the given Keycode.
|
||||
//
|
||||
// The host will interpret the key as being held down continuously until a
|
||||
// corresponding key-up event is transmitted, e.g., via method Up().
|
||||
//
|
||||
// See godoc comment on method Press() for details on what input is accepted and
|
||||
// how it is interpreted.
|
||||
func (kb *Keyboard) Down(c Keycode) error {
|
||||
var res uint8
|
||||
msb := c >> 8
|
||||
if msb >= 0xC2 {
|
||||
if msb < 0xE0 {
|
||||
c = ((msb & 0x1F) << 6) | Keycode(c.key())
|
||||
} else {
|
||||
switch msb {
|
||||
case 0xF0:
|
||||
return kb.down(uint8(c), 0)
|
||||
|
||||
case 0xE0:
|
||||
return kb.down(0, uint8(c))
|
||||
|
||||
case 0xE2:
|
||||
return kb.downSys(uint8(c))
|
||||
|
||||
default:
|
||||
if 0xE4 <= msb && msb <= 0xE7 {
|
||||
return kb.downCon(uint16(c & 0x03FF))
|
||||
}
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
}
|
||||
}
|
||||
c = keycode(uint16(c))
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
if d := deadkey(c); 0 != d {
|
||||
res = kb.mod
|
||||
if 0 != res {
|
||||
kb.mod = 0
|
||||
kb.dc.keyboardSendKeys(false)
|
||||
}
|
||||
kb.down(d.key(), d.mod())
|
||||
kb.up(d.key(), d.mod())
|
||||
}
|
||||
return kb.down(c.key(), c.mod()|res)
|
||||
}
|
||||
|
||||
func (kb *Keyboard) down(key uint8, mod uint8) error {
|
||||
send := false
|
||||
if 0 != mod {
|
||||
if kb.mod&mod != mod {
|
||||
kb.mod |= mod
|
||||
send = true
|
||||
}
|
||||
}
|
||||
if 0 != key {
|
||||
for _, k := range kb.key {
|
||||
if k == key {
|
||||
goto end
|
||||
}
|
||||
}
|
||||
for i, k := range kb.key {
|
||||
if 0 == k {
|
||||
kb.key[i] = key
|
||||
send = true
|
||||
goto end
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
end:
|
||||
if send {
|
||||
if !kb.dc.keyboardSendKeys(false) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *Keyboard) downCon(key uint16) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for _, k := range kb.con {
|
||||
if key == k {
|
||||
return nil // already pressed
|
||||
}
|
||||
}
|
||||
for i, k := range kb.con {
|
||||
if 0 == k {
|
||||
kb.con[i] = key
|
||||
if !kb.dc.keyboardSendKeys(true) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
|
||||
func (kb *Keyboard) downSys(key uint8) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for _, k := range kb.sys {
|
||||
if key == k {
|
||||
return nil // already pressed
|
||||
}
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
if 0 == k {
|
||||
kb.sys[i] = key
|
||||
if !kb.dc.keyboardSendKeys(true) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
|
||||
// Up transmits a key-up event for the given Keycode.
|
||||
//
|
||||
// See godoc comment on method Press() for details on what input is accepted and
|
||||
// how it is interpreted.
|
||||
func (kb *Keyboard) Up(c Keycode) error {
|
||||
msb := c >> 8
|
||||
if msb >= 0xC2 {
|
||||
if msb < 0xE0 {
|
||||
c = ((msb & 0x1F) << 6) | Keycode(c.key())
|
||||
} else {
|
||||
switch msb {
|
||||
case 0xF0:
|
||||
return kb.up(uint8(c), 0)
|
||||
|
||||
case 0xE0:
|
||||
return kb.up(0, uint8(c))
|
||||
|
||||
case 0xE2:
|
||||
return kb.upSys(uint8(c))
|
||||
|
||||
default:
|
||||
if 0xE4 <= msb && msb <= 0xE7 {
|
||||
return kb.upCon(uint16(c & 0x03FF))
|
||||
}
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
}
|
||||
}
|
||||
c = keycode(uint16(c))
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
return kb.up(c.key(), c.mod())
|
||||
}
|
||||
|
||||
// Release transmits a key-up event for all keyboard keys currently pressed as
|
||||
// if the user removed his/her hands from the keyboard entirely.
|
||||
func (kb *Keyboard) Release() error {
|
||||
|
||||
bits := uint16(kb.mod)
|
||||
kb.mod = 0
|
||||
for i, k := range kb.key {
|
||||
bits |= uint16(k)
|
||||
kb.key[i] = 0
|
||||
}
|
||||
if 0 != bits {
|
||||
if !kb.dc.keyboardSendKeys(false) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
bits = 0
|
||||
for i, k := range kb.con {
|
||||
bits |= k
|
||||
kb.con[i] = 0
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
bits |= uint16(k)
|
||||
kb.sys[i] = 0
|
||||
}
|
||||
if 0 != bits {
|
||||
if !kb.dc.keyboardSendKeys(true) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *Keyboard) up(key uint8, mod uint8) error {
|
||||
send := false
|
||||
if 0 != mod {
|
||||
if kb.mod&mod != 0 {
|
||||
kb.mod &^= mod
|
||||
send = true
|
||||
}
|
||||
}
|
||||
if 0 != key {
|
||||
for i, k := range kb.key {
|
||||
if key == k {
|
||||
kb.key[i] = 0
|
||||
send = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if send {
|
||||
if !kb.dc.keyboardSendKeys(false) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *Keyboard) upCon(key uint16) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for i, k := range kb.con {
|
||||
if key == k {
|
||||
kb.con[i] = 0
|
||||
if !kb.dc.keyboardSendKeys(true) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *Keyboard) upSys(key uint8) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
if key == k {
|
||||
kb.sys[i] = 0
|
||||
if !kb.dc.keyboardSendKeys(true) {
|
||||
return ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Keycode is a package-defined bitmap used to encode the value of a given key.
|
||||
type Keycode uint16
|
||||
|
||||
// keycode returns the given Unicode codepoint translated to a Keycode sequence.
|
||||
// Unicode codepoints greater than U+FFFF are unsupported.
|
||||
//go:inline
|
||||
func keycode(p uint16) Keycode {
|
||||
if p < 0x80 {
|
||||
return ascii[p]
|
||||
} else if p >= 0xA0 && p < 0x0100 {
|
||||
return iso88591[p-0xA0]
|
||||
} else if uint16(UNICODE20AC) == p {
|
||||
return UNICODE20AC.mask()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func deadkey(c Keycode) Keycode {
|
||||
switch c & deadkeysMask {
|
||||
case acuteAccentBits:
|
||||
return deadkeyAcuteAccent
|
||||
case circumflexBits:
|
||||
return deadkeyCircumflex
|
||||
case diaeresisBits:
|
||||
return deadkeyDiaeresis
|
||||
case graveAccentBits:
|
||||
return deadkeyGraveAccent
|
||||
case tildeBits:
|
||||
return deadkeyTilde
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) mask() Keycode { return c & keycodeMask }
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) key() uint8 { return uint8(c & keyMask) }
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) mod() uint8 {
|
||||
var m Keycode
|
||||
if 0 != c&shiftMask {
|
||||
m |= KeyModifierShift
|
||||
}
|
||||
if 0 != c&altgrMask {
|
||||
m |= KeyModifierRightAlt
|
||||
}
|
||||
return uint8(m)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) Shift() Keycode { return c | KeyModifierShift }
|
||||
|
||||
const (
|
||||
hidKeyboardKeyCount = 6 // Max number of simultaneous keypresses
|
||||
hidKeyboardSysCount = 3
|
||||
hidKeyboardConCount = 4
|
||||
)
|
||||
|
||||
// Keycodes common to all Keyboard layouts
|
||||
const (
|
||||
KeyModifierCtrl Keycode = 0x01 | 0xE000
|
||||
KeyModifierShift Keycode = 0x02 | 0xE000
|
||||
KeyModifierAlt Keycode = 0x04 | 0xE000
|
||||
KeyModifierGUI Keycode = 0x08 | 0xE000
|
||||
KeyModifierLeftCtrl Keycode = 0x01 | 0xE000
|
||||
KeyModifierLeftShift Keycode = 0x02 | 0xE000
|
||||
KeyModifierLeftAlt Keycode = 0x04 | 0xE000
|
||||
KeyModifierLeftGUI Keycode = 0x08 | 0xE000
|
||||
KeyModifierRightCtrl Keycode = 0x10 | 0xE000
|
||||
KeyModifierRightShift Keycode = 0x20 | 0xE000
|
||||
KeyModifierRightAlt Keycode = 0x40 | 0xE000
|
||||
KeyModifierRightGUI Keycode = 0x80 | 0xE000
|
||||
|
||||
KeySystemPowerDown Keycode = 0x81 | 0xE200
|
||||
KeySystemSleep Keycode = 0x82 | 0xE200
|
||||
KeySystemWakeUp Keycode = 0x83 | 0xE200
|
||||
|
||||
KeyMediaPlay Keycode = 0xB0 | 0xE400
|
||||
KeyMediaPause Keycode = 0xB1 | 0xE400
|
||||
KeyMediaRecord Keycode = 0xB2 | 0xE400
|
||||
KeyMediaFastForward Keycode = 0xB3 | 0xE400
|
||||
KeyMediaRewind Keycode = 0xB4 | 0xE400
|
||||
KeyMediaNextTrack Keycode = 0xB5 | 0xE400
|
||||
KeyMediaPrevTrack Keycode = 0xB6 | 0xE400
|
||||
KeyMediaStop Keycode = 0xB7 | 0xE400
|
||||
KeyMediaEject Keycode = 0xB8 | 0xE400
|
||||
KeyMediaRandomPlay Keycode = 0xB9 | 0xE400
|
||||
KeyMediaPlayPause Keycode = 0xCD | 0xE400
|
||||
KeyMediaPlaySkip Keycode = 0xCE | 0xE400
|
||||
KeyMediaMute Keycode = 0xE2 | 0xE400
|
||||
KeyMediaVolumeInc Keycode = 0xE9 | 0xE400
|
||||
KeyMediaVolumeDec Keycode = 0xEA | 0xE400
|
||||
|
||||
KeyA Keycode = 4 | 0xF000
|
||||
KeyB Keycode = 5 | 0xF000
|
||||
KeyC Keycode = 6 | 0xF000
|
||||
KeyD Keycode = 7 | 0xF000
|
||||
KeyE Keycode = 8 | 0xF000
|
||||
KeyF Keycode = 9 | 0xF000
|
||||
KeyG Keycode = 10 | 0xF000
|
||||
KeyH Keycode = 11 | 0xF000
|
||||
KeyI Keycode = 12 | 0xF000
|
||||
KeyJ Keycode = 13 | 0xF000
|
||||
KeyK Keycode = 14 | 0xF000
|
||||
KeyL Keycode = 15 | 0xF000
|
||||
KeyM Keycode = 16 | 0xF000
|
||||
KeyN Keycode = 17 | 0xF000
|
||||
KeyO Keycode = 18 | 0xF000
|
||||
KeyP Keycode = 19 | 0xF000
|
||||
KeyQ Keycode = 20 | 0xF000
|
||||
KeyR Keycode = 21 | 0xF000
|
||||
KeyS Keycode = 22 | 0xF000
|
||||
KeyT Keycode = 23 | 0xF000
|
||||
KeyU Keycode = 24 | 0xF000
|
||||
KeyV Keycode = 25 | 0xF000
|
||||
KeyW Keycode = 26 | 0xF000
|
||||
KeyX Keycode = 27 | 0xF000
|
||||
KeyY Keycode = 28 | 0xF000
|
||||
KeyZ Keycode = 29 | 0xF000
|
||||
Key1 Keycode = 30 | 0xF000
|
||||
Key2 Keycode = 31 | 0xF000
|
||||
Key3 Keycode = 32 | 0xF000
|
||||
Key4 Keycode = 33 | 0xF000
|
||||
Key5 Keycode = 34 | 0xF000
|
||||
Key6 Keycode = 35 | 0xF000
|
||||
Key7 Keycode = 36 | 0xF000
|
||||
Key8 Keycode = 37 | 0xF000
|
||||
Key9 Keycode = 38 | 0xF000
|
||||
Key0 Keycode = 39 | 0xF000
|
||||
KeyEnter Keycode = 40 | 0xF000
|
||||
KeyEsc Keycode = 41 | 0xF000
|
||||
KeyBackspace Keycode = 42 | 0xF000
|
||||
KeyTab Keycode = 43 | 0xF000
|
||||
KeySpace Keycode = 44 | 0xF000
|
||||
KeyMinus Keycode = 45 | 0xF000
|
||||
KeyEqual Keycode = 46 | 0xF000
|
||||
KeyLeftBrace Keycode = 47 | 0xF000
|
||||
KeyRightBrace Keycode = 48 | 0xF000
|
||||
KeyBackslash Keycode = 49 | 0xF000
|
||||
KeyNonUsNum Keycode = 50 | 0xF000
|
||||
KeySemicolon Keycode = 51 | 0xF000
|
||||
KeyQuote Keycode = 52 | 0xF000
|
||||
KeyTilde Keycode = 53 | 0xF000
|
||||
KeyComma Keycode = 54 | 0xF000
|
||||
KeyPeriod Keycode = 55 | 0xF000
|
||||
KeySlash Keycode = 56 | 0xF000
|
||||
KeyCapsLock Keycode = 57 | 0xF000
|
||||
KeyF1 Keycode = 58 | 0xF000
|
||||
KeyF2 Keycode = 59 | 0xF000
|
||||
KeyF3 Keycode = 60 | 0xF000
|
||||
KeyF4 Keycode = 61 | 0xF000
|
||||
KeyF5 Keycode = 62 | 0xF000
|
||||
KeyF6 Keycode = 63 | 0xF000
|
||||
KeyF7 Keycode = 64 | 0xF000
|
||||
KeyF8 Keycode = 65 | 0xF000
|
||||
KeyF9 Keycode = 66 | 0xF000
|
||||
KeyF10 Keycode = 67 | 0xF000
|
||||
KeyF11 Keycode = 68 | 0xF000
|
||||
KeyF12 Keycode = 69 | 0xF000
|
||||
KeyPrintscreen Keycode = 70 | 0xF000
|
||||
KeyScrollLock Keycode = 71 | 0xF000
|
||||
KeyPause Keycode = 72 | 0xF000
|
||||
KeyInsert Keycode = 73 | 0xF000
|
||||
KeyHome Keycode = 74 | 0xF000
|
||||
KeyPageUp Keycode = 75 | 0xF000
|
||||
KeyDelete Keycode = 76 | 0xF000
|
||||
KeyEnd Keycode = 77 | 0xF000
|
||||
KeyPageDown Keycode = 78 | 0xF000
|
||||
KeyRight Keycode = 79 | 0xF000
|
||||
KeyLeft Keycode = 80 | 0xF000
|
||||
KeyDown Keycode = 81 | 0xF000
|
||||
KeyUp Keycode = 82 | 0xF000
|
||||
KeyNumLock Keycode = 83 | 0xF000
|
||||
KeypadSlash Keycode = 84 | 0xF000
|
||||
KeypadAsterisk Keycode = 85 | 0xF000
|
||||
KeypadMinus Keycode = 86 | 0xF000
|
||||
KeypadPlus Keycode = 87 | 0xF000
|
||||
KeypadEnter Keycode = 88 | 0xF000
|
||||
Keypad1 Keycode = 89 | 0xF000
|
||||
Keypad2 Keycode = 90 | 0xF000
|
||||
Keypad3 Keycode = 91 | 0xF000
|
||||
Keypad4 Keycode = 92 | 0xF000
|
||||
Keypad5 Keycode = 93 | 0xF000
|
||||
Keypad6 Keycode = 94 | 0xF000
|
||||
Keypad7 Keycode = 95 | 0xF000
|
||||
Keypad8 Keycode = 96 | 0xF000
|
||||
Keypad9 Keycode = 97 | 0xF000
|
||||
Keypad0 Keycode = 98 | 0xF000
|
||||
KeypadPeriod Keycode = 99 | 0xF000
|
||||
KeyNonUSBS Keycode = 100 | 0xF000
|
||||
KeyMenu Keycode = 101 | 0xF000
|
||||
KeyF13 Keycode = 104 | 0xF000
|
||||
KeyF14 Keycode = 105 | 0xF000
|
||||
KeyF15 Keycode = 106 | 0xF000
|
||||
KeyF16 Keycode = 107 | 0xF000
|
||||
KeyF17 Keycode = 108 | 0xF000
|
||||
KeyF18 Keycode = 109 | 0xF000
|
||||
KeyF19 Keycode = 110 | 0xF000
|
||||
KeyF20 Keycode = 111 | 0xF000
|
||||
KeyF21 Keycode = 112 | 0xF000
|
||||
KeyF22 Keycode = 113 | 0xF000
|
||||
KeyF23 Keycode = 114 | 0xF000
|
||||
KeyF24 Keycode = 115 | 0xF000
|
||||
|
||||
KeyUpArrow Keycode = KeyUp
|
||||
KeyDownArrow Keycode = KeyDown
|
||||
KeyLeftArrow Keycode = KeyLeft
|
||||
KeyRightArrow Keycode = KeyRight
|
||||
KeyReturn Keycode = KeyEnter
|
||||
KeyLeftCtrl Keycode = KeyModifierLeftCtrl
|
||||
KeyLeftShift Keycode = KeyModifierLeftShift
|
||||
KeyLeftAlt Keycode = KeyModifierLeftAlt
|
||||
KeyLeftGUI Keycode = KeyModifierLeftGUI
|
||||
KeyRightCtrl Keycode = KeyModifierRightCtrl
|
||||
KeyRightShift Keycode = KeyModifierRightShift
|
||||
KeyRightAlt Keycode = KeyModifierRightAlt
|
||||
KeyRightGUI Keycode = KeyModifierRightGUI
|
||||
)
|
||||
|
||||
// Keycodes for layout US English (0x0904)
|
||||
const (
|
||||
keycodeMask Keycode = 0x07FF
|
||||
keyMask Keycode = 0x003F
|
||||
|
||||
shiftMask Keycode = 0x0040
|
||||
altgrMask Keycode = 0x0080
|
||||
deadkeysMask Keycode = 0x0700
|
||||
circumflexBits Keycode = 0x0100
|
||||
acuteAccentBits Keycode = 0x0200
|
||||
graveAccentBits Keycode = 0x0300
|
||||
tildeBits Keycode = 0x0400
|
||||
diaeresisBits Keycode = 0x0500
|
||||
deadkeyCircumflex Keycode = Key6 | shiftMask
|
||||
deadkeyAcuteAccent Keycode = KeyQuote
|
||||
deadkeyGraveAccent Keycode = KeyTilde
|
||||
deadkeyTilde Keycode = KeyTilde | shiftMask
|
||||
deadkeyDiaeresis Keycode = KeyQuote | shiftMask
|
||||
|
||||
ASCII00 Keycode = 0 // 0 NUL
|
||||
ASCII01 Keycode = 0 // 1 SOH
|
||||
ASCII02 Keycode = 0 // 2 STX
|
||||
ASCII03 Keycode = 0 // 3 ETX
|
||||
ASCII04 Keycode = 0 // 4 EOT
|
||||
ASCII05 Keycode = 0 // 5 ENQ
|
||||
ASCII06 Keycode = 0 // 6 ACK
|
||||
ASCII07 Keycode = 0 // 7 BEL
|
||||
ASCII08 Keycode = KeyBackspace // 8 BS
|
||||
ASCII09 Keycode = KeyTab // 9 TAB
|
||||
ASCII0A Keycode = KeyEnter // 10 LF
|
||||
ASCII0B Keycode = 0 // 11 VT
|
||||
ASCII0C Keycode = 0 // 12 FF
|
||||
ASCII0D Keycode = 0 // 13 CR
|
||||
ASCII0E Keycode = 0 // 14 SO
|
||||
ASCII0F Keycode = 0 // 15 SI
|
||||
ASCII10 Keycode = 0 // 16 DEL
|
||||
ASCII11 Keycode = 0 // 17 DC1
|
||||
ASCII12 Keycode = 0 // 18 DC2
|
||||
ASCII13 Keycode = 0 // 19 DC3
|
||||
ASCII14 Keycode = 0 // 20 DC4
|
||||
ASCII15 Keycode = 0 // 21 NAK
|
||||
ASCII16 Keycode = 0 // 22 SYN
|
||||
ASCII17 Keycode = 0 // 23 ETB
|
||||
ASCII18 Keycode = 0 // 24 CAN
|
||||
ASCII19 Keycode = 0 // 25 EM
|
||||
ASCII1A Keycode = 0 // 26 SUB
|
||||
ASCII1B Keycode = 0 // 27 ESC
|
||||
ASCII1C Keycode = 0 // 28 FS
|
||||
ASCII1D Keycode = 0 // 29 GS
|
||||
ASCII1E Keycode = 0 // 30 RS
|
||||
ASCII1F Keycode = 0 // 31 US
|
||||
|
||||
ASCII20 Keycode = KeySpace // 32 SPACE
|
||||
ASCII21 Keycode = Key1 | shiftMask // 33 !
|
||||
ASCII22 Keycode = diaeresisBits | KeySpace // 34 "
|
||||
ASCII23 Keycode = Key3 | shiftMask // 35 #
|
||||
ASCII24 Keycode = Key4 | shiftMask // 36 $
|
||||
ASCII25 Keycode = Key5 | shiftMask // 37 %
|
||||
ASCII26 Keycode = Key7 | shiftMask // 38 &
|
||||
ASCII27 Keycode = acuteAccentBits | KeySpace // 39 '
|
||||
ASCII28 Keycode = Key9 | shiftMask // 40 (
|
||||
ASCII29 Keycode = Key0 | shiftMask // 41 )
|
||||
ASCII2A Keycode = Key8 | shiftMask // 42 *
|
||||
ASCII2B Keycode = KeyEqual | shiftMask // 43 +
|
||||
ASCII2C Keycode = KeyComma // 44 ,
|
||||
ASCII2D Keycode = KeyMinus // 45 -
|
||||
ASCII2E Keycode = KeyPeriod // 46 .
|
||||
ASCII2F Keycode = KeySlash // 47 /
|
||||
ASCII30 Keycode = Key0 // 48 0
|
||||
ASCII31 Keycode = Key1 // 49 1
|
||||
ASCII32 Keycode = Key2 // 50 2
|
||||
ASCII33 Keycode = Key3 // 51 3
|
||||
ASCII34 Keycode = Key4 // 52 4
|
||||
ASCII35 Keycode = Key5 // 53 5
|
||||
ASCII36 Keycode = Key6 // 54 6
|
||||
ASCII37 Keycode = Key7 // 55 7
|
||||
ASCII38 Keycode = Key8 // 55 8
|
||||
ASCII39 Keycode = Key9 // 57 9
|
||||
ASCII3A Keycode = KeySemicolon | shiftMask // 58 :
|
||||
ASCII3B Keycode = KeySemicolon // 59 ;
|
||||
ASCII3C Keycode = KeyComma | shiftMask // 60 <
|
||||
ASCII3D Keycode = KeyEqual // 61 =
|
||||
ASCII3E Keycode = KeyPeriod | shiftMask // 62 >
|
||||
ASCII3F Keycode = KeySlash | shiftMask // 63 ?
|
||||
ASCII40 Keycode = Key2 | shiftMask // 64 @
|
||||
ASCII41 Keycode = KeyA | shiftMask // 65 A
|
||||
ASCII42 Keycode = KeyB | shiftMask // 66 B
|
||||
ASCII43 Keycode = KeyC | shiftMask // 67 C
|
||||
ASCII44 Keycode = KeyD | shiftMask // 68 D
|
||||
ASCII45 Keycode = KeyE | shiftMask // 69 E
|
||||
ASCII46 Keycode = KeyF | shiftMask // 70 F
|
||||
ASCII47 Keycode = KeyG | shiftMask // 71 G
|
||||
ASCII48 Keycode = KeyH | shiftMask // 72 H
|
||||
ASCII49 Keycode = KeyI | shiftMask // 73 I
|
||||
ASCII4A Keycode = KeyJ | shiftMask // 74 J
|
||||
ASCII4B Keycode = KeyK | shiftMask // 75 K
|
||||
ASCII4C Keycode = KeyL | shiftMask // 76 L
|
||||
ASCII4D Keycode = KeyM | shiftMask // 77 M
|
||||
ASCII4E Keycode = KeyN | shiftMask // 78 N
|
||||
ASCII4F Keycode = KeyO | shiftMask // 79 O
|
||||
ASCII50 Keycode = KeyP | shiftMask // 80 P
|
||||
ASCII51 Keycode = KeyQ | shiftMask // 81 Q
|
||||
ASCII52 Keycode = KeyR | shiftMask // 82 R
|
||||
ASCII53 Keycode = KeyS | shiftMask // 83 S
|
||||
ASCII54 Keycode = KeyT | shiftMask // 84 T
|
||||
ASCII55 Keycode = KeyU | shiftMask // 85 U
|
||||
ASCII56 Keycode = KeyV | shiftMask // 86 V
|
||||
ASCII57 Keycode = KeyW | shiftMask // 87 W
|
||||
ASCII58 Keycode = KeyX | shiftMask // 88 X
|
||||
ASCII59 Keycode = KeyY | shiftMask // 89 Y
|
||||
ASCII5A Keycode = KeyZ | shiftMask // 90 Z
|
||||
ASCII5B Keycode = KeyLeftBrace // 91 [
|
||||
ASCII5C Keycode = KeyBackslash // 92 \
|
||||
ASCII5D Keycode = KeyRightBrace // 93 ]
|
||||
ASCII5E Keycode = circumflexBits | KeySpace // 94 ^
|
||||
ASCII5F Keycode = KeyMinus | shiftMask // 95
|
||||
ASCII60 Keycode = graveAccentBits | KeySpace // 96 `
|
||||
ASCII61 Keycode = KeyA // 97 a
|
||||
ASCII62 Keycode = KeyB // 98 b
|
||||
ASCII63 Keycode = KeyC // 99 c
|
||||
ASCII64 Keycode = KeyD // 100 d
|
||||
ASCII65 Keycode = KeyE // 101 e
|
||||
ASCII66 Keycode = KeyF // 102 f
|
||||
ASCII67 Keycode = KeyG // 103 g
|
||||
ASCII68 Keycode = KeyH // 104 h
|
||||
ASCII69 Keycode = KeyI // 105 i
|
||||
ASCII6A Keycode = KeyJ // 106 j
|
||||
ASCII6B Keycode = KeyK // 107 k
|
||||
ASCII6C Keycode = KeyL // 108 l
|
||||
ASCII6D Keycode = KeyM // 109 m
|
||||
ASCII6E Keycode = KeyN // 110 n
|
||||
ASCII6F Keycode = KeyO // 111 o
|
||||
ASCII70 Keycode = KeyP // 112 p
|
||||
ASCII71 Keycode = KeyQ // 113 q
|
||||
ASCII72 Keycode = KeyR // 114 r
|
||||
ASCII73 Keycode = KeyS // 115 s
|
||||
ASCII74 Keycode = KeyT // 116 t
|
||||
ASCII75 Keycode = KeyU // 117 u
|
||||
ASCII76 Keycode = KeyV // 118 v
|
||||
ASCII77 Keycode = KeyW // 119 w
|
||||
ASCII78 Keycode = KeyX // 120 x
|
||||
ASCII79 Keycode = KeyY // 121 y
|
||||
ASCII7A Keycode = KeyZ // 122 z
|
||||
ASCII7B Keycode = KeyLeftBrace | shiftMask // 123 {
|
||||
ASCII7C Keycode = KeyBackslash | shiftMask // 124 |
|
||||
ASCII7D Keycode = KeyRightBrace | shiftMask // 125 }
|
||||
ASCII7E Keycode = tildeBits | KeySpace // 126 ~
|
||||
ASCII7F Keycode = KeyBackspace // 127 DEL
|
||||
ISO88591A0 Keycode = KeySpace // 160 Nonbreakng Space
|
||||
ISO88591A1 Keycode = Key1 | altgrMask // 161 ¡ Inverted Exclamation
|
||||
ISO88591A2 Keycode = KeyC | altgrMask | shiftMask // 162 ¢ Cent SIGN
|
||||
ISO88591A3 Keycode = Key4 | altgrMask | shiftMask // 163 £ Pound Sign
|
||||
ISO88591A4 Keycode = Key4 | altgrMask // 164 ¤ Currency or Euro Sign
|
||||
ISO88591A5 Keycode = KeyMinus | altgrMask // 165 ¥ YEN SIGN
|
||||
ISO88591A6 Keycode = KeyBackslash | altgrMask | shiftMask // 166 ¦ BROKEN BAR ??
|
||||
ISO88591A7 Keycode = KeyS | altgrMask | shiftMask // 167 § SECTION SIGN
|
||||
ISO88591A8 Keycode = KeyQuote | altgrMask | shiftMask // 168 ¨ DIAERESIS
|
||||
ISO88591A9 Keycode = KeyC | altgrMask // 169 © COPYRIGHT SIGN
|
||||
ISO88591AA Keycode = 0 // 170 ª FEMININE ORDINAL
|
||||
ISO88591AB Keycode = KeyLeftBrace | altgrMask // 171 « LEFT DOUBLE ANGLE QUOTE
|
||||
ISO88591AC Keycode = KeyBackslash | altgrMask // 172 ¬ NOT SIGN ??
|
||||
ISO88591AD Keycode = 0 // 173 SOFT HYPHEN
|
||||
ISO88591AE Keycode = KeyR | altgrMask // 174 ® REGISTERED SIGN
|
||||
ISO88591AF Keycode = 0 // 175 ¯ MACRON
|
||||
ISO88591B0 Keycode = KeySemicolon | altgrMask | shiftMask // 176 ° DEGREE SIGN
|
||||
ISO88591B1 Keycode = 0 // 177 ± PLUS-MINUS SIGN
|
||||
ISO88591B2 Keycode = Key2 | altgrMask // 178 ² SUPERSCRIPT TWO
|
||||
ISO88591B3 Keycode = Key3 | altgrMask // 179 ³ SUPERSCRIPT THREE
|
||||
ISO88591B4 Keycode = KeyQuote | altgrMask // 180 ´ ACUTE ACCENT
|
||||
ISO88591B5 Keycode = KeyM | altgrMask // 181 µ MICRO SIGN
|
||||
ISO88591B6 Keycode = KeySemicolon | altgrMask // 182 ¶ PILCROW SIGN
|
||||
ISO88591B7 Keycode = 0 // 183 · MIDDLE DOT
|
||||
ISO88591B8 Keycode = 0 // 184 ¸ CEDILLA
|
||||
ISO88591B9 Keycode = Key1 | altgrMask | shiftMask // 185 ¹ SUPERSCRIPT ONE
|
||||
ISO88591BA Keycode = 0 // 186 º MASCULINE ORDINAL
|
||||
ISO88591BB Keycode = KeyRightBrace | altgrMask // 187 » RIGHT DOUBLE ANGLE QUOTE
|
||||
ISO88591BC Keycode = Key6 | altgrMask // 188 ¼ FRACTION ONE QUARTER
|
||||
ISO88591BD Keycode = Key7 | altgrMask // 189 ½ FRACTION ONE HALF
|
||||
ISO88591BE Keycode = Key8 | altgrMask // 190 ¾ FRACTION THREE QUARTERS
|
||||
ISO88591BF Keycode = KeySlash | altgrMask // 191 ¿ INVERTED QUESTION MARK
|
||||
ISO88591C0 Keycode = graveAccentBits | KeyA | shiftMask // 192 À A GRAVE
|
||||
ISO88591C1 Keycode = KeyA | altgrMask | shiftMask // 193 Á A ACUTE
|
||||
ISO88591C2 Keycode = circumflexBits | KeyA | shiftMask // 194 Â A CIRCUMFLEX
|
||||
ISO88591C3 Keycode = tildeBits | KeyA | shiftMask // 195 Ã A TILDE
|
||||
ISO88591C4 Keycode = KeyQ | altgrMask | shiftMask // 196 Ä A DIAERESIS
|
||||
ISO88591C5 Keycode = KeyW | altgrMask | shiftMask // 197 Å A RING ABOVE
|
||||
ISO88591C6 Keycode = KeyZ | altgrMask | shiftMask // 198 Æ AE
|
||||
ISO88591C7 Keycode = KeyComma | altgrMask | shiftMask // 199 Ç C CEDILLA
|
||||
ISO88591C8 Keycode = graveAccentBits | KeyE | shiftMask // 200 È E GRAVE
|
||||
ISO88591C9 Keycode = KeyE | altgrMask | shiftMask // 201 É E ACUTE
|
||||
ISO88591CA Keycode = circumflexBits | KeyE | shiftMask // 202 Ê E CIRCUMFLEX
|
||||
ISO88591CB Keycode = diaeresisBits | KeyE | shiftMask // 203 Ë E DIAERESIS
|
||||
ISO88591CC Keycode = graveAccentBits | KeyI | shiftMask // 204 Ì I GRAVE
|
||||
ISO88591CD Keycode = KeyI | altgrMask | shiftMask // 205 Í I ACUTE
|
||||
ISO88591CE Keycode = circumflexBits | KeyI | shiftMask // 206 Î I CIRCUMFLEX
|
||||
ISO88591CF Keycode = diaeresisBits | KeyI | shiftMask // 207 Ï I DIAERESIS
|
||||
ISO88591D0 Keycode = KeyD | altgrMask | shiftMask // 208 Ð ETH
|
||||
ISO88591D1 Keycode = KeyN | altgrMask | shiftMask // 209 Ñ N TILDE
|
||||
ISO88591D2 Keycode = graveAccentBits | KeyO | shiftMask // 210 Ò O GRAVE
|
||||
ISO88591D3 Keycode = KeyO | altgrMask | shiftMask // 211 Ó O ACUTE
|
||||
ISO88591D4 Keycode = circumflexBits | KeyO | shiftMask // 212 Ô O CIRCUMFLEX
|
||||
ISO88591D5 Keycode = tildeBits | KeyO | shiftMask // 213 Õ O TILDE
|
||||
ISO88591D6 Keycode = KeyP | altgrMask | shiftMask // 214 Ö O DIAERESIS
|
||||
ISO88591D7 Keycode = KeyEqual | altgrMask // 215 × MULTIPLICATION
|
||||
ISO88591D8 Keycode = KeyL | altgrMask | shiftMask // 216 Ø O STROKE
|
||||
ISO88591D9 Keycode = graveAccentBits | KeyU | shiftMask // 217 Ù U GRAVE
|
||||
ISO88591DA Keycode = KeyU | altgrMask | shiftMask // 218 Ú U ACUTE
|
||||
ISO88591DB Keycode = circumflexBits | KeyU | shiftMask // 219 Û U CIRCUMFLEX
|
||||
ISO88591DC Keycode = KeyY | altgrMask | shiftMask // 220 Ü U DIAERESIS
|
||||
ISO88591DD Keycode = acuteAccentBits | KeyY | shiftMask // 221 Ý Y ACUTE
|
||||
ISO88591DE Keycode = KeyT | altgrMask | shiftMask // 222 Þ THORN
|
||||
ISO88591DF Keycode = KeyS | altgrMask // 223 ß SHARP S
|
||||
ISO88591E0 Keycode = graveAccentBits | KeyA // 224 à a GRAVE
|
||||
ISO88591E1 Keycode = KeyA | altgrMask // 225 á a ACUTE
|
||||
ISO88591E2 Keycode = circumflexBits | KeyA // 226 â a CIRCUMFLEX
|
||||
ISO88591E3 Keycode = tildeBits | KeyA // 227 ã a TILDE
|
||||
ISO88591E4 Keycode = diaeresisBits | KeyA // 228 ä a DIAERESIS
|
||||
ISO88591E5 Keycode = KeyW | altgrMask // 229 å a RING ABOVE
|
||||
ISO88591E6 Keycode = KeyZ | altgrMask // 230 æ ae
|
||||
ISO88591E7 Keycode = KeyComma | altgrMask // 231 ç c CEDILLA
|
||||
ISO88591E8 Keycode = graveAccentBits | KeyE // 232 è e GRAVE
|
||||
ISO88591E9 Keycode = acuteAccentBits | KeyE // 233 é e ACUTE
|
||||
ISO88591EA Keycode = circumflexBits | KeyE // 234 ê e CIRCUMFLEX
|
||||
ISO88591EB Keycode = diaeresisBits | KeyE // 235 ë e DIAERESIS
|
||||
ISO88591EC Keycode = graveAccentBits | KeyI // 236 ì i GRAVE
|
||||
ISO88591ED Keycode = KeyI | altgrMask // 237 í i ACUTE
|
||||
ISO88591EE Keycode = circumflexBits | KeyI // 238 î i CIRCUMFLEX
|
||||
ISO88591EF Keycode = diaeresisBits | KeyI // 239 ï i DIAERESIS
|
||||
ISO88591F0 Keycode = KeyD | altgrMask // 240 ð ETH
|
||||
ISO88591F1 Keycode = KeyN | altgrMask // 241 ñ n TILDE
|
||||
ISO88591F2 Keycode = graveAccentBits | KeyO // 242 ò o GRAVE
|
||||
ISO88591F3 Keycode = KeyO | altgrMask // 243 ó o ACUTE
|
||||
ISO88591F4 Keycode = circumflexBits | KeyO // 244 ô o CIRCUMFLEX
|
||||
ISO88591F5 Keycode = tildeBits | KeyO // 245 õ o TILDE
|
||||
ISO88591F6 Keycode = KeyP | altgrMask // 246 ö o DIAERESIS
|
||||
ISO88591F7 Keycode = KeyEqual | altgrMask | shiftMask // 247 ÷ DIVISION
|
||||
ISO88591F8 Keycode = KeyL | altgrMask // 248 ø o STROKE
|
||||
ISO88591F9 Keycode = graveAccentBits | KeyU // 249 ù u GRAVE
|
||||
ISO88591FA Keycode = KeyU | altgrMask // 250 ú u ACUTE
|
||||
ISO88591FB Keycode = circumflexBits | KeyU // 251 û u CIRCUMFLEX
|
||||
ISO88591FC Keycode = KeyY | altgrMask // 252 ü u DIAERESIS
|
||||
ISO88591FD Keycode = acuteAccentBits | KeyY // 253 ý y ACUTE
|
||||
ISO88591FE Keycode = KeyT | altgrMask // 254 þ THORN
|
||||
ISO88591FF Keycode = diaeresisBits | KeyY // 255 ÿ y DIAERESIS
|
||||
UNICODE20AC Keycode = Key5 | altgrMask // 20AC € Euro Sign
|
||||
)
|
||||
|
||||
var ascii = [...]Keycode{
|
||||
ASCII00.mask(), ASCII01.mask(), ASCII02.mask(), ASCII03.mask(),
|
||||
ASCII04.mask(), ASCII05.mask(), ASCII06.mask(), ASCII07.mask(),
|
||||
ASCII08.mask(), ASCII09.mask(), ASCII0A.mask(), ASCII0B.mask(),
|
||||
ASCII0C.mask(), ASCII0D.mask(), ASCII0E.mask(), ASCII0F.mask(),
|
||||
ASCII10.mask(), ASCII11.mask(), ASCII12.mask(), ASCII13.mask(),
|
||||
ASCII14.mask(), ASCII15.mask(), ASCII16.mask(), ASCII17.mask(),
|
||||
ASCII18.mask(), ASCII19.mask(), ASCII1A.mask(), ASCII1B.mask(),
|
||||
ASCII1C.mask(), ASCII1D.mask(), ASCII1E.mask(), ASCII1F.mask(),
|
||||
ASCII20.mask(), ASCII21.mask(), ASCII22.mask(), ASCII23.mask(),
|
||||
ASCII24.mask(), ASCII25.mask(), ASCII26.mask(), ASCII27.mask(),
|
||||
ASCII28.mask(), ASCII29.mask(), ASCII2A.mask(), ASCII2B.mask(),
|
||||
ASCII2C.mask(), ASCII2D.mask(), ASCII2E.mask(), ASCII2F.mask(),
|
||||
ASCII30.mask(), ASCII31.mask(), ASCII32.mask(), ASCII33.mask(),
|
||||
ASCII34.mask(), ASCII35.mask(), ASCII36.mask(), ASCII37.mask(),
|
||||
ASCII38.mask(), ASCII39.mask(), ASCII3A.mask(), ASCII3B.mask(),
|
||||
ASCII3C.mask(), ASCII3D.mask(), ASCII3E.mask(), ASCII3F.mask(),
|
||||
ASCII40.mask(), ASCII41.mask(), ASCII42.mask(), ASCII43.mask(),
|
||||
ASCII44.mask(), ASCII45.mask(), ASCII46.mask(), ASCII47.mask(),
|
||||
ASCII48.mask(), ASCII49.mask(), ASCII4A.mask(), ASCII4B.mask(),
|
||||
ASCII4C.mask(), ASCII4D.mask(), ASCII4E.mask(), ASCII4F.mask(),
|
||||
ASCII50.mask(), ASCII51.mask(), ASCII52.mask(), ASCII53.mask(),
|
||||
ASCII54.mask(), ASCII55.mask(), ASCII56.mask(), ASCII57.mask(),
|
||||
ASCII58.mask(), ASCII59.mask(), ASCII5A.mask(), ASCII5B.mask(),
|
||||
ASCII5C.mask(), ASCII5D.mask(), ASCII5E.mask(), ASCII5F.mask(),
|
||||
ASCII60.mask(), ASCII61.mask(), ASCII62.mask(), ASCII63.mask(),
|
||||
ASCII64.mask(), ASCII65.mask(), ASCII66.mask(), ASCII67.mask(),
|
||||
ASCII68.mask(), ASCII69.mask(), ASCII6A.mask(), ASCII6B.mask(),
|
||||
ASCII6C.mask(), ASCII6D.mask(), ASCII6E.mask(), ASCII6F.mask(),
|
||||
ASCII70.mask(), ASCII71.mask(), ASCII72.mask(), ASCII73.mask(),
|
||||
ASCII74.mask(), ASCII75.mask(), ASCII76.mask(), ASCII77.mask(),
|
||||
ASCII78.mask(), ASCII79.mask(), ASCII7A.mask(), ASCII7B.mask(),
|
||||
ASCII7C.mask(), ASCII7D.mask(), ASCII7E.mask(), ASCII7F.mask(),
|
||||
}
|
||||
|
||||
var iso88591 = [...]Keycode{
|
||||
ISO88591A0.mask(), ISO88591A1.mask(), ISO88591A2.mask(), ISO88591A3.mask(),
|
||||
ISO88591A4.mask(), ISO88591A5.mask(), ISO88591A6.mask(), ISO88591A7.mask(),
|
||||
ISO88591A8.mask(), ISO88591A9.mask(), ISO88591AA.mask(), ISO88591AB.mask(),
|
||||
ISO88591AC.mask(), ISO88591AD.mask(), ISO88591AE.mask(), ISO88591AF.mask(),
|
||||
ISO88591B0.mask(), ISO88591B1.mask(), ISO88591B2.mask(), ISO88591B3.mask(),
|
||||
ISO88591B4.mask(), ISO88591B5.mask(), ISO88591B6.mask(), ISO88591B7.mask(),
|
||||
ISO88591B8.mask(), ISO88591B9.mask(), ISO88591BA.mask(), ISO88591BB.mask(),
|
||||
ISO88591BC.mask(), ISO88591BD.mask(), ISO88591BE.mask(), ISO88591BF.mask(),
|
||||
ISO88591C0.mask(), ISO88591C1.mask(), ISO88591C2.mask(), ISO88591C3.mask(),
|
||||
ISO88591C4.mask(), ISO88591C5.mask(), ISO88591C6.mask(), ISO88591C7.mask(),
|
||||
ISO88591C8.mask(), ISO88591C9.mask(), ISO88591CA.mask(), ISO88591CB.mask(),
|
||||
ISO88591CC.mask(), ISO88591CD.mask(), ISO88591CE.mask(), ISO88591CF.mask(),
|
||||
ISO88591D0.mask(), ISO88591D1.mask(), ISO88591D2.mask(), ISO88591D3.mask(),
|
||||
ISO88591D4.mask(), ISO88591D5.mask(), ISO88591D6.mask(), ISO88591D7.mask(),
|
||||
ISO88591D8.mask(), ISO88591D9.mask(), ISO88591DA.mask(), ISO88591DB.mask(),
|
||||
ISO88591DC.mask(), ISO88591DD.mask(), ISO88591DE.mask(), ISO88591DF.mask(),
|
||||
ISO88591E0.mask(), ISO88591E1.mask(), ISO88591E2.mask(), ISO88591E3.mask(),
|
||||
ISO88591E4.mask(), ISO88591E5.mask(), ISO88591E6.mask(), ISO88591E7.mask(),
|
||||
ISO88591E8.mask(), ISO88591E9.mask(), ISO88591EA.mask(), ISO88591EB.mask(),
|
||||
ISO88591EC.mask(), ISO88591ED.mask(), ISO88591EE.mask(), ISO88591EF.mask(),
|
||||
ISO88591F0.mask(), ISO88591F1.mask(), ISO88591F2.mask(), ISO88591F3.mask(),
|
||||
ISO88591F4.mask(), ISO88591F5.mask(), ISO88591F6.mask(), ISO88591F7.mask(),
|
||||
ISO88591F8.mask(), ISO88591F9.mask(), ISO88591FA.mask(), ISO88591FB.mask(),
|
||||
ISO88591FC.mask(), ISO88591FD.mask(), ISO88591FE.mask(), ISO88591FF.mask(),
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//go:build usb.hid
|
||||
// +build usb.hid
|
||||
|
||||
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")
|
||||
)
|
||||
|
||||
// HID represents a virtual keyboard/mouse/joystick device (with a serial data
|
||||
// Rx/Tx interface) using the USB HID device class driver.
|
||||
type HID struct {
|
||||
// Port is the MCU's native USB core number. If in doubt, leave it
|
||||
// uninitialized for default (0).
|
||||
Port int
|
||||
core *core
|
||||
}
|
||||
|
||||
type HIDConfig struct {
|
||||
BusSpeed Speed
|
||||
}
|
||||
|
||||
func (hid *HID) Configure(config HIDConfig) error {
|
||||
|
||||
c := class{id: classDeviceHID, config: 1}
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
var st status
|
||||
hid.core, st = initCore(hid.Port, config.BusSpeed, c)
|
||||
if !st.ok() {
|
||||
return ErrHIDInvalidPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hid *HID) Ready() bool {
|
||||
return hid.core.dc.keyboard().ready()
|
||||
}
|
||||
|
||||
func (hid *HID) Keyboard() *Keyboard {
|
||||
return hid.core.dc.keyboard()
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package usb
|
||||
|
||||
// Hardware abstraction for USB ports configured as either host or device.
|
||||
|
||||
// CoreCount defines the total number of USB cores which may be configured in
|
||||
// device or host mode.
|
||||
const CoreCount = descCoreCount
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// Speed represents the configured USB data transfer rate, or bus speed, for
|
||||
// communication between host and device.
|
||||
type Speed uint8
|
||||
|
||||
// Constant definitions for USB data transfer rates. Note that every transfer
|
||||
// rate may not be supported by every target due to either hardware or software
|
||||
// limitations. By far, the most commonly-supported rate is USB 1.1 Full-Speed
|
||||
// (12 Mbit/sec). If unsure, either use FullSpeed or leave it undefined and let
|
||||
// the driver use the default speed for your target.
|
||||
const (
|
||||
LowSpeed Speed = iota + 1 // 1.5 Mbit/sec (USB 1.0)
|
||||
FullSpeed // 12 Mbit/sec (USB 1.1)
|
||||
HighSpeed // 480 Mbit/sec (USB 2.0)
|
||||
SuperSpeed // 5 Gbit/sec (USB 3.0)
|
||||
DualSuperSpeed // 10 Gbit/sec (USB 3.1, Dual-Lane SS)
|
||||
)
|
||||
|
||||
// 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, speed Speed, class class) (*core, status) {
|
||||
|
||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
||||
return nil, statusInvalid
|
||||
}
|
||||
|
||||
if modeIdle != coreInstance[port].mode {
|
||||
// Check if requested port is already configured as requested class. If so,
|
||||
// just return a reference to the existing core instead of an error.
|
||||
//
|
||||
// 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, speed, 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, speed, 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 supported host/device class configurations.
|
||||
const (
|
||||
classDeviceCDC = 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 classDeviceCDC, 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
|
||||
statusFail // Failure
|
||||
)
|
||||
|
||||
// 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,341 @@
|
||||
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 {
|
||||
var b [8]uint8
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return b[:]
|
||||
}
|
||||
b[0] = uint8(u)
|
||||
b[1] = uint8(u >> 8)
|
||||
b[2] = uint8(u >> 16)
|
||||
b[3] = uint8(u >> 24)
|
||||
b[4] = uint8(u >> 32)
|
||||
b[5] = uint8(u >> 40)
|
||||
b[6] = uint8(u >> 48)
|
||||
b[7] = uint8(u >> 56)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var b [4]uint8
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return b[:]
|
||||
}
|
||||
b[0] = uint8(u)
|
||||
b[1] = uint8(u >> 8)
|
||||
b[2] = uint8(u >> 16)
|
||||
b[3] = uint8(u >> 24)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var b [2]uint8
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return b[:]
|
||||
}
|
||||
b[0] = uint8(u)
|
||||
b[1] = uint8(u >> 8)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var b [8]uint8
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return b[:]
|
||||
}
|
||||
b[7] = uint8(u)
|
||||
b[6] = uint8(u >> 8)
|
||||
b[5] = uint8(u >> 16)
|
||||
b[4] = uint8(u >> 24)
|
||||
b[3] = uint8(u >> 32)
|
||||
b[2] = uint8(u >> 40)
|
||||
b[1] = uint8(u >> 48)
|
||||
b[0] = uint8(u >> 56)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var b [4]uint8
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return b[:]
|
||||
}
|
||||
b[3] = uint8(u)
|
||||
b[2] = uint8(u >> 8)
|
||||
b[1] = uint8(u >> 16)
|
||||
b[0] = uint8(u >> 24)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var b [2]uint8
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return b[:]
|
||||
}
|
||||
b[1] = uint8(u)
|
||||
b[0] = uint8(u >> 8)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// 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 endpointValid(address uint8) bool {
|
||||
return address&descEndpointInvalid == 0
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func unpackEndpoint(address uint8) (number, direction uint8) {
|
||||
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos,
|
||||
(address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func packEndpoint(number, direction uint8) (address uint8) {
|
||||
return ((number << descEndptAddrNumberPos) & descEndptAddrNumberMsk) |
|
||||
((direction << descEndptAddrDirectionPos) & descEndptAddrDirectionMsk)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func endpointNumber(address uint8) (number uint8) {
|
||||
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func endpointDirection(address uint8) (direction uint8) {
|
||||
return (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)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func indexEndpoint(index uint8) uint8 {
|
||||
return ((index >> 1) & descEndptAddrNumberMsk) |
|
||||
((index & 0x1) << descEndptAddrDirectionPos)
|
||||
}
|
||||
|
||||
// wrap computes the index into a circular buffer of length mod by walking
|
||||
// forward n elements if n is positive, or reverse -n elements if n is negative.
|
||||
// For example, both wrap(12, 10) and wrap(-18, 10) return 2.
|
||||
//go:inline
|
||||
func wrap(n, mod int) int {
|
||||
if mod <= 0 {
|
||||
// Buffer length (mod) must be positive.
|
||||
return 0
|
||||
}
|
||||
if n < 0 {
|
||||
if -n < mod {
|
||||
// Do not wrap around (no underflow).
|
||||
return mod + n
|
||||
}
|
||||
return mod - (-n % mod)
|
||||
}
|
||||
if n < mod {
|
||||
// Do not wrap around (no overflow).
|
||||
return n
|
||||
}
|
||||
return n % mod
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -25,14 +25,16 @@ func init() {
|
||||
initClocks()
|
||||
initRTC()
|
||||
initSERCOMClocks()
|
||||
initUSBClock()
|
||||
initADCClock()
|
||||
|
||||
// connect to USB CDC interface
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
if !machine.USB.Configured() {
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
}
|
||||
//// connect to USB CDC interface
|
||||
//machine.Serial.Configure(usb.UARTConfig{})
|
||||
//if !machine.USB.Configured() {
|
||||
// machine.USB.Configure(usb.UARTConfig{})
|
||||
//}
|
||||
|
||||
machine.InitUSB()
|
||||
machine.InitSerial()
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
|
||||
@@ -36,7 +36,7 @@ func handleHardFault(sp *interruptStack) {
|
||||
if fault.Mem().WhileUnstackingException() {
|
||||
print(" while unstacking exception")
|
||||
}
|
||||
if fault.Mem().WileStackingException() {
|
||||
if fault.Mem().WhileStackingException() {
|
||||
print(" while stacking exception")
|
||||
}
|
||||
if fault.Mem().DuringFPLazyStatePres() {
|
||||
@@ -162,13 +162,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
|
||||
return fs&arm.SCB_CFSR_MUNSTKERR != 0
|
||||
}
|
||||
|
||||
// WileStackingException: stacking for an exception entry has caused one or more
|
||||
// WhileStackingException: stacking for an exception entry has caused one or more
|
||||
// access violations
|
||||
//
|
||||
// "When this bit is 1, the SP is still adjusted but the values in the context
|
||||
// area on the stack might be incorrect. The processor has not written a fault
|
||||
// address to the MMAR."
|
||||
func (fs MemFaultStatus) WileStackingException() bool {
|
||||
func (fs MemFaultStatus) WhileStackingException() bool {
|
||||
return fs&arm.SCB_CFSR_MSTKERR != 0
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -35,7 +36,7 @@ func main() {
|
||||
// initialize cache and MPU
|
||||
initCache()
|
||||
|
||||
// enable SysTick, GPIO, and peripherals
|
||||
// enable SysTick, GPIO, USB, and other peripherals
|
||||
initPeripherals()
|
||||
|
||||
// reenable interrupts
|
||||
@@ -107,7 +108,8 @@ func initPeripherals() {
|
||||
initPins() // configure GPIO
|
||||
|
||||
enablePeripheralClocks() // activate peripheral clock gates
|
||||
initUART() // configure UART (initialized first for debugging)
|
||||
initUSB() // configure USB CDC-ACM (UART0)
|
||||
initUART() // configure hardware UART (UART1)
|
||||
}
|
||||
|
||||
func initPins() {
|
||||
@@ -122,6 +124,11 @@ func initUART() {
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
func initUSB() {
|
||||
// machine.HID0.Configure(usb.HIDConfig{})
|
||||
machine.UART0.Configure(usb.UARTConfig{})
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
@@ -36,15 +36,31 @@ var (
|
||||
Denominator: 1, // 30-bit DENOM of fractional loop divider
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
}
|
||||
Usb1PllConfig = nxp.ClockConfigUsbPll{
|
||||
Instance: 1, // USB PLL instance
|
||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
Usb1PhyConfig = nxp.ClockConfigUsbPhy{
|
||||
Instance: 1, // USB PHY number (1 or 2)
|
||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||
DCal: 0xC, // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig: nxp.ClockConfigUsbPll{
|
||||
Instance: 1, // USB PLL number (1 or 2)
|
||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
||||
},
|
||||
}
|
||||
Usb2PllConfig = nxp.ClockConfigUsbPll{
|
||||
Instance: 2, // USB PLL instance
|
||||
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
|
||||
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
|
||||
Usb2PhyConfig = nxp.ClockConfigUsbPhy{
|
||||
Instance: 2, // USB PHY number (1 or 2)
|
||||
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
|
||||
DCal: 0xC, // Decode to trim the nominal 17.78mA current source
|
||||
TxCal45DP: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig: nxp.ClockConfigUsbPll{
|
||||
Instance: 2, // USB PLL number (1 or 2)
|
||||
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -86,7 +102,7 @@ func initClocks() {
|
||||
|
||||
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
|
||||
nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) |
|
||||
((13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||
((0x13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
|
||||
|
||||
// wait until DCDC_STS_DC_OK bit is asserted
|
||||
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
|
||||
@@ -105,6 +121,8 @@ func initClocks() {
|
||||
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
|
||||
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
|
||||
|
||||
nxp.ClockIpUsbOh3.Enable(false) // disable USB
|
||||
|
||||
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
|
||||
nxp.ClockIpGpt1S.Enable(false) //
|
||||
nxp.ClockIpGpt2.Enable(false) //
|
||||
@@ -113,6 +131,11 @@ func initClocks() {
|
||||
|
||||
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
|
||||
|
||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||
nxp.ClockIpGpio2.Enable(false) //
|
||||
nxp.ClockIpGpio3.Enable(false) //
|
||||
nxp.ClockIpGpio4.Enable(false) //
|
||||
|
||||
nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1
|
||||
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
|
||||
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
|
||||
@@ -121,9 +144,9 @@ func initClocks() {
|
||||
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
|
||||
|
||||
nxp.ClockIpSemc.Enable(false) // disable SEMC
|
||||
nxp.DivIpSemc.Div(1) // divide SEMC_PODF (DIV2)
|
||||
nxp.DivIpSemc.Div(7) // divide SEMC_PODF (DIV8)
|
||||
nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2
|
||||
nxp.MuxIpSemc.Mux(1) // SEMC select SEMC_ALT
|
||||
nxp.MuxIpSemc.Mux(0) // SEMC select PERIPH_CLK
|
||||
|
||||
if false {
|
||||
// TODO: external flash is on this bus, configured via DCD block
|
||||
@@ -192,7 +215,7 @@ func initClocks() {
|
||||
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
|
||||
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
|
||||
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
|
||||
nxp.MuxIpLcdifPre.Mux(5) // LCDIF_PRE select PLL3_PFD1
|
||||
nxp.MuxIpLcdifPre.Mux(4) // LCDIF_PRE select PLL2_PFD1
|
||||
|
||||
nxp.ClockIpSpdif.Enable(false) // disable SPDIF
|
||||
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
|
||||
@@ -210,43 +233,43 @@ func initClocks() {
|
||||
|
||||
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
|
||||
|
||||
// Disable Audio/Video/Ethernet PLLs
|
||||
nxp.CCM_ANALOG.PLL_AUDIO.Set(nxp.CCM_ANALOG_PLL_AUDIO_POWERDOWN_Msk)
|
||||
nxp.CCM_ANALOG.PLL_VIDEO.Set(nxp.CCM_ANALOG_PLL_VIDEO_POWERDOWN_Msk)
|
||||
nxp.CCM_ANALOG.PLL_ENET.Set(nxp.CCM_ANALOG_PLL_ENET_POWERDOWN_Msk)
|
||||
|
||||
ArmPllConfig.Configure() // init ARM PLL
|
||||
// SYS PLL (PLL2) @ 528 MHz
|
||||
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
|
||||
// PFD1 = 594 MHz -> (currently unused)
|
||||
// PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz
|
||||
// PFD3 = 454.73 MHz -> (currently unused)
|
||||
// PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz
|
||||
// PFD3 = 594 MHz -> (currently unused)
|
||||
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
|
||||
|
||||
// USB1 PLL (PLL3) @ 480 MHz
|
||||
// PFD0 -> (currently unused)
|
||||
// PFD1 -> (currently unused)
|
||||
// PFD2 -> (currently unused)
|
||||
// PFD3 -> (currently unused)
|
||||
Usb1PllConfig.Configure() // init USB1 PLL and PFDs
|
||||
Usb2PllConfig.Configure() // init USB2 PLL
|
||||
Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL
|
||||
Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL
|
||||
|
||||
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
|
||||
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
|
||||
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
|
||||
nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC
|
||||
|
||||
// set LVDS1 clock source
|
||||
// set LVDS1 clock source (ARM_PLL)
|
||||
nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) |
|
||||
((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk))
|
||||
|
||||
// set CLOCK_OUT1 divider
|
||||
// set CLOCK_OUT1 divider (DIV1)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) |
|
||||
((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk))
|
||||
// set CLOCK_OUT1 source
|
||||
// set CLOCK_OUT1 source (PLL2/DIV2)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) |
|
||||
((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk))
|
||||
// set CLOCK_OUT2 divider
|
||||
// set CLOCK_OUT2 divider (DIV1)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) |
|
||||
((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk))
|
||||
// set CLOCK_OUT2 source
|
||||
// set CLOCK_OUT2 source (SPDIF0)
|
||||
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) |
|
||||
((18 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||
((29 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
|
||||
|
||||
nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT
|
||||
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
|
||||
@@ -254,15 +277,10 @@ func initClocks() {
|
||||
|
||||
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
|
||||
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
|
||||
// set GPT1 High frequency reference clock source
|
||||
// set GPT1 High frequency reference clock source (PERCLK)
|
||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk)
|
||||
// set GPT2 High frequency reference clock source
|
||||
// set GPT2 High frequency reference clock source (PERCLK)
|
||||
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk)
|
||||
|
||||
nxp.ClockIpGpio1.Enable(false) // disable GPIO
|
||||
nxp.ClockIpGpio2.Enable(false) //
|
||||
nxp.ClockIpGpio3.Enable(false) //
|
||||
nxp.ClockIpGpio4.Enable(false) //
|
||||
}
|
||||
|
||||
func enableTimerClocks() {
|
||||
|
||||
@@ -142,7 +142,7 @@ func timerSleep(cycles uint32) bool {
|
||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts
|
||||
nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
|
||||
for {
|
||||
//arm.Asm("wfi") // TODO: causes hardfault! why?
|
||||
waitForEvents()
|
||||
if pitActive.Get() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user