mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-31 10:27:27 +00:00 
			
		
		
		
	support multi-port CDC devices
This commit is contained in:
		
							parent
							
								
									ce97a3408b
								
							
						
					
					
						commit
						bbed92eafb
					
				
							
								
								
									
										303
									
								
								test/arduino_leonardo_bridge_castrated_cdc/CDC.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								test/arduino_leonardo_bridge_castrated_cdc/CDC.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,303 @@ | |||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* 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 <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_INTERFACE,3,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0), // kai
 | ||||||
|  | 	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), // kai:removed
 | ||||||
|  | 	D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0), | ||||||
|  | 	D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),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] += 1;	// 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) */ | ||||||
							
								
								
									
										8
									
								
								test/arduino_leonardo_bridge_castrated_cdc/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/arduino_leonardo_bridge_castrated_cdc/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | ## castrated CDC test (single interface with 3 endpoints) | ||||||
|  | 
 | ||||||
|  | As mentioned [here](https://arduino.stackexchange.com/a/31695/62145), Arduino functions can be _overwritten_ by copying complete files into the own project.  | ||||||
|  | 
 | ||||||
|  | This is used to create a castrated CDC device with a single interface containing 3 endpoints.  | ||||||
|  | 
 | ||||||
|  | The modifications have been done against Arduino 1.8.10, for changes see comments containing `kai`. | ||||||
|  | 
 | ||||||
							
								
								
									
										301
									
								
								test/arduino_leonardo_bridge_castrated_cdc/USBCore.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								test/arduino_leonardo_bridge_castrated_cdc/USBCore.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | |||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010, 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.   | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #ifndef __USBCORE_H__ | ||||||
|  | #define __USBCORE_H__ | ||||||
|  | 
 | ||||||
|  | #include "USBAPI.h" | ||||||
|  | 
 | ||||||
|  | //	Standard requests
 | ||||||
|  | #define GET_STATUS			0 | ||||||
|  | #define CLEAR_FEATURE		1 | ||||||
|  | #define SET_FEATURE			3 | ||||||
|  | #define SET_ADDRESS			5 | ||||||
|  | #define GET_DESCRIPTOR		6 | ||||||
|  | #define SET_DESCRIPTOR		7 | ||||||
|  | #define GET_CONFIGURATION	8 | ||||||
|  | #define SET_CONFIGURATION	9 | ||||||
|  | #define GET_INTERFACE		10 | ||||||
|  | #define SET_INTERFACE		11 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // bmRequestType
 | ||||||
|  | #define REQUEST_HOSTTODEVICE	0x00 | ||||||
|  | #define REQUEST_DEVICETOHOST	0x80 | ||||||
|  | #define REQUEST_DIRECTION		0x80 | ||||||
|  | 
 | ||||||
|  | #define REQUEST_STANDARD		0x00 | ||||||
|  | #define REQUEST_CLASS			0x20 | ||||||
|  | #define REQUEST_VENDOR			0x40 | ||||||
|  | #define REQUEST_TYPE			0x60 | ||||||
|  | 
 | ||||||
|  | #define REQUEST_DEVICE			0x00 | ||||||
|  | #define REQUEST_INTERFACE		0x01 | ||||||
|  | #define REQUEST_ENDPOINT		0x02 | ||||||
|  | #define REQUEST_OTHER			0x03 | ||||||
|  | #define REQUEST_RECIPIENT		0x03 | ||||||
|  | 
 | ||||||
|  | #define REQUEST_DEVICETOHOST_CLASS_INTERFACE    (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE) | ||||||
|  | #define REQUEST_HOSTTODEVICE_CLASS_INTERFACE    (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE) | ||||||
|  | #define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE) | ||||||
|  | 
 | ||||||
|  | //	Class requests
 | ||||||
|  | 
 | ||||||
|  | #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 | ||||||
|  | 
 | ||||||
|  | //	Descriptors
 | ||||||
|  | 
 | ||||||
|  | #define USB_DEVICE_DESC_SIZE 18 | ||||||
|  | #define USB_CONFIGUARTION_DESC_SIZE 9 | ||||||
|  | #define USB_INTERFACE_DESC_SIZE 9 | ||||||
|  | #define USB_ENDPOINT_DESC_SIZE 7 | ||||||
|  | 
 | ||||||
|  | #define USB_DEVICE_DESCRIPTOR_TYPE             1 | ||||||
|  | #define USB_CONFIGURATION_DESCRIPTOR_TYPE      2 | ||||||
|  | #define USB_STRING_DESCRIPTOR_TYPE             3 | ||||||
|  | #define USB_INTERFACE_DESCRIPTOR_TYPE          4 | ||||||
|  | #define USB_ENDPOINT_DESCRIPTOR_TYPE           5 | ||||||
|  | 
 | ||||||
|  | // usb_20.pdf Table 9.6 Standard Feature Selectors
 | ||||||
|  | #define DEVICE_REMOTE_WAKEUP                   1 | ||||||
|  | #define ENDPOINT_HALT                          2 | ||||||
|  | #define TEST_MODE                              3 | ||||||
|  | 
 | ||||||
|  | // usb_20.pdf Figure 9-4. Information Returned by a GetStatus() Request to a Device
 | ||||||
|  | #define FEATURE_SELFPOWERED_ENABLED     (1 << 0) | ||||||
|  | #define FEATURE_REMOTE_WAKEUP_ENABLED   (1 << 1) | ||||||
|  | 
 | ||||||
|  | #define USB_DEVICE_CLASS_COMMUNICATIONS        0x02 | ||||||
|  | #define USB_DEVICE_CLASS_HUMAN_INTERFACE       0x03 | ||||||
|  | #define USB_DEVICE_CLASS_STORAGE               0x08 | ||||||
|  | #define USB_DEVICE_CLASS_VENDOR_SPECIFIC       0xFF | ||||||
|  | 
 | ||||||
|  | #define USB_CONFIG_POWERED_MASK                0x40 | ||||||
|  | #define USB_CONFIG_BUS_POWERED                 0x80 | ||||||
|  | #define USB_CONFIG_SELF_POWERED                0xC0 | ||||||
|  | #define USB_CONFIG_REMOTE_WAKEUP               0x20 | ||||||
|  | 
 | ||||||
|  | // bMaxPower in Configuration Descriptor
 | ||||||
|  | #define USB_CONFIG_POWER_MA(mA)                ((mA)/2) | ||||||
|  | 
 | ||||||
|  | // bEndpointAddress in Endpoint Descriptor
 | ||||||
|  | #define USB_ENDPOINT_DIRECTION_MASK            0x80 | ||||||
|  | #define USB_ENDPOINT_OUT(addr)                 (lowByte((addr) | 0x00)) | ||||||
|  | #define USB_ENDPOINT_IN(addr)                  (lowByte((addr) | 0x80)) | ||||||
|  | 
 | ||||||
|  | #define USB_ENDPOINT_TYPE_MASK                 0x03 | ||||||
|  | #define USB_ENDPOINT_TYPE_CONTROL              0x00 | ||||||
|  | #define USB_ENDPOINT_TYPE_ISOCHRONOUS          0x01 | ||||||
|  | #define USB_ENDPOINT_TYPE_BULK                 0x02 | ||||||
|  | #define USB_ENDPOINT_TYPE_INTERRUPT            0x03 | ||||||
|  | 
 | ||||||
|  | #define TOBYTES(x) ((x) & 0xFF),(((x) >> 8) & 0xFF) | ||||||
|  | 
 | ||||||
|  | #define CDC_V1_10                               0x0110 | ||||||
|  | #define CDC_COMMUNICATION_INTERFACE_CLASS       0x02 | ||||||
|  | 
 | ||||||
|  | #define CDC_CALL_MANAGEMENT                     0x01 | ||||||
|  | #define CDC_ABSTRACT_CONTROL_MODEL              0x02 | ||||||
|  | #define CDC_HEADER                              0x00 | ||||||
|  | #define CDC_ABSTRACT_CONTROL_MANAGEMENT         0x02 | ||||||
|  | #define CDC_UNION                               0x06 | ||||||
|  | #define CDC_CS_INTERFACE                        0x24 | ||||||
|  | #define CDC_CS_ENDPOINT                         0x25 | ||||||
|  | #define CDC_DATA_INTERFACE_CLASS                0x0A | ||||||
|  | 
 | ||||||
|  | #define MSC_SUBCLASS_SCSI						0x06  | ||||||
|  | #define MSC_PROTOCOL_BULK_ONLY					0x50  | ||||||
|  | 
 | ||||||
|  | #ifndef USB_VERSION | ||||||
|  | #define USB_VERSION 0x200 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | //	Device
 | ||||||
|  | typedef struct { | ||||||
|  | 	u8 len;				// 18
 | ||||||
|  | 	u8 dtype;			// 1 USB_DEVICE_DESCRIPTOR_TYPE
 | ||||||
|  | 	u16 usbVersion;		// 0x200 or 0x210
 | ||||||
|  | 	u8	deviceClass; | ||||||
|  | 	u8	deviceSubClass; | ||||||
|  | 	u8	deviceProtocol; | ||||||
|  | 	u8	packetSize0;	// Packet 0
 | ||||||
|  | 	u16	idVendor; | ||||||
|  | 	u16	idProduct; | ||||||
|  | 	u16	deviceVersion;	// 0x100
 | ||||||
|  | 	u8	iManufacturer; | ||||||
|  | 	u8	iProduct; | ||||||
|  | 	u8	iSerialNumber; | ||||||
|  | 	u8	bNumConfigurations; | ||||||
|  | } DeviceDescriptor; | ||||||
|  | 
 | ||||||
|  | //	Config
 | ||||||
|  | typedef struct { | ||||||
|  | 	u8	len;			// 9
 | ||||||
|  | 	u8	dtype;			// 2
 | ||||||
|  | 	u16 clen;			// total length
 | ||||||
|  | 	u8	numInterfaces; | ||||||
|  | 	u8	config; | ||||||
|  | 	u8	iconfig; | ||||||
|  | 	u8	attributes; | ||||||
|  | 	u8	maxPower; | ||||||
|  | } ConfigDescriptor; | ||||||
|  | 
 | ||||||
|  | //	String
 | ||||||
|  | 
 | ||||||
|  | //	Interface
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 9
 | ||||||
|  | 	u8 dtype;	// 4
 | ||||||
|  | 	u8 number; | ||||||
|  | 	u8 alternate; | ||||||
|  | 	u8 numEndpoints; | ||||||
|  | 	u8 interfaceClass; | ||||||
|  | 	u8 interfaceSubClass; | ||||||
|  | 	u8 protocol; | ||||||
|  | 	u8 iInterface; | ||||||
|  | } InterfaceDescriptor; | ||||||
|  | 
 | ||||||
|  | //	Endpoint
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 7
 | ||||||
|  | 	u8 dtype;	// 5
 | ||||||
|  | 	u8 addr; | ||||||
|  | 	u8 attr; | ||||||
|  | 	u16 packetSize; | ||||||
|  | 	u8 interval; | ||||||
|  | } EndpointDescriptor; | ||||||
|  | 
 | ||||||
|  | // Interface Association Descriptor
 | ||||||
|  | // Used to bind 2 interfaces together in CDC compostite device
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;				// 8
 | ||||||
|  | 	u8 dtype;			// 11
 | ||||||
|  | 	u8 firstInterface; | ||||||
|  | 	u8 interfaceCount; | ||||||
|  | 	u8 functionClass; | ||||||
|  | 	u8 funtionSubClass; | ||||||
|  | 	u8 functionProtocol; | ||||||
|  | 	u8 iInterface; | ||||||
|  | } IADDescriptor; | ||||||
|  | 
 | ||||||
|  | //	CDC CS interface descriptor
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 5
 | ||||||
|  | 	u8 dtype;	// 0x24
 | ||||||
|  | 	u8 subtype; | ||||||
|  | 	u8 d0; | ||||||
|  | 	u8 d1; | ||||||
|  | } CDCCSInterfaceDescriptor; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 4
 | ||||||
|  | 	u8 dtype;	// 0x24
 | ||||||
|  | 	u8 subtype; | ||||||
|  | 	u8 d0; | ||||||
|  | } CDCCSInterfaceDescriptor4; | ||||||
|  | 
 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  |     u8	len; | ||||||
|  |     u8 	dtype;		// 0x24
 | ||||||
|  |     u8 	subtype;	// 1
 | ||||||
|  |     u8 	bmCapabilities; | ||||||
|  |     u8 	bDataInterface; | ||||||
|  | } CMFunctionalDescriptor; | ||||||
|  | 	 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  |     u8	len; | ||||||
|  |     u8 	dtype;		// 0x24
 | ||||||
|  |     u8 	subtype;	// 1
 | ||||||
|  |     u8 	bmCapabilities; | ||||||
|  | } ACMFunctionalDescriptor; | ||||||
|  | 
 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  | 	//	IAD
 | ||||||
|  | 	IADDescriptor				iad;	// Only needed on compound device
 | ||||||
|  | 
 | ||||||
|  | 	//	Control
 | ||||||
|  | 	InterfaceDescriptor			cif;	// 
 | ||||||
|  | 	CDCCSInterfaceDescriptor	header; | ||||||
|  | 	CMFunctionalDescriptor		callManagement;			// Call Management
 | ||||||
|  | 	ACMFunctionalDescriptor		controlManagement;		// ACM
 | ||||||
|  | 	CDCCSInterfaceDescriptor	functionalDescriptor;	// CDC_UNION
 | ||||||
|  | 	EndpointDescriptor			cifin; | ||||||
|  | 
 | ||||||
|  | 	//	Data
 | ||||||
|  | 	//InterfaceDescriptor			dif; // kai:removed
 | ||||||
|  | 	EndpointDescriptor			in; | ||||||
|  | 	EndpointDescriptor			out; | ||||||
|  | } CDCDescriptor; | ||||||
|  | 
 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  | 	InterfaceDescriptor			msc; | ||||||
|  | 	EndpointDescriptor			in; | ||||||
|  | 	EndpointDescriptor			out; | ||||||
|  | } MSCDescriptor; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs) \ | ||||||
|  | 	{ 18, 1, USB_VERSION, _class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs } | ||||||
|  | 
 | ||||||
|  | #define D_CONFIG(_totalLength,_interfaces) \ | ||||||
|  | 	{ 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) } | ||||||
|  | 
 | ||||||
|  | #define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \ | ||||||
|  | 	{ 9, 4, _n, 0, _numEndpoints, _class,_subClass, _protocol, 0 } | ||||||
|  | 
 | ||||||
|  | #define D_ENDPOINT(_addr,_attr,_packetSize, _interval) \ | ||||||
|  | 	{ 7, 5, _addr,_attr,_packetSize, _interval } | ||||||
|  | 
 | ||||||
|  | #define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \ | ||||||
|  | 	{ 8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0 } | ||||||
|  | 
 | ||||||
|  | #define D_CDCCS(_subtype,_d0,_d1)	{ 5, 0x24, _subtype, _d0, _d1 } | ||||||
|  | #define D_CDCCS4(_subtype,_d0)		{ 4, 0x24, _subtype, _d0 } | ||||||
|  | 
 | ||||||
|  | // Bootloader related fields
 | ||||||
|  | // Old Caterina bootloader places the MAGIC key into unsafe RAM locations (it can be rewritten
 | ||||||
|  | // by the running sketch before to actual reboot).
 | ||||||
|  | // Newer bootloaders, recognizable by the LUFA "signature" at the end of the flash, can handle both
 | ||||||
|  | // the usafe and the safe location.
 | ||||||
|  | #ifndef MAGIC_KEY | ||||||
|  | #define MAGIC_KEY 0x7777 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef MAGIC_KEY_POS | ||||||
|  | #define MAGIC_KEY_POS 0x0800 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef NEW_LUFA_SIGNATURE | ||||||
|  | #define NEW_LUFA_SIGNATURE 0xDCFB | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,64 @@ | |||||||
|  | /*
 | ||||||
|  |   bridge USB-serial to hardware-serial | ||||||
|  | 
 | ||||||
|  |   for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro) | ||||||
|  |   hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB | ||||||
|  | 
 | ||||||
|  |   see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino 
 | ||||||
|  |   -> CDC.cpp|HardwareSerial.cpp for serial implementation details | ||||||
|  | 
 | ||||||
|  |   this sketch is mainly for demonstration / test of CDC communication | ||||||
|  |   performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | uint32_t baud = 9600; | ||||||
|  | uint8_t databits = 8; | ||||||
|  | uint8_t stopbits = 1; | ||||||
|  | uint8_t parity = 0; | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  |   Serial.begin(baud); // USB
 | ||||||
|  |   Serial1.begin(baud, SERIAL_8N1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  |   // show USB connected state
 | ||||||
|  |   if (Serial) TXLED1; | ||||||
|  |   else        TXLED0; | ||||||
|  | 
 | ||||||
|  |   // configure hardware serial
 | ||||||
|  |   if (Serial.baud() != baud || | ||||||
|  |       Serial.numbits() != databits || | ||||||
|  |       Serial.stopbits() != stopbits || | ||||||
|  |       Serial.paritytype() != parity) { | ||||||
|  |     baud = Serial.baud(); | ||||||
|  |     databits = Serial.numbits(); | ||||||
|  |     stopbits = Serial.stopbits(); | ||||||
|  |     parity = Serial.paritytype(); | ||||||
|  |     uint8_t config = 0; // ucsrc register
 | ||||||
|  |     switch (databits) { | ||||||
|  |       case 5: break; | ||||||
|  |       case 6: config |= 2; break; | ||||||
|  |       case 7: config |= 4; break; | ||||||
|  |       case 8: config |= 6; break; | ||||||
|  |       default: config |= 6; | ||||||
|  |     } | ||||||
|  |     switch (stopbits) { | ||||||
|  |       case 2: config |= 8; | ||||||
|  |         // 1.5 stopbits not supported
 | ||||||
|  |     } | ||||||
|  |     switch (parity) { | ||||||
|  |       case 1: config |= 0x30; break; // odd
 | ||||||
|  |       case 2: config |= 0x20; break; // even
 | ||||||
|  |         // mark, space not supported
 | ||||||
|  |     } | ||||||
|  |     Serial1.end(); | ||||||
|  |     Serial1.begin(baud, config); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // bridge
 | ||||||
|  |   if (Serial.available() > 0) | ||||||
|  |     Serial1.write(Serial.read()); | ||||||
|  |   if (Serial1.available() > 0) | ||||||
|  |     Serial.write(Serial1.read()); | ||||||
|  | } | ||||||
							
								
								
									
										320
									
								
								test/arduino_leonardo_bridge_multi_cdc/CDC.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								test/arduino_leonardo_bridge_multi_cdc/CDC.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,320 @@ | |||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* 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) */ | ||||||
							
								
								
									
										8
									
								
								test/arduino_leonardo_bridge_multi_cdc/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/arduino_leonardo_bridge_multi_cdc/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | ## multiple CDC interface test | ||||||
|  | 
 | ||||||
|  | As mentioned [here](https://arduino.stackexchange.com/a/31695/62145),  Arduino functions can be _overwritten_ by copying complete files into the own project.  | ||||||
|  | 
 | ||||||
|  | This is used to create a device with 2 CDC interfaces. For simplicity only one of both is functional (configurable in USBDesc.h) | ||||||
|  | 
 | ||||||
|  | The modifications have been done against Arduino 1.8.10, for changes see comments containing `kai`. | ||||||
|  | 
 | ||||||
							
								
								
									
										870
									
								
								test/arduino_leonardo_bridge_multi_cdc/USBCore.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										870
									
								
								test/arduino_leonardo_bridge_multi_cdc/USBCore.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,870 @@ | |||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Copyright (c) 2010, Peter Barrett
 | ||||||
|  | ** Sleep/Wakeup support added by Michael Dreher | ||||||
|  | **   | ||||||
|  | ** 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 "USBAPI.h" | ||||||
|  | #include "USBDesc.h" // kai:added
 | ||||||
|  | #include "PluggableUSB.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #if defined(USBCON) | ||||||
|  | 
 | ||||||
|  | /** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */ | ||||||
|  | #define TX_RX_LED_PULSE_MS 100 | ||||||
|  | volatile u8 TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */ | ||||||
|  | volatile u8 RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */ | ||||||
|  | 
 | ||||||
|  | //==================================================================
 | ||||||
|  | //==================================================================
 | ||||||
|  | 
 | ||||||
|  | extern const u16 STRING_LANGUAGE[] PROGMEM; | ||||||
|  | extern const u8 STRING_PRODUCT[] PROGMEM; | ||||||
|  | extern const u8 STRING_MANUFACTURER[] PROGMEM; | ||||||
|  | extern const DeviceDescriptor USB_DeviceDescriptorIAD PROGMEM; | ||||||
|  | 
 | ||||||
|  | const u16 STRING_LANGUAGE[2] = { | ||||||
|  | 	(3<<8) | (2+2), | ||||||
|  | 	0x0409	// English
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #ifndef USB_PRODUCT | ||||||
|  | // If no product is provided, use USB IO Board
 | ||||||
|  | #define USB_PRODUCT     "USB IO Board" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | const u8 STRING_PRODUCT[] PROGMEM = USB_PRODUCT; | ||||||
|  | 
 | ||||||
|  | #if USB_VID == 0x2341 | ||||||
|  | #  if defined(USB_MANUFACTURER) | ||||||
|  | #    undef USB_MANUFACTURER | ||||||
|  | #  endif | ||||||
|  | #  define USB_MANUFACTURER "Arduino LLC" | ||||||
|  | #elif USB_VID == 0x1b4f | ||||||
|  | #  if defined(USB_MANUFACTURER) | ||||||
|  | #    undef USB_MANUFACTURER | ||||||
|  | #  endif | ||||||
|  | #  define USB_MANUFACTURER "SparkFun" | ||||||
|  | #elif !defined(USB_MANUFACTURER) | ||||||
|  | // Fall through to unknown if no manufacturer name was provided in a macro
 | ||||||
|  | #  define USB_MANUFACTURER "Unknown" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | const u8 STRING_MANUFACTURER[] PROGMEM = USB_MANUFACTURER; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define DEVICE_CLASS 0x02 | ||||||
|  | 
 | ||||||
|  | //	DEVICE DESCRIPTOR
 | ||||||
|  | const DeviceDescriptor USB_DeviceDescriptorIAD = | ||||||
|  | 	D_DEVICE(0xEF,0x02,0x01,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,ISERIAL,1); | ||||||
|  | 
 | ||||||
|  | //==================================================================
 | ||||||
|  | //==================================================================
 | ||||||
|  | 
 | ||||||
|  | volatile u8 _usbConfiguration = 0; | ||||||
|  | volatile u8 _usbCurrentStatus = 0; // meaning of bits see usb_20.pdf, Figure 9-4. Information Returned by a GetStatus() Request to a Device
 | ||||||
|  | volatile u8 _usbSuspendState = 0; // copy of UDINT to check SUSPI and WAKEUPI bits
 | ||||||
|  | 
 | ||||||
|  | static inline void WaitIN(void) | ||||||
|  | { | ||||||
|  | 	while (!(UEINTX & (1<<TXINI))) | ||||||
|  | 		; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void ClearIN(void) | ||||||
|  | { | ||||||
|  | 	UEINTX = ~(1<<TXINI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void WaitOUT(void) | ||||||
|  | { | ||||||
|  | 	while (!(UEINTX & (1<<RXOUTI))) | ||||||
|  | 		; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 WaitForINOrOUT() | ||||||
|  | { | ||||||
|  | 	while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI)))) | ||||||
|  | 		; | ||||||
|  | 	return (UEINTX & (1<<RXOUTI)) == 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void ClearOUT(void) | ||||||
|  | { | ||||||
|  | 	UEINTX = ~(1<<RXOUTI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void Recv(volatile u8* data, u8 count) | ||||||
|  | { | ||||||
|  | 	while (count--) | ||||||
|  | 		*data++ = UEDATX; | ||||||
|  | 	 | ||||||
|  | 	RXLED1;					// light the RX LED
 | ||||||
|  | 	RxLEDPulse = TX_RX_LED_PULSE_MS;	 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 Recv8() | ||||||
|  | { | ||||||
|  | 	RXLED1;					// light the RX LED
 | ||||||
|  | 	RxLEDPulse = TX_RX_LED_PULSE_MS; | ||||||
|  | 
 | ||||||
|  | 	return UEDATX;	 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void Send8(u8 d) | ||||||
|  | { | ||||||
|  | 	UEDATX = d; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void SetEP(u8 ep) | ||||||
|  | { | ||||||
|  | 	UENUM = ep; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 FifoByteCount() | ||||||
|  | { | ||||||
|  | 	return UEBCLX; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 ReceivedSetupInt() | ||||||
|  | { | ||||||
|  | 	return UEINTX & (1<<RXSTPI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void ClearSetupInt() | ||||||
|  | { | ||||||
|  | 	UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void Stall() | ||||||
|  | { | ||||||
|  | 	UECONX = (1<<STALLRQ) | (1<<EPEN); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 ReadWriteAllowed() | ||||||
|  | { | ||||||
|  | 	return UEINTX & (1<<RWAL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 Stalled() | ||||||
|  | { | ||||||
|  | 	return UEINTX & (1<<STALLEDI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 FifoFree() | ||||||
|  | { | ||||||
|  | 	return UEINTX & (1<<FIFOCON); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void ReleaseRX() | ||||||
|  | { | ||||||
|  | 	UEINTX = 0x6B;	// FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void ReleaseTX() | ||||||
|  | { | ||||||
|  | 	UEINTX = 0x3A;	// FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 FrameNumber() | ||||||
|  | { | ||||||
|  | 	return UDFNUML; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //==================================================================
 | ||||||
|  | //==================================================================
 | ||||||
|  | 
 | ||||||
|  | u8 USBGetConfiguration(void) | ||||||
|  | { | ||||||
|  | 	return _usbConfiguration; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define USB_RECV_TIMEOUT | ||||||
|  | class LockEP | ||||||
|  | { | ||||||
|  | 	u8 _sreg; | ||||||
|  | public: | ||||||
|  | 	LockEP(u8 ep) : _sreg(SREG) | ||||||
|  | 	{ | ||||||
|  | 		cli(); | ||||||
|  | 		SetEP(ep & 7); | ||||||
|  | 	} | ||||||
|  | 	~LockEP() | ||||||
|  | 	{ | ||||||
|  | 		SREG = _sreg; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | //	Number of bytes, assumes a rx endpoint
 | ||||||
|  | u8 USB_Available(u8 ep) | ||||||
|  | { | ||||||
|  | 	LockEP lock(ep); | ||||||
|  | 	return FifoByteCount(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Non Blocking receive
 | ||||||
|  | //	Return number of bytes read
 | ||||||
|  | int USB_Recv(u8 ep, void* d, int len) | ||||||
|  | { | ||||||
|  | 	if (!_usbConfiguration || len < 0) | ||||||
|  | 		return -1; | ||||||
|  | 	 | ||||||
|  | 	LockEP lock(ep); | ||||||
|  | 	u8 n = FifoByteCount(); | ||||||
|  | 	len = min(n,len); | ||||||
|  | 	n = len; | ||||||
|  | 	u8* dst = (u8*)d; | ||||||
|  | 	while (n--) | ||||||
|  | 		*dst++ = Recv8(); | ||||||
|  | 	if (len && !FifoByteCount())	// release empty buffer
 | ||||||
|  | 		ReleaseRX(); | ||||||
|  | 	 | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Recv 1 byte if ready
 | ||||||
|  | int USB_Recv(u8 ep) | ||||||
|  | { | ||||||
|  | 	u8 c; | ||||||
|  | 	if (USB_Recv(ep,&c,1) != 1) | ||||||
|  | 		return -1; | ||||||
|  | 	return c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Space in send EP
 | ||||||
|  | u8 USB_SendSpace(u8 ep) | ||||||
|  | { | ||||||
|  | 	LockEP lock(ep); | ||||||
|  | 	if (!ReadWriteAllowed()) | ||||||
|  | 		return 0; | ||||||
|  | 	return USB_EP_SIZE - FifoByteCount(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Blocking Send of data to an endpoint
 | ||||||
|  | int USB_Send(u8 ep, const void* d, int len) | ||||||
|  | { | ||||||
|  | 	if (!_usbConfiguration) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (_usbSuspendState & (1<<SUSPI)) { | ||||||
|  | 		//send a remote wakeup
 | ||||||
|  | 		UDCON |= (1 << RMWKUP); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int r = len; | ||||||
|  | 	const u8* data = (const u8*)d; | ||||||
|  | 	u8 timeout = 250;		// 250ms timeout on send? TODO
 | ||||||
|  | 	bool sendZlp = false; | ||||||
|  | 
 | ||||||
|  | 	while (len || sendZlp) | ||||||
|  | 	{ | ||||||
|  | 		u8 n = USB_SendSpace(ep); | ||||||
|  | 		if (n == 0) | ||||||
|  | 		{ | ||||||
|  | 			if (!(--timeout)) | ||||||
|  | 				return -1; | ||||||
|  | 			delay(1); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (n > len) { | ||||||
|  | 			n = len; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		{ | ||||||
|  | 			LockEP lock(ep); | ||||||
|  | 			// Frame may have been released by the SOF interrupt handler
 | ||||||
|  | 			if (!ReadWriteAllowed()) | ||||||
|  | 				continue; | ||||||
|  | 
 | ||||||
|  | 			len -= n; | ||||||
|  | 			if (ep & TRANSFER_ZERO) | ||||||
|  | 			{ | ||||||
|  | 				while (n--) | ||||||
|  | 					Send8(0); | ||||||
|  | 			} | ||||||
|  | 			else if (ep & TRANSFER_PGM) | ||||||
|  | 			{ | ||||||
|  | 				while (n--) | ||||||
|  | 					Send8(pgm_read_byte(data++)); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				while (n--) | ||||||
|  | 					Send8(*data++); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (sendZlp) { | ||||||
|  | 				ReleaseTX(); | ||||||
|  | 				sendZlp = false; | ||||||
|  | 			} else if (!ReadWriteAllowed()) { // ...release if buffer is full...
 | ||||||
|  | 				ReleaseTX(); | ||||||
|  | 				if (len == 0) sendZlp = true; | ||||||
|  | 			} else if ((len == 0) && (ep & TRANSFER_RELEASE)) { // ...or if forced with TRANSFER_RELEASE
 | ||||||
|  | 				// XXX: TRANSFER_RELEASE is never used can be removed?
 | ||||||
|  | 				ReleaseTX(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	TXLED1;					// light the TX LED
 | ||||||
|  | 	TxLEDPulse = TX_RX_LED_PULSE_MS; | ||||||
|  | 	return r; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u8 _initEndpoints[USB_ENDPOINTS] = | ||||||
|  | { | ||||||
|  | 	0,                      // Control Endpoint
 | ||||||
|  | 	 | ||||||
|  |   EP_TYPE_INTERRUPT_IN,   // CDC_ENDPOINT_ACM
 | ||||||
|  |   EP_TYPE_BULK_OUT,       // CDC_ENDPOINT_OUT
 | ||||||
|  |   EP_TYPE_BULK_IN,        // CDC_ENDPOINT_IN
 | ||||||
|  | 
 | ||||||
|  |   // kai:added
 | ||||||
|  |   EP_TYPE_INTERRUPT_IN,   // CDC_ENDPOINT_ACM
 | ||||||
|  |   EP_TYPE_BULK_OUT,       // CDC_ENDPOINT_OUT
 | ||||||
|  |   EP_TYPE_BULK_IN,        // CDC_ENDPOINT_IN
 | ||||||
|  | 
 | ||||||
|  | 	// Following endpoints are automatically initialized to 0
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define EP_SINGLE_64 0x32	// EP0
 | ||||||
|  | #define EP_DOUBLE_64 0x36	// Other endpoints
 | ||||||
|  | #define EP_SINGLE_16 0x12 | ||||||
|  | 
 | ||||||
|  | static | ||||||
|  | void InitEP(u8 index, u8 type, u8 size) | ||||||
|  | { | ||||||
|  | 	UENUM = index; | ||||||
|  | 	UECONX = (1<<EPEN); | ||||||
|  | 	UECFG0X = type; | ||||||
|  | 	UECFG1X = size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static | ||||||
|  | void InitEndpoints() | ||||||
|  | { | ||||||
|  | 	for (u8 i = 1; i < sizeof(_initEndpoints) && _initEndpoints[i] != 0; i++) | ||||||
|  | 	{ | ||||||
|  | 		UENUM = i; | ||||||
|  | 		UECONX = (1<<EPEN); | ||||||
|  | 		UECFG0X = _initEndpoints[i]; | ||||||
|  | #if USB_EP_SIZE == 16 | ||||||
|  | 		UECFG1X = EP_SINGLE_16; | ||||||
|  | #elif USB_EP_SIZE == 64 | ||||||
|  | 		UECFG1X = EP_DOUBLE_64; | ||||||
|  | #else | ||||||
|  | #error Unsupported value for USB_EP_SIZE | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 	UERST = 0x7E;	// And reset them
 | ||||||
|  | 	UERST = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Handle CLASS_INTERFACE requests
 | ||||||
|  | static | ||||||
|  | bool ClassInterfaceRequest(USBSetup& setup) | ||||||
|  | { | ||||||
|  | 	u8 i = setup.wIndex; | ||||||
|  | 
 | ||||||
|  |   if (CDC_CLASS_INTERFACE == i) // kai
 | ||||||
|  |     return CDC_Setup(setup); | ||||||
|  | 
 | ||||||
|  | #ifdef PLUGGABLE_USB_ENABLED | ||||||
|  | 	return PluggableUSB().setup(setup); | ||||||
|  | #endif | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int _cmark; | ||||||
|  | static int _cend; | ||||||
|  | void InitControl(int end) | ||||||
|  | { | ||||||
|  | 	SetEP(0); | ||||||
|  | 	_cmark = 0; | ||||||
|  | 	_cend = end; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static | ||||||
|  | bool SendControl(u8 d) | ||||||
|  | { | ||||||
|  | 	if (_cmark < _cend) | ||||||
|  | 	{ | ||||||
|  | 		if (!WaitForINOrOUT()) | ||||||
|  | 			return false; | ||||||
|  | 		Send8(d); | ||||||
|  | 		if (!((_cmark + 1) & 0x3F)) | ||||||
|  | 			ClearIN();	// Fifo is full, release this packet
 | ||||||
|  | 	} | ||||||
|  | 	_cmark++; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Clipped by _cmark/_cend
 | ||||||
|  | int USB_SendControl(u8 flags, const void* d, int len) | ||||||
|  | { | ||||||
|  | 	int sent = len; | ||||||
|  | 	const u8* data = (const u8*)d; | ||||||
|  | 	bool pgm = flags & TRANSFER_PGM; | ||||||
|  | 	while (len--) | ||||||
|  | 	{ | ||||||
|  | 		u8 c = pgm ? pgm_read_byte(data++) : *data++; | ||||||
|  | 		if (!SendControl(c)) | ||||||
|  | 			return -1; | ||||||
|  | 	} | ||||||
|  | 	return sent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Send a USB descriptor string. The string is stored in PROGMEM as a
 | ||||||
|  | // plain ASCII string but is sent out as UTF-16 with the correct 2-byte
 | ||||||
|  | // prefix
 | ||||||
|  | static bool USB_SendStringDescriptor(const u8*string_P, u8 string_len, uint8_t flags) { | ||||||
|  |         SendControl(2 + string_len * 2); | ||||||
|  |         SendControl(3); | ||||||
|  |         bool pgm = flags & TRANSFER_PGM; | ||||||
|  |         for(u8 i = 0; i < string_len; i++) { | ||||||
|  |                 bool r = SendControl(pgm ? pgm_read_byte(&string_P[i]) : string_P[i]); | ||||||
|  |                 r &= SendControl(0); // high byte
 | ||||||
|  |                 if(!r) { | ||||||
|  |                         return false; | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Does not timeout or cross fifo boundaries
 | ||||||
|  | int USB_RecvControl(void* d, int len) | ||||||
|  | { | ||||||
|  | 	auto length = len; | ||||||
|  | 	while(length) | ||||||
|  | 	{ | ||||||
|  | 		// Dont receive more than the USB Control EP has to offer
 | ||||||
|  | 		// Use fixed 64 because control EP always have 64 bytes even on 16u2.
 | ||||||
|  | 		auto recvLength = length; | ||||||
|  | 		if(recvLength > 64){ | ||||||
|  | 			recvLength = 64; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Write data to fit to the end (not the beginning) of the array
 | ||||||
|  | 		WaitOUT(); | ||||||
|  | 		Recv((u8*)d + len - length, recvLength); | ||||||
|  | 		ClearOUT(); | ||||||
|  | 		length -= recvLength; | ||||||
|  | 	} | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u8 SendInterfaces() | ||||||
|  | { | ||||||
|  | 	u8 interfaces = 0; | ||||||
|  | 
 | ||||||
|  | 	CDC_GetInterface(&interfaces); | ||||||
|  | 
 | ||||||
|  | #ifdef PLUGGABLE_USB_ENABLED | ||||||
|  | 	PluggableUSB().getInterface(&interfaces); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return interfaces; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Construct a dynamic configuration descriptor
 | ||||||
|  | //	This really needs dynamic endpoint allocation etc
 | ||||||
|  | //	TODO
 | ||||||
|  | static | ||||||
|  | bool SendConfiguration(int maxlen) | ||||||
|  | { | ||||||
|  | 	//	Count and measure interfaces
 | ||||||
|  | 	InitControl(0); | ||||||
|  | 	u8 interfaces = SendInterfaces(); | ||||||
|  | 	ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces); | ||||||
|  | 
 | ||||||
|  | 	//	Now send them
 | ||||||
|  | 	InitControl(maxlen); | ||||||
|  | 	USB_SendControl(0,&config,sizeof(ConfigDescriptor)); | ||||||
|  | 	SendInterfaces(); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static | ||||||
|  | bool SendDescriptor(USBSetup& setup) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	u8 t = setup.wValueH; | ||||||
|  | 	if (USB_CONFIGURATION_DESCRIPTOR_TYPE == t) | ||||||
|  | 		return SendConfiguration(setup.wLength); | ||||||
|  | 
 | ||||||
|  | 	InitControl(setup.wLength); | ||||||
|  | #ifdef PLUGGABLE_USB_ENABLED | ||||||
|  | 	ret = PluggableUSB().getDescriptor(setup); | ||||||
|  | 	if (ret != 0) { | ||||||
|  | 		return (ret > 0 ? true : false); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	const u8* desc_addr = 0; | ||||||
|  | 	if (USB_DEVICE_DESCRIPTOR_TYPE == t) | ||||||
|  | 	{ | ||||||
|  | 		desc_addr = (const u8*)&USB_DeviceDescriptorIAD; | ||||||
|  | 	} | ||||||
|  | 	else if (USB_STRING_DESCRIPTOR_TYPE == t) | ||||||
|  | 	{ | ||||||
|  | 		if (setup.wValueL == 0) { | ||||||
|  | 			desc_addr = (const u8*)&STRING_LANGUAGE; | ||||||
|  | 		} | ||||||
|  | 		else if (setup.wValueL == IPRODUCT) { | ||||||
|  | 			return USB_SendStringDescriptor(STRING_PRODUCT, strlen(USB_PRODUCT), TRANSFER_PGM); | ||||||
|  | 		} | ||||||
|  | 		else if (setup.wValueL == IMANUFACTURER) { | ||||||
|  | 			return USB_SendStringDescriptor(STRING_MANUFACTURER, strlen(USB_MANUFACTURER), TRANSFER_PGM); | ||||||
|  | 		} | ||||||
|  | 		else if (setup.wValueL == ISERIAL) { | ||||||
|  | #ifdef PLUGGABLE_USB_ENABLED | ||||||
|  | 			char name[ISERIAL_MAX_LEN]; | ||||||
|  | 			PluggableUSB().getShortName(name); | ||||||
|  | 			return USB_SendStringDescriptor((uint8_t*)name, strlen(name), 0); | ||||||
|  | #endif | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (desc_addr == 0) | ||||||
|  | 		return false; | ||||||
|  | 	u8 desc_length = pgm_read_byte(desc_addr); | ||||||
|  | 
 | ||||||
|  | 	USB_SendControl(TRANSFER_PGM,desc_addr,desc_length); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Endpoint 0 interrupt
 | ||||||
|  | ISR(USB_COM_vect) | ||||||
|  | { | ||||||
|  |     SetEP(0); | ||||||
|  | 	if (!ReceivedSetupInt()) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	USBSetup setup; | ||||||
|  | 	Recv((u8*)&setup,8); | ||||||
|  | 	ClearSetupInt(); | ||||||
|  | 
 | ||||||
|  | 	u8 requestType = setup.bmRequestType; | ||||||
|  | 	if (requestType & REQUEST_DEVICETOHOST) | ||||||
|  | 		WaitIN(); | ||||||
|  | 	else | ||||||
|  | 		ClearIN(); | ||||||
|  | 
 | ||||||
|  |     bool ok = true; | ||||||
|  | 	if (REQUEST_STANDARD == (requestType & REQUEST_TYPE)) | ||||||
|  | 	{ | ||||||
|  | 		//	Standard Requests
 | ||||||
|  | 		u8 r = setup.bRequest; | ||||||
|  | 		u16 wValue = setup.wValueL | (setup.wValueH << 8); | ||||||
|  | 		if (GET_STATUS == r) | ||||||
|  | 		{ | ||||||
|  | 			if (requestType == (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_DEVICE)) | ||||||
|  | 			{ | ||||||
|  | 				Send8(_usbCurrentStatus); | ||||||
|  | 				Send8(0); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				// TODO: handle the HALT state of an endpoint here
 | ||||||
|  | 				// see "Figure 9-6. Information Returned by a GetStatus() Request to an Endpoint" in usb_20.pdf for more information
 | ||||||
|  | 				Send8(0); | ||||||
|  | 				Send8(0); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else if (CLEAR_FEATURE == r) | ||||||
|  | 		{ | ||||||
|  | 			if((requestType == (REQUEST_HOSTTODEVICE | REQUEST_STANDARD | REQUEST_DEVICE)) | ||||||
|  | 				&& (wValue == DEVICE_REMOTE_WAKEUP)) | ||||||
|  | 			{ | ||||||
|  | 				_usbCurrentStatus &= ~FEATURE_REMOTE_WAKEUP_ENABLED; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else if (SET_FEATURE == r) | ||||||
|  | 		{ | ||||||
|  | 			if((requestType == (REQUEST_HOSTTODEVICE | REQUEST_STANDARD | REQUEST_DEVICE)) | ||||||
|  | 				&& (wValue == DEVICE_REMOTE_WAKEUP)) | ||||||
|  | 			{ | ||||||
|  | 				_usbCurrentStatus |= FEATURE_REMOTE_WAKEUP_ENABLED; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else if (SET_ADDRESS == r) | ||||||
|  | 		{ | ||||||
|  | 			WaitIN(); | ||||||
|  | 			UDADDR = setup.wValueL | (1<<ADDEN); | ||||||
|  | 		} | ||||||
|  | 		else if (GET_DESCRIPTOR == r) | ||||||
|  | 		{ | ||||||
|  | 			ok = SendDescriptor(setup); | ||||||
|  | 		} | ||||||
|  | 		else if (SET_DESCRIPTOR == r) | ||||||
|  | 		{ | ||||||
|  | 			ok = false; | ||||||
|  | 		} | ||||||
|  | 		else if (GET_CONFIGURATION == r) | ||||||
|  | 		{ | ||||||
|  | 			Send8(1); | ||||||
|  | 		} | ||||||
|  | 		else if (SET_CONFIGURATION == r) | ||||||
|  | 		{ | ||||||
|  | 			if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT)) | ||||||
|  | 			{ | ||||||
|  | 				InitEndpoints(); | ||||||
|  | 				_usbConfiguration = setup.wValueL; | ||||||
|  | 			} else | ||||||
|  | 				ok = false; | ||||||
|  | 		} | ||||||
|  | 		else if (GET_INTERFACE == r) | ||||||
|  | 		{ | ||||||
|  | 		} | ||||||
|  | 		else if (SET_INTERFACE == r) | ||||||
|  | 		{ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		InitControl(setup.wLength);		//	Max length of transfer
 | ||||||
|  | 		ok = ClassInterfaceRequest(setup); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ok) | ||||||
|  | 		ClearIN(); | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		Stall(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void USB_Flush(u8 ep) | ||||||
|  | { | ||||||
|  | 	SetEP(ep); | ||||||
|  | 	if (FifoByteCount()) | ||||||
|  | 		ReleaseTX(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void USB_ClockDisable() | ||||||
|  | { | ||||||
|  | #if defined(OTGPADE) | ||||||
|  | 	USBCON = (USBCON & ~(1<<OTGPADE)) | (1<<FRZCLK); // freeze clock and disable VBUS Pad
 | ||||||
|  | #else // u2 Series
 | ||||||
|  | 	USBCON = (1 << FRZCLK); // freeze clock
 | ||||||
|  | #endif | ||||||
|  | 	PLLCSR &= ~(1<<PLLE);  // stop PLL
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void USB_ClockEnable() | ||||||
|  | { | ||||||
|  | #if defined(UHWCON) | ||||||
|  | 	UHWCON |= (1<<UVREGE);			// power internal reg
 | ||||||
|  | #endif | ||||||
|  | 	USBCON = (1<<USBE) | (1<<FRZCLK);	// clock frozen, usb enabled
 | ||||||
|  | 
 | ||||||
|  | // ATmega32U4
 | ||||||
|  | #if defined(PINDIV) | ||||||
|  | #if F_CPU == 16000000UL | ||||||
|  | 	PLLCSR |= (1<<PINDIV);                   // Need 16 MHz xtal
 | ||||||
|  | #elif F_CPU == 8000000UL | ||||||
|  | 	PLLCSR &= ~(1<<PINDIV);                  // Need  8 MHz xtal
 | ||||||
|  | #else | ||||||
|  | #error "Clock rate of F_CPU not supported" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #elif defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__) | ||||||
|  | 	// for the u2 Series the datasheet is confusing. On page 40 its called PINDIV and on page 290 its called PLLP0
 | ||||||
|  | #if F_CPU == 16000000UL | ||||||
|  | 	// Need 16 MHz xtal
 | ||||||
|  | 	PLLCSR |= (1 << PLLP0); | ||||||
|  | #elif F_CPU == 8000000UL | ||||||
|  | 	// Need 8 MHz xtal
 | ||||||
|  | 	PLLCSR &= ~(1 << PLLP0); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // AT90USB646, AT90USB647, AT90USB1286, AT90USB1287
 | ||||||
|  | #elif defined(PLLP2) | ||||||
|  | #if F_CPU == 16000000UL | ||||||
|  | #if defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) | ||||||
|  | 	// For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x.
 | ||||||
|  | 	PLLCSR = (PLLCSR & ~(1<<PLLP1)) | ((1<<PLLP2) | (1<<PLLP0)); // Need 16 MHz xtal
 | ||||||
|  | #elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) | ||||||
|  | 	// For AT90USB64x only. Do not use with AT90USB128x.
 | ||||||
|  | 	PLLCSR = (PLLCSR & ~(1<<PLLP0)) | ((1<<PLLP2) | (1<<PLLP1)); // Need 16 MHz xtal
 | ||||||
|  | #else | ||||||
|  | #error "USB Chip not supported, please defined method of USB PLL initialization" | ||||||
|  | #endif | ||||||
|  | #elif F_CPU == 8000000UL | ||||||
|  | 	// for Atmel AT90USB128x and AT90USB64x
 | ||||||
|  | 	PLLCSR = (PLLCSR & ~(1<<PLLP2)) | ((1<<PLLP1) | (1<<PLLP0)); // Need 8 MHz xtal
 | ||||||
|  | #else | ||||||
|  | #error "Clock rate of F_CPU not supported" | ||||||
|  | #endif | ||||||
|  | #else | ||||||
|  | #error "USB Chip not supported, please defined method of USB PLL initialization" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	PLLCSR |= (1<<PLLE); | ||||||
|  | 	while (!(PLLCSR & (1<<PLOCK)))		// wait for lock pll
 | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Some tests on specific versions of macosx (10.7.3), reported some
 | ||||||
|  | 	// strange behaviors when the board is reset using the serial
 | ||||||
|  | 	// port touch at 1200 bps. This delay fixes this behavior.
 | ||||||
|  | 	delay(1); | ||||||
|  | #if defined(OTGPADE) | ||||||
|  | 	USBCON = (USBCON & ~(1<<FRZCLK)) | (1<<OTGPADE);	// start USB clock, enable VBUS Pad
 | ||||||
|  | #else | ||||||
|  | 	USBCON &= ~(1 << FRZCLK);	// start USB clock
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(RSTCPU) | ||||||
|  | #if defined(LSM) | ||||||
|  | 	UDCON &= ~((1<<RSTCPU) | (1<<LSM) | (1<<RMWKUP) | (1<<DETACH));	// enable attach resistor, set full speed mode
 | ||||||
|  | #else // u2 Series
 | ||||||
|  | 	UDCON &= ~((1 << RSTCPU) | (1 << RMWKUP) | (1 << DETACH));	// enable attach resistor, set full speed mode
 | ||||||
|  | #endif | ||||||
|  | #else | ||||||
|  | 	// AT90USB64x and AT90USB128x don't have RSTCPU
 | ||||||
|  | 	UDCON &= ~((1<<LSM) | (1<<RMWKUP) | (1<<DETACH));	// enable attach resistor, set full speed mode
 | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	General interrupt
 | ||||||
|  | ISR(USB_GEN_vect) | ||||||
|  | { | ||||||
|  | 	u8 udint = UDINT; | ||||||
|  | 	UDINT &= ~((1<<EORSTI) | (1<<SOFI)); // clear the IRQ flags for the IRQs which are handled here, except WAKEUPI and SUSPI (see below)
 | ||||||
|  | 
 | ||||||
|  | 	//	End of Reset
 | ||||||
|  | 	if (udint & (1<<EORSTI)) | ||||||
|  | 	{ | ||||||
|  | 		InitEP(0,EP_TYPE_CONTROL,EP_SINGLE_64);	// init ep0
 | ||||||
|  | 		_usbConfiguration = 0;			// not configured yet
 | ||||||
|  | 		UEIENX = 1 << RXSTPE;			// Enable interrupts for ep0
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	//	Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too
 | ||||||
|  | 	if (udint & (1<<SOFI)) | ||||||
|  | 	{ | ||||||
|  | 		USB_Flush(CDC_TX);				// Send a tx frame if found
 | ||||||
|  | 		 | ||||||
|  | 		// check whether the one-shot period has elapsed.  if so, turn off the LED
 | ||||||
|  | 		if (TxLEDPulse && !(--TxLEDPulse)) | ||||||
|  | 			TXLED0; | ||||||
|  | 		if (RxLEDPulse && !(--RxLEDPulse)) | ||||||
|  | 			RXLED0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// the WAKEUPI interrupt is triggered as soon as there are non-idle patterns on the data
 | ||||||
|  | 	// lines. Thus, the WAKEUPI interrupt can occur even if the controller is not in the "suspend" mode.
 | ||||||
|  | 	// Therefore the we enable it only when USB is suspended
 | ||||||
|  | 	if (udint & (1<<WAKEUPI)) | ||||||
|  | 	{ | ||||||
|  | 		UDIEN = (UDIEN & ~(1<<WAKEUPE)) | (1<<SUSPE); // Disable interrupts for WAKEUP and enable interrupts for SUSPEND
 | ||||||
|  | 
 | ||||||
|  | 		//TODO
 | ||||||
|  | 		// WAKEUPI shall be cleared by software (USB clock inputs must be enabled before).
 | ||||||
|  | 		//USB_ClockEnable();
 | ||||||
|  | 		UDINT &= ~(1<<WAKEUPI); | ||||||
|  | 		_usbSuspendState = (_usbSuspendState & ~(1<<SUSPI)) | (1<<WAKEUPI); | ||||||
|  | 	} | ||||||
|  | 	else if (udint & (1<<SUSPI)) // only one of the WAKEUPI / SUSPI bits can be active at time
 | ||||||
|  | 	{ | ||||||
|  | 		UDIEN = (UDIEN & ~(1<<SUSPE)) | (1<<WAKEUPE); // Disable interrupts for SUSPEND and enable interrupts for WAKEUP
 | ||||||
|  | 
 | ||||||
|  | 		//TODO
 | ||||||
|  | 		//USB_ClockDisable();
 | ||||||
|  | 
 | ||||||
|  | 		UDINT &= ~((1<<WAKEUPI) | (1<<SUSPI)); // clear any already pending WAKEUP IRQs and the SUSPI request
 | ||||||
|  | 		_usbSuspendState = (_usbSuspendState & ~(1<<WAKEUPI)) | (1<<SUSPI); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	VBUS or counting frames
 | ||||||
|  | //	Any frame counting?
 | ||||||
|  | u8 USBConnected() | ||||||
|  | { | ||||||
|  | 	u8 f = UDFNUML; | ||||||
|  | 	delay(3); | ||||||
|  | 	return f != UDFNUML; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //=======================================================================
 | ||||||
|  | //=======================================================================
 | ||||||
|  | 
 | ||||||
|  | USBDevice_ USBDevice; | ||||||
|  | 
 | ||||||
|  | USBDevice_::USBDevice_() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void USBDevice_::attach() | ||||||
|  | { | ||||||
|  | 	_usbConfiguration = 0; | ||||||
|  | 	_usbCurrentStatus = 0; | ||||||
|  | 	_usbSuspendState = 0; | ||||||
|  | 	USB_ClockEnable(); | ||||||
|  | 
 | ||||||
|  | 	UDINT &= ~((1<<WAKEUPI) | (1<<SUSPI)); // clear already pending WAKEUP / SUSPEND requests
 | ||||||
|  | 	UDIEN = (1<<EORSTE) | (1<<SOFE) | (1<<SUSPE);	// Enable interrupts for EOR (End of Reset), SOF (start of frame) and SUSPEND
 | ||||||
|  | 	 | ||||||
|  | 	TX_RX_LED_INIT; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void USBDevice_::detach() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //	Check for interrupts
 | ||||||
|  | //	TODO: VBUS detection
 | ||||||
|  | bool USBDevice_::configured() | ||||||
|  | { | ||||||
|  | 	return _usbConfiguration; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void USBDevice_::poll() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool USBDevice_::wakeupHost() | ||||||
|  | { | ||||||
|  | 	// clear any previous wakeup request which might have been set but could be processed at that time
 | ||||||
|  | 	// e.g. because the host was not suspended at that time
 | ||||||
|  | 	UDCON &= ~(1 << RMWKUP); | ||||||
|  | 
 | ||||||
|  | 	if(!(UDCON & (1 << RMWKUP)) | ||||||
|  | 	  && (_usbSuspendState & (1<<SUSPI)) | ||||||
|  | 	  && (_usbCurrentStatus & FEATURE_REMOTE_WAKEUP_ENABLED)) | ||||||
|  | 	{ | ||||||
|  | 		// This short version will only work, when the device has not been suspended. Currently the
 | ||||||
|  | 		// Arduino core doesn't handle SUSPEND at all, so this is ok.
 | ||||||
|  | 		USB_ClockEnable(); | ||||||
|  | 		UDCON |= (1 << RMWKUP); // send the wakeup request
 | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool USBDevice_::isSuspended() | ||||||
|  | { | ||||||
|  | 	return (_usbSuspendState & (1 << SUSPI)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* if defined(USBCON) */ | ||||||
							
								
								
									
										317
									
								
								test/arduino_leonardo_bridge_multi_cdc/USBCore.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								test/arduino_leonardo_bridge_multi_cdc/USBCore.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,317 @@ | |||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010, 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.   | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #ifndef __USBCORE_H__ | ||||||
|  | #define __USBCORE_H__ | ||||||
|  | 
 | ||||||
|  | #include "USBAPI.h" | ||||||
|  | 
 | ||||||
|  | //	Standard requests
 | ||||||
|  | #define GET_STATUS			0 | ||||||
|  | #define CLEAR_FEATURE		1 | ||||||
|  | #define SET_FEATURE			3 | ||||||
|  | #define SET_ADDRESS			5 | ||||||
|  | #define GET_DESCRIPTOR		6 | ||||||
|  | #define SET_DESCRIPTOR		7 | ||||||
|  | #define GET_CONFIGURATION	8 | ||||||
|  | #define SET_CONFIGURATION	9 | ||||||
|  | #define GET_INTERFACE		10 | ||||||
|  | #define SET_INTERFACE		11 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // bmRequestType
 | ||||||
|  | #define REQUEST_HOSTTODEVICE	0x00 | ||||||
|  | #define REQUEST_DEVICETOHOST	0x80 | ||||||
|  | #define REQUEST_DIRECTION		0x80 | ||||||
|  | 
 | ||||||
|  | #define REQUEST_STANDARD		0x00 | ||||||
|  | #define REQUEST_CLASS			0x20 | ||||||
|  | #define REQUEST_VENDOR			0x40 | ||||||
|  | #define REQUEST_TYPE			0x60 | ||||||
|  | 
 | ||||||
|  | #define REQUEST_DEVICE			0x00 | ||||||
|  | #define REQUEST_INTERFACE		0x01 | ||||||
|  | #define REQUEST_ENDPOINT		0x02 | ||||||
|  | #define REQUEST_OTHER			0x03 | ||||||
|  | #define REQUEST_RECIPIENT		0x03 | ||||||
|  | 
 | ||||||
|  | #define REQUEST_DEVICETOHOST_CLASS_INTERFACE    (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE) | ||||||
|  | #define REQUEST_HOSTTODEVICE_CLASS_INTERFACE    (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE) | ||||||
|  | #define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE) | ||||||
|  | 
 | ||||||
|  | //	Class requests
 | ||||||
|  | 
 | ||||||
|  | #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 | ||||||
|  | 
 | ||||||
|  | //	Descriptors
 | ||||||
|  | 
 | ||||||
|  | #define USB_DEVICE_DESC_SIZE 18 | ||||||
|  | #define USB_CONFIGUARTION_DESC_SIZE 9 | ||||||
|  | #define USB_INTERFACE_DESC_SIZE 9 | ||||||
|  | #define USB_ENDPOINT_DESC_SIZE 7 | ||||||
|  | 
 | ||||||
|  | #define USB_DEVICE_DESCRIPTOR_TYPE             1 | ||||||
|  | #define USB_CONFIGURATION_DESCRIPTOR_TYPE      2 | ||||||
|  | #define USB_STRING_DESCRIPTOR_TYPE             3 | ||||||
|  | #define USB_INTERFACE_DESCRIPTOR_TYPE          4 | ||||||
|  | #define USB_ENDPOINT_DESCRIPTOR_TYPE           5 | ||||||
|  | 
 | ||||||
|  | // usb_20.pdf Table 9.6 Standard Feature Selectors
 | ||||||
|  | #define DEVICE_REMOTE_WAKEUP                   1 | ||||||
|  | #define ENDPOINT_HALT                          2 | ||||||
|  | #define TEST_MODE                              3 | ||||||
|  | 
 | ||||||
|  | // usb_20.pdf Figure 9-4. Information Returned by a GetStatus() Request to a Device
 | ||||||
|  | #define FEATURE_SELFPOWERED_ENABLED     (1 << 0) | ||||||
|  | #define FEATURE_REMOTE_WAKEUP_ENABLED   (1 << 1) | ||||||
|  | 
 | ||||||
|  | #define USB_DEVICE_CLASS_COMMUNICATIONS        0x02 | ||||||
|  | #define USB_DEVICE_CLASS_HUMAN_INTERFACE       0x03 | ||||||
|  | #define USB_DEVICE_CLASS_STORAGE               0x08 | ||||||
|  | #define USB_DEVICE_CLASS_VENDOR_SPECIFIC       0xFF | ||||||
|  | 
 | ||||||
|  | #define USB_CONFIG_POWERED_MASK                0x40 | ||||||
|  | #define USB_CONFIG_BUS_POWERED                 0x80 | ||||||
|  | #define USB_CONFIG_SELF_POWERED                0xC0 | ||||||
|  | #define USB_CONFIG_REMOTE_WAKEUP               0x20 | ||||||
|  | 
 | ||||||
|  | // bMaxPower in Configuration Descriptor
 | ||||||
|  | #define USB_CONFIG_POWER_MA(mA)                ((mA)/2) | ||||||
|  | 
 | ||||||
|  | // bEndpointAddress in Endpoint Descriptor
 | ||||||
|  | #define USB_ENDPOINT_DIRECTION_MASK            0x80 | ||||||
|  | #define USB_ENDPOINT_OUT(addr)                 (lowByte((addr) | 0x00)) | ||||||
|  | #define USB_ENDPOINT_IN(addr)                  (lowByte((addr) | 0x80)) | ||||||
|  | 
 | ||||||
|  | #define USB_ENDPOINT_TYPE_MASK                 0x03 | ||||||
|  | #define USB_ENDPOINT_TYPE_CONTROL              0x00 | ||||||
|  | #define USB_ENDPOINT_TYPE_ISOCHRONOUS          0x01 | ||||||
|  | #define USB_ENDPOINT_TYPE_BULK                 0x02 | ||||||
|  | #define USB_ENDPOINT_TYPE_INTERRUPT            0x03 | ||||||
|  | 
 | ||||||
|  | #define TOBYTES(x) ((x) & 0xFF),(((x) >> 8) & 0xFF) | ||||||
|  | 
 | ||||||
|  | #define CDC_V1_10                               0x0110 | ||||||
|  | #define CDC_COMMUNICATION_INTERFACE_CLASS       0x02 | ||||||
|  | 
 | ||||||
|  | #define CDC_CALL_MANAGEMENT                     0x01 | ||||||
|  | #define CDC_ABSTRACT_CONTROL_MODEL              0x02 | ||||||
|  | #define CDC_HEADER                              0x00 | ||||||
|  | #define CDC_ABSTRACT_CONTROL_MANAGEMENT         0x02 | ||||||
|  | #define CDC_UNION                               0x06 | ||||||
|  | #define CDC_CS_INTERFACE                        0x24 | ||||||
|  | #define CDC_CS_ENDPOINT                         0x25 | ||||||
|  | #define CDC_DATA_INTERFACE_CLASS                0x0A | ||||||
|  | 
 | ||||||
|  | #define MSC_SUBCLASS_SCSI						0x06  | ||||||
|  | #define MSC_PROTOCOL_BULK_ONLY					0x50  | ||||||
|  | 
 | ||||||
|  | #ifndef USB_VERSION | ||||||
|  | #define USB_VERSION 0x200 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | //	Device
 | ||||||
|  | typedef struct { | ||||||
|  | 	u8 len;				// 18
 | ||||||
|  | 	u8 dtype;			// 1 USB_DEVICE_DESCRIPTOR_TYPE
 | ||||||
|  | 	u16 usbVersion;		// 0x200 or 0x210
 | ||||||
|  | 	u8	deviceClass; | ||||||
|  | 	u8	deviceSubClass; | ||||||
|  | 	u8	deviceProtocol; | ||||||
|  | 	u8	packetSize0;	// Packet 0
 | ||||||
|  | 	u16	idVendor; | ||||||
|  | 	u16	idProduct; | ||||||
|  | 	u16	deviceVersion;	// 0x100
 | ||||||
|  | 	u8	iManufacturer; | ||||||
|  | 	u8	iProduct; | ||||||
|  | 	u8	iSerialNumber; | ||||||
|  | 	u8	bNumConfigurations; | ||||||
|  | } DeviceDescriptor; | ||||||
|  | 
 | ||||||
|  | //	Config
 | ||||||
|  | typedef struct { | ||||||
|  | 	u8	len;			// 9
 | ||||||
|  | 	u8	dtype;			// 2
 | ||||||
|  | 	u16 clen;			// total length
 | ||||||
|  | 	u8	numInterfaces; | ||||||
|  | 	u8	config; | ||||||
|  | 	u8	iconfig; | ||||||
|  | 	u8	attributes; | ||||||
|  | 	u8	maxPower; | ||||||
|  | } ConfigDescriptor; | ||||||
|  | 
 | ||||||
|  | //	String
 | ||||||
|  | 
 | ||||||
|  | //	Interface
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 9
 | ||||||
|  | 	u8 dtype;	// 4
 | ||||||
|  | 	u8 number; | ||||||
|  | 	u8 alternate; | ||||||
|  | 	u8 numEndpoints; | ||||||
|  | 	u8 interfaceClass; | ||||||
|  | 	u8 interfaceSubClass; | ||||||
|  | 	u8 protocol; | ||||||
|  | 	u8 iInterface; | ||||||
|  | } InterfaceDescriptor; | ||||||
|  | 
 | ||||||
|  | //	Endpoint
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 7
 | ||||||
|  | 	u8 dtype;	// 5
 | ||||||
|  | 	u8 addr; | ||||||
|  | 	u8 attr; | ||||||
|  | 	u16 packetSize; | ||||||
|  | 	u8 interval; | ||||||
|  | } EndpointDescriptor; | ||||||
|  | 
 | ||||||
|  | // Interface Association Descriptor
 | ||||||
|  | // Used to bind 2 interfaces together in CDC compostite device
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;				// 8
 | ||||||
|  | 	u8 dtype;			// 11
 | ||||||
|  | 	u8 firstInterface; | ||||||
|  | 	u8 interfaceCount; | ||||||
|  | 	u8 functionClass; | ||||||
|  | 	u8 funtionSubClass; | ||||||
|  | 	u8 functionProtocol; | ||||||
|  | 	u8 iInterface; | ||||||
|  | } IADDescriptor; | ||||||
|  | 
 | ||||||
|  | //	CDC CS interface descriptor
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 5
 | ||||||
|  | 	u8 dtype;	// 0x24
 | ||||||
|  | 	u8 subtype; | ||||||
|  | 	u8 d0; | ||||||
|  | 	u8 d1; | ||||||
|  | } CDCCSInterfaceDescriptor; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	u8 len;		// 4
 | ||||||
|  | 	u8 dtype;	// 0x24
 | ||||||
|  | 	u8 subtype; | ||||||
|  | 	u8 d0; | ||||||
|  | } CDCCSInterfaceDescriptor4; | ||||||
|  | 
 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  |     u8	len; | ||||||
|  |     u8 	dtype;		// 0x24
 | ||||||
|  |     u8 	subtype;	// 1
 | ||||||
|  |     u8 	bmCapabilities; | ||||||
|  |     u8 	bDataInterface; | ||||||
|  | } CMFunctionalDescriptor; | ||||||
|  | 	 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  |     u8	len; | ||||||
|  |     u8 	dtype;		// 0x24
 | ||||||
|  |     u8 	subtype;	// 1
 | ||||||
|  |     u8 	bmCapabilities; | ||||||
|  | } ACMFunctionalDescriptor; | ||||||
|  | 
 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  | 	//	IAD
 | ||||||
|  | 	IADDescriptor				iad;	// Only needed on compound device
 | ||||||
|  | 
 | ||||||
|  | 	//	Control
 | ||||||
|  | 	InterfaceDescriptor			cif;	// 
 | ||||||
|  | 	CDCCSInterfaceDescriptor	header; | ||||||
|  | 	CMFunctionalDescriptor		callManagement;			// Call Management
 | ||||||
|  | 	ACMFunctionalDescriptor		controlManagement;		// ACM
 | ||||||
|  | 	CDCCSInterfaceDescriptor	functionalDescriptor;	// CDC_UNION
 | ||||||
|  | 	EndpointDescriptor			cifin; | ||||||
|  | 
 | ||||||
|  | 	//	Data
 | ||||||
|  | 	InterfaceDescriptor			dif; | ||||||
|  | 	EndpointDescriptor			in; | ||||||
|  | 	EndpointDescriptor			out; | ||||||
|  | 
 | ||||||
|  |   // kai:added
 | ||||||
|  |   IADDescriptor       iad2;  // Only needed on compound device
 | ||||||
|  | 
 | ||||||
|  |   //  Control
 | ||||||
|  |   InterfaceDescriptor     cif2;  // 
 | ||||||
|  |   CDCCSInterfaceDescriptor  header2; | ||||||
|  |   CMFunctionalDescriptor    callManagement2;     // Call Management
 | ||||||
|  |   ACMFunctionalDescriptor   controlManagement2;    // ACM
 | ||||||
|  |   CDCCSInterfaceDescriptor  functionalDescriptor2; // CDC_UNION
 | ||||||
|  |   EndpointDescriptor      cifin2; | ||||||
|  | 
 | ||||||
|  |   //  Data
 | ||||||
|  |   InterfaceDescriptor     dif2; | ||||||
|  |   EndpointDescriptor      in2; | ||||||
|  |   EndpointDescriptor      out2; | ||||||
|  | } CDCDescriptor; | ||||||
|  | 
 | ||||||
|  | typedef struct  | ||||||
|  | { | ||||||
|  | 	InterfaceDescriptor			msc; | ||||||
|  | 	EndpointDescriptor			in; | ||||||
|  | 	EndpointDescriptor			out; | ||||||
|  | } MSCDescriptor; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs) \ | ||||||
|  | 	{ 18, 1, USB_VERSION, _class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs } | ||||||
|  | 
 | ||||||
|  | #define D_CONFIG(_totalLength,_interfaces) \ | ||||||
|  | 	{ 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) } | ||||||
|  | 
 | ||||||
|  | #define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \ | ||||||
|  | 	{ 9, 4, _n, 0, _numEndpoints, _class,_subClass, _protocol, 0 } | ||||||
|  | 
 | ||||||
|  | #define D_ENDPOINT(_addr,_attr,_packetSize, _interval) \ | ||||||
|  | 	{ 7, 5, _addr,_attr,_packetSize, _interval } | ||||||
|  | 
 | ||||||
|  | #define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \ | ||||||
|  | 	{ 8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0 } | ||||||
|  | 
 | ||||||
|  | #define D_CDCCS(_subtype,_d0,_d1)	{ 5, 0x24, _subtype, _d0, _d1 } | ||||||
|  | #define D_CDCCS4(_subtype,_d0)		{ 4, 0x24, _subtype, _d0 } | ||||||
|  | 
 | ||||||
|  | // Bootloader related fields
 | ||||||
|  | // Old Caterina bootloader places the MAGIC key into unsafe RAM locations (it can be rewritten
 | ||||||
|  | // by the running sketch before to actual reboot).
 | ||||||
|  | // Newer bootloaders, recognizable by the LUFA "signature" at the end of the flash, can handle both
 | ||||||
|  | // the usafe and the safe location.
 | ||||||
|  | #ifndef MAGIC_KEY | ||||||
|  | #define MAGIC_KEY 0x7777 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef MAGIC_KEY_POS | ||||||
|  | #define MAGIC_KEY_POS 0x0800 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef NEW_LUFA_SIGNATURE | ||||||
|  | #define NEW_LUFA_SIGNATURE 0xDCFB | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										77
									
								
								test/arduino_leonardo_bridge_multi_cdc/USBDesc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								test/arduino_leonardo_bridge_multi_cdc/USBDesc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | /*
 | ||||||
|  |    Copyright (c) 2011, Peter Barrett | ||||||
|  |    Copyright (c) 2015, Arduino LLC | ||||||
|  | 
 | ||||||
|  |    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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #define PLUGGABLE_USB_ENABLED | ||||||
|  | 
 | ||||||
|  | #if defined(EPRST6) | ||||||
|  | #define USB_ENDPOINTS 7 // AtMegaxxU4
 | ||||||
|  | #else | ||||||
|  | #define USB_ENDPOINTS 5 // AtMegaxxU2
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define ISERIAL_MAX_LEN     20 | ||||||
|  | 
 | ||||||
|  | #define CDC_INTERFACE_COUNT	2 | ||||||
|  | #define CDC_ENPOINT_COUNT	3 | ||||||
|  | 
 | ||||||
|  | // kai:begin
 | ||||||
|  | #undef CDC_ACM_INTERFACE | ||||||
|  | #undef CDC_DATA_INTERFACE | ||||||
|  | #undef CDC_FIRST_ENDPOINT | ||||||
|  | #undef CDC_ENDPOINT_ACM | ||||||
|  | #undef CDC_ENDPOINT_OUT | ||||||
|  | #undef CDC_ENDPOINT_IN | ||||||
|  | #undef CDC_RX | ||||||
|  | #undef CDC_TX | ||||||
|  | 
 | ||||||
|  | #define CDC_ACM_INTERFACE1	0	// CDC ACM
 | ||||||
|  | #define CDC_DATA_INTERFACE1	1	// CDC Data
 | ||||||
|  | #define CDC_FIRST_ENDPOINT1	1 | ||||||
|  | #define CDC_ENDPOINT_ACM1	(CDC_FIRST_ENDPOINT1)							// CDC First
 | ||||||
|  | #define CDC_ENDPOINT_OUT1	(CDC_FIRST_ENDPOINT1+1) | ||||||
|  | #define CDC_ENDPOINT_IN1		(CDC_FIRST_ENDPOINT1+2) | ||||||
|  | 
 | ||||||
|  | #define CDC_ACM_INTERFACE2  2 // CDC ACM
 | ||||||
|  | #define CDC_DATA_INTERFACE2  3 // CDC Data
 | ||||||
|  | #define CDC_FIRST_ENDPOINT2  4  | ||||||
|  | #define CDC_ENDPOINT_ACM2  (CDC_FIRST_ENDPOINT2)              // CDC First
 | ||||||
|  | #define CDC_ENDPOINT_OUT2  (CDC_FIRST_ENDPOINT2+1) | ||||||
|  | #define CDC_ENDPOINT_IN2  (CDC_FIRST_ENDPOINT2+2) | ||||||
|  | 
 | ||||||
|  | // only one of both interfaces is functional:
 | ||||||
|  | #define ACTIVE_INTERFACE 1 | ||||||
|  | 
 | ||||||
|  | #if ACTIVE_INTERFACE == 1 | ||||||
|  | # define CDC_CLASS_INTERFACE CDC_ACM_INTERFACE1 | ||||||
|  | # define CDC_RX              CDC_ENDPOINT_OUT1 | ||||||
|  | # define CDC_TX              CDC_ENDPOINT_IN1 | ||||||
|  | #else | ||||||
|  | # define CDC_CLASS_INTERFACE CDC_ACM_INTERFACE2 | ||||||
|  | # define CDC_RX              CDC_ENDPOINT_OUT2 | ||||||
|  | # define CDC_TX              CDC_ENDPOINT_IN2 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #undef USB_VID | ||||||
|  | #define USB_VID 0x2342 | ||||||
|  | // kai:end
 | ||||||
|  | 
 | ||||||
|  | #define INTERFACE_COUNT    (MSC_INTERFACE + MSC_INTERFACE_COUNT) | ||||||
|  | 
 | ||||||
|  | #define IMANUFACTURER   1 | ||||||
|  | #define IPRODUCT        2 | ||||||
|  | #define ISERIAL         3 | ||||||
| @ -0,0 +1,64 @@ | |||||||
|  | /*
 | ||||||
|  |   bridge USB-serial to hardware-serial | ||||||
|  | 
 | ||||||
|  |   for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro) | ||||||
|  |   hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB | ||||||
|  | 
 | ||||||
|  |   see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino 
 | ||||||
|  |   -> CDC.cpp|HardwareSerial.cpp for serial implementation details | ||||||
|  | 
 | ||||||
|  |   this sketch is mainly for demonstration / test of CDC communication | ||||||
|  |   performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | uint32_t baud = 9600; | ||||||
|  | uint8_t databits = 8; | ||||||
|  | uint8_t stopbits = 1; | ||||||
|  | uint8_t parity = 0; | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  |   Serial.begin(baud); // USB
 | ||||||
|  |   Serial1.begin(baud, SERIAL_8N1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  |   // show USB connected state
 | ||||||
|  |   if (Serial) TXLED1; | ||||||
|  |   else        TXLED0; | ||||||
|  | 
 | ||||||
|  |   // configure hardware serial
 | ||||||
|  |   if (Serial.baud() != baud || | ||||||
|  |       Serial.numbits() != databits || | ||||||
|  |       Serial.stopbits() != stopbits || | ||||||
|  |       Serial.paritytype() != parity) { | ||||||
|  |     baud = Serial.baud(); | ||||||
|  |     databits = Serial.numbits(); | ||||||
|  |     stopbits = Serial.stopbits(); | ||||||
|  |     parity = Serial.paritytype(); | ||||||
|  |     uint8_t config = 0; // ucsrc register
 | ||||||
|  |     switch (databits) { | ||||||
|  |       case 5: break; | ||||||
|  |       case 6: config |= 2; break; | ||||||
|  |       case 7: config |= 4; break; | ||||||
|  |       case 8: config |= 6; break; | ||||||
|  |       default: config |= 6; | ||||||
|  |     } | ||||||
|  |     switch (stopbits) { | ||||||
|  |       case 2: config |= 8; | ||||||
|  |         // 1.5 stopbits not supported
 | ||||||
|  |     } | ||||||
|  |     switch (parity) { | ||||||
|  |       case 1: config |= 0x30; break; // odd
 | ||||||
|  |       case 2: config |= 0x20; break; // even
 | ||||||
|  |         // mark, space not supported
 | ||||||
|  |     } | ||||||
|  |     Serial1.end(); | ||||||
|  |     Serial1.begin(baud, config); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // bridge
 | ||||||
|  |   if (Serial.available() > 0) | ||||||
|  |     Serial1.write(Serial.read()); | ||||||
|  |   if (Serial1.available() > 0) | ||||||
|  |     Serial.write(Serial1.read()); | ||||||
|  | } | ||||||
| @ -164,6 +164,11 @@ public class DeviceTest implements SerialInputOutputManager.Listener { | |||||||
|         context = InstrumentationRegistry.getContext(); |         context = InstrumentationRegistry.getContext(); | ||||||
|         usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); |         usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||||
|         List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager); |         List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager); | ||||||
|  |         if(availableDrivers.isEmpty()) { | ||||||
|  |             ProbeTable customTable = new ProbeTable(); | ||||||
|  |             customTable.addProduct(0x2342, 0x8036, CdcAcmSerialDriver.class); // arduino multiport cdc witch custom VID | ||||||
|  |             availableDrivers = new UsbSerialProber(customTable).findAllDrivers(usbManager); | ||||||
|  |         } | ||||||
|         assertEquals("no USB device found", 1, availableDrivers.size()); |         assertEquals("no USB device found", 1, availableDrivers.size()); | ||||||
|         usbSerialDriver = availableDrivers.get(0); |         usbSerialDriver = availableDrivers.get(0); | ||||||
|         if(test_device_driver != null) { |         if(test_device_driver != null) { | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ import android.hardware.usb.UsbInterface; | |||||||
| import android.util.Log; | import android.util.Log; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Collections; | import java.util.ArrayList; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| @ -47,11 +47,26 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | |||||||
|     private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); |     private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); | ||||||
| 
 | 
 | ||||||
|     private final UsbDevice mDevice; |     private final UsbDevice mDevice; | ||||||
|     private final UsbSerialPort mPort; |     private final List<UsbSerialPort> mPorts; | ||||||
| 
 | 
 | ||||||
|     public CdcAcmSerialDriver(UsbDevice device) { |     public CdcAcmSerialDriver(UsbDevice device) { | ||||||
|         mDevice = device; |         mDevice = device; | ||||||
|         mPort = new CdcAcmSerialPort(device, 0); |         mPorts = new ArrayList<>(); | ||||||
|  | 
 | ||||||
|  |         int controlInterfaceCount = 0; | ||||||
|  |         int dataInterfaceCount = 0; | ||||||
|  |         for( int i = 0; i < device.getInterfaceCount(); i++) { | ||||||
|  |             if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM) | ||||||
|  |                 controlInterfaceCount++; | ||||||
|  |             if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) | ||||||
|  |                 dataInterfaceCount++; | ||||||
|  |         } | ||||||
|  |         for( int port = 0; port < Math.min(controlInterfaceCount, dataInterfaceCount); port++) { | ||||||
|  |             mPorts.add(new CdcAcmSerialPort(mDevice, port)); | ||||||
|  |         } | ||||||
|  |         if(mPorts.size() == 0) { | ||||||
|  |             mPorts.add(new CdcAcmSerialPort(mDevice, -1)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @ -61,7 +76,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public List<UsbSerialPort> getPorts() { |     public List<UsbSerialPort> getPorts() { | ||||||
|         return Collections.singletonList(mPort); |         return mPorts; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     class CdcAcmSerialPort extends CommonUsbSerialPort { |     class CdcAcmSerialPort extends CommonUsbSerialPort { | ||||||
| @ -95,7 +110,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | |||||||
| 
 | 
 | ||||||
|         @Override |         @Override | ||||||
|         public void openInt(UsbDeviceConnection connection) throws IOException { |         public void openInt(UsbDeviceConnection connection) throws IOException { | ||||||
|             if (1 == mDevice.getInterfaceCount()) { |             if (mPortNumber == -1) { | ||||||
|                 Log.d(TAG,"device might be castrated ACM device, trying single interface logic"); |                 Log.d(TAG,"device might be castrated ACM device, trying single interface logic"); | ||||||
|                 openSingleInterface(); |                 openSingleInterface(); | ||||||
|             } else { |             } else { | ||||||
| @ -105,77 +120,51 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void openSingleInterface() throws IOException { |         private void openSingleInterface() throws IOException { | ||||||
|             // the following code is inspired by the cdc-acm driver |             // the following code is inspired by the cdc-acm driver in the linux kernel | ||||||
|             // in the linux kernel |  | ||||||
| 
 | 
 | ||||||
|             mControlIndex = 0; |             mControlIndex = 0; | ||||||
|             mControlInterface = mDevice.getInterface(0); |             mControlInterface = mDevice.getInterface(0); | ||||||
|             Log.d(TAG, "Control iface=" + mControlInterface); |  | ||||||
| 
 |  | ||||||
|             mDataInterface = mDevice.getInterface(0); |             mDataInterface = mDevice.getInterface(0); | ||||||
|             Log.d(TAG, "data iface=" + mDataInterface); |  | ||||||
| 
 |  | ||||||
|             if (!mConnection.claimInterface(mControlInterface, true)) { |             if (!mConnection.claimInterface(mControlInterface, true)) { | ||||||
|                 throw new IOException("Could not claim shared control/data interface"); |                 throw new IOException("Could not claim shared control/data interface"); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             int endCount = mControlInterface.getEndpointCount(); |             for (int i = 0; i < mControlInterface.getEndpointCount(); ++i) { | ||||||
| 
 |  | ||||||
|             if (endCount < 3) { |  | ||||||
|                 Log.d(TAG,"not enough endpoints - need 3. count=" + mControlInterface.getEndpointCount()); |  | ||||||
|                 throw new IOException("Insufficient number of endpoints (" + mControlInterface.getEndpointCount() + ")"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Analyse endpoints for their properties |  | ||||||
|             mControlEndpoint = null; |  | ||||||
|             mReadEndpoint = null; |  | ||||||
|             mWriteEndpoint = null; |  | ||||||
|             for (int i = 0; i < endCount; ++i) { |  | ||||||
|                 UsbEndpoint ep = mControlInterface.getEndpoint(i); |                 UsbEndpoint ep = mControlInterface.getEndpoint(i); | ||||||
|                 if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && |                 if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) { | ||||||
|                     (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) { |  | ||||||
|                     Log.d(TAG,"Found controlling endpoint"); |  | ||||||
|                     mControlEndpoint = ep; |                     mControlEndpoint = ep; | ||||||
|                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && |                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { | ||||||
|                            (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { |  | ||||||
|                     Log.d(TAG,"Found reading endpoint"); |  | ||||||
|                     mReadEndpoint = ep; |                     mReadEndpoint = ep; | ||||||
|                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && |                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { | ||||||
|                         (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { |  | ||||||
|                     Log.d(TAG,"Found writing endpoint"); |  | ||||||
|                     mWriteEndpoint = ep; |                     mWriteEndpoint = ep; | ||||||
|                 } |                 } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|                 if ((mControlEndpoint != null) && |  | ||||||
|                     (mReadEndpoint != null) && |  | ||||||
|                     (mWriteEndpoint != null)) { |  | ||||||
|                     Log.d(TAG,"Found all required endpoints"); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 |             if (mControlEndpoint == null) { | ||||||
|             if ((mControlEndpoint == null) || |                 throw new IOException("No control endpoint"); | ||||||
|                     (mReadEndpoint == null) || |  | ||||||
|                     (mWriteEndpoint == null)) { |  | ||||||
|                 Log.d(TAG,"Could not establish all endpoints"); |  | ||||||
|                 throw new IOException("Could not establish all endpoints"); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void openInterface() throws IOException { |         private void openInterface() throws IOException { | ||||||
|             Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); |             Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); | ||||||
| 
 | 
 | ||||||
|  |             int controlInterfaceCount = 0; | ||||||
|  |             int dataInterfaceCount = 0; | ||||||
|             mControlInterface = null; |             mControlInterface = null; | ||||||
|             mDataInterface = null; |             mDataInterface = null; | ||||||
|             for (int i = 0; i < mDevice.getInterfaceCount(); i++) { |             for (int i = 0; i < mDevice.getInterfaceCount(); i++) { | ||||||
|                 UsbInterface usbInterface = mDevice.getInterface(i); |                 UsbInterface usbInterface = mDevice.getInterface(i); | ||||||
|                 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) { |                 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) { | ||||||
|                     mControlIndex = i; |                     if(controlInterfaceCount == mPortNumber) { | ||||||
|                     mControlInterface = usbInterface; |                         mControlIndex = i; | ||||||
|  |                         mControlInterface = usbInterface; | ||||||
|  |                     } | ||||||
|  |                     controlInterfaceCount++; | ||||||
|                 } |                 } | ||||||
|                 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { |                 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { | ||||||
|                     mDataInterface = usbInterface; |                     if(dataInterfaceCount == mPortNumber) { | ||||||
|  |                         mDataInterface = usbInterface; | ||||||
|  |                     } | ||||||
|  |                     dataInterfaceCount++; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -202,8 +191,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | |||||||
|                 throw new IOException("Could not claim data interface"); |                 throw new IOException("Could not claim data interface"); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             mReadEndpoint = null; |  | ||||||
|             mWriteEndpoint = null; |  | ||||||
|             for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { |             for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { | ||||||
|                 UsbEndpoint ep = mDataInterface.getEndpoint(i); |                 UsbEndpoint ep = mDataInterface.getEndpoint(i); | ||||||
|                 if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) |                 if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user