diff options
author | sago35 <[email protected]> | 2022-07-10 18:33:52 +0900 |
---|---|---|
committer | GitHub <[email protected]> | 2022-07-10 11:33:52 +0200 |
commit | d434058aefffeb450d71313f7144a82eceba60c8 (patch) | |
tree | a066d0742e9f9fc71ce5db2fbbac09749f1ec630 | |
parent | 56780c26917647900bda67018a22c962bce2333a (diff) | |
download | tinygo-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.go | 259 | ||||
-rw-r--r-- | src/machine/machine_atsamd51_usb.go | 266 | ||||
-rw-r--r-- | src/machine/machine_nrf52840_usb.go | 351 | ||||
-rw-r--r-- | src/machine/serial-usb.go | 3 | ||||
-rw-r--r-- | src/machine/usb.go | 188 | ||||
-rw-r--r-- | src/machine/usb/cdc/buffer.go | 119 | ||||
-rw-r--r-- | src/machine/usb/cdc/cdc.go | 61 | ||||
-rw-r--r-- | src/machine/usb/cdc/usbcdc.go | 189 | ||||
-rw-r--r-- | src/machine/usb/hid/hid.go | 16 | ||||
-rw-r--r-- | src/machine/usb/hid/keyboard/keyboard.go | 16 | ||||
-rw-r--r-- | src/machine/usb/hid/mouse/mouse.go | 24 | ||||
-rw-r--r-- | src/machine/usb_descriptor.go | 3 | ||||
-rw-r--r-- | src/runtime/runtime_atsamd21.go | 2 | ||||
-rw-r--r-- | src/runtime/runtime_atsamd51.go | 2 | ||||
-rw-r--r-- | src/runtime/runtime_nrf52840.go | 2 |
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() |