aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorsago35 <[email protected]>2022-07-10 18:33:52 +0900
committerGitHub <[email protected]>2022-07-10 11:33:52 +0200
commitd434058aefffeb450d71313f7144a82eceba60c8 (patch)
treea066d0742e9f9fc71ce5db2fbbac09749f1ec630
parent56780c26917647900bda67018a22c962bce2333a (diff)
downloadtinygo-d434058aefffeb450d71313f7144a82eceba60c8.tar.gz
tinygo-d434058aefffeb450d71313f7144a82eceba60c8.zip
samd21,samd51,nrf52840: move usbcdc to machine/usb/cdc (#2972)
* samd21,samd51,nrf52840: move usbcdc to machine/usb/cdc
-rw-r--r--src/machine/machine_atsamd21_usb.go259
-rw-r--r--src/machine/machine_atsamd51_usb.go266
-rw-r--r--src/machine/machine_nrf52840_usb.go351
-rw-r--r--src/machine/serial-usb.go3
-rw-r--r--src/machine/usb.go188
-rw-r--r--src/machine/usb/cdc/buffer.go119
-rw-r--r--src/machine/usb/cdc/cdc.go61
-rw-r--r--src/machine/usb/cdc/usbcdc.go189
-rw-r--r--src/machine/usb/hid/hid.go16
-rw-r--r--src/machine/usb/hid/keyboard/keyboard.go16
-rw-r--r--src/machine/usb/hid/mouse/mouse.go24
-rw-r--r--src/machine/usb_descriptor.go3
-rw-r--r--src/runtime/runtime_atsamd21.go2
-rw-r--r--src/runtime/runtime_atsamd51.go2
-rw-r--r--src/runtime/runtime_nrf52840.go2
15 files changed, 662 insertions, 839 deletions
diff --git a/src/machine/machine_atsamd21_usb.go b/src/machine/machine_atsamd21_usb.go
index d79a52d5c..1a920100e 100644
--- a/src/machine/machine_atsamd21_usb.go
+++ b/src/machine/machine_atsamd21_usb.go
@@ -1,141 +1,14 @@
//go:build sam && atsamd21
// +build sam,atsamd21
-// Peripheral abstraction layer for the atsamd21.
-//
-// Datasheet:
-// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
-//
package machine
import (
"device/sam"
"runtime/interrupt"
- "runtime/volatile"
"unsafe"
)
-// 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
-}
-
-var (
- USB = &USBCDC{Buffer: NewRingBuffer()}
- waitHidTxc bool
-)
-
-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_EPINTFLAG_TRCPT1)
-
- // send data by setting bank ready
- setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_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_EPINTFLAG_TRCPT1) != 0 {
- setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
- setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_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 SAMD21 specific.
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
@@ -193,10 +66,6 @@ func (dev *USBDevice) Configure(config UARTConfig) {
dev.initcomplete = true
}
-func (usbcdc *USBCDC) Configure(config UARTConfig) {
- // dummy
-}
-
func handlePadCalibration() {
// Load Pad Calibration data from non-volatile memory
// This requires registers that are not included in the SVD file.
@@ -258,11 +127,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
// 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...
- if hidCallback != nil && !waitHidTxc {
- hidCallback()
- }
}
// Endpoint 0 Setup interrupt
@@ -283,11 +148,8 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
ok = handleStandardSetup(setup)
} else {
// Class Interface Requests
- if setup.WIndex == usb_CDC_ACM_INTERFACE {
- ok = cdcSetup(setup)
- } else if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
- SendZlp()
- ok = true
+ if setup.WIndex < uint16(len(usbSetupHandler)) && usbSetupHandler[setup.WIndex] != nil {
+ ok = usbSetupHandler[setup.WIndex](setup)
}
}
@@ -313,22 +175,16 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
for i = 1; i < uint32(len(endPoints)); i++ {
// Check if endpoint has a pending interrupt
epFlags := getEPINTFLAG(i)
- if (epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 ||
- (epFlags&sam.USB_DEVICE_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_EPSTATUSCLR_BK1RDY)
- setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
-
- if i == usb_CDC_ENDPOINT_IN {
- USB.waitTxc = false
- }
- case usb_HID_ENDPOINT_IN:
- setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
- waitHidTxc = false
+ setEPINTFLAG(i, epFlags)
+ if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
+ buf := handleEndpointRx(i)
+ if usbRxHandler[i] != nil {
+ usbRxHandler[i](buf)
+ }
+ handleEndpointRxComplete(i)
+ } else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
+ if usbTxHandler[i] != nil {
+ usbTxHandler[i]()
}
}
}
@@ -448,66 +304,8 @@ func handleUSBSetAddress(setup USBSetup) bool {
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[:], setup.WLength)
- 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 {
- EnterBootloader()
- } 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
-}
-
-// SendUSBHIDPacket sends a packet for USBHID (interrupt / in).
-func SendUSBHIDPacket(ep uint32, data []byte) bool {
- if waitHidTxc {
- return false
- }
+// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
+func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0)
// clear transfer complete flag
@@ -516,8 +314,6 @@ func SendUSBHIDPacket(ep uint32, data []byte) bool {
// send data by setting bank ready
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
- waitHidTxc = true
-
return true
}
@@ -527,10 +323,15 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
if 0 < maxsize && maxsize < l {
l = maxsize
}
- copy(udd_ep_in_cache_buffer[ep][:], data[:l])
// Set endpoint address for sending data
- usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
+ if ep == 0 {
+ copy(udd_ep_control_cache_buffer[:], data[:l])
+ usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
+ } else {
+ copy(udd_ep_in_cache_buffer[ep][:], data[:l])
+ 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)
@@ -540,7 +341,7 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
}
-func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
+func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
var b [cdcLineInfoSize]byte
// address
@@ -557,7 +358,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
timeout--
if timeout == 0 {
- return b, errUSBCDCReadTimeout
+ return b, ErrUSBReadTimeout
}
}
@@ -566,7 +367,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
timeout--
if timeout == 0 {
- return b, errUSBCDCReadTimeout
+ return b, ErrUSBReadTimeout
}
}
@@ -575,7 +376,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
if bytesread != cdcLineInfoSize {
- return b, errUSBCDCBytesRead
+ return b, ErrUSBBytesRead
}
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
@@ -583,16 +384,15 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
return b, nil
}
-func handleEndpoint(ep uint32) {
+func handleEndpointRx(ep uint32) []byte {
// 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)))
- }
+ return udd_ep_out_cache_buffer[ep][:count]
+}
+func handleEndpointRxComplete(ep uint32) {
// set byte count to zero
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
@@ -601,6 +401,7 @@ func handleEndpoint(ep uint32) {
// set ready for next data
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
+
}
func SendZlp() {
diff --git a/src/machine/machine_atsamd51_usb.go b/src/machine/machine_atsamd51_usb.go
index a7d9e8910..2788fd0fd 100644
--- a/src/machine/machine_atsamd51_usb.go
+++ b/src/machine/machine_atsamd51_usb.go
@@ -1,142 +1,14 @@
//go:build (sam && atsamd51) || (sam && atsame5x)
// +build sam,atsamd51 sam,atsame5x
-// Peripheral abstraction layer for the atsamd51.
-//
-// Datasheet:
-// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
-//
package machine
import (
"device/sam"
"runtime/interrupt"
- "runtime/volatile"
"unsafe"
)
-// 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
-}
-
-var (
- // USB is a USB CDC interface.
- USB = &USBCDC{Buffer: NewRingBuffer()}
- waitHidTxc bool
-)
-
-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
@@ -188,7 +60,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
// enable USB
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
- // enable IRQ at highest priority
+ // enable IRQ
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()
@@ -197,10 +69,6 @@ func (dev *USBDevice) Configure(config UARTConfig) {
dev.initcomplete = true
}
-func (usbcdc *USBCDC) Configure(config UARTConfig) {
- // dummy
-}
-
func handlePadCalibration() {
// Load Pad Calibration data from non-volatile memory
// This requires registers that are not included in the SVD file.
@@ -244,7 +112,7 @@ func handlePadCalibration() {
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
}
-func handleUSBIRQ(interrupt.Interrupt) {
+func handleUSBIRQ(intr interrupt.Interrupt) {
// reset all interrupt flags
flags := sam.USB_DEVICE.INTFLAG.Get()
sam.USB_DEVICE.INTFLAG.Set(flags)
@@ -262,10 +130,6 @@ func handleUSBIRQ(interrupt.Interrupt) {
// Start of frame
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
- USB.Flush()
- if hidCallback != nil && !waitHidTxc {
- hidCallback()
- }
// if you want to blink LED showing traffic, this would be the place...
}
@@ -287,11 +151,8 @@ func handleUSBIRQ(interrupt.Interrupt) {
ok = handleStandardSetup(setup)
} else {
// Class Interface Requests
- if setup.WIndex == usb_CDC_ACM_INTERFACE {
- ok = cdcSetup(setup)
- } else if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
- SendZlp()
- ok = true
+ if setup.WIndex < uint16(len(usbSetupHandler)) && usbSetupHandler[setup.WIndex] != nil {
+ ok = usbSetupHandler[setup.WIndex](setup)
}
}
@@ -317,22 +178,16 @@ func handleUSBIRQ(interrupt.Interrupt) {
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
- }
- case usb_HID_ENDPOINT_IN:
- setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
- waitHidTxc = false
+ setEPINTFLAG(i, epFlags)
+ if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 {
+ buf := handleEndpointRx(i)
+ if usbRxHandler[i] != nil {
+ usbRxHandler[i](buf)
+ }
+ handleEndpointRxComplete(i)
+ } else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
+ if usbTxHandler[i] != nil {
+ usbTxHandler[i]()
}
}
}
@@ -452,67 +307,8 @@ func handleUSBSetAddress(setup USBSetup) bool {
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[:], setup.WLength)
- 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 {
- EnterBootloader()
- } 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
-}
-
-// SendUSBHIDPacket sends a packet for USBHID (interrupt / in).
-func SendUSBHIDPacket(ep uint32, data []byte) bool {
- if waitHidTxc {
- return false
- }
-
+// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
+func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0)
// clear transfer complete flag
@@ -521,8 +317,6 @@ func SendUSBHIDPacket(ep uint32, data []byte) bool {
// send data by setting bank ready
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
- waitHidTxc = true
-
return true
}
@@ -532,10 +326,15 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
if 0 < maxsize && maxsize < l {
l = maxsize
}
- copy(udd_ep_in_cache_buffer[ep][:], data[:l])
// Set endpoint address for sending data
- usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
+ if ep == 0 {
+ copy(udd_ep_control_cache_buffer[:], data[:l])
+ usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
+ } else {
+ copy(udd_ep_in_cache_buffer[ep][:], data[:l])
+ 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)
@@ -545,7 +344,7 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
}
-func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
+func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
var b [cdcLineInfoSize]byte
// address
@@ -562,16 +361,16 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
timeout--
if timeout == 0 {
- return b, errUSBCDCReadTimeout
+ return b, ErrUSBReadTimeout
}
}
// Wait until OUT transfer is completed.
timeout = 300000
- for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
+ for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) == 0 {
timeout--
if timeout == 0 {
- return b, errUSBCDCReadTimeout
+ return b, ErrUSBReadTimeout
}
}
@@ -580,7 +379,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
if bytesread != cdcLineInfoSize {
- return b, errUSBCDCBytesRead
+ return b, ErrUSBBytesRead
}
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
@@ -588,16 +387,15 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
return b, nil
}
-func handleEndpoint(ep uint32) {
+func handleEndpointRx(ep uint32) []byte {
// 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)))
- }
+ return udd_ep_out_cache_buffer[ep][:count]
+}
+func handleEndpointRxComplete(ep uint32) {
// set byte count to zero
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
diff --git a/src/machine/machine_nrf52840_usb.go b/src/machine/machine_nrf52840_usb.go
index 2193e1430..28378fb48 100644
--- a/src/machine/machine_nrf52840_usb.go
+++ b/src/machine/machine_nrf52840_usb.go
@@ -11,127 +11,15 @@ import (
"unsafe"
)
-// USBCDC is the USB CDC aka serial over USB interface on the nRF52840
-type USBCDC struct {
- Buffer *RingBuffer
- TxIdx volatile.Register8
- waitTxc bool
- waitTxcRetryCount uint8
- sent bool
-}
-
-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
- enterCriticalSection()
- sendViaEPIn(
- usb_CDC_ENDPOINT_IN,
- &udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk],
- int(sz),
- )
- if bk == usbcdcTxBank1st {
- usbcdc.TxIdx.Set(usbcdcTxBank2nd)
- } else {
- usbcdc.TxIdx.Set(usbcdcTxBank1st)
- }
-
- 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 !easyDMABusy.HasBits(1) {
- 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
-}
-
var (
- USB = &_USB
- _USB = USBCDC{Buffer: NewRingBuffer()}
- waitHidTxc bool
-
sendOnEP0DATADONE struct {
- ptr *byte
- count int
+ ptr *byte
+ count int
+ offset int
}
-
- epinen uint32
- epouten uint32
- easyDMABusy volatile.Register8
- epout0data_setlinecoding bool
+ epinen uint32
+ epouten uint32
+ easyDMABusy volatile.Register8
)
// enterCriticalSection is used to protect access to easyDMA - only one thing
@@ -171,7 +59,7 @@ func (dev *USBDevice) Configure(config UARTConfig) {
intr.Enable()
// enable interrupt for end of reset and start of frame
- nrf.USBD.INTEN.Set(nrf.USBD_INTENSET_USBEVENT | nrf.USBD_INTENSET_SOF)
+ nrf.USBD.INTEN.Set(nrf.USBD_INTENSET_USBEVENT)
// errata 187
// https://infocenter.nordicsemi.com/topic/errata_nRF52840_EngB/ERR/nRF52840/EngineeringB/latest/anomaly_840_187.html
@@ -199,17 +87,10 @@ func (dev *USBDevice) Configure(config UARTConfig) {
dev.initcomplete = true
}
-func (usbcdc *USBCDC) Configure(config UARTConfig) {
- // dummy
-}
-
func handleUSBIRQ(interrupt.Interrupt) {
if nrf.USBD.EVENTS_SOF.Get() == 1 {
nrf.USBD.EVENTS_SOF.Set(0)
- USB.Flush()
- if hidCallback != nil && !waitHidTxc {
- hidCallback()
- }
+
// if you want to blink LED showing traffic, this would be the place...
}
@@ -230,22 +111,27 @@ func handleUSBIRQ(interrupt.Interrupt) {
if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 {
// done sending packet - either need to send another or enter status stage
nrf.USBD.EVENTS_EP0DATADONE.Set(0)
- if epout0data_setlinecoding {
- nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
- nrf.USBD.EPOUT[0].MAXCNT.Set(64)
- nrf.USBD.TASKS_STARTEPOUT[0].Set(1)
- return
- }
if sendOnEP0DATADONE.ptr != nil {
// previous data was too big for one packet, so send a second
+ ptr := sendOnEP0DATADONE.ptr
+ count := sendOnEP0DATADONE.count
+ if count > usbEndpointPacketSize {
+ sendOnEP0DATADONE.offset += usbEndpointPacketSize
+ sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[sendOnEP0DATADONE.offset]
+ count = usbEndpointPacketSize
+ }
+ sendOnEP0DATADONE.count -= count
sendViaEPIn(
0,
- sendOnEP0DATADONE.ptr,
- sendOnEP0DATADONE.count,
+ ptr,
+ count,
)
// clear, so we know we're done
- sendOnEP0DATADONE.ptr = nil
+ if sendOnEP0DATADONE.count == 0 {
+ sendOnEP0DATADONE.ptr = nil
+ sendOnEP0DATADONE.offset = 0
+ }
} else {
// no more data, so set status stage
SendZlp() // nrf.USBD.TASKS_EP0STATUS.Set(1)
@@ -266,11 +152,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
// Standard Requests
ok = handleStandardSetup(setup)
} else {
- if setup.WIndex == usb_CDC_ACM_INTERFACE {
- ok = cdcSetup(setup)
- } else if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
- SendZlp()
- ok = true
+ // Class Interface Requests
+ if setup.WIndex < uint16(len(usbSetupHandler)) && usbSetupHandler[setup.WIndex] != nil {
+ ok = usbSetupHandler[setup.WIndex](setup)
}
}
@@ -290,25 +174,16 @@ func handleUSBIRQ(interrupt.Interrupt) {
// Check if endpoint has a pending interrupt
inDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPIN1<<(i-1)) > 0
outDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPOUT1<<(i-1)) > 0
- if inDataDone || outDataDone {
- switch i {
- case usb_CDC_ENDPOINT_OUT:
- // setup buffer to receive from host
- if outDataDone {
- enterCriticalSection()
- nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i]))))
- count := nrf.USBD.SIZE.EPOUT[i].Get()
- nrf.USBD.EPOUT[i].MAXCNT.Set(count)
- nrf.USBD.TASKS_STARTEPOUT[i].Set(1)
- }
- case usb_CDC_ENDPOINT_IN: //, usb_CDC_ENDPOINT_ACM:
- if inDataDone {
- USB.waitTxc = false
- exitCriticalSection()
- }
- case usb_HID_ENDPOINT_IN:
- waitHidTxc = false
+ if inDataDone {
+ if usbTxHandler[i] != nil {
+ usbTxHandler[i]()
}
+ } else if outDataDone {
+ enterCriticalSection()
+ nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i]))))
+ count := nrf.USBD.SIZE.EPOUT[i].Get()
+ nrf.USBD.EPOUT[i].MAXCNT.Set(count)
+ nrf.USBD.TASKS_STARTEPOUT[i].Set(1)
}
}
}
@@ -317,32 +192,16 @@ func handleUSBIRQ(interrupt.Interrupt) {
for i := 0; i < len(endPoints); i++ {
if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 {
nrf.USBD.EVENTS_ENDEPOUT[i].Set(0)
- if i == 0 && epout0data_setlinecoding {
- epout0data_setlinecoding = false
- count := int(nrf.USBD.SIZE.EPOUT[0].Get())
- if count >= 7 {
- parseUSBLineInfo(udd_ep_out_cache_buffer[0][:count])
- if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
- EnterBootloader()
- }
- }
- nrf.USBD.TASKS_EP0STATUS.Set(1)
- }
- if i == usb_CDC_ENDPOINT_OUT {
- USB.handleEndpoint(uint32(i))
+ buf := handleEndpointRx(uint32(i))
+ if usbRxHandler[i] != nil {
+ usbRxHandler[i](buf)
}
+ handleEndpointRxComplete(uint32(i))
exitCriticalSection()
}
}
}
-func parseUSBLineInfo(b []byte) {
- 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]
-}
-
func parseUSBSetupRegisters() USBSetup {
return USBSetup{
BmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()),
@@ -383,59 +242,13 @@ func initEndpoint(ep, config uint32) {
}
}
-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[:], setup.WLength)
- return true
- }
- }
-
- if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
- if setup.BRequest == usb_CDC_SET_LINE_CODING {
- epout0data_setlinecoding = true
- nrf.USBD.TASKS_EP0RCVOUT.Set(1)
- return true
- }
-
- if setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
- usbLineInfo.lineState = setup.WValueL
- if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
- EnterBootloader()
- }
- nrf.USBD.TASKS_EP0STATUS.Set(1)
- }
-
- if setup.BRequest == usb_CDC_SEND_BREAK {
- nrf.USBD.TASKS_EP0STATUS.Set(1)
- }
- return true
- }
- return false
-}
-
-// SendUSBHIDPacket sends a packet for USBHID (interrupt / in).
-func SendUSBHIDPacket(ep uint32, data []byte) bool {
- if waitHidTxc {
- return false
- }
-
+// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
+func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0)
// clear transfer complete flag
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
- waitHidTxc = true
-
return true
}
@@ -445,28 +258,38 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
if 0 < int(maxsize) && int(maxsize) < count {
count = int(maxsize)
}
- copy(udd_ep_in_cache_buffer[ep][:], data[:count])
- if ep == 0 && count > usbEndpointPacketSize {
- sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][usbEndpointPacketSize]
- sendOnEP0DATADONE.count = count - usbEndpointPacketSize
- count = usbEndpointPacketSize
+
+ if ep == 0 {
+ copy(udd_ep_control_cache_buffer[:], data[:count])
+ if count > usbEndpointPacketSize {
+ sendOnEP0DATADONE.offset = usbEndpointPacketSize
+ sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[sendOnEP0DATADONE.offset]
+ sendOnEP0DATADONE.count = count - usbEndpointPacketSize
+ count = usbEndpointPacketSize
+ }
+ sendViaEPIn(
+ ep,
+ &udd_ep_control_cache_buffer[0],
+ count,
+ )
+ } else {
+ copy(udd_ep_in_cache_buffer[ep][:], data[:count])
+ sendViaEPIn(
+ ep,
+ &udd_ep_in_cache_buffer[ep][0],
+ count,
+ )
}
- sendViaEPIn(
- ep,
- &udd_ep_in_cache_buffer[ep][0],
- count,
- )
}
-func (usbcdc *USBCDC) handleEndpoint(ep uint32) {
+func handleEndpointRx(ep uint32) []byte {
// get data
count := int(nrf.USBD.EPOUT[ep].AMOUNT.Get())
- // move to ring buffer
- for i := 0; i < count; i++ {
- usbcdc.Receive(byte(udd_ep_out_cache_buffer[ep][i]))
- }
+ return udd_ep_out_cache_buffer[ep][:count]
+}
+func handleEndpointRxComplete(ep uint32) {
// set ready for next data
nrf.USBD.SIZE.EPOUT[ep].Set(0)
}
@@ -497,3 +320,47 @@ func handleUSBSetAddress(setup USBSetup) bool {
// nrf USBD handles this
return true
}
+
+func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
+ var b [cdcLineInfoSize]byte
+
+ nrf.USBD.TASKS_EP0RCVOUT.Set(1)
+
+ nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
+ nrf.USBD.EPOUT[0].MAXCNT.Set(64)
+
+ timeout := 300000
+ count := 0
+ for {
+ if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 {
+ nrf.USBD.EVENTS_EP0DATADONE.Set(0)
+ count = int(nrf.USBD.SIZE.EPOUT[0].Get())
+ nrf.USBD.TASKS_STARTEPOUT[0].Set(1)
+ break
+ }
+ timeout--
+ if timeout == 0 {
+ return b, ErrUSBReadTimeout
+ }
+ }
+
+ timeout = 300000
+ for {
+ if nrf.USBD.EVENTS_ENDEPOUT[0].Get() == 1 {
+ nrf.USBD.EVENTS_ENDEPOUT[0].Set(0)
+ break
+ }
+
+ timeout--
+ if timeout == 0 {
+ return b, ErrUSBReadTimeout
+ }
+ }
+
+ nrf.USBD.TASKS_EP0STATUS.Set(1)
+ nrf.USBD.TASKS_EP0RCVOUT.Set(0)
+
+ copy(b[:7], udd_ep_out_cache_buffer[0][:count])
+
+ return b, nil
+}
diff --git a/src/machine/serial-usb.go b/src/machine/serial-usb.go
index 0bec8e297..702cab1ee 100644
--- a/src/machine/serial-usb.go
+++ b/src/machine/serial-usb.go
@@ -4,7 +4,8 @@
package machine
// Serial is implemented via USB (USB-CDC).
-var Serial = USB
+var Serial Serialer
func InitSerial() {
+ Serial = USBCDC
}
diff --git a/src/machine/usb.go b/src/machine/usb.go
index ebdacfdaf..3656edc7b 100644
--- a/src/machine/usb.go
+++ b/src/machine/usb.go
@@ -14,25 +14,25 @@ type USBDevice struct {
var (
USBDev = &USBDevice{}
+ USBCDC Serialer
)
+
+type Serialer interface {
+ WriteByte(c byte) error
+ Write(data []byte) (n int, err error)
+ Configure(config UARTConfig) error
+ Buffered() int
+ ReadByte() (byte, error)
+}
+
var usbDescriptor = descriptorCDC
-var (
- errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty")
- errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
- errUSBCDCReadTimeout = errors.New("USB-CDC read timeout")
- errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read")
+const (
+ usbDescriptorConfigCDC = 1 << iota
+ usbDescriptorConfigHID
)
-const cdcLineInfoSize = 7
-
-type cdcLineInfo struct {
- dwDTERate uint32
- bCharFormat uint8
- bParityType uint8
- bDataBits uint8
- lineState uint8
-}
+var usbDescriptorConfig uint8 = usbDescriptorConfigCDC
// strToUTF16LEDescriptor converts a utf8 string into a string descriptor
// note: the following code only converts ascii characters to UTF16LE. In order
@@ -54,22 +54,25 @@ var (
usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English
)
+const cdcLineInfoSize = 7
+
+var (
+ ErrUSBReadTimeout = errors.New("USB read timeout")
+ ErrUSBBytesRead = errors.New("USB invalid number of bytes read")
+)
+
var (
- usbEndpointDescriptors [8]usbDeviceDescriptor
+ usbEndpointDescriptors [numberOfEndpoints]usbDeviceDescriptor
- udd_ep_in_cache_buffer [7][128]uint8
- udd_ep_out_cache_buffer [7][128]uint8
+ udd_ep_control_cache_buffer [256]uint8
+ udd_ep_in_cache_buffer [7][64]uint8
+ udd_ep_out_cache_buffer [7][64]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}
)
const (
@@ -77,6 +80,7 @@ const (
usb_IPRODUCT = 2
usb_ISERIAL = 3
+ usb_ENDPOINT_TYPE_DISABLE = 0xFF
usb_ENDPOINT_TYPE_CONTROL = 0x00
usb_ENDPOINT_TYPE_ISOCHRONOUS = 0x01
usb_ENDPOINT_TYPE_BULK = 0x02
@@ -95,8 +99,8 @@ const (
usbEndpointOut = 0x00
usbEndpointIn = 0x80
+ numberOfEndpoints = 8
usbEndpointPacketSize = 64 // 64 for Full Speed, EPT size max is 1024
- usb_EPT_NUM = 7
// standard requests
usb_GET_STATUS = 0
@@ -123,12 +127,15 @@ const (
usb_CONFIG_SELF_POWERED = 0xC0
usb_CONFIG_REMOTE_WAKEUP = 0x20
- // CDC
+ // Interface
+ numberOfInterfaces = 3
usb_CDC_ACM_INTERFACE = 0 // CDC ACM
usb_CDC_DATA_INTERFACE = 1 // CDC Data
usb_CDC_FIRST_ENDPOINT = 1
+ usb_HID_INTERFACE = 2 // HID
// Endpoint
+ usb_CONTROL_ENDPOINT = 0
usb_CDC_ENDPOINT_ACM = 1
usb_CDC_ENDPOINT_OUT = 2
usb_CDC_ENDPOINT_IN = 3
@@ -153,27 +160,20 @@ const (
usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
+)
- // CDC Class requests
- usb_CDC_SET_LINE_CODING = 0x20
- usb_CDC_GET_LINE_CODING = 0x21
- usb_CDC_SET_CONTROL_LINE_STATE = 0x22
- usb_CDC_SEND_BREAK = 0x23
-
- usb_CDC_V1_10 = 0x0110
- usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
-
- usb_CDC_CALL_MANAGEMENT = 0x01
- usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
- usb_CDC_HEADER = 0x00
- usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
- usb_CDC_UNION = 0x06
- usb_CDC_CS_INTERFACE = 0x24
- usb_CDC_CS_ENDPOINT = 0x25
- usb_CDC_DATA_INTERFACE_CLASS = 0x0A
-
- usb_CDC_LINESTATE_DTR = 0x01
- usb_CDC_LINESTATE_RTS = 0x02
+var (
+ usbTxHandler [numberOfEndpoints]func()
+ usbRxHandler [numberOfEndpoints]func([]byte)
+ usbSetupHandler [numberOfInterfaces]func(USBSetup) bool
+
+ endPoints = []uint32{
+ usb_CONTROL_ENDPOINT: usb_ENDPOINT_TYPE_CONTROL,
+ usb_CDC_ENDPOINT_ACM: (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
+ usb_CDC_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
+ usb_CDC_ENDPOINT_IN: (usb_ENDPOINT_TYPE_BULK | usbEndpointIn),
+ usb_HID_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Interrupt In
+ }
)
// usbDeviceDescBank is the USB device endpoint descriptor.
@@ -209,73 +209,6 @@ func newUSBSetup(data []byte) USBSetup {
return u
}
-// USBCDC is the serial interface that works over the USB port.
-// To implement the USBCDC interface for a board, you must declare a concrete type as follows:
-//
-// type USBCDC struct {
-// Buffer *RingBuffer
-// }
-//
-// You can also add additional members to this struct depending on your implementation,
-// but the *RingBuffer is required.
-// When you are declaring the USBCDC for your board, make sure that you also declare the
-// RingBuffer using the NewRingBuffer() function:
-//
-// USBCDC{Buffer: NewRingBuffer()}
-//
-
-// Read from the RX buffer.
-func (usbcdc *USBCDC) Read(data []byte) (n int, err error) {
- // check if RX buffer is empty
- size := usbcdc.Buffered()
- if size == 0 {
- return 0, nil
- }
-
- // Make sure we do not read more from buffer than the data slice can hold.
- if len(data) < size {
- size = len(data)
- }
-
- // only read number of bytes used from buffer
- for i := 0; i < size; i++ {
- v, _ := usbcdc.ReadByte()
- data[i] = v
- }
-
- return size, nil
-}
-
-// Write data to the USBCDC.
-func (usbcdc *USBCDC) Write(data []byte) (n int, err error) {
- for _, v := range data {
- usbcdc.WriteByte(v)
- }
- return len(data), nil
-}
-
-// ReadByte reads a single byte from the RX buffer.
-// If there is no data in the buffer, returns an error.
-func (usbcdc *USBCDC) ReadByte() (byte, error) {
- // check if RX buffer is empty
- buf, ok := usbcdc.Buffer.Get()
- if !ok {
- return 0, errUSBCDCBufferEmpty
- }
- return buf, nil
-}
-
-// Buffered returns the number of bytes currently stored in the RX buffer.
-func (usbcdc *USBCDC) Buffered() int {
- return int(usbcdc.Buffer.Used())
-}
-
-// Receive handles adding data to the UART's data buffer.
-// Usually called by the IRQ handler for a machine.
-func (usbcdc *USBCDC) Receive(data byte) {
- usbcdc.Buffer.Put(data)
-}
-
// sendDescriptor creates and sends the various USB descriptor types that
// can be requested by the host.
func sendDescriptor(setup USBSetup) {
@@ -285,6 +218,11 @@ func sendDescriptor(setup USBSetup) {
return
case usb_DEVICE_DESCRIPTOR_TYPE:
// composite descriptor
+ if (usbDescriptorConfig & usbDescriptorConfigHID) > 0 {
+ usbDescriptor = descriptorCDCHID
+ } else {
+ usbDescriptor = descriptorCDC
+ }
usbDescriptor.Configure(usb_VID, usb_PID)
sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
return
@@ -402,17 +340,21 @@ func handleStandardSetup(setup USBSetup) bool {
}
}
-// EnableHID enables HID. This function must be executed from the init().
-func EnableHID(callback func()) {
- usbDescriptor = descriptorCDCHID
- endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
- (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
- (usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
- (usb_ENDPOINT_TYPE_BULK | usbEndpointIn),
- (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)}
-
- hidCallback = callback
+func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(USBSetup) bool) {
+ usbDescriptorConfig |= usbDescriptorConfigCDC
+ endPoints[usb_CDC_ENDPOINT_ACM] = (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)
+ endPoints[usb_CDC_ENDPOINT_OUT] = (usb_ENDPOINT_TYPE_BULK | usbEndpointOut)
+ endPoints[usb_CDC_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)
+ usbRxHandler[usb_CDC_ENDPOINT_OUT] = rxHandler
+ usbTxHandler[usb_CDC_ENDPOINT_IN] = txHandler
+ usbSetupHandler[usb_CDC_ACM_INTERFACE] = setupHandler // 0x02 (Communications and CDC Control)
+ usbSetupHandler[usb_CDC_DATA_INTERFACE] = nil // 0x0A (CDC-Data)
}
-// hidCallback is a variable that holds the callback when using HID.
-var hidCallback func()
+// EnableHID enables HID. This function must be executed from the init().
+func EnableHID(txHandler func(), rxHandler func([]byte), setupHandler func(USBSetup) bool) {
+ usbDescriptorConfig |= usbDescriptorConfigHID
+ endPoints[usb_HID_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)
+ usbTxHandler[usb_HID_ENDPOINT_IN] = txHandler
+ usbSetupHandler[usb_HID_INTERFACE] = setupHandler // 0x03 (HID - Human Interface Device)
+}
diff --git a/src/machine/usb/cdc/buffer.go b/src/machine/usb/cdc/buffer.go
new file mode 100644
index 000000000..ad5eb3645
--- /dev/null
+++ b/src/machine/usb/cdc/buffer.go
@@ -0,0 +1,119 @@
+package cdc
+
+import (
+ "runtime/volatile"
+)
+
+const rxRingBufferSize = 128
+
+// rxRingBuffer is ring buffer implementation inspired by post at
+// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
+type rxRingBuffer struct {
+ buffer [rxRingBufferSize]volatile.Register8
+ head volatile.Register8
+ tail volatile.Register8
+}
+
+// NewRxRingBuffer returns a new ring buffer.
+func NewRxRingBuffer() *rxRingBuffer {
+ return &rxRingBuffer{}
+}
+
+// Used returns how many bytes in buffer have been used.
+func (rb *rxRingBuffer) Used() uint8 {
+ return uint8(rb.head.Get() - rb.tail.Get())
+}
+
+// Put stores a byte in the buffer. If the buffer is already
+// full, the method will return false.
+func (rb *rxRingBuffer) Put(val byte) bool {
+ if rb.Used() != rxRingBufferSize {
+ rb.head.Set(rb.head.Get() + 1)
+ rb.buffer[rb.head.Get()%rxRingBufferSize].Set(val)
+ return true
+ }
+ return false
+}
+
+// Get returns a byte from the buffer. If the buffer is empty,
+// the method will return a false as the second value.
+func (rb *rxRingBuffer) Get() (byte, bool) {
+ if rb.Used() != 0 {
+ rb.tail.Set(rb.tail.Get() + 1)
+ return rb.buffer[rb.tail.Get()%rxRingBufferSize].Get(), true
+ }
+ return 0, false
+}
+
+// Clear resets the head and tail pointer to zero.
+func (rb *rxRingBuffer) Clear() {
+ rb.head.Set(0)
+ rb.tail.Set(0)
+}
+
+const txRingBufferSize = 8
+
+// txRingBuffer is ring buffer implementation inspired by post at
+// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
+type txRingBuffer struct {
+ buffer [txRingBufferSize]struct {
+ buf [64]byte
+ size int
+ }
+ head volatile.Register8
+ tail volatile.Register8
+}
+
+// NewTxRingBuffer returns a new ring buffer.
+func NewTxRingBuffer() *txRingBuffer {
+ return &txRingBuffer{}
+}
+
+// Used returns how many bytes in buffer have been used.
+func (rb *txRingBuffer) Used() uint8 {
+ return uint8(rb.head.Get() - rb.tail.Get())
+}
+
+// Put stores a byte in the buffer. If the buffer is already
+// full, the method will return false.
+func (rb *txRingBuffer) Put(val []byte) bool {
+ if rb.Used() == txRingBufferSize {
+ return false
+ }
+
+ if rb.Used() == 0 {
+ rb.head.Set(rb.head.Get() + 1)
+ rb.buffer[rb.head.Get()%txRingBufferSize].size = 0
+ }
+ buf := &rb.buffer[rb.head.Get()%txRingBufferSize]
+
+ for i := 0; i < len(val); i++ {
+ if buf.size == 64 {
+ // next
+ // TODO: Make sure that data is not corrupted even when the buffer is full
+ rb.head.Set(rb.head.Get() + 1)
+ buf = &rb.buffer[rb.head.Get()%txRingBufferSize]
+ rb.buffer[rb.head.Get()%txRingBufferSize].size = 0
+ }
+ buf.buf[buf.size] = val[i]
+ buf.size++
+ }
+ return true
+}
+
+// Get returns a byte from the buffer. If the buffer is empty,
+// the method will return a false as the second value.
+func (rb *txRingBuffer) Get() ([]byte, bool) {
+ if rb.Used() != 0 {
+ rb.tail.Set(rb.tail.Get() + 1)
+ size := rb.buffer[rb.tail.Get()%txRingBufferSize].size
+ return rb.buffer[rb.tail.Get()%txRingBufferSize].buf[:size], true
+ }
+ return nil, false
+}
+
+// Clear resets the head and tail pointer to zero.
+func (rb *txRingBuffer) Clear() {
+ rb.head.Set(0)
+ rb.tail.Set(0)
+}
diff --git a/src/machine/usb/cdc/cdc.go b/src/machine/usb/cdc/cdc.go
new file mode 100644
index 000000000..f180535df
--- /dev/null
+++ b/src/machine/usb/cdc/cdc.go
@@ -0,0 +1,61 @@
+package cdc
+
+const (
+ cdcEndpointACM = 1
+ cdcEndpointOut = 2
+ cdcEndpointIn = 3
+)
+
+// New returns USBCDC struct.
+func New() *USBCDC {
+ if USB == nil {
+ USB = &USBCDC{
+ rxBuffer: NewRxRingBuffer(),
+ txBuffer: NewTxRingBuffer(),
+ }
+ }
+ return USB
+}
+
+const (
+ // bmRequestType
+ usb_REQUEST_HOSTTODEVICE = 0x00
+ usb_REQUEST_DEVICETOHOST = 0x80
+ usb_REQUEST_DIRECTION = 0x80
+
+ usb_REQUEST_STANDARD = 0x00
+ usb_REQUEST_CLASS = 0x20
+ usb_REQUEST_VENDOR = 0x40
+ usb_REQUEST_TYPE = 0x60
+
+ usb_REQUEST_DEVICE = 0x00
+ usb_REQUEST_INTERFACE = 0x01
+ usb_REQUEST_ENDPOINT = 0x02
+ usb_REQUEST_OTHER = 0x03
+ usb_REQUEST_RECIPIENT = 0x1F
+
+ usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
+ usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
+ usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
+
+ // CDC Class requests
+ usb_CDC_SET_LINE_CODING = 0x20
+ usb_CDC_GET_LINE_CODING = 0x21
+ usb_CDC_SET_CONTROL_LINE_STATE = 0x22
+ usb_CDC_SEND_BREAK = 0x23
+
+ usb_CDC_V1_10 = 0x0110
+ usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
+
+ usb_CDC_CALL_MANAGEMENT = 0x01
+ usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
+ usb_CDC_HEADER = 0x00
+ usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
+ usb_CDC_UNION = 0x06
+ usb_CDC_CS_INTERFACE = 0x24
+ usb_CDC_CS_ENDPOINT = 0x25
+ usb_CDC_DATA_INTERFACE_CLASS = 0x0A
+
+ usb_CDC_LINESTATE_DTR = 0x01
+ usb_CDC_LINESTATE_RTS = 0x02
+)
diff --git a/src/machine/usb/cdc/usbcdc.go b/src/machine/usb/cdc/usbcdc.go
new file mode 100644
index 000000000..a21089688
--- /dev/null
+++ b/src/machine/usb/cdc/usbcdc.go
@@ -0,0 +1,189 @@
+package cdc
+
+import (
+ "errors"
+ "machine"
+ "runtime/interrupt"
+)
+
+var (
+ ErrBufferEmpty = errors.New("USB-CDC buffer empty")
+)
+
+const cdcLineInfoSize = 7
+
+type cdcLineInfo struct {
+ dwDTERate uint32
+ bCharFormat uint8
+ bParityType uint8
+ bDataBits uint8
+ lineState uint8
+}
+
+// Read from the RX buffer.
+func (usbcdc *USBCDC) Read(data []byte) (n int, err error) {
+ // check if RX buffer is empty
+ size := usbcdc.Buffered()
+ if size == 0 {
+ return 0, nil
+ }
+
+ // Make sure we do not read more from buffer than the data slice can hold.
+ if len(data) < size {
+ size = len(data)
+ }
+
+ // only read number of bytes used from buffer
+ for i := 0; i < size; i++ {
+ v, _ := usbcdc.ReadByte()
+ data[i] = v
+ }
+
+ return size, nil
+}
+
+// ReadByte reads a single byte from the RX buffer.
+// If there is no data in the buffer, returns an error.
+func (usbcdc *USBCDC) ReadByte() (byte, error) {
+ // check if RX buffer is empty
+ buf, ok := usbcdc.rxBuffer.Get()
+ if !ok {
+ return 0, ErrBufferEmpty
+ }
+ return buf, nil
+}
+
+// Buffered returns the number of bytes currently stored in the RX buffer.
+func (usbcdc *USBCDC) Buffered() int {
+ return int(usbcdc.rxBuffer.Used())
+}
+
+// Receive handles adding data to the UART's data buffer.
+// Usually called by the IRQ handler for a machine.
+func (usbcdc *USBCDC) Receive(data byte) {
+ usbcdc.rxBuffer.Put(data)
+}
+
+// USBCDC is the USB CDC aka serial over USB interface.
+type USBCDC struct {
+ rxBuffer *rxRingBuffer
+ txBuffer *txRingBuffer
+ waitTxc bool
+}
+
+var (
+ // USB is a USB CDC interface.
+ USB *USBCDC
+
+ 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 machine.UARTConfig) error {
+ return nil
+}
+
+// Flush flushes buffered data.
+func (usbcdc *USBCDC) Flush() {
+ mask := interrupt.Disable()
+ if b, ok := usbcdc.txBuffer.Get(); ok {
+ machine.SendUSBInPacket(cdcEndpointIn, b)
+ } else {
+ usbcdc.waitTxc = false
+ }
+ interrupt.Restore(mask)
+}
+
+// Write data to the USBCDC.
+func (usbcdc *USBCDC) Write(data []byte) (n int, err error) {
+ if usbLineInfo.lineState > 0 {
+ mask := interrupt.Disable()
+ usbcdc.txBuffer.Put(data)
+ if !usbcdc.waitTxc {
+ usbcdc.waitTxc = true
+ usbcdc.Flush()
+ }
+ interrupt.Restore(mask)
+ }
+ return len(data), nil
+}
+
+// WriteByte writes a byte of data to the USB CDC interface.
+func (usbcdc *USBCDC) WriteByte(c byte) error {
+ usbcdc.Write([]byte{c})
+ 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
+}
+
+func cdcCallbackRx(b []byte) {
+ for i := range b {
+ USB.Receive(b[i])
+ }
+}
+
+func cdcSetup(setup machine.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)
+
+ machine.SendUSBInPacket(0, b[:])
+ return true
+ }
+ }
+
+ if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
+ if setup.BRequest == usb_CDC_SET_LINE_CODING {
+ b, err := machine.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 {
+ machine.EnterBootloader()
+ } else {
+ // TODO: cancel any reset
+ }
+ machine.SendZlp()
+ }
+
+ if setup.BRequest == usb_CDC_SEND_BREAK {
+ // TODO: something with this value?
+ // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
+ // return false;
+ machine.SendZlp()
+ }
+ return true
+ }
+ return false
+}
+
+func EnableUSBCDC() {
+ machine.USBCDC = New()
+ machine.EnableCDC(USB.Flush, cdcCallbackRx, cdcSetup)
+}
diff --git a/src/machine/usb/hid/hid.go b/src/machine/usb/hid/hid.go
index fd9317511..1429dab2b 100644
--- a/src/machine/usb/hid/hid.go
+++ b/src/machine/usb/hid/hid.go
@@ -14,6 +14,9 @@ var (
const (
hidEndpoint = 4
+
+ usb_SET_REPORT_TYPE = 33
+ usb_SET_IDLE = 10
)
type hidDevicer interface {
@@ -27,7 +30,7 @@ var size int
// calls machine.EnableHID for USB configuration
func SetCallbackHandler(d hidDevicer) {
if size == 0 {
- machine.EnableHID(callback)
+ machine.EnableHID(callback, nil, callbackSetup)
}
devices[size] = d
@@ -45,7 +48,16 @@ func callback() {
}
}
+func callbackSetup(setup machine.USBSetup) bool {
+ ok := false
+ if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
+ machine.SendZlp()
+ ok = true
+ }
+ return ok
+}
+
// SendUSBPacket sends a HIDPacket.
func SendUSBPacket(b []byte) {
- machine.SendUSBHIDPacket(hidEndpoint, b)
+ machine.SendUSBInPacket(hidEndpoint, b)
}
diff --git a/src/machine/usb/hid/keyboard/keyboard.go b/src/machine/usb/hid/keyboard/keyboard.go
index 1f5643385..77dacf688 100644
--- a/src/machine/usb/hid/keyboard/keyboard.go
+++ b/src/machine/usb/hid/keyboard/keyboard.go
@@ -41,7 +41,8 @@ type keyboard struct {
// wideChar holds high bits for the UTF-8 decoder.
wideChar uint16
- buf *hid.RingBuffer
+ buf *hid.RingBuffer
+ waitTxc bool
}
// decodeState represents a state in the UTF-8 decode state machine.
@@ -74,13 +75,24 @@ func newKeyboard() *keyboard {
}
func (kb *keyboard) Callback() bool {
+ kb.waitTxc = false
if b, ok := kb.buf.Get(); ok {
+ kb.waitTxc = true
hid.SendUSBPacket(b)
return true
}
return false
}
+func (kb *keyboard) tx(b []byte) {
+ if kb.waitTxc {
+ kb.buf.Put(b)
+ } else {
+ kb.waitTxc = true
+ hid.SendUSBPacket(b)
+ }
+}
+
func (kb *keyboard) ready() bool {
return true
}
@@ -214,7 +226,7 @@ func (kb *keyboard) Press(c Keycode) error {
}
func (kb *keyboard) sendKey(consumer bool, b []byte) bool {
- kb.buf.Put(b)
+ kb.tx(b)
return true
}
diff --git a/src/machine/usb/hid/mouse/mouse.go b/src/machine/usb/hid/mouse/mouse.go
index 49a189f50..bc079c96a 100644
--- a/src/machine/usb/hid/mouse/mouse.go
+++ b/src/machine/usb/hid/mouse/mouse.go
@@ -15,8 +15,9 @@ const (
)
type mouse struct {
- buf *hid.RingBuffer
- button Button
+ buf *hid.RingBuffer
+ button Button
+ waitTxc bool
}
func init() {
@@ -38,13 +39,24 @@ func newMouse() *mouse {
}
func (m *mouse) Callback() bool {
+ m.waitTxc = false
if b, ok := m.buf.Get(); ok {
+ m.waitTxc = true
hid.SendUSBPacket(b[:5])
return true
}
return false
}
+func (m *mouse) tx(b []byte) {
+ if m.waitTxc {
+ m.buf.Put(b)
+ } else {
+ m.waitTxc = true
+ hid.SendUSBPacket(b)
+ }
+}
+
// Move is a function that moves the mouse cursor.
func (m *mouse) Move(vx, vy int) {
if vx == 0 && vy == 0 {
@@ -65,7 +77,7 @@ func (m *mouse) Move(vx, vy int) {
vy = 127
}
- m.buf.Put([]byte{
+ m.tx([]byte{
0x01, byte(m.button), byte(vx), byte(vy), 0x00,
})
}
@@ -79,7 +91,7 @@ func (m *mouse) Click(btn Button) {
// Press presses the given mouse buttons.
func (m *mouse) Press(btn Button) {
m.button |= btn
- m.buf.Put([]byte{
+ m.tx([]byte{
0x01, byte(m.button), 0x00, 0x00, 0x00,
})
}
@@ -87,7 +99,7 @@ func (m *mouse) Press(btn Button) {
// Release releases the given mouse buttons.
func (m *mouse) Release(btn Button) {
m.button &= ^btn
- m.buf.Put([]byte{
+ m.tx([]byte{
0x01, byte(m.button), 0x00, 0x00, 0x00,
})
}
@@ -105,7 +117,7 @@ func (m *mouse) Wheel(v int) {
v = 127
}
- m.buf.Put([]byte{
+ m.tx([]byte{
0x01, byte(m.button), 0x00, 0x00, byte(v),
})
}
diff --git a/src/machine/usb_descriptor.go b/src/machine/usb_descriptor.go
index 9a1220c42..4489a546b 100644
--- a/src/machine/usb_descriptor.go
+++ b/src/machine/usb_descriptor.go
@@ -14,6 +14,9 @@ func (d *USBDescriptor) Configure(idVendor, idProduct uint16) {
d.Device[9] = byte(idVendor >> 8)
d.Device[10] = byte(idProduct)
d.Device[11] = byte(idProduct >> 8)
+
+ d.Configuration[2] = byte(len(d.Configuration))
+ d.Configuration[3] = byte(len(d.Configuration) >> 8)
}
var descriptorCDC = USBDescriptor{
diff --git a/src/runtime/runtime_atsamd21.go b/src/runtime/runtime_atsamd21.go
index df0e8a493..0f411366b 100644
--- a/src/runtime/runtime_atsamd21.go
+++ b/src/runtime/runtime_atsamd21.go
@@ -7,6 +7,7 @@ import (
"device/arm"
"device/sam"
"machine"
+ "machine/usb/cdc"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
@@ -28,6 +29,7 @@ func init() {
initUSBClock()
initADCClock()
+ cdc.EnableUSBCDC()
machine.USBDev.Configure(machine.UARTConfig{})
machine.InitSerial()
}
diff --git a/src/runtime/runtime_atsamd51.go b/src/runtime/runtime_atsamd51.go
index cb29d9650..f1e585ee2 100644
--- a/src/runtime/runtime_atsamd51.go
+++ b/src/runtime/runtime_atsamd51.go
@@ -7,6 +7,7 @@ import (
"device/arm"
"device/sam"
"machine"
+ "machine/usb/cdc"
"runtime/interrupt"
"runtime/volatile"
)
@@ -28,6 +29,7 @@ func init() {
initUSBClock()
initADCClock()
+ cdc.EnableUSBCDC()
machine.USBDev.Configure(machine.UARTConfig{})
machine.InitSerial()
}
diff --git a/src/runtime/runtime_nrf52840.go b/src/runtime/runtime_nrf52840.go
index a4907b925..cd1d892e5 100644
--- a/src/runtime/runtime_nrf52840.go
+++ b/src/runtime/runtime_nrf52840.go
@@ -7,6 +7,7 @@ import (
"device/arm"
"device/nrf"
"machine"
+ "machine/usb/cdc"
"runtime/interrupt"
"runtime/volatile"
)
@@ -28,6 +29,7 @@ func main() {
}
func init() {
+ cdc.EnableUSBCDC()
machine.USBDev.Configure(machine.UARTConfig{})
machine.InitSerial()
initLFCLK()