mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-30 18:07:21 +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) */
 |