Mercurial > louis > kiibohd-controller
diff Output/pjrcUSB/arm/usb_dev.c @ 420:23a1868b4ac2
Adding dynamic USB power support
- Each scan module now has a current change callback which passes the available current as a parameter
- No longer attempts to use the max 500 mA immediately, starts with 100 mA then goes to 500 mA after enumeration
- If enumeration fails due to bMaxPower of 500 mA, then attempt again at 100 mA (might also be possible to go even lower to 20 mA in certain cases)
- Now working with the Apple Ipad (no over-power messages)
- Fixed Wake-up behaviour on Apple Ipad (and likely other iOS devices)
- More effecient set_feature/clear_feature handling (device handler)
- Initial power handling via Interconnect (still needs work to get it more dynamic)
author | Jacob Alexander <haata@kiibohd.com> |
---|---|
date | Sun, 21 Feb 2016 19:56:52 -0800 |
parents | d8f61e15aca1 |
children | f10c2dad7f55 |
line wrap: on
line diff
--- a/Output/pjrcUSB/arm/usb_dev.c Sun Feb 21 14:19:52 2016 -0800 +++ b/Output/pjrcUSB/arm/usb_dev.c Sun Feb 21 19:56:52 2016 -0800 @@ -1,7 +1,7 @@ /* Teensyduino Core Library * http://www.pjrc.com/teensy/ * Copyright (c) 2013 PJRC.COM, LLC. - * Modifications by Jacob Alexander (2013-2015) + * Modifications by Jacob Alexander (2013-2016) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -168,6 +168,9 @@ static uint8_t reply_buffer[8]; +static uint8_t power_neg_delay; +static uint32_t power_neg_time; + // ----- Functions ----- @@ -188,6 +191,34 @@ ep0_tx_bdt_bank ^= 1; } +// Used to check any USB state changes that may not have a proper interrupt +// Called once per scan loop, should take minimal processing time or it may affect other modules +void usb_device_check() +{ + // Check to see if we're still waiting for the next USB request after Get Configuration Descriptor + // If still waiting, restart the USB initialization with a lower power requirement + if ( power_neg_delay ) + { + // Check if 100 ms has elapsed + if ( systick_millis_count - power_neg_time > 100 ) + { + // Update bMaxPower + // The value set is in increments of 2 mA + // So 50 * 2 mA = 100 mA + // XXX Currently only transitions to 100 mA + // It may be possible to transition down again to 20 mA + *usb_bMaxPower = 50; + + // Re-initialize USB + 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 + usb_init(); + } + } +} + static void usb_setup() { const uint8_t *data = NULL; @@ -199,6 +230,13 @@ const uint8_t *cfg; int i; + // If another request is made, disable the power negotiation check + // See GET_DESCRIPTOR - Configuration + if ( power_neg_delay ) + { + power_neg_delay = 0; + } + switch ( setup.wRequestAndType ) { case 0x0500: // SET_ADDRESS @@ -212,6 +250,10 @@ Output_Available = usb_configuration; reg = &USB0_ENDPT1; cfg = usb_endpoint_config_table; + + // Now configured so we can utilize bMaxPower now + Output_update_usb_current( *usb_bMaxPower * 2 ); + // clear all BDT entries, free any allocated memory... for ( i = 4; i < ( NUM_ENDPOINTS + 1) * 4; i++ ) { @@ -324,9 +366,27 @@ goto send; case 0x0100: // CLEAR_FEATURE (device) + switch ( setup.wValue ) + { + // CLEAR_FEATURE(DEVICE_REMOTE_WAKEUP) + // See SET_FEATURE(DEVICE_REMOTE_WAKEUP) for details + case 0x1: + goto send; + } + + warn_msg("SET_FEATURE - Device wValue("); + printHex( setup.wValue ); + print( ")" NL ); + endpoint0_stall(); + return; + case 0x0101: // CLEAR_FEATURE (interface) // TODO: Currently ignoring, perhaps useful? -HaaTa - warn_print("CLEAR_FEATURE - Device/Interface"); + warn_msg("CLEAR_FEATURE - Interface wValue("); + printHex( setup.wValue ); + print(") wIndex("); + printHex( setup.wIndex ); + print( ")" NL ); endpoint0_stall(); return; @@ -342,9 +402,30 @@ goto send; case 0x0300: // SET_FEATURE (device) + switch ( setup.wValue ) + { + // SET_FEATURE(DEVICE_REMOTE_WAKEUP) + // XXX: Only used to confirm Remote Wake + // Used on Mac OSX and Windows not on Linux + // Good post on the behaviour: + // http://community.silabs.com/t5/8-bit-MCU/Remote-wakeup-HID/m-p/74957#M30802 + case 0x1: + goto send; + } + + warn_msg("SET_FEATURE - Device wValue("); + printHex( setup.wValue ); + print( ")" NL ); + endpoint0_stall(); + return; + case 0x0301: // SET_FEATURE (interface) // TODO: Currently ignoring, perhaps useful? -HaaTa - warn_print("SET_FEATURE - Device/Interface"); + warn_msg("SET_FEATURE - Interface wValue("); + printHex( setup.wValue ); + print(") wIndex("); + printHex( setup.wIndex ); + print( ")" NL ); endpoint0_stall(); return; @@ -385,6 +466,27 @@ { datalen = list->length; } + + // XXX Power negotiation hack -HaaTa + // Some devices such as the Apple Ipad do not support bMaxPower greater than 100 mA + // However, there is no provision in the basic USB 2.0 stack for power negotiation + // To get around this: + // * Attempt to set bMaxPower to 500 mA first + // * If more than 100 ms passes since retrieving a Get Configuration Descriptor + // (Descriptor with bMaxPower in it) + // * Change usb_bMaxPower to 50 (100 mA) + // * Restart the USB init process + // According to notes online, it says that some Apple devices can only do 20 mA + // However, in my testing this hasn't been the case + // (you can also draw as much current as you want if you just lie in the descriptor :P) + // If this becomes an issue we can use this hack a second time to negotiate down to 20 mA + // (which should be fine for just the mcu) + if ( setup.wValue == 0x0200 && setup.wIndex == 0x0 ) + { + power_neg_delay = 1; + power_neg_time = systick_millis_count; + } + #if UART_DEBUG print("Desc found, "); printHex32( (uint32_t)data ); @@ -862,6 +964,11 @@ void usb_tx( uint32_t endpoint, usb_packet_t *packet ) { + // 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 + Output_update_usb_current( *usb_bMaxPower * 2 ); + bdt_t *b = &table[ index( endpoint, TX, EVEN ) ]; uint8_t next; @@ -1161,9 +1268,12 @@ USB0_ISTAT = USB_ISTAT_ERROR; } + // USB Host signalling device to enter 'sleep' state + // The USB Module triggers this interrupt when it detects the bus has been idle for 3 ms if ( (status & USB_ISTAT_SLEEP /* 10 */ ) ) { - //serial_print("sleep\n"); + info_print("Host has requested USB sleep/suspend state"); + Output_update_usb_current( 100 ); // Set to 100 mA USB0_ISTAT = USB_ISTAT_SLEEP; } } @@ -1220,6 +1330,9 @@ // enable d+ pullup USB0_CONTROL = USB_CONTROL_DPPULLUPNONOTG; + // Do not check for power negotiation delay until Get Configuration Descriptor + power_neg_delay = 0; + return 1; }