mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-31 02:17:23 +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(); | ||||
|         usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||
|         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()); | ||||
|         usbSerialDriver = availableDrivers.get(0); | ||||
|         if(test_device_driver != null) { | ||||
|  | ||||
| @ -29,7 +29,7 @@ import android.hardware.usb.UsbInterface; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.Collections; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| @ -47,11 +47,26 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | ||||
|     private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); | ||||
| 
 | ||||
|     private final UsbDevice mDevice; | ||||
|     private final UsbSerialPort mPort; | ||||
|     private final List<UsbSerialPort> mPorts; | ||||
| 
 | ||||
|     public CdcAcmSerialDriver(UsbDevice 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 | ||||
| @ -61,7 +76,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | ||||
| 
 | ||||
|     @Override | ||||
|     public List<UsbSerialPort> getPorts() { | ||||
|         return Collections.singletonList(mPort); | ||||
|         return mPorts; | ||||
|     } | ||||
| 
 | ||||
|     class CdcAcmSerialPort extends CommonUsbSerialPort { | ||||
| @ -95,7 +110,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | ||||
| 
 | ||||
|         @Override | ||||
|         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"); | ||||
|                 openSingleInterface(); | ||||
|             } else { | ||||
| @ -105,77 +120,51 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { | ||||
|         } | ||||
| 
 | ||||
|         private void openSingleInterface() throws IOException { | ||||
|             // the following code is inspired by the cdc-acm driver | ||||
|             // in the linux kernel | ||||
|             // the following code is inspired by the cdc-acm driver in the linux kernel | ||||
| 
 | ||||
|             mControlIndex = 0; | ||||
|             mControlInterface = mDevice.getInterface(0); | ||||
|             Log.d(TAG, "Control iface=" + mControlInterface); | ||||
| 
 | ||||
|             mDataInterface = mDevice.getInterface(0); | ||||
|             Log.d(TAG, "data iface=" + mDataInterface); | ||||
| 
 | ||||
|             if (!mConnection.claimInterface(mControlInterface, true)) { | ||||
|                 throw new IOException("Could not claim shared control/data interface"); | ||||
|             } | ||||
| 
 | ||||
|             int endCount = mControlInterface.getEndpointCount(); | ||||
| 
 | ||||
|             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) { | ||||
|             for (int i = 0; i < mControlInterface.getEndpointCount(); ++i) { | ||||
|                 UsbEndpoint ep = mControlInterface.getEndpoint(i); | ||||
|                 if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && | ||||
|                     (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) { | ||||
|                     Log.d(TAG,"Found controlling endpoint"); | ||||
|                 if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) { | ||||
|                     mControlEndpoint = ep; | ||||
|                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && | ||||
|                            (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { | ||||
|                     Log.d(TAG,"Found reading endpoint"); | ||||
|                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { | ||||
|                     mReadEndpoint = ep; | ||||
|                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && | ||||
|                         (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { | ||||
|                     Log.d(TAG,"Found writing endpoint"); | ||||
|                 } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { | ||||
|                     mWriteEndpoint = ep; | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 if ((mControlEndpoint != null) && | ||||
|                     (mReadEndpoint != null) && | ||||
|                     (mWriteEndpoint != null)) { | ||||
|                     Log.d(TAG,"Found all required endpoints"); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if ((mControlEndpoint == null) || | ||||
|                     (mReadEndpoint == null) || | ||||
|                     (mWriteEndpoint == null)) { | ||||
|                 Log.d(TAG,"Could not establish all endpoints"); | ||||
|                 throw new IOException("Could not establish all endpoints"); | ||||
|             if (mControlEndpoint == null) { | ||||
|                 throw new IOException("No control endpoint"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private void openInterface() throws IOException { | ||||
|             Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); | ||||
| 
 | ||||
|             int controlInterfaceCount = 0; | ||||
|             int dataInterfaceCount = 0; | ||||
|             mControlInterface = null; | ||||
|             mDataInterface = null; | ||||
|             for (int i = 0; i < mDevice.getInterfaceCount(); i++) { | ||||
|                 UsbInterface usbInterface = mDevice.getInterface(i); | ||||
|                 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) { | ||||
|                     mControlIndex = i; | ||||
|                     mControlInterface = usbInterface; | ||||
|                     if(controlInterfaceCount == mPortNumber) { | ||||
|                         mControlIndex = i; | ||||
|                         mControlInterface = usbInterface; | ||||
|                     } | ||||
|                     controlInterfaceCount++; | ||||
|                 } | ||||
|                 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"); | ||||
|             } | ||||
| 
 | ||||
|             mReadEndpoint = null; | ||||
|             mWriteEndpoint = null; | ||||
|             for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { | ||||
|                 UsbEndpoint ep = mDataInterface.getEndpoint(i); | ||||
|                 if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user