diff --git a/cores/arduino/USB/CDC.cpp b/cores/arduino/USB/CDC.cpp
index e6fc4a5bad496a9046c611612d0ddbc495bbbee7..9388652b0be76a5c15d039c97965ccb2738199ec 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 1591948c20f426a21498c464a12285f45af9c679..5cc0ba1282134f54b22475cd7c915a7f34ed95d6 100644
--- a/cores/arduino/USB/USBAPI.h
+++ b/cores/arduino/USB/USBAPI.h
@@ -92,7 +92,6 @@ public:
 	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 @@ public:
 
 	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 59cf5f74c63843835591b3a2ae8ebaeaaf0e843f..d435248897d0f1072a1dcad379c0d210739bd32a 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)))
 	{