Skip to content

USB Device improvements and stability fixes #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Oct 12, 2015
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