From 01b917fb11456ddf4332039b054346064bb3b89f Mon Sep 17 00:00:00 2001 From: sago35 Date: Wed, 4 Nov 2020 20:56:33 +0900 Subject: [PATCH] atsamd5x: improve USBCDC --- src/machine/machine_atsamd51.go | 108 +++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 22 deletions(-) diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index 8b035fc2..1553d167 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -11,6 +11,7 @@ import ( "device/arm" "device/sam" "runtime/interrupt" + "runtime/volatile" "unsafe" ) @@ -1733,36 +1734,93 @@ func (pwm PWM) getMux() PinMode { // USBCDC is the USB CDC aka serial over USB interface on the SAMD21. type USBCDC struct { - Buffer *RingBuffer + Buffer *RingBuffer + TxIdx volatile.Register8 + waitTxc bool + sent bool +} + +const ( + usbcdcTxSizeMask uint8 = 0x3F + usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask + usbcdcTxBank1st uint8 = 0x00 + usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1 +) + +// 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 + return nil + } + usbcdc.waitTxc = true + + // 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) + UART0.sent = true + } + } + return nil } // WriteByte writes a byte of data to the USB CDC interface. -func (usbcdc USBCDC) WriteByte(c byte) error { +func (usbcdc *USBCDC) WriteByte(c byte) error { // Supposedly to handle problem with Windows USB serial ports? if usbLineInfo.lineState > 0 { - // set the data - udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c + ok := false + for { + mask := interrupt.Disable() - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN])))) + idx := UART0.TxIdx.Get() + if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask { + udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c + UART0.TxIdx.Set(idx + 1) + ok = true + } - // 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) + interrupt.Restore(mask) - // set count of bytes to be sent - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((1 & 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) - - // wait for transfer to complete - timeout := 3000 - for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 { - timeout-- - if timeout == 0 { - return errUSBCDCWriteByteTimeout + if ok { + break + } else { + mask := interrupt.Disable() + if UART0.sent { + if UART0.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) + UART0.waitTxc = false + UART0.Flush() + } + } else { + UART0.Flush() + } + } + interrupt.Restore(mask) } } } @@ -1971,9 +2029,15 @@ func handleUSBIRQ(interrupt.Interrupt) { 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 { + UART0.waitTxc = false + } } } } + + UART0.Flush() } func initEndpoint(ep, config uint32) {