From cea1d20f2789f23846f2beddec6f4f703f72ce9d Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Tue, 23 Feb 2016 09:21:39 -0500 Subject: [PATCH 01/17] Enable transfer complete in USBDeviceClass::recv once bank is empty --- cores/arduino/USB/USBCore.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 444756f2d..fc14b3c4c 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -553,6 +553,9 @@ uint32_t USBDeviceClass::recv(uint32_t ep, void *_data, uint32_t len) // Clear Transfer complete 0 flag usbd.epBank0AckTransferComplete(ep); + + // Enable Transfer complete 0 interrupt + usbd.epBank0EnableTransferComplete(ep); } return len; From e4f7bf513d366d4e47fe969057ab6ae72aca8168 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Tue, 23 Feb 2016 09:36:34 -0500 Subject: [PATCH 02/17] Add private availableForStore method, and use in Serial_::accept --- cores/arduino/USB/CDC.cpp | 11 +++++++++++ cores/arduino/USB/USBAPI.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/cores/arduino/USB/CDC.cpp b/cores/arduino/USB/CDC.cpp index 9c38debb6..adb3ab075 100644 --- a/cores/arduino/USB/CDC.cpp +++ b/cores/arduino/USB/CDC.cpp @@ -351,6 +351,17 @@ bool Serial_::rts() { return _usbLineInfo.lineState & 0x2; } +int Serial_::availableForStore(void) { + ring_buffer *buffer = &cdc_rx_buffer; + + if (buffer->full) + return 0; + else if (buffer->head >= buffer->tail) + return CDC_SERIAL_BUFFER_SIZE - 1 - buffer->head + buffer->tail; + else + return buffer->tail - buffer->head - 1; +} + Serial_ SerialUSB(USBDevice); #endif diff --git a/cores/arduino/USB/USBAPI.h b/cores/arduino/USB/USBAPI.h index 716edbfd5..8573248d9 100644 --- a/cores/arduino/USB/USBAPI.h +++ b/cores/arduino/USB/USBAPI.h @@ -168,6 +168,8 @@ class Serial_ : public Stream }; private: + int availableForStore(void); + USBDeviceClass &usb; RingBuffer *_cdc_rx_buffer; }; From 9f678cb41608e3867746eb7a62d799718f89c2dc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Feb 2016 09:17:43 -0500 Subject: [PATCH 03/17] USB-CDC: Refactored EP OUT handling --- cores/arduino/USB/CDC.cpp | 40 ++++++++++++----------------------- cores/arduino/USB/USBAPI.h | 6 ++++-- cores/arduino/USB/USBCore.cpp | 17 +++++++++++---- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/cores/arduino/USB/CDC.cpp b/cores/arduino/USB/CDC.cpp index adb3ab075..6ae5e6581 100644 --- a/cores/arduino/USB/CDC.cpp +++ b/cores/arduino/USB/CDC.cpp @@ -161,32 +161,21 @@ void Serial_::end(void) { } -void Serial_::accept(void) +void Serial_::accept(uint8_t *data, uint32_t size) { - uint8_t buffer[CDC_SERIAL_BUFFER_SIZE]; - uint32_t len = usb.recv(CDC_ENDPOINT_OUT, &buffer, CDC_SERIAL_BUFFER_SIZE); - - uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0); - __disable_irq(); - ring_buffer *ringBuffer = &cdc_rx_buffer; uint32_t i = ringBuffer->head; - - // if we should be storing the received character into the location - // just before the tail (meaning that the head would advance to the - // current location of the tail), we're about to overflow the buffer - // and so we don't write the character or advance the head. - uint32_t k = 0; - while (len > 0 && !ringBuffer->full) { - len--; - ringBuffer->buffer[i++] = buffer[k++]; + while (size--) { + ringBuffer->buffer[i++] = *data; + data++; i %= CDC_SERIAL_BUFFER_SIZE; - if (i == ringBuffer->tail) - ringBuffer->full = true; } ringBuffer->head = i; - if (enableInterrupts) { - __enable_irq(); + if (i == ringBuffer->tail) ringBuffer->full = true; + if (availableForStore() < EPX_SIZE) { + stalled = true; + } else { + usb.epOut(CDC_ENDPOINT_OUT); } } @@ -196,9 +185,6 @@ int Serial_::available(void) if (buffer->full) { return CDC_SERIAL_BUFFER_SIZE; } - if (buffer->head == buffer->tail) { - USB->DEVICE.DeviceEndpoint[CDC_ENDPOINT_OUT].EPINTENSET.reg = USB_DEVICE_EPINTENCLR_TRCPT(1); - } return (uint32_t)(CDC_SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % CDC_SERIAL_BUFFER_SIZE; } @@ -228,11 +214,11 @@ int Serial_::read(void) { ring_buffer *buffer = &cdc_rx_buffer; - // if the head isn't ahead of the tail, we don't have any characters - if (buffer->head == buffer->tail && !buffer->full) + // if we have enough space enable OUT endpoint to receive more data + if (stalled && availableForStore() >= EPX_SIZE) { - if (usb.available(CDC_ENDPOINT_OUT)) - accept(); + stalled = false; + usb.epOut(CDC_ENDPOINT_OUT); } if (buffer->head == buffer->tail && !buffer->full) { diff --git a/cores/arduino/USB/USBAPI.h b/cores/arduino/USB/USBAPI.h index 8573248d9..2a6a86ce8 100644 --- a/cores/arduino/USB/USBAPI.h +++ b/cores/arduino/USB/USBAPI.h @@ -92,6 +92,7 @@ class USBDeviceClass { uint32_t available(uint32_t ep); void flush(uint32_t ep); void stall(uint32_t ep); + void epOut(uint32_t ep); // private? uint32_t armSend(uint32_t ep, const void *data, uint32_t len); @@ -112,14 +113,14 @@ extern USBDeviceClass USBDevice; class Serial_ : public Stream { public: - Serial_(USBDeviceClass &_usb) : usb(_usb) { } + Serial_(USBDeviceClass &_usb) : usb(_usb), stalled(false) { } void begin(uint32_t baud_count); void begin(unsigned long, uint8_t); void end(void); virtual int available(void); virtual int availableForWrite(void); - virtual void accept(void); + virtual void accept(uint8_t *data, uint32_t size); virtual int peek(void); virtual int read(void); virtual void flush(void); @@ -172,6 +173,7 @@ class Serial_ : public Stream USBDeviceClass &usb; RingBuffer *_cdc_rx_buffer; + bool stalled; }; extern Serial_ SerialUSB; diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index fc14b3c4c..17a363eaa 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -247,15 +247,23 @@ bool USBDeviceClass::sendDescriptor(USBSetup &setup) return true; } +void USBDeviceClass::epOut(uint32_t ep) +{ + usbd.epBank0AckTransferComplete(ep); + //usbd.epBank0AckTransferFailed(ep); + usbd.epBank0EnableTransferComplete(ep); + usbd.epBank0ResetReady(ep); +} void USBDeviceClass::handleEndpoint(uint8_t ep) { #if defined(CDC_ENABLED) - if (ep == CDC_ENDPOINT_OUT) + if (ep == CDC_ENDPOINT_OUT && usbd.epBank0IsTransferComplete(CDC_ENDPOINT_OUT)) { - // Handle received bytes - if (available(CDC_ENDPOINT_OUT)) - SerialUSB.accept(); + // Ack Transfer complete + usbd.epBank0AckTransferComplete(CDC_ENDPOINT_OUT); + + SerialUSB.accept(udd_ep_out_cache_buffer[CDC_ENDPOINT_OUT], available(CDC_ENDPOINT_OUT)); } if (ep == CDC_ENDPOINT_IN) { @@ -438,6 +446,7 @@ void USBDeviceClass::initEP(uint32_t ep, uint32_t config) // Release OUT EP usbd.epBank0SetMultiPacketSize(ep, 64); usbd.epBank0SetByteCount(ep, 0); + epOut(ep); } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0))) { From 19ae0eb4d437eff9efa54bb14de636006f94b13c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 24 Jun 2016 15:59:57 +0200 Subject: [PATCH 04/17] USB-CDC: access to rx buffer is now ISR-protected --- cores/arduino/USB/CDC.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cores/arduino/USB/CDC.cpp b/cores/arduino/USB/CDC.cpp index 6ae5e6581..e6fc4a5ba 100644 --- a/cores/arduino/USB/CDC.cpp +++ b/cores/arduino/USB/CDC.cpp @@ -214,24 +214,24 @@ int Serial_::read(void) { ring_buffer *buffer = &cdc_rx_buffer; + uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0); + __disable_irq(); + // if we have enough space enable OUT endpoint to receive more data if (stalled && availableForStore() >= EPX_SIZE) { stalled = false; usb.epOut(CDC_ENDPOINT_OUT); } - if (buffer->head == buffer->tail && !buffer->full) - { - return -1; - } - else + int c = -1; + if (buffer->head != buffer->tail || buffer->full) { - unsigned char c = buffer->buffer[buffer->tail]; + c = buffer->buffer[buffer->tail]; buffer->tail = (uint32_t)(buffer->tail + 1) % CDC_SERIAL_BUFFER_SIZE; buffer->full = false; - - return c; } + if (enableInterrupts) __enable_irq(); + return c; } void Serial_::flush(void) From 07263f8bf364f27049fb4e0bf385e9e5ce565da2 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 24 Jun 2016 16:00:25 +0200 Subject: [PATCH 05/17] USB-CDC: reset EP OUT count upon reception --- cores/arduino/USB/USBCore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 17a363eaa..233784431 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -252,6 +252,7 @@ void USBDeviceClass::epOut(uint32_t ep) usbd.epBank0AckTransferComplete(ep); //usbd.epBank0AckTransferFailed(ep); usbd.epBank0EnableTransferComplete(ep); + usbd.epBank0SetByteCount(ep, 0); usbd.epBank0ResetReady(ep); } From 40d9554ed333c363fee2f384295b8bcf800a6a97 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 24 Jun 2016 16:01:17 +0200 Subject: [PATCH 06/17] USB-CDC: Avoid need to send ZLP by sending up to (EPX_SIZE-1) bytes at a time --- cores/arduino/USB/USBCore.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 233784431..1be72247a 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -627,8 +627,8 @@ uint32_t USBDeviceClass::send(uint32_t ep, const void *data, uint32_t len) // Flash area while (len != 0) { - if (len >= 64) { - length = 64; + if (len >= EPX_SIZE) { + length = EPX_SIZE - 1; } else { length = len; } From 34bf5421f853215d1586353708f1451ce265c360 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 28 Jun 2016 15:43:19 +0200 Subject: [PATCH 07/17] USB: set return type of USBDevice::recv(ep) to int this is the proper type to encapsulate a byte when data is available or -1 when there is no data. --- cores/arduino/USB/USBAPI.h | 2 +- cores/arduino/USB/USBCore.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/arduino/USB/USBAPI.h b/cores/arduino/USB/USBAPI.h index 2a6a86ce8..1591948c2 100644 --- a/cores/arduino/USB/USBAPI.h +++ b/cores/arduino/USB/USBAPI.h @@ -88,7 +88,7 @@ class USBDeviceClass { uint32_t send(uint32_t ep, const void *data, uint32_t len); void sendZlp(uint32_t ep); uint32_t recv(uint32_t ep, void *data, uint32_t len); - uint32_t recv(uint32_t ep); + int recv(uint32_t ep); uint32_t available(uint32_t ep); void flush(uint32_t ep); void stall(uint32_t ep); diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 1be72247a..c66f75a49 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -572,7 +572,7 @@ uint32_t USBDeviceClass::recv(uint32_t ep, void *_data, uint32_t len) } // Recv 1 byte if ready -uint32_t USBDeviceClass::recv(uint32_t ep) +int USBDeviceClass::recv(uint32_t ep) { uint8_t c; if (recv(ep, &c, 1) != 1) { From b2ddb2267d6dde5e9c13083cb581435cc0a4b0fe Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 28 Jun 2016 15:54:38 +0200 Subject: [PATCH 08/17] USB: Added interface to possibly setup dynamic USB EP Handler --- cores/arduino/USB/USBCore.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index c66f75a49..2a7fa11f5 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -46,6 +46,12 @@ extern "C" void UDD_Handler(void) { USBDevice.ISRHandler(); } +class EPHandler { +public: + virtual void handleEndpoint() = 0; + virtual uint32_t recv(void *_data, uint32_t len) = 0; + virtual uint32_t available() const = 0; +}; const uint16_t STRING_LANGUAGE[2] = { @@ -88,6 +94,8 @@ uint8_t udd_ep_out_cache_buffer[7][64]; static __attribute__((__aligned__(4))) //__attribute__((__section__(".bss_hram0"))) uint8_t udd_ep_in_cache_buffer[7][64]; +static EPHandler *epHandlers[7]; + //================================================================== // Send a USB descriptor string. The string is stored as a @@ -532,7 +540,11 @@ uint32_t USBDeviceClass::recvControl(void *_data, uint32_t len) // Number of bytes, assumes a rx endpoint uint32_t USBDeviceClass::available(uint32_t ep) { - return usbd.epBank0ByteCount(ep); + if (epHandlers[ep]) { + return epHandlers[ep]->available(); + } else { + return usbd.epBank0ByteCount(ep); + } } // Non Blocking receive @@ -542,6 +554,10 @@ uint32_t USBDeviceClass::recv(uint32_t ep, void *_data, uint32_t len) if (!_usbConfiguration) return -1; + if (epHandlers[ep]) { + return epHandlers[ep]->recv(_data, len); + } + if (available(ep) < len) len = available(ep); @@ -891,7 +907,11 @@ void USBDeviceClass::ISRHandler() if (usbd.epBank0IsTransferComplete(i) || usbd.epBank1IsTransferComplete(i)) { - handleEndpoint(i); + if (epHandlers[i]) { + epHandlers[i]->handleEndpoint(); + } else { + handleEndpoint(i); + } } ept_int &= ~(1 << i); } From 9f24bbe9a8a28d9193d0247dab77e9990f75eb09 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 28 Jun 2016 16:01:26 +0200 Subject: [PATCH 09/17] USB: Added an OUT EP handler with double buffering. --- cores/arduino/USB/USBCore.cpp | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 2a7fa11f5..59cf5f74c 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -53,6 +53,138 @@ class EPHandler { virtual uint32_t available() const = 0; }; +class DoubleBufferedEPOutHandler : public EPHandler { +public: + DoubleBufferedEPOutHandler(uint32_t endPoint, uint32_t bufferSize) : + ep(endPoint), size(bufferSize), + current(0), incoming(0), + first0(0), last0(0), ready0(false), + first1(0), last1(0), ready1(false), + notify(false) + { + data0 = reinterpret_cast(malloc(size)); + data1 = reinterpret_cast(malloc(size)); + + usbd.epBank0SetSize(ep, 64); + usbd.epBank0SetType(ep, 3); // BULK OUT + + release(); + } + + // Read one byte from the buffer, if the buffer is empty -1 is returned + int read() { + if (current == 0) { + if (!ready0) { + return -1; + } + if (first0 == last0) { + first0 = 0; + last0 = 0; + ready0 = false; + if (notify) { + release(); + } + current = 1; + return -1; + } + return data0[first0++]; + } else { + if (!ready1) { + return -1; + } + if (first1 == last1) { + first1 = 0; + last1 = 0; + ready1 = false; + if (notify) { + release(); + } + current = 0; + return -1; + } + return data1[first1++]; + } + } + + virtual void handleEndpoint() + { + if (usbd.epBank0IsTransferComplete(ep)) + { + // Ack Transfer complete + usbd.epBank0AckTransferComplete(ep); + + // Update counters and swap banks + if (incoming == 0) { + last0 = usbd.epBank0ByteCount(ep); + ready0 = true; + incoming = 1; + } else { + last1 = usbd.epBank0ByteCount(ep); + ready1 = true; + incoming = 0; + } + release(); + } + } + + virtual uint32_t recv(void *_data, uint32_t len) + { + uint8_t *data = reinterpret_cast(_data); + uint32_t i; + for (i=0; i Date: Tue, 28 Jun 2016 15:57:06 +0200 Subject: [PATCH 10/17] USB-CDC: OUT EP is now handled with a custom EPHandler --- cores/arduino/USB/CDC.cpp | 85 +++++------------------------------ cores/arduino/USB/USBAPI.h | 2 - cores/arduino/USB/USBCore.cpp | 25 +---------- 3 files changed, 13 insertions(+), 99 deletions(-) diff --git a/cores/arduino/USB/CDC.cpp b/cores/arduino/USB/CDC.cpp index e6fc4a5ba..9388652b0 100644 --- a/cores/arduino/USB/CDC.cpp +++ b/cores/arduino/USB/CDC.cpp @@ -33,14 +33,6 @@ #define CDC_LINESTATE_READY (CDC_LINESTATE_RTS | CDC_LINESTATE_DTR) -struct ring_buffer { - uint8_t buffer[CDC_SERIAL_BUFFER_SIZE]; - volatile uint32_t head; - volatile uint32_t tail; - volatile bool full; -}; -ring_buffer cdc_rx_buffer = {{0}, 0, 0, false}; - typedef struct { uint32_t dwDTERate; uint8_t bCharFormat; @@ -146,7 +138,6 @@ bool CDC_Setup(USBSetup& setup) return false; } -uint32_t _serialPeek = -1; void Serial_::begin(uint32_t /* baud_count */) { // uart config is ignored in USB-CDC @@ -161,31 +152,9 @@ void Serial_::end(void) { } -void Serial_::accept(uint8_t *data, uint32_t size) -{ - ring_buffer *ringBuffer = &cdc_rx_buffer; - uint32_t i = ringBuffer->head; - while (size--) { - ringBuffer->buffer[i++] = *data; - data++; - i %= CDC_SERIAL_BUFFER_SIZE; - } - ringBuffer->head = i; - if (i == ringBuffer->tail) ringBuffer->full = true; - if (availableForStore() < EPX_SIZE) { - stalled = true; - } else { - usb.epOut(CDC_ENDPOINT_OUT); - } -} - int Serial_::available(void) { - ring_buffer *buffer = &cdc_rx_buffer; - if (buffer->full) { - return CDC_SERIAL_BUFFER_SIZE; - } - return (uint32_t)(CDC_SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % CDC_SERIAL_BUFFER_SIZE; + return usb.available(CDC_ENDPOINT_OUT); } int Serial_::availableForWrite(void) @@ -195,43 +164,24 @@ int Serial_::availableForWrite(void) return (EPX_SIZE - 1); } +int _serialPeek = -1; + int Serial_::peek(void) { - ring_buffer *buffer = &cdc_rx_buffer; - if (buffer->head == buffer->tail && !buffer->full) { - return -1; - } else { - return buffer->buffer[buffer->tail]; - } + if (_serialPeek != -1) + return _serialPeek; + _serialPeek = read(); + return _serialPeek; } - -// if the ringBuffer is empty: try to fill it -// if it's still empty: return -1 -// else return the last char -// so the buffer is filled only when needed int Serial_::read(void) { - ring_buffer *buffer = &cdc_rx_buffer; - - uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0); - __disable_irq(); - - // if we have enough space enable OUT endpoint to receive more data - if (stalled && availableForStore() >= EPX_SIZE) - { - stalled = false; - usb.epOut(CDC_ENDPOINT_OUT); + if (_serialPeek != -1) { + int res = _serialPeek; + _serialPeek = -1; + return res; } - int c = -1; - if (buffer->head != buffer->tail || buffer->full) - { - c = buffer->buffer[buffer->tail]; - buffer->tail = (uint32_t)(buffer->tail + 1) % CDC_SERIAL_BUFFER_SIZE; - buffer->full = false; - } - if (enableInterrupts) __enable_irq(); - return c; + return usb.recv(CDC_ENDPOINT_OUT); } void Serial_::flush(void) @@ -337,17 +287,6 @@ bool Serial_::rts() { return _usbLineInfo.lineState & 0x2; } -int Serial_::availableForStore(void) { - ring_buffer *buffer = &cdc_rx_buffer; - - if (buffer->full) - return 0; - else if (buffer->head >= buffer->tail) - return CDC_SERIAL_BUFFER_SIZE - 1 - buffer->head + buffer->tail; - else - return buffer->tail - buffer->head - 1; -} - Serial_ SerialUSB(USBDevice); #endif diff --git a/cores/arduino/USB/USBAPI.h b/cores/arduino/USB/USBAPI.h index 1591948c2..5cc0ba128 100644 --- a/cores/arduino/USB/USBAPI.h +++ b/cores/arduino/USB/USBAPI.h @@ -92,7 +92,6 @@ class USBDeviceClass { uint32_t available(uint32_t ep); void flush(uint32_t ep); void stall(uint32_t ep); - void epOut(uint32_t ep); // private? uint32_t armSend(uint32_t ep, const void *data, uint32_t len); @@ -120,7 +119,6 @@ class Serial_ : public Stream virtual int available(void); virtual int availableForWrite(void); - virtual void accept(uint8_t *data, uint32_t size); virtual int peek(void); virtual int read(void); virtual void flush(void); diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index 59cf5f74c..d43524889 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -387,25 +387,9 @@ bool USBDeviceClass::sendDescriptor(USBSetup &setup) return true; } -void USBDeviceClass::epOut(uint32_t ep) -{ - usbd.epBank0AckTransferComplete(ep); - //usbd.epBank0AckTransferFailed(ep); - usbd.epBank0EnableTransferComplete(ep); - usbd.epBank0SetByteCount(ep, 0); - usbd.epBank0ResetReady(ep); -} - void USBDeviceClass::handleEndpoint(uint8_t ep) { #if defined(CDC_ENABLED) - if (ep == CDC_ENDPOINT_OUT && usbd.epBank0IsTransferComplete(CDC_ENDPOINT_OUT)) - { - // Ack Transfer complete - usbd.epBank0AckTransferComplete(CDC_ENDPOINT_OUT); - - SerialUSB.accept(udd_ep_out_cache_buffer[CDC_ENDPOINT_OUT], available(CDC_ENDPOINT_OUT)); - } if (ep == CDC_ENDPOINT_IN) { // NAK on endpoint IN, the bank is not yet filled in. @@ -580,14 +564,7 @@ void USBDeviceClass::initEP(uint32_t ep, uint32_t config) } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0))) { - usbd.epBank0SetSize(ep, 64); - usbd.epBank0SetAddress(ep, &udd_ep_out_cache_buffer[ep]); - usbd.epBank0SetType(ep, 3); // BULK OUT - - // Release OUT EP - usbd.epBank0SetMultiPacketSize(ep, 64); - usbd.epBank0SetByteCount(ep, 0); - epOut(ep); + epHandlers[ep] = new DoubleBufferedEPOutHandler(ep, 64); } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0))) { From 3ba1a352968ed74503a26c3ba3616141eddfec57 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 20 Jul 2016 16:58:30 +0200 Subject: [PATCH 11/17] Moved EPHandler in USBDevice headers The reference to the upper USBDevice class is passed on the EPHandler constructor. --- cores/arduino/USB/SAMD21_USBDevice.h | 146 ++++++++++++++++++++++++++- cores/arduino/USB/USBCore.cpp | 142 +------------------------- 2 files changed, 145 insertions(+), 143 deletions(-) diff --git a/cores/arduino/USB/SAMD21_USBDevice.h b/cores/arduino/USB/SAMD21_USBDevice.h index 203a1c787..9cf9c0b2a 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.h +++ b/cores/arduino/USB/SAMD21_USBDevice.h @@ -62,8 +62,8 @@ class USBDevice_SAMD21G18x { // USB Interrupts inline bool isEndOfResetInterrupt() { return usb.INTFLAG.bit.EORST; } inline void ackEndOfResetInterrupt() { usb.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; } - inline void enableEndOfResetInterrupt() { usb.INTENSET.bit.EORST = 1; } - inline void disableEndOfResetInterrupt() { usb.INTENCLR.bit.EORST = 1; } + inline void enableEndOfResetInterrupt() { usb.INTENSET.bit.EORST = 1; } + inline void disableEndOfResetInterrupt() { usb.INTENCLR.bit.EORST = 1; } inline bool isStartOfFrameInterrupt() { return usb.INTFLAG.bit.SOF; } inline void ackStartOfFrameInterrupt() { usb.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF; } @@ -195,3 +195,145 @@ void USBDevice_SAMD21G18x::calibrate() { usb.PADCAL.bit.TRIM = pad_trim; } +class EPHandler { +public: + virtual void handleEndpoint() = 0; + virtual uint32_t recv(void *_data, uint32_t len) = 0; + virtual uint32_t available() const = 0; +}; + +class DoubleBufferedEPOutHandler : public EPHandler { +public: + DoubleBufferedEPOutHandler(USBDevice_SAMD21G18x &usbDev, uint32_t endPoint, uint32_t bufferSize) : + usbd(usbDev), + ep(endPoint), size(bufferSize), + current(0), incoming(0), + first0(0), last0(0), ready0(false), + first1(0), last1(0), ready1(false), + notify(false) + { + data0 = reinterpret_cast(malloc(size)); + data1 = reinterpret_cast(malloc(size)); + + usbd.epBank0SetSize(ep, 64); + usbd.epBank0SetType(ep, 3); // BULK OUT + + release(); + } + + // Read one byte from the buffer, if the buffer is empty -1 is returned + int read() { + if (current == 0) { + if (!ready0) { + return -1; + } + if (first0 == last0) { + first0 = 0; + last0 = 0; + ready0 = false; + if (notify) { + release(); + } + current = 1; + return -1; + } + return data0[first0++]; + } else { + if (!ready1) { + return -1; + } + if (first1 == last1) { + first1 = 0; + last1 = 0; + ready1 = false; + if (notify) { + release(); + } + current = 0; + return -1; + } + return data1[first1++]; + } + } + + virtual void handleEndpoint() + { + if (usbd.epBank0IsTransferComplete(ep)) + { + // Ack Transfer complete + usbd.epBank0AckTransferComplete(ep); + + // Update counters and swap banks + if (incoming == 0) { + last0 = usbd.epBank0ByteCount(ep); + ready0 = true; + incoming = 1; + } else { + last1 = usbd.epBank0ByteCount(ep); + ready1 = true; + incoming = 0; + } + release(); + } + } + + virtual uint32_t recv(void *_data, uint32_t len) + { + uint8_t *data = reinterpret_cast(_data); + uint32_t i; + for (i=0; i(malloc(size)); - data1 = reinterpret_cast(malloc(size)); - - usbd.epBank0SetSize(ep, 64); - usbd.epBank0SetType(ep, 3); // BULK OUT - - release(); - } - - // Read one byte from the buffer, if the buffer is empty -1 is returned - int read() { - if (current == 0) { - if (!ready0) { - return -1; - } - if (first0 == last0) { - first0 = 0; - last0 = 0; - ready0 = false; - if (notify) { - release(); - } - current = 1; - return -1; - } - return data0[first0++]; - } else { - if (!ready1) { - return -1; - } - if (first1 == last1) { - first1 = 0; - last1 = 0; - ready1 = false; - if (notify) { - release(); - } - current = 0; - return -1; - } - return data1[first1++]; - } - } - - virtual void handleEndpoint() - { - if (usbd.epBank0IsTransferComplete(ep)) - { - // Ack Transfer complete - usbd.epBank0AckTransferComplete(ep); - - // Update counters and swap banks - if (incoming == 0) { - last0 = usbd.epBank0ByteCount(ep); - ready0 = true; - incoming = 1; - } else { - last1 = usbd.epBank0ByteCount(ep); - ready1 = true; - incoming = 0; - } - release(); - } - } - - virtual uint32_t recv(void *_data, uint32_t len) - { - uint8_t *data = reinterpret_cast(_data); - uint32_t i; - for (i=0; i Date: Wed, 20 Jul 2016 17:24:59 +0200 Subject: [PATCH 12/17] Fixed some comments --- cores/arduino/USB/USBCore.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index bd11d39b1..d27502c91 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Arduino LLC. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -86,6 +86,9 @@ uint8_t udd_ep_out_cache_buffer[7][64]; static __attribute__((__aligned__(4))) //__attribute__((__section__(".bss_hram0"))) uint8_t udd_ep_in_cache_buffer[7][64]; +// Some EP are handled using EPHanlders. +// Possibly all the sparse EP handling subroutines will be +// converted into reusable EPHandlers in the future. static EPHandler *epHandlers[7]; //================================================================== @@ -898,7 +901,3 @@ void USBDeviceClass::ISRHandler() // USBDevice class instance USBDeviceClass USBDevice; -// USB_Handler ISR -// extern "C" void USB_Handler(void) { -// USBDevice.ISRHandler(); -// } From a9127228d65ebca28cd9d9be448cce4fff51324b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 24 Aug 2016 00:29:47 +0200 Subject: [PATCH 13/17] USB: Slightly refactored EP OUT handler Now the release() function only performs the action that is called for, i.e. release the endpoint and let it receive data. All the buffers handling has been inlined in the callers, this also slightly improves performance because it allows to remove some redundant checks. --- cores/arduino/USB/SAMD21_USBDevice.h | 50 +++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/cores/arduino/USB/SAMD21_USBDevice.h b/cores/arduino/USB/SAMD21_USBDevice.h index 9cf9c0b2a..8c5e8f80a 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.h +++ b/cores/arduino/USB/SAMD21_USBDevice.h @@ -195,6 +195,10 @@ void USBDevice_SAMD21G18x::calibrate() { usb.PADCAL.bit.TRIM = pad_trim; } +/* + * USB EP generic handlers. + */ + class EPHandler { public: virtual void handleEndpoint() = 0; @@ -218,6 +222,8 @@ class DoubleBufferedEPOutHandler : public EPHandler { usbd.epBank0SetSize(ep, 64); usbd.epBank0SetType(ep, 3); // BULK OUT + usbd.epBank0SetAddress(ep, data0); + release(); } @@ -229,12 +235,12 @@ class DoubleBufferedEPOutHandler : public EPHandler { } if (first0 == last0) { first0 = 0; - last0 = 0; + current = 1; ready0 = false; if (notify) { + notify = false; release(); } - current = 1; return -1; } return data0[first0++]; @@ -244,12 +250,12 @@ class DoubleBufferedEPOutHandler : public EPHandler { } if (first1 == last1) { first1 = 0; - last1 = 0; + current = 0; ready1 = false; if (notify) { + notify = false; release(); } - current = 0; return -1; } return data1[first1++]; @@ -262,16 +268,29 @@ class DoubleBufferedEPOutHandler : public EPHandler { { // Ack Transfer complete usbd.epBank0AckTransferComplete(ep); + //usbd.epBank0AckTransferFailed(ep); // Update counters and swap banks if (incoming == 0) { last0 = usbd.epBank0ByteCount(ep); - ready0 = true; incoming = 1; + usbd.epBank0SetAddress(ep, data1); + ready0 = true; + if (ready1) { + notify = true; + return; + } + notify = false; } else { last1 = usbd.epBank0ByteCount(ep); - ready1 = true; incoming = 0; + usbd.epBank0SetAddress(ep, data0); + ready1 = true; + if (ready0) { + notify = true; + return; + } + notify = false; } release(); } @@ -295,28 +314,11 @@ class DoubleBufferedEPOutHandler : public EPHandler { } void release() { - if (incoming == 0) { - if (ready0) { - notify = true; - return; - } - usbd.epBank0SetAddress(ep, data0); - } else { - if (ready1) { - notify = true; - return; - } - usbd.epBank0SetAddress(ep, data1); - } - usbd.epBank0AckTransferComplete(ep); - //usbd.epBank0AckTransferFailed(ep); - usbd.epBank0EnableTransferComplete(ep); - // Release OUT EP + usbd.epBank0EnableTransferComplete(ep); usbd.epBank0SetMultiPacketSize(ep, size); usbd.epBank0SetByteCount(ep, 0); usbd.epBank0ResetReady(ep); - notify = false; } private: From 2e44a4d96182f55127b61fc9d0a29981a44ecb8f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 24 Aug 2016 00:22:07 +0200 Subject: [PATCH 14/17] USBD: Added critical sections to EP OUT handler --- cores/arduino/USB/SAMD21_USBDevice.h | 111 +++++++++++++++++++-------- 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/cores/arduino/USB/SAMD21_USBDevice.h b/cores/arduino/USB/SAMD21_USBDevice.h index 8c5e8f80a..a6259f90f 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.h +++ b/cores/arduino/USB/SAMD21_USBDevice.h @@ -195,6 +195,32 @@ void USBDevice_SAMD21G18x::calibrate() { usb.PADCAL.bit.TRIM = pad_trim; } +/* + * Synchronization primitives. + * TODO: Move into a separate header file and make an API out of it + */ + +class __Guard { +public: + __Guard() : primask(__get_PRIMASK()), loops(1) { + __disable_irq(); + } + ~__Guard() { + if (primask == 0) { + __enable_irq(); + // http://infocenter.arm.com/help/topic/com.arm.doc.dai0321a/BIHBFEIB.html + __ISB(); + } + } + uint32_t enter() { return loops--; } +private: + uint32_t primask; + uint32_t loops; +}; + +#define synchronized for (__Guard __guard; __guard.enter(); ) + + /* * USB EP generic handlers. */ @@ -222,39 +248,51 @@ class DoubleBufferedEPOutHandler : public EPHandler { usbd.epBank0SetSize(ep, 64); usbd.epBank0SetType(ep, 3); // BULK OUT - usbd.epBank0SetAddress(ep, data0); + usbd.epBank0SetAddress(ep, const_cast(data0)); release(); } // Read one byte from the buffer, if the buffer is empty -1 is returned int read() { + // R/W: current, first0/1, ready0/1, notify + // R : last0/1, data0/1 if (current == 0) { - if (!ready0) { - return -1; + synchronized { + if (!ready0) { + return -1; + } } + // when ready0==true the buffer is not being filled and last0 is constant if (first0 == last0) { first0 = 0; current = 1; - ready0 = false; - if (notify) { - notify = false; - release(); + synchronized { + ready0 = false; + if (notify) { + notify = false; + release(); + } } return -1; } return data0[first0++]; } else { - if (!ready1) { - return -1; + synchronized { + if (!ready1) { + return -1; + } } + // when ready1==true the buffer is not being filled and last1 is constant if (first1 == last1) { first1 = 0; current = 0; - ready1 = false; - if (notify) { - notify = false; - release(); + synchronized { + ready1 = false; + if (notify) { + notify = false; + release(); + } } return -1; } @@ -264,33 +302,39 @@ class DoubleBufferedEPOutHandler : public EPHandler { virtual void handleEndpoint() { + // R/W : incoming, ready0/1 + // W : last0/1, notify if (usbd.epBank0IsTransferComplete(ep)) { // Ack Transfer complete usbd.epBank0AckTransferComplete(ep); - //usbd.epBank0AckTransferFailed(ep); + //usbd.epBank0AckTransferFailed(ep); // XXX // Update counters and swap banks if (incoming == 0) { last0 = usbd.epBank0ByteCount(ep); incoming = 1; - usbd.epBank0SetAddress(ep, data1); + usbd.epBank0SetAddress(ep, const_cast(data1)); ready0 = true; - if (ready1) { - notify = true; - return; + synchronized { + if (ready1) { + notify = true; + return; + } + notify = false; } - notify = false; } else { last1 = usbd.epBank0ByteCount(ep); incoming = 0; - usbd.epBank0SetAddress(ep, data0); - ready1 = true; - if (ready0) { - notify = true; - return; + usbd.epBank0SetAddress(ep, const_cast(data0)); + synchronized { + ready1 = true; + if (ready0) { + notify = true; + return; + } + notify = false; } - notify = false; } release(); } @@ -298,6 +342,7 @@ class DoubleBufferedEPOutHandler : public EPHandler { virtual uint32_t recv(void *_data, uint32_t len) { + // TODO Write an optimized version of this one uint8_t *data = reinterpret_cast(_data); uint32_t i; for (i=0; i Date: Wed, 24 Aug 2016 12:37:27 +0200 Subject: [PATCH 15/17] USB: EP0 ::recv method has been merged with ::read method It makes no sense to have ::recv calling repeatedly ::read in this case. --- cores/arduino/USB/SAMD21_USBDevice.h | 37 ++++++++++++---------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/cores/arduino/USB/SAMD21_USBDevice.h b/cores/arduino/USB/SAMD21_USBDevice.h index a6259f90f..7d4c7b81c 100644 --- a/cores/arduino/USB/SAMD21_USBDevice.h +++ b/cores/arduino/USB/SAMD21_USBDevice.h @@ -253,17 +253,23 @@ class DoubleBufferedEPOutHandler : public EPHandler { release(); } - // Read one byte from the buffer, if the buffer is empty -1 is returned - int read() { + virtual uint32_t recv(void *_data, uint32_t len) + { + uint8_t *data = reinterpret_cast(_data); + // R/W: current, first0/1, ready0/1, notify // R : last0/1, data0/1 if (current == 0) { synchronized { if (!ready0) { - return -1; + return 0; } } // when ready0==true the buffer is not being filled and last0 is constant + uint32_t i; + for (i=0; i(_data); - uint32_t i; - for (i=0; i Date: Wed, 24 Aug 2016 12:47:51 +0200 Subject: [PATCH 16/17] USB-CDC: Increased buffer size to 256 bytes This change improves read performance when massive data is sent via USB by exploiting the hardware capability to handle multi-packet transfers autonomously. --- cores/arduino/USB/USBCore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp index d27502c91..bd815d367 100644 --- a/cores/arduino/USB/USBCore.cpp +++ b/cores/arduino/USB/USBCore.cpp @@ -427,7 +427,7 @@ void USBDeviceClass::initEP(uint32_t ep, uint32_t config) } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0))) { - epHandlers[ep] = new DoubleBufferedEPOutHandler(usbd, ep, 64); + epHandlers[ep] = new DoubleBufferedEPOutHandler(usbd, ep, 256); } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0))) { From 4691ef4ba094fa7d53119427b738b7fe464b9345 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 24 Aug 2016 12:39:56 +0200 Subject: [PATCH 17/17] USB: Added optimized Stream::readBytes for EP OUT handler This bypass the generic (but inefficient) implementation of Stream::readBytes and uses an optimized version of readBytes than can do efficient multi-byte reads. --- cores/arduino/USB/CDC.cpp | 14 ++++++++++++++ cores/arduino/USB/USBAPI.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/cores/arduino/USB/CDC.cpp b/cores/arduino/USB/CDC.cpp index 9388652b0..c65c63850 100644 --- a/cores/arduino/USB/CDC.cpp +++ b/cores/arduino/USB/CDC.cpp @@ -184,6 +184,20 @@ int Serial_::read(void) return usb.recv(CDC_ENDPOINT_OUT); } +size_t Serial_::readBytes(char *buffer, size_t length) +{ + size_t count = 0; + _startMillis = millis(); + while (count < length) + { + uint32_t n = usb.recv(CDC_ENDPOINT_OUT, buffer+count, length-count); + if (n == 0 && (millis() - _startMillis) >= _timeout) + break; + count += n; + } + return count; +} + void Serial_::flush(void) { usb.flush(CDC_ENDPOINT_IN); diff --git a/cores/arduino/USB/USBAPI.h b/cores/arduino/USB/USBAPI.h index 5cc0ba128..53c249a4a 100644 --- a/cores/arduino/USB/USBAPI.h +++ b/cores/arduino/USB/USBAPI.h @@ -127,6 +127,8 @@ class Serial_ : public Stream using Print::write; // pull in write(str) from Print operator bool(); + size_t readBytes(char *buffer, size_t length); + // This method allows processing "SEND_BREAK" requests sent by // the USB host. Those requests indicate that the host wants to // send a BREAK signal and are accompanied by a single uint16_t