mirror of
				https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
				synced 2025-10-31 03:14:16 +00:00 
			
		
		
		
	
		
			
	
	
		
			542 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			542 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //                                                                          //
 | ||
|  | //                  ____  ____    ____                                      //
 | ||
|  | //                 / ___||  _ \  / ___|    ___  _ __   _ __                 //
 | ||
|  | //                | |    | | | || |       / __|| '_ \ | '_ \                //
 | ||
|  | //                | |___ | |_| || |___  _| (__ | |_) || |_) |               //
 | ||
|  | //                 \____||____/  \____|(_)\___|| .__/ | .__/                //
 | ||
|  | //                                             |_|    |_|                   //
 | ||
|  | //                                                                          //
 | ||
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | 
 | ||
|  | /* 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. | ||
|  | */ | ||
|  | 
 | ||
|  | // Updated for the XMegaForArduino project by Bob Frazier, S.F.T. Inc.
 | ||
|  | 
 | ||
|  | /////////////////////////////////////////////////////////////////////////////////
 | ||
|  | // XMEGA NOTES:
 | ||
|  | //
 | ||
|  | // a) major re-factoring, including API functions
 | ||
|  | // b) K&R style is hard to read.  I won't use it.  Hard tabs are evil.  Same.
 | ||
|  | //
 | ||
|  | /////////////////////////////////////////////////////////////////////////////////
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #include "Platform.h"
 | ||
|  | #include "USBAPI.h"
 | ||
|  | #include <avr/wdt.h>
 | ||
|  | 
 | ||
|  | #if defined(USBCON)
 | ||
|  | #ifdef CDC_ENABLED
 | ||
|  | 
 | ||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
 | ||
|  | #define PROGMEM_ORIG PROGMEM
 | ||
|  | #else // PROGMEM workaround
 | ||
|  | 
 | ||
|  | // to avoid the bogus "initialized variables" warning
 | ||
|  | #ifdef PROGMEM
 | ||
|  | #undef PROGMEM
 | ||
|  | #endif // PROGMEM re-define
 | ||
|  | 
 | ||
|  | #define PROGMEM __attribute__((section(".progmem.cdc")))
 | ||
|  | #define PROGMEM_ORIG __attribute__((__progmem__))
 | ||
|  | 
 | ||
|  | #endif // check for GNUC >= or < 4.6
 | ||
|  | 
 | ||
|  | 
 | ||
|  | typedef struct | ||
|  | { | ||
|  |   u32  dwDTERate;   // little-endian line rate
 | ||
|  |   u8   bCharFormat; // stop bits = one, one-and-a-half, two  (0, 1, 2 respectively)
 | ||
|  |   u8   bParityType; // none, odd, even, mark, space (0 through 4)
 | ||
|  |   u8   bDataBits;   // char bits 5, 6, 7, 8
 | ||
|  | } __attribute__((aligned(1))) LineInfo; | ||
|  | 
 | ||
|  | static volatile LineInfo _usbLineInfo; // for initialization, see CDC_Reset
 | ||
|  | static u8 _cdcLineState; | ||
|  | static u16 _cdcSerialState; | ||
|  | static uint16_t wInterval; | ||
|  | 
 | ||
|  | #define WEAK __attribute__ ((weak))
 | ||
|  | 
 | ||
|  | extern const DeviceDescriptor _cdcDeviceDescriptor PROGMEM; | ||
|  | extern const IADDescriptor _cdcIADDesc PROGMEM; | ||
|  | extern const CDCDescriptor _cdcInterface PROGMEM; | ||
|  | 
 | ||
|  | 
 | ||
|  | // CDC DEVICE DESCRIPTOR (for CDC device) - sent by CDC_SendDeviceDescriptor()
 | ||
|  | 
 | ||
|  | const DeviceDescriptor _cdcDeviceDescriptor PROGMEM = | ||
|  |   D_DEVICE(USB_DEVICE_CLASS_COMMUNICATIONS,     // device class (COMM)
 | ||
|  |            CDC_COMMUNICATION_INTERFACE_CLASS,   // device sub-class (CDC COMM)
 | ||
|  |            CDC_ABSTRACT_CONTROL_MODEL,          // device protocol (ACM)
 | ||
|  |            64,                                  // packet size (64)
 | ||
|  |            USB_VID,                             // vendor ID for the USB device
 | ||
|  |            USB_PID,                             // product ID for the USB device
 | ||
|  |            0x100,                               // device release version as BCD (1.00)
 | ||
|  |            USB_STRING_INDEX_MANUFACTURER,       // string index for mfg
 | ||
|  |            USB_STRING_INDEX_PRODUCT,            // string index for product name
 | ||
|  |            USB_STRING_INDEX_SERIAL,             // string index for serial number (0 for 'none')
 | ||
|  |            1);                                  // number of configurations (1)
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // IAD descriptor - REQUIRED for composite interfaces, sent via CDC_SendIAD()
 | ||
|  | 
 | ||
|  | const IADDescriptor _cdcIADDesc = D_IAD(0,                                  // first interface
 | ||
|  |                                         2,                                  // count (interfaces, not endpoints)
 | ||
|  |                                         CDC_COMMUNICATION_INTERFACE_CLASS,  // interface class
 | ||
|  |                                         CDC_ABSTRACT_CONTROL_MODEL,         // interface sub-class
 | ||
|  |                                         1);                                 // protocol
 | ||
|  | 
 | ||
|  | // CDC interface descriptor - sent by CDC_SendInterfaceData()
 | ||
|  | 
 | ||
|  | const CDCDescriptor _cdcInterface = // needs to be no more than 55 bytes in length
 | ||
|  | { | ||
|  |   //  FIRST INTERFACE
 | ||
|  |   //  CDC communication interface (endpoint 0)
 | ||
|  |   D_INTERFACE(CDC_ACM_INTERFACE,                            // 'n'
 | ||
|  |               1,                                            // number of endpoints
 | ||
|  |               CDC_COMMUNICATION_INTERFACE_CLASS,            // interface class
 | ||
|  |               CDC_ABSTRACT_CONTROL_MODEL,                   // interface sub-class
 | ||
|  |               0),                                           // protocol
 | ||
|  | 
 | ||
|  |   // these headers describe the supported interfaces
 | ||
|  |   D_CDCCS(CDC_HEADER,0x10,0x01),                            // CDCCS InterfaceDescriptor Header (1.10 bcd) - version 1.10?
 | ||
|  | //  D_CDCCS(CDC_CALL_MANAGEMENT,1,1),                         // Device handles call management (seems to be optional)
 | ||
|  |   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),            // IN endpoint for CDC_ENDPOINT_ACM
 | ||
|  |              USB_ENDPOINT_TYPE_INTERRUPT,                   // INTERRUPT type
 | ||
|  |              0x10,                                          // max packet size 16
 | ||
|  |              0x40),                                         // interval 64 frames i.e. 64 msec (see USB spec table 9-13)
 | ||
|  | 
 | ||
|  |   //  SECOND INTERFACE
 | ||
|  |   //  CDC data interface (endpoints 1, 2)
 | ||
|  |   D_INTERFACE(CDC_DATA_INTERFACE,                           // 'n'
 | ||
|  |               2,                                            // number of endpoints
 | ||
|  |               CDC_DATA_INTERFACE_CLASS,                     // interface class
 | ||
|  |               0,                                            // interface sub-class
 | ||
|  |               0),                                           // protocol
 | ||
|  | 
 | ||
|  |   D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),            // OUT endpoint, index 'CDC_ENDPOINT_OUT'
 | ||
|  |              USB_ENDPOINT_TYPE_BULK,                        // BULK data transfers
 | ||
|  |              0x40,                                          // max packet size 64
 | ||
|  |              1),                                            // interval 1 (was 0)
 | ||
|  | 
 | ||
|  |   D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN),             // IN endpoint, index 'CDC_ENDPOINT_IN'
 | ||
|  |              USB_ENDPOINT_TYPE_BULK,                        // BULK data transfers
 | ||
|  |              0x40,                                          // max packet size 64
 | ||
|  |              0)                                             // interval 0 (apparently not needed)
 | ||
|  | }; | ||
|  | 
 | ||
|  | void WEAK CDC_Reset(void) | ||
|  | { | ||
|  |   _usbLineInfo.dwDTERate = 115200; | ||
|  | 
 | ||
|  |   _usbLineInfo.bCharFormat | ||
|  |      = _usbLineInfo.bParityType | ||
|  |      = _usbLineInfo.bDataBits | ||
|  |      = 0; // says I'm not initialized, basically
 | ||
|  | 
 | ||
|  |   _cdcLineState = 0; | ||
|  |   _cdcSerialState = 0; | ||
|  | 
 | ||
|  |   wInterval = 0; | ||
|  | } | ||
|  | 
 | ||
|  | bool WEAK CDC_SendIAD(void) | ||
|  | { | ||
|  |   return USB_SendControl(TRANSFER_PGM, &_cdcIADDesc, sizeof(_cdcIADDesc)) | ||
|  |          != 0; | ||
|  | } | ||
|  | 
 | ||
|  | int WEAK CDC_GetNumInterfaces(void) | ||
|  | { | ||
|  |   return 2; // always 2
 | ||
|  | } | ||
|  | 
 | ||
|  | int WEAK CDC_GetInterfaceDataLength(void) | ||
|  | { | ||
|  |   return sizeof(_cdcInterface); | ||
|  | } | ||
|  | 
 | ||
|  | int WEAK CDC_SendInterfaceData(void) | ||
|  | { | ||
|  |   return USB_SendControl(TRANSFER_PGM, &_cdcInterface, sizeof(_cdcInterface)); | ||
|  | } | ||
|  | 
 | ||
|  | bool WEAK CDC_SendDeviceDescriptor(void) | ||
|  | { | ||
|  |   return 0 != USB_SendControl(TRANSFER_PGM, &_cdcDeviceDescriptor, sizeof(_cdcDeviceDescriptor)); | ||
|  | } | ||
|  | 
 | ||
|  | bool WEAK CDC_Setup(Setup& setup) | ||
|  | { | ||
|  |   u8 r = setup.bRequest; | ||
|  |   u8 requestType = setup.bmRequestType; | ||
|  | 
 | ||
|  |   if(REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType) | ||
|  |   { | ||
|  |     if (CDC_GET_LINE_CODING == r) | ||
|  |     { | ||
|  |       error_printP(F("Get Line Coding")); | ||
|  | 
 | ||
|  | #if 1
 | ||
|  |       USB_SendControl(0,(void*)&_usbLineInfo, sizeof(_usbLineInfo)/*7*/); | ||
|  | #endif // 0
 | ||
|  | 
 | ||
|  |       return true; | ||
|  |     } | ||
|  |   } | ||
|  |   else if(REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType) | ||
|  |   { | ||
|  |     if(CDC_SET_LINE_CODING == r) | ||
|  |     { | ||
|  |       error_printP_(F("CDC_SET_LINE_CODING")); | ||
|  | 
 | ||
|  |       // setup packet is followed by data?
 | ||
|  |       memcpy((void *)&_usbLineInfo, (char *)&(setup) + sizeof(Setup), sizeof(_usbLineInfo)); | ||
|  | 
 | ||
|  |       error_printP_(F("  rate:")); | ||
|  |       error_printL_(_usbLineInfo.dwDTERate); | ||
|  |       error_printP_(F("  fmt:")); | ||
|  |       error_printL_(_usbLineInfo.bCharFormat); | ||
|  |       error_printP_(F("  par:")); | ||
|  |       error_printL_(_usbLineInfo.bParityType); | ||
|  |       error_printP_(F("  bit:")); | ||
|  |       error_printL(_usbLineInfo.bDataBits); | ||
|  | 
 | ||
|  |       USB_SendControl(0, NULL, 0); // send a ZLP
 | ||
|  | 
 | ||
|  |       _cdcLineState = CONTROL_LINE_STATE_DTR; // for now... assume "this"
 | ||
|  | 
 | ||
|  |       // now set up the ACM interrupt info in '_cdcSerialState' and send it back
 | ||
|  | 
 | ||
|  |       _cdcSerialState = SERIAL_STATE_TX_CARRIER_DSR; // to tell host "I have data" (always)
 | ||
|  | 
 | ||
|  |       return true; | ||
|  |     } | ||
|  |     else if(CDC_SET_CONTROL_LINE_STATE == r) | ||
|  |     { | ||
|  |       error_printP_(F("Set Control Line State: ")); | ||
|  |       error_printL(setup.wValueL); | ||
|  | 
 | ||
|  |       _cdcLineState = setup.wValueL; | ||
|  | 
 | ||
|  |       // NOTE:  this next part is for the 'caterina' CDC bootloader, arduino/bootloaders/caterina/Caterina.c
 | ||
|  |       //        it has some "special" code in it, like using 0x0800 in RAM as an address for a 'key' (7777H)
 | ||
|  |       //        to indicate it was soft-booted.  XMEGA has better ways of handling this, like a CPU flag that
 | ||
|  |       //        indicates "I was soft-booted" as one example, and a 'WDT' timeout flag on top of that.
 | ||
|  | 
 | ||
|  |       // 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 _cdcLineState).
 | ||
|  |         if ((_cdcLineState & 0x01) == 0) | ||
|  |         { | ||
|  | // This section of code is support for the 'caterina' bootloader, which allows USB flashing (apparently)
 | ||
|  | //
 | ||
|  | //          *(uint16_t *)0x0800 = 0x7777; note that on XMEGA this is a VERY bad thing
 | ||
|  | //          wdt_enable(WDTO_120MS);
 | ||
|  | //
 | ||
|  | //          on the atmega, address 800H is the start of the final 256-byte page in RAM space for 2k RAM
 | ||
|  | //
 | ||
|  | //          atmega328(p) RAM goes from 0x100 through 0x8ff - see datasheet for atmega 328 [etc.] section 8.3
 | ||
|  | //          32U4 RAM goes through 0xaff - see datasheet for U4 processors, section 5.2
 | ||
|  | //          8/16/32U2 RAM goes through 4FFH so this won't even work - see datasheet for U2 processors, section 7.2
 | ||
|  | //          basically it's a 'hack' and needs to be re-evaluated
 | ||
|  | 
 | ||
|  |           // TODO:  would it be safe to enable interrupts, NOT return from this function,
 | ||
|  |           //        and simply wait until the appropriate time has elapsed?  Or, as is
 | ||
|  |           //        handled in the section below, this 'wait period' is canceled
 | ||
|  | 
 | ||
|  |           // TODO:  if I use a function that's part of the USB driver to trigger a soft boot, I can detect
 | ||
|  |           //        that a soft boot has taken place using the bits in the 'RESET' status register.  If all
 | ||
|  |           //        I have to do is detect this, it's not a problem, and I won't need "magic memory locations"
 | ||
|  | 
 | ||
|  | //          TODO:  timeout-based reboot
 | ||
|  |         } | ||
|  |         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.
 | ||
|  | 
 | ||
|  | // This section of code is support for the 'caterina' bootloader, which allows USB flashing (apparently)
 | ||
|  | //
 | ||
|  | //          TODO:  reset whatever boot timeout I did
 | ||
|  | //          wdt_disable();
 | ||
|  | //          wdt_reset();
 | ||
|  | //          *(uint16_t *)0x0800 = 0x0; note that on XMEGA this is a VERY bad thing
 | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       USB_SendControl(0, NULL, 0); // send a ZLP
 | ||
|  | 
 | ||
|  |       return true; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   // unrecognized request - report it
 | ||
|  | 
 | ||
|  |   error_printP_(F("CDC request: type=")); | ||
|  |   error_printL_(requestType); | ||
|  |   error_printP_(F(" request=")); | ||
|  |   error_printL(r);   | ||
|  |   return false; | ||
|  | } | ||
|  | 
 | ||
|  | // 'frame received' callback - notification that a 'Start Of Frame' took place
 | ||
|  | 
 | ||
|  | void CDC_FrameReceived(void) | ||
|  | { | ||
|  | bool bSend = false; | ||
|  | 
 | ||
|  |   // NOTE:  if I haven't configured the baud/bits yet, or the DTR bit is cleared,
 | ||
|  |   //        do NOT send anything nor muck with the flags.  Wait until the device
 | ||
|  |   //        is actually RUNNING, first.
 | ||
|  | 
 | ||
|  |   if(!_usbLineInfo.bDataBits || !(_cdcLineState & CONTROL_LINE_STATE_DTR)) | ||
|  |   { | ||
|  |     return; // don't do anything if I haven't properly set up the data bits yet
 | ||
|  |   } | ||
|  | 
 | ||
|  |   if(USB_Available(CDC_RX) >= 64) // allow ~64 buffered bytes
 | ||
|  |   { | ||
|  |     if(_cdcSerialState & SERIAL_STATE_RX_CARRIER_DCD) // was on?
 | ||
|  |     { | ||
|  |       _cdcSerialState &= ~SERIAL_STATE_RX_CARRIER_DCD; | ||
|  | 
 | ||
|  |       bSend = true; | ||
|  |     } | ||
|  |   } | ||
|  |   else | ||
|  |   { | ||
|  |     if(!(_cdcSerialState & SERIAL_STATE_RX_CARRIER_DCD)) // was off?
 | ||
|  |     { | ||
|  |       _cdcSerialState |= SERIAL_STATE_RX_CARRIER_DCD; | ||
|  | 
 | ||
|  |       bSend = true; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  | //  if(USB_SendQLength(CDC_TX) > 0) // anything to send??
 | ||
|  | //  {
 | ||
|  | //    if(!(_cdcSerialState & SERIAL_STATE_TX_CARRIER_DSR))
 | ||
|  | //    {
 | ||
|  | //      _cdcSerialState |= SERIAL_STATE_TX_CARRIER_DSR; // to tell host "I have data"
 | ||
|  | //
 | ||
|  | //      bSend = true;
 | ||
|  | //    }
 | ||
|  | //  }
 | ||
|  | //  else
 | ||
|  | //  {
 | ||
|  | //    if(_cdcSerialState & SERIAL_STATE_TX_CARRIER_DSR)
 | ||
|  | //    {
 | ||
|  | //      _cdcSerialState &= ~SERIAL_STATE_TX_CARRIER_DSR; // to tell host "I have data"
 | ||
|  | //
 | ||
|  | //      bSend = true;
 | ||
|  | //    }
 | ||
|  | //  }
 | ||
|  | 
 | ||
|  |   if((bSend || wInterval >= 64) // will send every 64 'bus cycles' or when there's a change
 | ||
|  |      && !USB_SendQLength(CDC_ACM)) | ||
|  |   { | ||
|  |     CDC_SendACM(); | ||
|  | 
 | ||
|  |     wInterval = 0; | ||
|  |   } | ||
|  |   else if(wInterval < 64) | ||
|  |   { | ||
|  |     wInterval++; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | void CDC_SendACM(void) | ||
|  | { | ||
|  |   USB_Send(CDC_ACM, &_cdcSerialState, sizeof(_cdcSerialState), 1); | ||
|  | } | ||
|  | 
 | ||
|  | void Serial_::begin(unsigned long baud_count) | ||
|  | { | ||
|  |   peek_buffer = -1; | ||
|  | } | ||
|  | 
 | ||
|  | void Serial_::begin(unsigned long baud_count, byte config) | ||
|  | { | ||
|  |   peek_buffer = -1; | ||
|  | } | ||
|  | 
 | ||
|  | void Serial_::end(void) | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | int Serial_::available(void) | ||
|  | { | ||
|  |   if (peek_buffer >= 0) | ||
|  |   { | ||
|  |     return 1 + USB_Available(CDC_RX); | ||
|  |   } | ||
|  | 
 | ||
|  |   return USB_Available(CDC_RX); | ||
|  | } | ||
|  | 
 | ||
|  | int Serial_::peek(void) | ||
|  | { | ||
|  |   if (peek_buffer < 0) | ||
|  |   { | ||
|  |     if(USBDevice.configured()) | ||
|  |     { | ||
|  |       peek_buffer = USB_Recv(CDC_RX); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return peek_buffer; | ||
|  | } | ||
|  | 
 | ||
|  | int Serial_::read(void) | ||
|  | { | ||
|  |   if (peek_buffer >= 0) | ||
|  |   { | ||
|  |     int c = peek_buffer; | ||
|  |     peek_buffer = -1; | ||
|  |     return c; | ||
|  |   } | ||
|  | 
 | ||
|  |   if(USBDevice.configured()) | ||
|  |   { | ||
|  |     return USB_Recv(CDC_RX); | ||
|  |   } | ||
|  | 
 | ||
|  |   return -1; | ||
|  | } | ||
|  | 
 | ||
|  | void Serial_::flush(void) | ||
|  | { | ||
|  |   if(USBDevice.configured()) | ||
|  |   { | ||
|  |     USB_Flush(CDC_TX); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | size_t Serial_::write(uint8_t c) | ||
|  | { | ||
|  |   return write(&c, 1); | ||
|  | } | ||
|  | 
 | ||
|  | size_t Serial_::write(const uint8_t *buffer, size_t size) | ||
|  | { | ||
|  |   /* only try to send bytes if the high-level CDC connection itself
 | ||
|  |    is open (not just the pipe) - the OS should set _cdcLineState when the port | ||
|  |    is opened and clear _cdcLineState 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. */ | ||
|  | 
 | ||
|  |   // NOTE:  if my outgoing buffer is too full, stop sending
 | ||
|  | 
 | ||
|  |   // 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(USBDevice.configured() && // make sure I'm running
 | ||
|  | //     !USB_IsStalled(CDC_TX) && // make sure I'm not stalled
 | ||
|  |      !USB_IsSendQFull(CDC_TX)) // make sure I'm not flooding the queue
 | ||
|  |   {   | ||
|  |     if(_cdcLineState & CONTROL_LINE_STATE_DTR) // make sure DTR is set
 | ||
|  |     { | ||
|  |       if(size > 128) | ||
|  |       { | ||
|  |         size = 128; // adjust size DOWN to limit output buffer size
 | ||
|  |       } | ||
|  | 
 | ||
|  |       int r = USB_Send(CDC_TX, buffer, size, 1); | ||
|  | 
 | ||
|  |       // TODO:  check for partial sends and retry??
 | ||
|  | 
 | ||
|  |       if(r > 0) | ||
|  |       { | ||
|  |         CDC_FrameReceived(); // inform the host of my data send/receive state
 | ||
|  | 
 | ||
|  |         return r; | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   // TODO:  block?
 | ||
|  | 
 | ||
|  |   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.
 | ||
|  | 
 | ||
|  | Serial_::operator bool() | ||
|  | { | ||
|  |   bool result = false; | ||
|  |   if(USBDevice.configured() | ||
|  |      && (_cdcLineState & CONTROL_LINE_STATE_DTR) | ||
|  |      && !USB_IsSendQFull(CDC_TX) | ||
|  | //     && !USB_IsStalled(CDC_TX)
 | ||
|  |      ) | ||
|  |   { | ||
|  |     result = true; | ||
|  |   } | ||
|  | 
 | ||
|  | // We add a short delay before returning to fix a bug observed by Federico
 | ||
|  | // where the port is configured (_cdcLineState != 0) but not quite opened.
 | ||
|  | //  delay(10);
 | ||
|  | 
 | ||
|  |   if(!result) | ||
|  |   { | ||
|  |     if(!USBDevice.configured()) | ||
|  |     { | ||
|  |       error_printP(F("USB device not configured")); | ||
|  |     } | ||
|  |     else if(!(_cdcLineState & CONTROL_LINE_STATE_DTR)) | ||
|  |     { | ||
|  |       error_printP(F("DTR is off")); | ||
|  |     } | ||
|  |     else if(USB_IsSendQFull(CDC_TX)) | ||
|  |     { | ||
|  |       error_printP(F("Send Queue FULL")); | ||
|  |     } | ||
|  | //    else if(USB_IsStalled(CDC_TX))
 | ||
|  | //    {
 | ||
|  | //      error_printP(F("USB is stalled"));
 | ||
|  | //    }
 | ||
|  |   } | ||
|  | 
 | ||
|  |   return result; | ||
|  | } | ||
|  | 
 | ||
|  | Serial_ Serial; | ||
|  | 
 | ||
|  | #endif
 | ||
|  | #endif /* if defined(USBCON) */
 | ||
|  | 
 |