Mercurial > louis > kiibohd-controller
comparison 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 |
comparison
equal
deleted
inserted
replaced
419:910be0f02758 | 420:23a1868b4ac2 |
---|---|
1 /* Teensyduino Core Library | 1 /* Teensyduino Core Library |
2 * http://www.pjrc.com/teensy/ | 2 * http://www.pjrc.com/teensy/ |
3 * Copyright (c) 2013 PJRC.COM, LLC. | 3 * Copyright (c) 2013 PJRC.COM, LLC. |
4 * Modifications by Jacob Alexander (2013-2015) | 4 * Modifications by Jacob Alexander (2013-2016) |
5 * | 5 * |
6 * Permission is hereby granted, free of charge, to any person obtaining | 6 * Permission is hereby granted, free of charge, to any person obtaining |
7 * a copy of this software and associated documentation files (the | 7 * a copy of this software and associated documentation files (the |
8 * "Software"), to deal in the Software without restriction, including | 8 * "Software"), to deal in the Software without restriction, including |
9 * without limitation the rights to use, copy, modify, merge, publish, | 9 * without limitation the rights to use, copy, modify, merge, publish, |
166 volatile uint8_t usb_configuration = 0; | 166 volatile uint8_t usb_configuration = 0; |
167 volatile uint8_t usb_reboot_timer = 0; | 167 volatile uint8_t usb_reboot_timer = 0; |
168 | 168 |
169 static uint8_t reply_buffer[8]; | 169 static uint8_t reply_buffer[8]; |
170 | 170 |
171 static uint8_t power_neg_delay; | |
172 static uint32_t power_neg_time; | |
173 | |
171 | 174 |
172 | 175 |
173 // ----- Functions ----- | 176 // ----- Functions ----- |
174 | 177 |
175 static void endpoint0_stall() | 178 static void endpoint0_stall() |
184 { | 187 { |
185 table[index(0, TX, ep0_tx_bdt_bank)].addr = (void *)data; | 188 table[index(0, TX, ep0_tx_bdt_bank)].addr = (void *)data; |
186 table[index(0, TX, ep0_tx_bdt_bank)].desc = BDT_DESC(len, ep0_tx_data_toggle); | 189 table[index(0, TX, ep0_tx_bdt_bank)].desc = BDT_DESC(len, ep0_tx_data_toggle); |
187 ep0_tx_data_toggle ^= 1; | 190 ep0_tx_data_toggle ^= 1; |
188 ep0_tx_bdt_bank ^= 1; | 191 ep0_tx_bdt_bank ^= 1; |
192 } | |
193 | |
194 // Used to check any USB state changes that may not have a proper interrupt | |
195 // Called once per scan loop, should take minimal processing time or it may affect other modules | |
196 void usb_device_check() | |
197 { | |
198 // Check to see if we're still waiting for the next USB request after Get Configuration Descriptor | |
199 // If still waiting, restart the USB initialization with a lower power requirement | |
200 if ( power_neg_delay ) | |
201 { | |
202 // Check if 100 ms has elapsed | |
203 if ( systick_millis_count - power_neg_time > 100 ) | |
204 { | |
205 // Update bMaxPower | |
206 // The value set is in increments of 2 mA | |
207 // So 50 * 2 mA = 100 mA | |
208 // XXX Currently only transitions to 100 mA | |
209 // It may be possible to transition down again to 20 mA | |
210 *usb_bMaxPower = 50; | |
211 | |
212 // Re-initialize USB | |
213 power_neg_delay = 0; | |
214 usb_configuration = 0; // Clear USB configuration if we have one | |
215 USB0_CONTROL = 0; // Disable D+ Pullup to simulate disconnect | |
216 delay(10); // Delay is necessary to simulate disconnect | |
217 usb_init(); | |
218 } | |
219 } | |
189 } | 220 } |
190 | 221 |
191 static void usb_setup() | 222 static void usb_setup() |
192 { | 223 { |
193 const uint8_t *data = NULL; | 224 const uint8_t *data = NULL; |
197 volatile uint8_t *reg; | 228 volatile uint8_t *reg; |
198 uint8_t epconf; | 229 uint8_t epconf; |
199 const uint8_t *cfg; | 230 const uint8_t *cfg; |
200 int i; | 231 int i; |
201 | 232 |
233 // If another request is made, disable the power negotiation check | |
234 // See GET_DESCRIPTOR - Configuration | |
235 if ( power_neg_delay ) | |
236 { | |
237 power_neg_delay = 0; | |
238 } | |
239 | |
202 switch ( setup.wRequestAndType ) | 240 switch ( setup.wRequestAndType ) |
203 { | 241 { |
204 case 0x0500: // SET_ADDRESS | 242 case 0x0500: // SET_ADDRESS |
205 goto send; | 243 goto send; |
206 | 244 |
210 #endif | 248 #endif |
211 usb_configuration = setup.wValue; | 249 usb_configuration = setup.wValue; |
212 Output_Available = usb_configuration; | 250 Output_Available = usb_configuration; |
213 reg = &USB0_ENDPT1; | 251 reg = &USB0_ENDPT1; |
214 cfg = usb_endpoint_config_table; | 252 cfg = usb_endpoint_config_table; |
253 | |
254 // Now configured so we can utilize bMaxPower now | |
255 Output_update_usb_current( *usb_bMaxPower * 2 ); | |
256 | |
215 // clear all BDT entries, free any allocated memory... | 257 // clear all BDT entries, free any allocated memory... |
216 for ( i = 4; i < ( NUM_ENDPOINTS + 1) * 4; i++ ) | 258 for ( i = 4; i < ( NUM_ENDPOINTS + 1) * 4; i++ ) |
217 { | 259 { |
218 if ( table[i].desc & BDT_OWN ) | 260 if ( table[i].desc & BDT_OWN ) |
219 { | 261 { |
322 data = reply_buffer; | 364 data = reply_buffer; |
323 datalen = 2; | 365 datalen = 2; |
324 goto send; | 366 goto send; |
325 | 367 |
326 case 0x0100: // CLEAR_FEATURE (device) | 368 case 0x0100: // CLEAR_FEATURE (device) |
369 switch ( setup.wValue ) | |
370 { | |
371 // CLEAR_FEATURE(DEVICE_REMOTE_WAKEUP) | |
372 // See SET_FEATURE(DEVICE_REMOTE_WAKEUP) for details | |
373 case 0x1: | |
374 goto send; | |
375 } | |
376 | |
377 warn_msg("SET_FEATURE - Device wValue("); | |
378 printHex( setup.wValue ); | |
379 print( ")" NL ); | |
380 endpoint0_stall(); | |
381 return; | |
382 | |
327 case 0x0101: // CLEAR_FEATURE (interface) | 383 case 0x0101: // CLEAR_FEATURE (interface) |
328 // TODO: Currently ignoring, perhaps useful? -HaaTa | 384 // TODO: Currently ignoring, perhaps useful? -HaaTa |
329 warn_print("CLEAR_FEATURE - Device/Interface"); | 385 warn_msg("CLEAR_FEATURE - Interface wValue("); |
386 printHex( setup.wValue ); | |
387 print(") wIndex("); | |
388 printHex( setup.wIndex ); | |
389 print( ")" NL ); | |
330 endpoint0_stall(); | 390 endpoint0_stall(); |
331 return; | 391 return; |
332 | 392 |
333 case 0x0102: // CLEAR_FEATURE (endpoint) | 393 case 0x0102: // CLEAR_FEATURE (endpoint) |
334 i = setup.wIndex & 0x7F; | 394 i = setup.wIndex & 0x7F; |
340 (*(uint8_t *)(&USB0_ENDPT0 + setup.wIndex * 4)) &= ~0x02; | 400 (*(uint8_t *)(&USB0_ENDPT0 + setup.wIndex * 4)) &= ~0x02; |
341 // TODO: do we need to clear the data toggle here? | 401 // TODO: do we need to clear the data toggle here? |
342 goto send; | 402 goto send; |
343 | 403 |
344 case 0x0300: // SET_FEATURE (device) | 404 case 0x0300: // SET_FEATURE (device) |
405 switch ( setup.wValue ) | |
406 { | |
407 // SET_FEATURE(DEVICE_REMOTE_WAKEUP) | |
408 // XXX: Only used to confirm Remote Wake | |
409 // Used on Mac OSX and Windows not on Linux | |
410 // Good post on the behaviour: | |
411 // http://community.silabs.com/t5/8-bit-MCU/Remote-wakeup-HID/m-p/74957#M30802 | |
412 case 0x1: | |
413 goto send; | |
414 } | |
415 | |
416 warn_msg("SET_FEATURE - Device wValue("); | |
417 printHex( setup.wValue ); | |
418 print( ")" NL ); | |
419 endpoint0_stall(); | |
420 return; | |
421 | |
345 case 0x0301: // SET_FEATURE (interface) | 422 case 0x0301: // SET_FEATURE (interface) |
346 // TODO: Currently ignoring, perhaps useful? -HaaTa | 423 // TODO: Currently ignoring, perhaps useful? -HaaTa |
347 warn_print("SET_FEATURE - Device/Interface"); | 424 warn_msg("SET_FEATURE - Interface wValue("); |
425 printHex( setup.wValue ); | |
426 print(") wIndex("); | |
427 printHex( setup.wIndex ); | |
428 print( ")" NL ); | |
348 endpoint0_stall(); | 429 endpoint0_stall(); |
349 return; | 430 return; |
350 | 431 |
351 case 0x0302: // SET_FEATURE (endpoint) | 432 case 0x0302: // SET_FEATURE (endpoint) |
352 i = setup.wIndex & 0x7F; | 433 i = setup.wIndex & 0x7F; |
383 } | 464 } |
384 else | 465 else |
385 { | 466 { |
386 datalen = list->length; | 467 datalen = list->length; |
387 } | 468 } |
469 | |
470 // XXX Power negotiation hack -HaaTa | |
471 // Some devices such as the Apple Ipad do not support bMaxPower greater than 100 mA | |
472 // However, there is no provision in the basic USB 2.0 stack for power negotiation | |
473 // To get around this: | |
474 // * Attempt to set bMaxPower to 500 mA first | |
475 // * If more than 100 ms passes since retrieving a Get Configuration Descriptor | |
476 // (Descriptor with bMaxPower in it) | |
477 // * Change usb_bMaxPower to 50 (100 mA) | |
478 // * Restart the USB init process | |
479 // According to notes online, it says that some Apple devices can only do 20 mA | |
480 // However, in my testing this hasn't been the case | |
481 // (you can also draw as much current as you want if you just lie in the descriptor :P) | |
482 // If this becomes an issue we can use this hack a second time to negotiate down to 20 mA | |
483 // (which should be fine for just the mcu) | |
484 if ( setup.wValue == 0x0200 && setup.wIndex == 0x0 ) | |
485 { | |
486 power_neg_delay = 1; | |
487 power_neg_time = systick_millis_count; | |
488 } | |
489 | |
388 #if UART_DEBUG | 490 #if UART_DEBUG |
389 print("Desc found, "); | 491 print("Desc found, "); |
390 printHex32( (uint32_t)data ); | 492 printHex32( (uint32_t)data ); |
391 print(","); | 493 print(","); |
392 printHex( datalen ); | 494 printHex( datalen ); |
860 //#define index(endpoint, tx, odd) (((endpoint) << 2) | ((tx) << 1) | (odd)) | 962 //#define index(endpoint, tx, odd) (((endpoint) << 2) | ((tx) << 1) | (odd)) |
861 //#define stat2bufferdescriptor(stat) (table + ((stat) >> 2)) | 963 //#define stat2bufferdescriptor(stat) (table + ((stat) >> 2)) |
862 | 964 |
863 void usb_tx( uint32_t endpoint, usb_packet_t *packet ) | 965 void usb_tx( uint32_t endpoint, usb_packet_t *packet ) |
864 { | 966 { |
967 // Since we are transmitting data, USB will be brought out of sleep/suspend | |
968 // if it's in that state | |
969 // Use the currently set descriptor value | |
970 Output_update_usb_current( *usb_bMaxPower * 2 ); | |
971 | |
865 bdt_t *b = &table[ index( endpoint, TX, EVEN ) ]; | 972 bdt_t *b = &table[ index( endpoint, TX, EVEN ) ]; |
866 uint8_t next; | 973 uint8_t next; |
867 | 974 |
868 endpoint--; | 975 endpoint--; |
869 if ( endpoint >= NUM_ENDPOINTS ) | 976 if ( endpoint >= NUM_ENDPOINTS ) |
1159 //serial_phex(err); | 1266 //serial_phex(err); |
1160 //serial_print("\n"); | 1267 //serial_print("\n"); |
1161 USB0_ISTAT = USB_ISTAT_ERROR; | 1268 USB0_ISTAT = USB_ISTAT_ERROR; |
1162 } | 1269 } |
1163 | 1270 |
1271 // USB Host signalling device to enter 'sleep' state | |
1272 // The USB Module triggers this interrupt when it detects the bus has been idle for 3 ms | |
1164 if ( (status & USB_ISTAT_SLEEP /* 10 */ ) ) | 1273 if ( (status & USB_ISTAT_SLEEP /* 10 */ ) ) |
1165 { | 1274 { |
1166 //serial_print("sleep\n"); | 1275 info_print("Host has requested USB sleep/suspend state"); |
1276 Output_update_usb_current( 100 ); // Set to 100 mA | |
1167 USB0_ISTAT = USB_ISTAT_SLEEP; | 1277 USB0_ISTAT = USB_ISTAT_SLEEP; |
1168 } | 1278 } |
1169 } | 1279 } |
1170 | 1280 |
1171 | 1281 |
1218 NVIC_ENABLE_IRQ( IRQ_USBOTG ); | 1328 NVIC_ENABLE_IRQ( IRQ_USBOTG ); |
1219 | 1329 |
1220 // enable d+ pullup | 1330 // enable d+ pullup |
1221 USB0_CONTROL = USB_CONTROL_DPPULLUPNONOTG; | 1331 USB0_CONTROL = USB_CONTROL_DPPULLUPNONOTG; |
1222 | 1332 |
1333 // Do not check for power negotiation delay until Get Configuration Descriptor | |
1334 power_neg_delay = 0; | |
1335 | |
1223 return 1; | 1336 return 1; |
1224 } | 1337 } |
1225 | 1338 |
1226 // return 0 if the USB is not configured, or the configuration | 1339 // return 0 if the USB is not configured, or the configuration |
1227 // number selected by the HOST | 1340 // number selected by the HOST |