]> git.donarmstrong.com Git - kiibohd-controller.git/blobdiff - Output/pjrcUSB/arm/usb_dev.c
Major USB update, fixes most (if not all) known issues
[kiibohd-controller.git] / Output / pjrcUSB / arm / usb_dev.c
index f46f10b2b92316f213cdec3f450f394b266378e7..42b545eff7c07ad957c275ebf2fadb77f0062237 100644 (file)
 #include "usb_dev.h"
 #include "usb_mem.h"
 
+#if enableVirtualSerialPort_define == 1
+#include "usb_serial.h"
+#endif
+
 
 
 // ----- Defines -----
@@ -180,7 +184,10 @@ static uint8_t usb_dev_sleep = 0;
 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 @@ static void endpoint0_transmit( const void *data, uint32_t len )
 
 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 @@ void usb_device_check()
                // 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 @@ void usb_device_check()
 
                        // Re-initialize USB
                        usb_reinit();
+#else
+                       warn_msg("USB Low Power Negotation Disabled, condition detected.");
+#endif
                }
        }
 }
@@ -237,6 +258,10 @@ static void usb_setup()
        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 @@ static void usb_setup()
                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 @@ static void usb_setup()
                        printHex( setup.wIndex );
                        print( NL );
                        endpoint0_stall();
-                       break;
+                       return;
                }
 
                goto send;
@@ -571,9 +614,9 @@ static void usb_setup()
                                datalen = list->length;
                                goto send;
                        }
-                }
-                endpoint0_stall();
-                return;
+               }
+               endpoint0_stall();
+               return;
 
        case 0x0A21: // HID SET_IDLE
                #ifdef UART_DEBUG
@@ -596,6 +639,7 @@ static void usb_setup()
                print(NL);
                #endif
                reply_buffer[0] = USBKeys_Idle_Config;
+               data = reply_buffer;
                datalen = 1;
                goto send;
 
@@ -620,13 +664,16 @@ static void usb_setup()
                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 @@ send:
        if ( size > EP0_SIZE )
                size = EP0_SIZE;
 
-       endpoint0_transmit(data, size);
+       endpoint0_transmit( data, size );
        data += size;
        datalen -= size;
 
@@ -666,7 +713,7 @@ send:
        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 @@ send:
 //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 @@ static void usb_control( uint32_t stat )
        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 @@ static void usb_control( uint32_t stat )
                //}
                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 @@ static void usb_control( uint32_t stat )
                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 @@ static void usb_control( uint32_t stat )
        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 @@ static void usb_control( uint32_t stat )
                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++ )
-                       {
-                               //serial_phex(*buf);
-                               *dst++ = *buf++;
-                       }
-                       //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 );
+                       // 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 SET_REPORT
-               if ( setup.wRequestAndType == 0x921 && setup.wValue & 0x200 )
+               // 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++ )
+                       {
+                               printHex( buf[ len ] );
+                               print(" ");
+                       }
+                       print("]");
+                       print( NL );
+                       #endif
+
                        // 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 @@ static void usb_control( uint32_t stat )
                                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 @@ static void usb_control( uint32_t stat )
                        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 @@ usb_packet_t *usb_rx( uint32_t endpoint )
        //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 @@ void usb_rx_memory( usb_packet_t *packet )
        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 @@ void usb_tx( uint32_t endpoint, usb_packet_t *packet )
                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 @@ void usb_isr()
 {
        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 @@ restart:
                        }
 
                        // CDC Interface
+                       #if enableVirtualSerialPort_define == 1
                        t = usb_cdc_transmit_flush_timer;
                        if ( t )
                        {
@@ -1119,6 +1256,7 @@ restart:
                                if ( t == 0 )
                                        usb_serial_flush_callback();
                        }
+                       #endif
 
                }
                USB0_ISTAT = USB_INTEN_SOFTOKEN;
@@ -1323,16 +1461,26 @@ restart:
        // 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 @@ uint8_t usb_init()
        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++ )
        {