| /* Copyright (c) 2011, Peter Barrett |
| ** |
| ** Permission to use, copy, modify, and/or distribute this software for |
| ** any purpose with or without fee is hereby granted, provided that the |
| ** above copyright notice and this permission notice appear in all copies. |
| ** |
| ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR |
| ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES |
| ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| ** SOFTWARE. |
| */ |
| |
| #include "Arduino.h" |
| #include "USBAPI.h" |
| |
| #if defined(USBCON) |
| |
| #ifdef CDC_ENABLED |
| |
| #define CDC_SERIAL_BUFFER_SIZE 64 |
| |
| #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 |
| { |
| uint8_t buffer[CDC_SERIAL_BUFFER_SIZE]; |
| volatile uint32_t head; |
| volatile uint32_t tail; |
| }; |
| |
| 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; |
| } LineInfo; |
| |
| static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; |
| |
| _Pragma("pack(1)") |
| static const CDCDescriptor _cdcInterface = |
| { |
| D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1), |
| |
| // CDC communication interface |
| D_INTERFACE(CDC_ACM_INTERFACE,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0), |
| D_CDCCS(CDC_HEADER,0x10,0x01), // Header (1.10 bcd) |
| D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // Device handles call management (not) |
| 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_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40), |
| |
| // 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,0x40,0), |
| D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,0x40,0) |
| }; |
| _Pragma("pack()") |
| |
| int WEAK CDC_GetInterface(uint8_t* interfaceNum) |
| { |
| interfaceNum[0] += 2; // uses 2 |
| return USBD_SendControl(0,&_cdcInterface,sizeof(_cdcInterface)); |
| } |
| |
| bool WEAK CDC_Setup(Setup& setup) |
| { |
| uint8_t r = setup.bRequest; |
| uint8_t requestType = setup.bmRequestType; |
| |
| if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType) |
| { |
| if (CDC_GET_LINE_CODING == r) |
| { |
| USBD_SendControl(0,(void*)&_usbLineInfo,7); |
| return true; |
| } |
| } |
| |
| if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType) |
| { |
| if (CDC_SET_LINE_CODING == r) |
| { |
| USBD_RecvControl((void*)&_usbLineInfo,7); |
| return true; |
| } |
| |
| if (CDC_SET_CONTROL_LINE_STATE == r) |
| { |
| _usbLineInfo.lineState = setup.wValueL; |
| // auto-reset into the bootloader is triggered when the port, already |
| // open at 1200 bps, is closed. this is the signal to start the watchdog |
| // with a relatively long period so it can finish housekeeping tasks |
| // like servicing endpoints before the sketch ends |
| if (1200 == _usbLineInfo.dwDTERate) |
| { |
| // We check DTR state to determine if host port is open (bit 0 of lineState). |
| if ((_usbLineInfo.lineState & 0x01) == 0) |
| { |
| /* TODO, AVR Stuff |
| *(uint16_t *)0x0800 = 0x7777; |
| wdt_enable(WDTO_120MS); |
| */ |
| } |
| else |
| { |
| // Most OSs do some intermediate steps when configuring ports and DTR can |
| // twiggle more than once before stabilizing. |
| // To avoid spurious resets we set the watchdog to 250ms and eventually |
| // cancel if DTR goes back high. |
| /* TODO, AVR Stuff |
| wdt_disable(); |
| wdt_reset(); |
| *(uint16_t *)0x0800 = 0x0; |
| */ |
| } |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| int _serialPeek = -1; |
| void Serial_::begin(uint32_t baud_count) |
| { |
| } |
| |
| void Serial_::end(void) |
| { |
| } |
| |
| void Serial_::accept(void) |
| { |
| ring_buffer *buffer = &cdc_rx_buffer; |
| uint32_t c = USBD_Recv(CDC_RX); |
| uint32_t i = (uint32_t)(buffer->head+1) % SERIAL_BUFFER_SIZE; |
| |
| // 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. |
| if (i != buffer->tail) { |
| buffer->buffer[buffer->head] = c; |
| buffer->head = i; |
| } |
| } |
| |
| int Serial_::available(void) |
| { |
| ring_buffer *buffer = &cdc_rx_buffer; |
| return (unsigned int)(SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % SERIAL_BUFFER_SIZE; |
| } |
| |
| int Serial_::peek(void) |
| { |
| ring_buffer *buffer = &cdc_rx_buffer; |
| |
| if (buffer->head == buffer->tail) |
| { |
| return -1; |
| } |
| else |
| { |
| return buffer->buffer[buffer->tail]; |
| } |
| } |
| |
| 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) |
| { |
| return -1; |
| } |
| else |
| { |
| unsigned char c = buffer->buffer[buffer->tail]; |
| buffer->tail = (unsigned int)(buffer->tail + 1) % SERIAL_BUFFER_SIZE; |
| return c; |
| } |
| } |
| |
| void Serial_::flush(void) |
| { |
| USBD_Flush(CDC_TX); |
| } |
| |
| size_t Serial_::write(uint8_t c) |
| { |
| /* only try to send bytes if the high-level CDC connection itself |
| is open (not just the pipe) - the OS should set lineState when the port |
| is opened and clear lineState when the port is closed. |
| bytes sent before the user opens the connection or after |
| the connection is closed are lost - just like with a UART. */ |
| |
| // 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 == CDC_LINESTATE_READY) |
| { |
| int r = USBD_Send(CDC_TX,&c,1); |
| |
| if (r > 0) |
| { |
| USBD_Flush(CDC_TX); |
| return r; |
| } else |
| { |
| setWriteError(); |
| return 0; |
| } |
| } |
| setWriteError(); |
| return 0; |
| } |
| |
| // This operator is a convenient way for a sketch to check whether the |
| // port has actually been configured and opened by the host (as opposed |
| // to just being connected to the host). It can be used, for example, in |
| // setup() before printing to ensure that an application on the host is |
| // actually ready to receive and display the data. |
| // We add a short delay before returning to fix a bug observed by Federico |
| // where the port is configured (lineState != 0) but not quite opened. |
| Serial_::operator bool() |
| { |
| bool result = false; |
| |
| if (_usbLineInfo.lineState == CDC_LINESTATE_READY) |
| { |
| result = true; |
| } |
| |
| delay(10); |
| return result; |
| } |
| |
| Serial_ Serial; |
| |
| #endif |
| #endif /* if defined(USBCON) */ |