mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-11-04 04:17:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			321 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
		
			9.4 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.  
 | 
						|
*/
 | 
						|
 | 
						|
#include "USBCore.h" // kai:added
 | 
						|
#include "USBAPI.h"
 | 
						|
#include "USBDesc.h" // kai:added
 | 
						|
#include <avr/wdt.h>
 | 
						|
#include <util/atomic.h>
 | 
						|
 | 
						|
#if defined(USBCON)
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	u32	dwDTERate;
 | 
						|
	u8	bCharFormat;
 | 
						|
	u8 	bParityType;
 | 
						|
	u8 	bDataBits;
 | 
						|
	u8	lineState;
 | 
						|
} LineInfo;
 | 
						|
 | 
						|
static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 };
 | 
						|
static volatile int32_t breakValue = -1;
 | 
						|
 | 
						|
static u8 wdtcsr_save;
 | 
						|
 | 
						|
#define WEAK __attribute__ ((weak))
 | 
						|
 | 
						|
extern const CDCDescriptor _cdcInterface PROGMEM;
 | 
						|
const CDCDescriptor _cdcInterface =
 | 
						|
{
 | 
						|
	D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1),
 | 
						|
 | 
						|
	//	CDC communication interface
 | 
						|
	D_INTERFACE(CDC_ACM_INTERFACE1,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_INTERFACE1,CDC_DATA_INTERFACE1),	// Communication interface is master, data interface is slave 0
 | 
						|
	D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM1),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40),
 | 
						|
 | 
						|
	//	CDC data interface
 | 
						|
	D_INTERFACE(CDC_DATA_INTERFACE1,2,CDC_DATA_INTERFACE_CLASS,0,0),
 | 
						|
	D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT1),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0),
 | 
						|
	D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN1 ),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0)
 | 
						|
	
 | 
						|
	, // kai: added
 | 
						|
  D_IAD(2,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1),
 | 
						|
 | 
						|
  //  CDC communication interface
 | 
						|
  D_INTERFACE(CDC_ACM_INTERFACE2,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_INTERFACE2,CDC_DATA_INTERFACE2),  // Communication interface is master, data interface is slave 0
 | 
						|
  D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM2),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40),
 | 
						|
  
 | 
						|
  //  CDC data interface
 | 
						|
  D_INTERFACE(CDC_DATA_INTERFACE2,2,CDC_DATA_INTERFACE_CLASS,0,0),
 | 
						|
  D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT2),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0),
 | 
						|
  D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN2),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0)
 | 
						|
};
 | 
						|
 | 
						|
bool isLUFAbootloader()
 | 
						|
{
 | 
						|
	return pgm_read_word(FLASHEND - 1) == NEW_LUFA_SIGNATURE;
 | 
						|
}
 | 
						|
 | 
						|
int CDC_GetInterface(u8* interfaceNum)
 | 
						|
{
 | 
						|
	interfaceNum[0] += 4;	// kai
 | 
						|
	return USB_SendControl(TRANSFER_PGM,&_cdcInterface,sizeof(_cdcInterface));
 | 
						|
}
 | 
						|
 | 
						|
bool CDC_Setup(USBSetup& setup)
 | 
						|
{
 | 
						|
	u8 r = setup.bRequest;
 | 
						|
	u8 requestType = setup.bmRequestType;
 | 
						|
 | 
						|
	if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
 | 
						|
	{
 | 
						|
		if (CDC_GET_LINE_CODING == r)
 | 
						|
		{
 | 
						|
			USB_SendControl(0,(void*)&_usbLineInfo,7);
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	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);
 | 
						|
		}
 | 
						|
 | 
						|
		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
 | 
						|
 | 
						|
			uint16_t magic_key_pos = MAGIC_KEY_POS;
 | 
						|
 | 
						|
// If we don't use the new RAMEND directly, check manually if we have a newer bootloader.
 | 
						|
// This is used to keep compatible with the old leonardo bootloaders.
 | 
						|
// You are still able to set the magic key position manually to RAMEND-1 to save a few bytes for this check.
 | 
						|
#if MAGIC_KEY_POS != (RAMEND-1)
 | 
						|
			// For future boards save the key in the inproblematic RAMEND
 | 
						|
			// Which is reserved for the main() return value (which will never return)
 | 
						|
			if (isLUFAbootloader()) {
 | 
						|
				// horray, we got a new bootloader!
 | 
						|
				magic_key_pos = (RAMEND-1);
 | 
						|
			}
 | 
						|
#endif
 | 
						|
 | 
						|
			// We check DTR state to determine if host port is open (bit 0 of lineState).
 | 
						|
			if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
 | 
						|
			{
 | 
						|
#if MAGIC_KEY_POS != (RAMEND-1)
 | 
						|
				// Backup ram value if its not a newer bootloader and it hasn't already been saved.
 | 
						|
				// This should avoid memory corruption at least a bit, not fully
 | 
						|
				if (magic_key_pos != (RAMEND-1) && *(uint16_t *)magic_key_pos != MAGIC_KEY) {
 | 
						|
					*(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
 | 
						|
				}
 | 
						|
#endif
 | 
						|
				// Store boot key
 | 
						|
				*(uint16_t *)magic_key_pos = MAGIC_KEY;
 | 
						|
				// Save the watchdog state in case the reset is aborted.
 | 
						|
				wdtcsr_save = WDTCSR;
 | 
						|
				wdt_enable(WDTO_120MS);
 | 
						|
			}
 | 
						|
			else if (*(uint16_t *)magic_key_pos == MAGIC_KEY)
 | 
						|
			{
 | 
						|
				// 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 120ms and eventually
 | 
						|
				// cancel if DTR goes back high.
 | 
						|
				// Cancellation is only done if an auto-reset was started, which is
 | 
						|
				// indicated by the magic key having been set.
 | 
						|
 | 
						|
				wdt_reset();
 | 
						|
				// Restore the watchdog state in case the sketch was using it.
 | 
						|
				WDTCSR |= (1<<WDCE) | (1<<WDE);
 | 
						|
				WDTCSR = wdtcsr_save;
 | 
						|
#if MAGIC_KEY_POS != (RAMEND-1)
 | 
						|
				// Restore backed up (old bootloader) magic key data
 | 
						|
				if (magic_key_pos != (RAMEND-1)) {
 | 
						|
					*(uint16_t *)magic_key_pos = *(uint16_t *)(RAMEND-1);
 | 
						|
				} else
 | 
						|
#endif
 | 
						|
				{
 | 
						|
				// Clean up RAMEND key
 | 
						|
					*(uint16_t *)magic_key_pos = 0x0000;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
		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;
 | 
						|
	}
 | 
						|
	return USB_Recv(CDC_RX);
 | 
						|
}
 | 
						|
 | 
						|
int Serial_::availableForWrite(void)
 | 
						|
{
 | 
						|
	return USB_SendSpace(CDC_TX);
 | 
						|
}
 | 
						|
 | 
						|
void Serial_::flush(void)
 | 
						|
{
 | 
						|
	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 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 > 0)	{
 | 
						|
		int r = USB_Send(CDC_TX,buffer,size);
 | 
						|
		if (r > 0) {
 | 
						|
			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 > 0) 
 | 
						|
		result = true;
 | 
						|
	delay(10);
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
unsigned long Serial_::baud() {
 | 
						|
	// Disable interrupts while reading a multi-byte value
 | 
						|
	uint32_t baudrate;
 | 
						|
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
 | 
						|
		baudrate =  _usbLineInfo.dwDTERate;
 | 
						|
	}
 | 
						|
	return baudrate;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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) */
 |