From acb1a47a78ef96a5fcfbae9013dcc7d39e30292e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 15 Jun 2015 21:17:35 +0200 Subject: [PATCH 1/4] Expose serial settings from CDC virtual serial port This allows a sketch to find out the settings chosen by the USB host (computer) and act accordingly. Other than reading the DTR flag and checking if the baudrate is 1200, the regular CDC code doesn't actually use any of these settings. By exposing these settings to the sketch, it can for example copy them to the hardware UART, turning the Leonardo into a proper USB-to-serial device. This can be useful to let the computer directly talk to whatever device is connected to the hardware serial port (like an XBee module). The Teensy core already supported these methods. This code was independently developed, but the method names were chosen to match the Teensy code, for compatibility (except that `dtr()` and `rtr()` return `bool`, while the Teensy version return a `uint8_t`). This change is applied to both the avr and sam cores, which have a very similar CDC implementation. --- cores/arduino/CDC.cpp | 27 +++++++++++++++++++++++++++ cores/arduino/USBAPI.h | 23 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index d694a2d..90a1698 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -18,6 +18,7 @@ #include "USBAPI.h" #include +#include #if defined(USBCON) @@ -205,6 +206,32 @@ Serial_::operator bool() { return result; } +unsigned long Serial_::baud() { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + return _usbLineInfo.dwDTERate; + } +} + +uint8_t Serial_::stopbits() { + return _usbLineInfo.bCharFormat; +} + +uint8_t Serial_::paritytype() { + return _usbLineInfo.bParityType; +} + +uint8_t Serial_::numbits() { + return _usbLineInfo.bDataBits; +} + +bool Serial_::dtr() { + return _usbLineInfo.lineState & 0x1; +} + +bool Serial_::rts() { + return _usbLineInfo.lineState & 0x2; +} + Serial_ Serial; #endif /* if defined(USBCON) */ diff --git a/cores/arduino/USBAPI.h b/cores/arduino/USBAPI.h index 4abd961..ada73b0 100644 --- a/cores/arduino/USBAPI.h +++ b/cores/arduino/USBAPI.h @@ -101,6 +101,29 @@ public: volatile uint8_t _rx_buffer_head; volatile uint8_t _rx_buffer_tail; unsigned char _rx_buffer[SERIAL_BUFFER_SIZE]; + + // These return the settings specified by the USB host for the + // serial port. These aren't really used, but are offered here + // in case a sketch wants to act on these settings. + uint32_t baud(); + uint8_t stopbits(); + uint8_t paritytype(); + uint8_t numbits(); + bool dtr(); + bool rts(); + enum { + ONE_STOP_BIT = 0, + ONE_AND_HALF_STOP_BIT = 1, + TWO_STOP_BITS = 2, + }; + enum { + NO_PARITY = 0, + ODD_PARITY = 1, + EVEN_PARITY = 2, + MARK_PARITY = 3, + SPACE_PARITY = 4, + }; + }; extern Serial_ Serial; From 31e0c941240f2b1d0e4f97d6e65d3a1ad66fbae5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 30 Jul 2015 15:33:37 +0200 Subject: [PATCH 2/4] Add Serial_::readBreak() to process SEND_BREAK requests This allows detecting when the USB host sends a break request and what the value of the request was. See the comments in USBAPI.h for details. This just modifies the avr core, not the sam core. --- cores/arduino/CDC.cpp | 18 ++++++++++++++++++ cores/arduino/USBAPI.h | 17 +++++++++++++++++ cores/arduino/USBCore.h | 1 + 3 files changed, 36 insertions(+) diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 90a1698..5e872c5 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -32,6 +32,7 @@ typedef struct } LineInfo; static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; +static volatile int32_t breakValue = -1; #define WEAK __attribute__ ((weak)) @@ -76,6 +77,11 @@ bool CDC_Setup(USBSetup& setup) if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType) { + if (CDC_SEND_BREAK == r) + { + breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; + } + if (CDC_SET_LINE_CODING == r) { USB_RecvControl((void*)&_usbLineInfo,7); @@ -207,6 +213,7 @@ Serial_::operator bool() { } unsigned long Serial_::baud() { + // Disable interrupts while reading a multi-byte value ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { return _usbLineInfo.dwDTERate; } @@ -232,6 +239,17 @@ bool Serial_::rts() { return _usbLineInfo.lineState & 0x2; } +int32_t Serial_::readBreak() { + int32_t ret; + // Disable IRQs while reading and clearing breakValue to make + // sure we don't overwrite a value just set by the ISR. + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + ret = breakValue; + breakValue = -1; + } + return ret; +} + Serial_ Serial; #endif /* if defined(USBCON) */ diff --git a/cores/arduino/USBAPI.h b/cores/arduino/USBAPI.h index ada73b0..3d355d2 100644 --- a/cores/arduino/USBAPI.h +++ b/cores/arduino/USBAPI.h @@ -102,6 +102,23 @@ public: volatile uint8_t _rx_buffer_tail; unsigned char _rx_buffer[SERIAL_BUFFER_SIZE]; + // 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 + // value, specifying the duration of the break. The value 0 + // means to end any current break, while the value 0xffff means + // to start an indefinite break. + // readBreak() will return the value of the most recent break + // request, but will return it at most once, returning -1 when + // readBreak() is called again (until another break request is + // received, which is again returned once). + // This also mean that if two break requests are received + // without readBreak() being called in between, the value of the + // first request is lost. + // Note that the value returned is a long, so it can return + // 0-0xffff as well as -1. + int32_t readBreak(); + // These return the settings specified by the USB host for the // serial port. These aren't really used, but are offered here // in case a sketch wants to act on these settings. diff --git a/cores/arduino/USBCore.h b/cores/arduino/USBCore.h index 66f8c05..eaeecef 100644 --- a/cores/arduino/USBCore.h +++ b/cores/arduino/USBCore.h @@ -57,6 +57,7 @@ #define CDC_SET_LINE_CODING 0x20 #define CDC_GET_LINE_CODING 0x21 #define CDC_SET_CONTROL_LINE_STATE 0x22 +#define CDC_SEND_BREAK 0x23 #define MSC_RESET 0xFF #define MSC_GET_MAX_LUN 0xFE From 303ebdb0ccbc13dec765f1ce7ca681d73a7e9b90 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 30 Jul 2015 17:54:32 +0200 Subject: [PATCH 3/4] Prevent losing bytes in HardwareSerial::end() end() already waited for the buffer to be empty, but then there could still be two bytes in the hardware registers that still need to be transmitted (which were dropped or kept in the buffer, depending on the exact timing). This changes the wait loop to a call to the flush() function, which already takes care of really waiting for all bytes to be transmitted, meaning it is safe to turn off the transmitter. --- cores/arduino/HardwareSerial.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index a2029a8..5cd89e5 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -138,8 +138,7 @@ void HardwareSerial::begin(unsigned long baud, byte config) void HardwareSerial::end() { // wait for transmission of outgoing data - while (_tx_buffer_head != _tx_buffer_tail) - ; + flush(); cbi(*_ucsrb, RXEN0); cbi(*_ucsrb, TXEN0); From 3baff3b4114f28cd6ae0bfbb25d4efeb6f03278b Mon Sep 17 00:00:00 2001 From: Peter Van Hoyweghen Date: Sat, 4 Jul 2015 08:28:52 +0200 Subject: [PATCH 4/4] Add Serial_::availableForWrite(). This makes the CDC "Serial" object on the Leonardo and similar boards support this recently introduced method as well. The CDC code in the sam core is not changed. --- cores/arduino/CDC.cpp | 5 +++++ cores/arduino/USBAPI.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 5e872c5..afbc65d 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -163,6 +163,11 @@ int Serial_::read(void) return USB_Recv(CDC_RX); } +int Serial_::availableForWrite(void) +{ + return USB_SendSpace(CDC_TX); +} + void Serial_::flush(void) { USB_Flush(CDC_TX); diff --git a/cores/arduino/USBAPI.h b/cores/arduino/USBAPI.h index 3d355d2..5f4947e 100644 --- a/cores/arduino/USBAPI.h +++ b/cores/arduino/USBAPI.h @@ -92,6 +92,7 @@ public: virtual int available(void); virtual int peek(void); virtual int read(void); + int availableForWrite(void); virtual void flush(void); virtual size_t write(uint8_t); virtual size_t write(const uint8_t*, size_t); @@ -188,6 +189,7 @@ int USB_SendControl(uint8_t flags, const void* d, int len); int USB_RecvControl(void* d, int len); uint8_t USB_Available(uint8_t ep); +uint8_t USB_SendSpace(uint8_t ep); int USB_Send(uint8_t ep, const void* data, int len); // blocking int USB_Recv(uint8_t ep, void* data, int len); // non-blocking int USB_Recv(uint8_t ep); // non-blocking