diff Output/pjrcUSB/arm/usb_dev.c @ 449:45feb80a2ad1

Major USB update, fixes most (if not all) known issues USB - General - Refactored descriptors - Enabled/Disable USB endpoints - Added debug flags for special features - Code cleanup - Interface count calculation based off of enabled endpoints - Delayed wTotalLength calculation to simplify descriptor offsets - Re-ordered endpoints and interfaces - Added more debug output - Added usbInitTime to show how long keyboard initialization took (Useful when debugging bad init sequences) - Added function for usb_resume() which takes care of the resume sequence * Resume is now only called if packets are starting to timeout USB - Special Options - Added enableDeviceRestartOnUSBTimeout * A last resort hammer for bad USB Chipsets/OSs, don't use if you can help it * Disabled - Added enableUSBResume * Enables host resume wake-up signalling, required to wake a computer from sleep * Enabled - Added enableUSBLowPowerNegotiation * Enables power negotiation hack * Required to use firmware with an IPad and other hard-limit low-power USB hosts * Hasn't been tested with the recent changes * Disabled - Added enableUSBSuspend * Enables power down events on host USB bus suspend * Enabled USB - Keyboard - Attempted to cleanup HID SET_REPORT * Works much better * Still has an issue under Linux which generates *a lot* of NAKs (initializes quickly regardless) + Not present on other keyboards + SETUP -> OUT -> IN : This sequence is the problem + Specifically during the OUT phase - Enabled USB - CDC Virtual Serial Port - Code cleanup - Added convenience struct USBCDCLineCoding for easier debugging - Attempted to cleanup CDC_SET_LING_CODING * Works much better * Still has an issue under Linux which generates *a lot* of NAKs (initializes quickly regardless) + SETUP -> OUT -> IN : This sequence is the problem + Specifically during the OUT phase + Likely the same issues as HID SET_REPORT - Enabled USB - Mouse - Enabled USB - Joystick - Disabled USB - RawIO - Initial code, API not used yet - Disabled DFU - Updated load script, now faster
author Jacob Alexander <haata@kiibohd.com>
date Tue, 31 May 2016 00:19:45 -0700
parents 56237ba5da6f
children
line wrap: on
line diff
--- a/Output/pjrcUSB/arm/usb_dev.c	Sun May 29 10:27:21 2016 -0700
+++ b/Output/pjrcUSB/arm/usb_dev.c	Tue May 31 00:19:45 2016 -0700
@@ -40,6 +40,10 @@
 #include "usb_dev.h"
 #include "usb_mem.h"
 
+#if enableVirtualSerialPort_define == 1
+#include "usb_serial.h"
+#endif
+
 
 
 // ----- Defines -----
@@ -180,7 +184,10 @@
 static void endpoint0_stall()
 {
 	#ifdef UART_DEBUG_UNKNOWN
-	print("STALL" NL );
+	print("STALL : ");
+	printInt32( systick_millis_count - USBInit_TimeStart );
+	print(" ms");
+	print(NL);
 	#endif
 	USB0_ENDPT0 = USB_ENDPT_EPSTALL | USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK;
 }
@@ -195,7 +202,6 @@
 
 void usb_reinit()
 {
-	power_neg_delay = 0;
 	usb_configuration = 0; // Clear USB configuration if we have one
 	USB0_CONTROL = 0; // Disable D+ Pullup to simulate disconnect
 	delay(10); // Delay is necessary to simulate disconnect
@@ -213,6 +219,18 @@
 		// Check if 100 ms has elapsed
 		if ( systick_millis_count - power_neg_time > 100 )
 		{
+			power_neg_delay = 0;
+
+			// USB Low Power Negotiation
+#if enableUSBLowPowerNegotiation_define == 1
+			// Check to see if bMaxPower has already be lowered
+			// This generally points to a USB bug (host or device?)
+			if ( *usb_bMaxPower == 50 )
+			{
+				warn_msg("Power negotiation delay detected again, likely a system/device USB bug");
+				return;
+			}
+
 			// Update bMaxPower
 			// The value set is in increments of 2 mA
 			// So 50 * 2 mA = 100 mA
@@ -222,6 +240,9 @@
 
 			// Re-initialize USB
 			usb_reinit();
+#else
+			warn_msg("USB Low Power Negotation Disabled, condition detected.");
+#endif
 		}
 	}
 }
@@ -237,6 +258,10 @@
 	const uint8_t *cfg;
 	int i;
 
+	// Reset USB Init timer
+	USBInit_TimeEnd = systick_millis_count;
+	USBInit_Ticks++;
+
 	// If another request is made, disable the power negotiation check
 	// See GET_DESCRIPTOR - Configuration
 	if ( power_neg_delay )
@@ -517,22 +542,40 @@
 		endpoint0_stall();
 		return;
 
+#if enableVirtualSerialPort_define == 1
 	case 0x2221: // CDC_SET_CONTROL_LINE_STATE
 		usb_cdc_line_rtsdtr = setup.wValue;
-		//serial_print("set control line state\n");
+		//info_print("set control line state");
 		goto send;
 
 	case 0x21A1: // CDC_GET_LINE_CODING
-		data = (uint8_t*)usb_cdc_line_coding;
+		data = (uint8_t*)&usb_cdc_line_coding;
 		datalen = sizeof( usb_cdc_line_coding );
 		goto send;
 
 	case 0x2021: // CDC_SET_LINE_CODING
-		// XXX Needed?
-		//serial_print("set coding, waiting...\n");
-		return;
+		// ZLP Reply
+		// Settings are applied in PID=OUT
+		goto send;
+#endif
 
 	case 0x0921: // HID SET_REPORT
+		// ZLP Reply
+		// Settings are applied in PID=OUT
+
+		#ifdef UART_DEBUG
+		print("report_type(");
+		printHex( setup.wValue >> 8 );
+		print(")report_id(");
+		printHex( setup.wValue & 0xFF );
+		print(")interface(");
+		printHex( setup.wIndex );
+		print(")len(");
+		printHex( setup.wLength );
+		print(")");
+		print( NL );
+		#endif
+
 		// Interface
 		switch ( setup.wIndex & 0xFF )
 		{
@@ -547,7 +590,7 @@
 			printHex( setup.wIndex );
 			print( NL );
 			endpoint0_stall();
-			break;
+			return;
 		}
 
 		goto send;
@@ -571,9 +614,9 @@
 				datalen = list->length;
 				goto send;
 			}
-                }
-                endpoint0_stall();
-                return;
+		}
+		endpoint0_stall();
+		return;
 
 	case 0x0A21: // HID SET_IDLE
 		#ifdef UART_DEBUG
@@ -596,6 +639,7 @@
 		print(NL);
 		#endif
 		reply_buffer[0] = USBKeys_Idle_Config;
+		data = reply_buffer;
 		datalen = 1;
 		goto send;
 
@@ -620,13 +664,16 @@
 		print(NL);
 		#endif
 		reply_buffer[0] = USBKeys_Protocol;
+		data = reply_buffer;
 		datalen = 1;
 		goto send;
 
 	// case 0xC940:
 	default:
 		#ifdef UART_DEBUG_UNKNOWN
-		print("UNKNOWN");
+		print("UNKNOWN: ");
+		printInt32(  systick_millis_count - USBInit_TimeStart );
+		print(" ms");
 		print(NL);
 		#endif
 		endpoint0_stall();
@@ -655,7 +702,7 @@
 	if ( size > EP0_SIZE )
 		size = EP0_SIZE;
 
-	endpoint0_transmit(data, size);
+	endpoint0_transmit( data, size );
 	data += size;
 	datalen -= size;
 
@@ -666,7 +713,7 @@
 	size = datalen;
 	if ( size > EP0_SIZE )
 		size = EP0_SIZE;
-	endpoint0_transmit(data, size);
+	endpoint0_transmit( data, size );
 	data += size;
 	datalen -= size;
 
@@ -689,9 +736,9 @@
 //to be set to their default values. This includes setting the data toggle of
 //any endpoint using data toggles to the value DATA0.
 
-//For endpoints using data toggle, regardless of whether an endpoint has the
-//Halt feature set, a ClearFeature(ENDPOINT_HALT) request always results in the
-//data toggle being reinitialized to DATA0.
+// For endpoints using data toggle, regardless of whether an endpoint has the
+// Halt feature set, a ClearFeature(ENDPOINT_HALT) request always results in the
+// data toggle being reinitialized to DATA0.
 
 static void usb_control( uint32_t stat )
 {
@@ -714,11 +761,9 @@
 	print(" - ");
 	#endif
 
-	switch (pid)
+	switch ( pid )
 	{
 	case 0x0D: // Setup received from host
-		//serial_print("PID=Setup\n");
-		//if (count != 8) ; // panic?
 		// grab the 8 byte setup info
 		setup.word1 = *(uint32_t *)(buf);
 		setup.word2 = *(uint32_t *)(buf + 4);
@@ -741,14 +786,14 @@
 		//}
 		table[index(0, TX, EVEN)].desc = 0;
 		table[index(0, TX, ODD)].desc = 0;
+
 		// first IN after Setup is always DATA1
 		ep0_tx_data_toggle = 1;
 
 		#ifdef UART_DEBUG_UNKNOWN
-		print("bmRequestType:");
-		printHex(setup.bmRequestType);
-		print(", bRequest:");
-		printHex(setup.bRequest);
+		printHex( stat );
+		print(" PID=SETUP wRequestAndType:");
+		printHex(setup.wRequestAndType);
 		print(", wValue:");
 		printHex(setup.wValue);
 		print(", wIndex:");
@@ -759,8 +804,12 @@
 		printHex32(setup.word1);
 		print(" ");
 		printHex32(setup.word2);
+		print(": ");
+		printInt32( systick_millis_count - USBInit_TimeStart );
+		print(" ms");
 		print(NL);
 		#endif
+
 		// actually "do" the setup request
 		usb_setup();
 		// unfreeze the USB, now that we're ready
@@ -770,7 +819,8 @@
 	case 0x01:  // OUT transaction received from host
 	case 0x02:
 		#ifdef UART_DEBUG_UNKNOWN
-		print("PID=OUT wRequestAndType:");
+		printHex( stat );
+		print(" PID=OUT   wRequestAndType:");
 		printHex(setup.wRequestAndType);
 		print(", wValue:");
 		printHex(setup.wValue);
@@ -782,43 +832,77 @@
 		printHex32(setup.word1);
 		print(" ");
 		printHex32(setup.word2);
+		print(": ");
+		printInt32( systick_millis_count - USBInit_TimeStart );
+		print(" ms");
 		print(NL);
 		#endif
 
 		// CDC Interface
-		if ( setup.wRequestAndType == 0x2021 /*CDC_SET_LINE_CODING*/ )
+		#if enableVirtualSerialPort_define == 1
+		// CDC_SET_LINE_CODING - PID=OUT
+		// XXX - Getting lots of NAKs in Linux
+		if ( setup.wRequestAndType == 0x2021 )
 		{
-			int i;
-			uint8_t *dst = (uint8_t *)usb_cdc_line_coding;
-			//serial_print("set line coding ");
-			for ( i = 0; i < 7; i++ )
+			// Copy over new line coding
+			memcpy( (void*)&usb_cdc_line_coding, buf, 7 );
+
+			#ifdef UART_DEBUG
+			// - Unused, but for the readers info -
+			print("dwDTERate(");
+			printInt32( usb_cdc_line_coding.dwDTERate );
+			print(")bCharFormat(");
+			printHex( usb_cdc_line_coding.bCharFormat );
+			print(")bParityType(");
+			printHex( usb_cdc_line_coding.bParityType );
+			print(")bDataBits(");
+			printHex( usb_cdc_line_coding.bDataBits );
+			print(")");
+			print( NL );
+			#endif
+
+			// XXX ZLP causes timeout/delay, why? -HaaTa
+			//endpoint0_transmit( NULL, 0 );
+		}
+		#endif
+
+		// Keyboard HID SET_REPORT - PID=OUT
+		#if enableKeyboard_define == 1
+		// XXX - Getting lots of NAKs in Linux
+		if ( setup.wRequestAndType == 0x0921 && setup.wValue & 0x200 )
+		{
+			#ifdef UART_DEBUG
+			print("report_type(");
+			printHex( setup.wValue >> 8 );
+			print(")report_id(");
+			printHex( setup.wValue & 0xFF );
+			print(")interface(");
+			printHex( setup.wIndex );
+			print(")len(");
+			printHex( setup.wLength );
+			print(")[");
+
+			for ( size_t len = 0; len < setup.wLength; len++ )
 			{
-				//serial_phex(*buf);
-				*dst++ = *buf++;
+				printHex( buf[ len ] );
+				print(" ");
 			}
-			//serial_phex32(usb_cdc_line_coding[0]);
-			//serial_print("\n");
-			if ( usb_cdc_line_coding[0] == 134 )
-				usb_reboot_timer = 15;
-			endpoint0_transmit( NULL, 0 );
-		}
+			print("]");
+			print( NL );
+			#endif
 
-		// Keyboard SET_REPORT
-		if ( setup.wRequestAndType == 0x921 && setup.wValue & 0x200 )
-		{
 			// Interface
 			switch ( setup.wIndex & 0xFF )
 			{
 			// Keyboard Interface
 			case KEYBOARD_INTERFACE:
 				USBKeys_LEDs = buf[0];
-				endpoint0_transmit( NULL, 0 );
 				break;
 			// NKRO Keyboard Interface
 			case NKRO_KEYBOARD_INTERFACE:
+				// Already set with the control sequence
 				// Only use 2nd byte, first byte is the report id
 				USBKeys_LEDs = buf[1];
-				endpoint0_transmit( NULL, 0 );
 				break;
 			default:
 				warn_msg("Unknown interface - ");
@@ -827,34 +911,48 @@
 				break;
 			}
 
-			#ifdef UART_DEBUG
-			for ( size_t len = 0; len < setup.wLength; len++ )
-			{
-				printHex( buf[ len ] );
-				print(" ");
-			}
-			print( NL );
-			#endif
+			// XXX ZLP causes timeout/delay, why? -HaaTa
+			//endpoint0_transmit( NULL, 0 );
 		}
+		#endif
 
 		// give the buffer back
 		b->desc = BDT_DESC( EP0_SIZE, DATA1 );
 		break;
 
 	case 0x09: // IN transaction completed to host
-		#ifdef UART_DEBUG
-		print("PID=IN:");
-		printHex(stat);
+		data = ep0_tx_ptr;
+
+		#ifdef UART_DEBUG_UNKNOWN
+		printHex( stat );
+		print(" PID=IN    wRequestAndType:");
+		printHex(setup.wRequestAndType);
+		print(", wValue:");
+		printHex(setup.wValue);
+		print(", wIndex:");
+		printHex(setup.wIndex);
+		print(", len:");
+		printHex(setup.wLength);
+		print(" -- ");
+		printHex32(setup.word1);
+		print(" ");
+		printHex32(setup.word2);
+		print(": ");
+		printInt32( systick_millis_count - USBInit_TimeStart );
+		print(" ms");
+		if ( data ) print(" DATA ");
 		print(NL);
 		#endif
 
 		// send remaining data, if any...
-		data = ep0_tx_ptr;
 		if ( data )
 		{
 			size = ep0_tx_len;
-			if (size > EP0_SIZE) size = EP0_SIZE;
-			endpoint0_transmit(data, size);
+			if (size > EP0_SIZE)
+			{
+				size = EP0_SIZE;
+			}
+			endpoint0_transmit( data, size );
 			data += size;
 			ep0_tx_len -= size;
 			ep0_tx_ptr = (ep0_tx_len > 0 || size == EP0_SIZE) ? data : NULL;
@@ -871,12 +969,34 @@
 			USB0_ADDR = setup.wValue;
 		}
 
+		// CDC_SET_LINE_CODING - PID=IN
+		#if enableVirtualSerialPort_define == 1
+		if ( setup.wRequestAndType == 0x2021 )
+		{
+			// XXX ZLP causes timeout/delay, why? -HaaTa
+			//endpoint0_transmit( NULL, 0 );
+		}
+		#endif
+
+		// Keyboard HID SET_REPORT - PID=IN
+		#if enableKeyboard_define == 1
+		// XXX - Getting lots of NAKs in Linux
+		if ( setup.wRequestAndType == 0x0921 && setup.wValue & 0x200 )
+		{
+			// XXX ZLP causes timeout/delay, why? -HaaTa
+			//endpoint0_transmit( NULL, 0 );
+		}
+		#endif
+
 		break;
 
 	default:
-		#ifdef UART_DEBUG
-		print("PID=unknown:");
+		#ifdef UART_DEBUG_UNKNOWN
+		print("PID=unknown: ");
 		printHex(pid);
+		print(": ");
+		printInt32( systick_millis_count - USBInit_TimeStart );
+		print(" ms");
 		print(NL);
 		#endif
 		break;
@@ -889,14 +1009,25 @@
 	//print("USB RX");
 	usb_packet_t *ret;
 	endpoint--;
+
+	// Make sure this is a valid endpoint
 	if ( endpoint >= NUM_ENDPOINTS )
+	{
 		return NULL;
+	}
+
 	__disable_irq();
+
+	// Receive packet, check pointer
 	ret = rx_first[endpoint];
 	if ( ret )
+	{
 		rx_first[ endpoint ] = ret->next;
-	usb_rx_byte_count_data[ endpoint ] -= ret->len;
+		usb_rx_byte_count_data[ endpoint ] -= ret->len;
+	}
+
 	__enable_irq();
+
 	//serial_print("rx, epidx=");
 	//serial_phex(endpoint);
 	//serial_print(", packet=");
@@ -994,17 +1125,16 @@
 	return;
 }
 
-//#define index(endpoint, tx, odd) (((endpoint) << 2) | ((tx) << 1) | (odd))
-//#define stat2bufferdescriptor(stat) (table + ((stat) >> 2))
-
-void usb_tx( uint32_t endpoint, usb_packet_t *packet )
+// Call whenever there's an action that may wake the host device
+void usb_resume()
 {
-	// Update expiry counter
-	USBKeys_Idle_Expiry = systick_millis_count;
-
 	// If we have been sleeping, try to wake up host
-	if ( usb_dev_sleep )
+	if ( usb_dev_sleep && usb_configured() )
 	{
+		#if enableUSBResume_define == 1
+		#if enableVirtualSerialPort_define != 1
+		info_print("Attempting to resume the host");
+		#endif
 		// Force wake-up for 10 ms
 		// According to the USB Spec a device must hold resume for at least 1 ms but no more than 15 ms
 		USB0_CTL |= USB_CTL_RESUME;
@@ -1012,8 +1142,18 @@
 		USB0_CTL &= ~(USB_CTL_RESUME);
 		delay(50); // Wait for at least 50 ms to make sure the bus is clear
 		usb_dev_sleep = 0; // Make sure we don't call this again, may crash system
+		#else
+		warn_print("Host Resume Disabled");
+		#endif
 	}
 
+}
+
+void usb_tx( uint32_t endpoint, usb_packet_t *packet )
+{
+	// Update expiry counter
+	USBKeys_Idle_Expiry = systick_millis_count;
+
 	// Since we are transmitting data, USB will be brought out of sleep/suspend
 	// if it's in that state
 	// Use the currently set descriptor value
@@ -1087,16 +1227,12 @@
 {
 	uint8_t status, stat, t;
 
-	//serial_print("isr");
-	//status = USB0_ISTAT;
-	//serial_phex(status);
-	//serial_print("\n");
 restart:
 	status = USB0_ISTAT;
 	/*
-	print("USB ISR STATUS: ");
+	print(" ISR(");
 	printHex( status );
-	print( NL );
+	print(") ");
 	*/
 
 	if ( (status & USB_INTEN_SOFTOKEN /* 04 */ ) )
@@ -1112,6 +1248,7 @@
 			}
 
 			// CDC Interface
+			#if enableVirtualSerialPort_define == 1
 			t = usb_cdc_transmit_flush_timer;
 			if ( t )
 			{
@@ -1119,6 +1256,7 @@
 				if ( t == 0 )
 					usb_serial_flush_callback();
 			}
+			#endif
 
 		}
 		USB0_ISTAT = USB_INTEN_SOFTOKEN;
@@ -1323,16 +1461,26 @@
 	// The USB Module triggers this interrupt when it detects the bus has been idle for 3 ms
 	if ( (status & USB_ISTAT_SLEEP /* 10 */ ) )
 	{
-		//info_print("Host has requested USB sleep/suspend state");
+#if enableUSBSuspend_define == 1
+		// Can cause issues with the virtual serial port
+		#if enableVirtualSerialPort_define != 1
+		info_print("Host has requested USB sleep/suspend state");
+		#endif
 		Output_update_usb_current( 100 ); // Set to 100 mA
 		usb_dev_sleep = 1;
+#else
+		info_print("USB Suspend Detected - Firmware USB Suspend Disabled");
+#endif
 		USB0_ISTAT |= USB_ISTAT_SLEEP;
 	}
 
 	// On USB Resume, unset the usb_dev_sleep so we don't keep sending resume signals
 	if ( (status & USB_ISTAT_RESUME /* 20 */ ) )
 	{
-		//info_print("Host has woken-up/resumed from sleep/suspend state");
+		// Can cause issues with the virtual serial port
+		#if enableVirtualSerialPort_define != 1
+		info_print("Host has woken-up/resumed from sleep/suspend state");
+		#endif
 		Output_update_usb_current( *usb_bMaxPower * 2 );
 		usb_dev_sleep = 0;
 		USB0_ISTAT |= USB_ISTAT_RESUME;
@@ -1347,6 +1495,13 @@
 	print("USB INIT"NL);
 	#endif
 
+	USBInit_TimeStart = systick_millis_count;
+	USBInit_Ticks = 0;
+
+	// XXX Set wTotalLength here instead of using defines
+	//     Simplifies defines considerably
+	usb_set_config_descriptor_size();
+
 	// Clear out endpoints table
 	for ( int i = 0; i <= NUM_ENDPOINTS * 4; i++ )
 	{