Mercurial > louis > kiibohd-controller
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++ ) {