Skip to content
Merged
179 changes: 91 additions & 88 deletions cores/arduino/USB/CDC.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
Copyright (c) 2015 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
Expand All @@ -16,122 +16,111 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <Arduino.h>
#include <Reset.h> // Needed for auto-reset with 1200bps port touch

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

// Include Atmel headers
#include "Arduino.h"
#include "sam.h"
#include "wiring_constants.h"
#include "USBCore.h"
#include "USB/USB_device.h"
#include "USBDesc.h"
#include "USBAPI.h"

#include "Reset.h"


#ifdef CDC_ENABLED

#define CDC_SERIAL_BUFFER_SIZE 64
#define CDC_SERIAL_BUFFER_SIZE 256

/* For information purpose only since RTS is not always handled by the terminal application */
#define CDC_LINESTATE_DTR 0x01 // Data Terminal Ready
#define CDC_LINESTATE_RTS 0x02 // Ready to Send

#define CDC_LINESTATE_READY (CDC_LINESTATE_RTS | CDC_LINESTATE_DTR)

struct ring_buffer
{
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};

typedef struct
{
uint32_t dwDTERate;
uint8_t bCharFormat;
uint8_t bParityType;
uint8_t bDataBits;
uint8_t lineState;
ring_buffer cdc_rx_buffer = {{0}, 0, 0, false};

typedef struct {
uint32_t dwDTERate;
uint8_t bCharFormat;
uint8_t bParityType;
uint8_t bDataBits;
uint8_t lineState;
} LineInfo;

_Pragma("pack(1)")
static volatile LineInfo _usbLineInfo = {
115200, // dWDTERate
0x00, // bCharFormat
0x00, // bParityType
0x08, // bDataBits
0x00 // lineState
115200, // dWDTERate
0x00, // bCharFormat
0x00, // bParityType
0x08, // bDataBits
0x00 // lineState
};

static const CDCDescriptor _cdcInterface =
{
#if (defined CDC_ENABLED) && defined(HID_ENABLED)
static const CDCDescriptor _cdcInterface = {
#if (defined CDC_ENABLED) && defined(HID_ENABLED)
D_IAD(0, 2, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0),
#endif
// CDC communication interface
D_INTERFACE(CDC_ACM_INTERFACE,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0),
D_CDCCS( CDC_HEADER, CDC_V1_10 & 0xFF, (CDC_V1_10>>8) & 0x0FF ), // Header (1.10 bcd)

D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
D_CDCCS(CDC_UNION,CDC_ACM_INTERFACE,CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0
D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // Device handles call management (not)
D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM),USB_ENDPOINT_TYPE_INTERRUPT,0x10, 0x10),

// CDC data interface
D_INTERFACE(CDC_DATA_INTERFACE,2,CDC_DATA_INTERFACE_CLASS,0,0),
D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,EPX_SIZE,0),
D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,EPX_SIZE,0)
#endif

// CDC communication interface
D_INTERFACE(CDC_ACM_INTERFACE, 1, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0),
D_CDCCS(CDC_HEADER, CDC_V1_10 & 0xFF, (CDC_V1_10>>8) & 0x0FF), // Header (1.10 bcd)

D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT, 6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
D_CDCCS(CDC_UNION, CDC_ACM_INTERFACE, CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0
D_CDCCS(CDC_CALL_MANAGEMENT, 1, 1), // Device handles call management (not)
D_ENDPOINT(USB_ENDPOINT_IN(CDC_ENDPOINT_ACM), USB_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10),

// CDC data interface
D_INTERFACE(CDC_DATA_INTERFACE, 2, CDC_DATA_INTERFACE_CLASS, 0, 0),
D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0),
D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0)
};
_Pragma("pack()")

const void* WEAK CDC_GetInterface(void)
const void* CDC_GetInterface(void)
{
return &_cdcInterface;
return &_cdcInterface;
}

uint32_t WEAK CDC_GetInterfaceLength(void)
uint32_t CDC_GetInterfaceLength(void)
{
return sizeof( _cdcInterface );
return sizeof(_cdcInterface);
}

bool WEAK CDC_Setup(Setup& setup)
bool CDC_Setup(Setup& setup)
{
uint8_t r = setup.bRequest;
uint8_t requestType = setup.bmRequestType;
uint8_t r = setup.bRequest;

if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
{
if (CDC_GET_LINE_CODING == r)
if (r == CDC_GET_LINE_CODING)
{
USBD_SendControl(0,(void*)&_usbLineInfo,7);
USBDevice.sendControl((void*)&_usbLineInfo, 7);
return true;
}
}

if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE)
{
if (CDC_SET_LINE_CODING == r)
if (r == CDC_SET_LINE_CODING)
{
USBD_RecvControl((void*)&_usbLineInfo,7);
USBDevice.recvControl((void*)&_usbLineInfo, 7);
}

if (CDC_SET_CONTROL_LINE_STATE == r)
if (r == CDC_SET_CONTROL_LINE_STATE)
{
_usbLineInfo.lineState = setup.wValueL;
}

if (CDC_SET_LINE_CODING == r || CDC_SET_CONTROL_LINE_STATE == r)
if (r == CDC_SET_LINE_CODING || r == CDC_SET_CONTROL_LINE_STATE)
{
// auto-reset into the bootloader is triggered when the port, already
// open at 1200 bps, is closed. We check DTR state to determine if host
// port is open (bit 0 of lineState).
if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
if (_usbLineInfo.dwDTERate == 1200 && (_usbLineInfo.lineState & 0x01) == 0)
{
initiateReset(250);
}
Expand All @@ -146,12 +135,14 @@ bool WEAK CDC_Setup(Setup& setup)
}

uint32_t _serialPeek = -1;
void Serial_::begin(uint32_t baud_count)
void Serial_::begin(uint32_t /* baud_count */)
{
// uart config is ignored in USB-CDC
}

void Serial_::begin(uint32_t baud_count, uint8_t config)
void Serial_::begin(uint32_t /* baud_count */, uint8_t /* config */)
{
// uart config is ignored in USB-CDC
}

void Serial_::end(void)
Expand All @@ -161,7 +152,7 @@ void Serial_::end(void)
void Serial_::accept(void)
{
uint8_t buffer[CDC_SERIAL_BUFFER_SIZE];
uint32_t len = USBD_Recv(CDC_ENDPOINT_OUT, (void*)&buffer, CDC_SERIAL_BUFFER_SIZE);
uint32_t len = usb.recv(CDC_ENDPOINT_OUT, &buffer, CDC_SERIAL_BUFFER_SIZE);

noInterrupts();
ring_buffer *ringBuffer = &cdc_rx_buffer;
Expand All @@ -172,58 +163,72 @@ void Serial_::accept(void)
// 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;
i = (i + 1) % CDC_SERIAL_BUFFER_SIZE;
while (i != ringBuffer->tail && len>0) {
while (len > 0 && !ringBuffer->full) {
len--;
ringBuffer->buffer[ringBuffer->head] = buffer[k++];
ringBuffer->head = i;
i = (i + 1) % CDC_SERIAL_BUFFER_SIZE;
ringBuffer->buffer[i++] = buffer[k++];
i %= CDC_SERIAL_BUFFER_SIZE;
if (i == ringBuffer->tail)
ringBuffer->full = true;
}
ringBuffer->head = i;
interrupts();
}

int Serial_::available(void)
{
ring_buffer *buffer = &cdc_rx_buffer;
if (buffer->full) {
return CDC_SERIAL_BUFFER_SIZE;
}
if (buffer->head == buffer->tail) {
USB->DEVICE.DeviceEndpoint[2].EPINTENSET.reg = USB_DEVICE_EPINTENCLR_TRCPT(1);
}
return (uint32_t)(CDC_SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % CDC_SERIAL_BUFFER_SIZE;
}

int Serial_::peek(void)
{
ring_buffer *buffer = &cdc_rx_buffer;

if (buffer->head == buffer->tail)
{
if (buffer->head == buffer->tail && !buffer->full) {
return -1;
}
else
{
} else {
return buffer->buffer[buffer->tail];
}
}


// 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;

// if the head isn't ahead of the tail, we don't have any characters
if (buffer->head == buffer->tail)
if (buffer->head == buffer->tail && !buffer->full)
{
if (usb.available(CDC_ENDPOINT_OUT))
accept();
}
if (buffer->head == buffer->tail && !buffer->full)
{
return -1;
}
else
{
unsigned char c = buffer->buffer[buffer->tail];
buffer->tail = (uint32_t)(buffer->tail + 1) % CDC_SERIAL_BUFFER_SIZE;
if (USBD_Available(CDC_ENDPOINT_OUT))
accept();
buffer->full = false;
// if (usb.available(CDC_ENDPOINT_OUT))
// accept();
return c;
}
}

void Serial_::flush(void)
{
USBD_Flush(CDC_ENDPOINT_IN);
usb.flush(CDC_ENDPOINT_IN);
}

size_t Serial_::write(const uint8_t *buffer, size_t size)
Expand All @@ -237,15 +242,13 @@ size_t Serial_::write(const uint8_t *buffer, size_t size)
// TODO - ZE - check behavior on different OSes and test what happens if an
// open connection isn't broken cleanly (cable is yanked out, host dies
// or locks up, or host virtual serial port hangs)
// if (_usbLineInfo.lineState > 0) // Problem with Windows(R)
if (_usbLineInfo.lineState > 0) // Problem with Windows(R)
{
uint32_t r = USBD_Send(CDC_ENDPOINT_IN, buffer, size);
uint32_t r = usb.send(CDC_ENDPOINT_IN, buffer, size);

if (r > 0)
{
if (r == 0) {
return r;
} else
{
} else {
setWriteError();
return 0;
}
Expand Down Expand Up @@ -282,6 +285,6 @@ Serial_::operator bool()
return result;
}

Serial_ SerialUSB;
Serial_ SerialUSB(USBDevice);

#endif
Loading