92b3b5081b
- Somewhat aggresive, may cause restarts if the keyboard/OS hasn't fully intialized the keyboard - Added GET_IDLE handling and correct usage of SET_IDLE - Initial implementation of idle send, commented out as it causes issues on Mac OSX for sleeping (keyboard has been working without it) - MacOSX seems to have some sort of data corruption on the USB link, not sure why (other OSs have no issues) - Cleaned up some code - Added a longer sleep after the resume sequence to prevent possible issues sending keys too soon (may need to be increased more) Ipad support now seems flaky, though Mac, Windows seems solid. Init sequence on Linux seems slow, even though there are no errors.
1410 lines
34 KiB
C
1410 lines
34 KiB
C
/* Teensyduino Core Library
|
|
* http://www.pjrc.com/teensy/
|
|
* Copyright (c) 2013 PJRC.COM, LLC.
|
|
* 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
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* 1. The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* 2. If the Software is incorporated into a build system that allows
|
|
* selection among a list of target devices, then similar target
|
|
* devices manufactured by PJRC.COM must be included in the list of
|
|
* target devices and selectable in the same manner.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
// ----- Includes -----
|
|
|
|
// Project Includes
|
|
#include <Lib/OutputLib.h>
|
|
#include <print.h>
|
|
#include <kll_defs.h>
|
|
|
|
// Local Includes
|
|
#include "usb_dev.h"
|
|
#include "usb_mem.h"
|
|
|
|
|
|
|
|
// ----- Defines -----
|
|
|
|
// DEBUG Mode
|
|
// XXX - Only use when using usbMuxUart Module
|
|
// Delay causes issues initializing more than 1 hid device (i.e. NKRO keyboard)
|
|
//#define UART_DEBUG 1
|
|
// Debug Unknown USB requests, usually what you want to debug USB issues
|
|
//#define UART_DEBUG_UNKNOWN 1
|
|
|
|
|
|
#define TX_STATE_BOTH_FREE_EVEN_FIRST 0
|
|
#define TX_STATE_BOTH_FREE_ODD_FIRST 1
|
|
#define TX_STATE_EVEN_FREE 2
|
|
#define TX_STATE_ODD_FREE 3
|
|
#define TX_STATE_NONE_FREE_EVEN_FIRST 4
|
|
#define TX_STATE_NONE_FREE_ODD_FIRST 5
|
|
|
|
#define BDT_OWN 0x80
|
|
#define BDT_DATA1 0x40
|
|
#define BDT_DATA0 0x00
|
|
#define BDT_DTS 0x08
|
|
#define BDT_STALL 0x04
|
|
|
|
#define TX 1
|
|
#define RX 0
|
|
#define ODD 1
|
|
#define EVEN 0
|
|
#define DATA0 0
|
|
#define DATA1 1
|
|
|
|
|
|
#define GET_STATUS 0
|
|
#define CLEAR_FEATURE 1
|
|
#define SET_FEATURE 3
|
|
#define SET_ADDRESS 5
|
|
#define GET_DESCRIPTOR 6
|
|
#define SET_DESCRIPTOR 7
|
|
#define GET_CONFIGURATION 8
|
|
#define SET_CONFIGURATION 9
|
|
#define GET_INTERFACE 10
|
|
#define SET_INTERFACE 11
|
|
#define SYNCH_FRAME 12
|
|
|
|
#define TX_STATE_BOTH_FREE_EVEN_FIRST 0
|
|
#define TX_STATE_BOTH_FREE_ODD_FIRST 1
|
|
#define TX_STATE_EVEN_FREE 2
|
|
#define TX_STATE_ODD_FREE 3
|
|
#define TX_STATE_NONE_FREE 4
|
|
|
|
|
|
|
|
|
|
|
|
// ----- Macros -----
|
|
|
|
#define BDT_PID(n) (((n) >> 2) & 15)
|
|
|
|
#define BDT_DESC(count, data) (BDT_OWN | BDT_DTS \
|
|
| ((data) ? BDT_DATA1 : BDT_DATA0) \
|
|
| ((count) << 16))
|
|
|
|
#define index(endpoint, tx, odd) (((endpoint) << 2) | ((tx) << 1) | (odd))
|
|
#define stat2bufferdescriptor(stat) (table + ((stat) >> 2))
|
|
|
|
|
|
|
|
// ----- Structs -----
|
|
|
|
// buffer descriptor table
|
|
|
|
typedef struct {
|
|
uint32_t desc;
|
|
void * addr;
|
|
} bdt_t;
|
|
|
|
static union {
|
|
struct {
|
|
union {
|
|
struct {
|
|
uint8_t bmRequestType;
|
|
uint8_t bRequest;
|
|
};
|
|
uint16_t wRequestAndType;
|
|
};
|
|
uint16_t wValue;
|
|
uint16_t wIndex;
|
|
uint16_t wLength;
|
|
};
|
|
struct {
|
|
uint32_t word1;
|
|
uint32_t word2;
|
|
};
|
|
} setup;
|
|
|
|
|
|
|
|
// ----- Variables -----
|
|
|
|
__attribute__ ((section(".usbdescriptortable"), used))
|
|
static bdt_t table[ (NUM_ENDPOINTS + 1) * 4 ];
|
|
|
|
static usb_packet_t *rx_first [ NUM_ENDPOINTS ];
|
|
static usb_packet_t *rx_last [ NUM_ENDPOINTS ];
|
|
static usb_packet_t *tx_first [ NUM_ENDPOINTS ];
|
|
static usb_packet_t *tx_last [ NUM_ENDPOINTS ];
|
|
uint16_t usb_rx_byte_count_data[ NUM_ENDPOINTS ];
|
|
|
|
static uint8_t tx_state[NUM_ENDPOINTS];
|
|
|
|
// SETUP always uses a DATA0 PID for the data field of the SETUP transaction.
|
|
// transactions in the data phase start with DATA1 and toggle (figure 8-12, USB1.1)
|
|
// Status stage uses a DATA1 PID.
|
|
|
|
static uint8_t ep0_rx0_buf[EP0_SIZE] __attribute__ ((aligned (4)));
|
|
static uint8_t ep0_rx1_buf[EP0_SIZE] __attribute__ ((aligned (4)));
|
|
static const uint8_t *ep0_tx_ptr = NULL;
|
|
static uint16_t ep0_tx_len;
|
|
static uint8_t ep0_tx_bdt_bank = 0;
|
|
static uint8_t ep0_tx_data_toggle = 0;
|
|
uint8_t usb_rx_memory_needed = 0;
|
|
|
|
volatile uint8_t usb_configuration = 0;
|
|
volatile uint8_t usb_reboot_timer = 0;
|
|
|
|
static uint8_t reply_buffer[8];
|
|
|
|
static uint8_t power_neg_delay;
|
|
static uint32_t power_neg_time;
|
|
|
|
static uint8_t usb_dev_sleep = 0;
|
|
|
|
|
|
|
|
// ----- Functions -----
|
|
|
|
static void endpoint0_stall()
|
|
{
|
|
#ifdef UART_DEBUG_UNKNOWN
|
|
print("STALL" NL );
|
|
#endif
|
|
USB0_ENDPT0 = USB_ENDPT_EPSTALL | USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK;
|
|
}
|
|
|
|
static void endpoint0_transmit( const void *data, uint32_t len )
|
|
{
|
|
table[index(0, TX, ep0_tx_bdt_bank)].addr = (void *)data;
|
|
table[index(0, TX, ep0_tx_bdt_bank)].desc = BDT_DESC(len, ep0_tx_data_toggle);
|
|
ep0_tx_data_toggle ^= 1;
|
|
ep0_tx_bdt_bank ^= 1;
|
|
}
|
|
|
|
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
|
|
usb_init();
|
|
}
|
|
|
|
// 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
|
|
usb_reinit();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usb_setup()
|
|
{
|
|
const uint8_t *data = NULL;
|
|
uint32_t datalen = 0;
|
|
const usb_descriptor_list_t *list;
|
|
uint32_t size;
|
|
volatile uint8_t *reg;
|
|
uint8_t epconf;
|
|
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
|
|
goto send;
|
|
|
|
case 0x0900: // SET_CONFIGURATION
|
|
#ifdef UART_DEBUG
|
|
print("CONFIGURE - ");
|
|
#endif
|
|
usb_configuration = setup.wValue;
|
|
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++ )
|
|
{
|
|
if ( table[i].desc & BDT_OWN )
|
|
{
|
|
usb_free( (usb_packet_t *)((uint8_t *)(table[ i ].addr) - 8) );
|
|
}
|
|
}
|
|
// free all queued packets
|
|
for ( i = 0; i < NUM_ENDPOINTS; i++ )
|
|
{
|
|
usb_packet_t *p, *n;
|
|
p = rx_first[i];
|
|
while ( p )
|
|
{
|
|
n = p->next;
|
|
usb_free(p);
|
|
p = n;
|
|
}
|
|
rx_first[ i ] = NULL;
|
|
rx_last[ i ] = NULL;
|
|
p = tx_first[i];
|
|
while (p)
|
|
{
|
|
n = p->next;
|
|
usb_free(p);
|
|
p = n;
|
|
}
|
|
tx_first[ i ] = NULL;
|
|
tx_last[ i ] = NULL;
|
|
usb_rx_byte_count_data[i] = 0;
|
|
|
|
switch ( tx_state[ i ] )
|
|
{
|
|
case TX_STATE_EVEN_FREE:
|
|
case TX_STATE_NONE_FREE_EVEN_FIRST:
|
|
tx_state[ i ] = TX_STATE_BOTH_FREE_EVEN_FIRST;
|
|
break;
|
|
case TX_STATE_ODD_FREE:
|
|
case TX_STATE_NONE_FREE_ODD_FIRST:
|
|
tx_state[ i ] = TX_STATE_BOTH_FREE_ODD_FIRST;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
usb_rx_memory_needed = 0;
|
|
for ( i = 1; i <= NUM_ENDPOINTS; i++ )
|
|
{
|
|
epconf = *cfg++;
|
|
*reg = epconf;
|
|
reg += 4;
|
|
if ( epconf & USB_ENDPT_EPRXEN )
|
|
{
|
|
usb_packet_t *p;
|
|
p = usb_malloc();
|
|
if ( p )
|
|
{
|
|
table[ index( i, RX, EVEN ) ].addr = p->buf;
|
|
table[ index( i, RX, EVEN ) ].desc = BDT_DESC( 64, 0 );
|
|
}
|
|
else
|
|
{
|
|
table[ index( i, RX, EVEN ) ].desc = 0;
|
|
usb_rx_memory_needed++;
|
|
}
|
|
p = usb_malloc();
|
|
if ( p )
|
|
{
|
|
table[ index( i, RX, ODD ) ].addr = p->buf;
|
|
table[ index( i, RX, ODD ) ].desc = BDT_DESC( 64, 1 );
|
|
}
|
|
else
|
|
{
|
|
table[ index( i, RX, ODD ) ].desc = 0;
|
|
usb_rx_memory_needed++;
|
|
}
|
|
}
|
|
table[ index( i, TX, EVEN ) ].desc = 0;
|
|
table[ index( i, TX, ODD ) ].desc = 0;
|
|
}
|
|
goto send;
|
|
|
|
case 0x0880: // GET_CONFIGURATION
|
|
reply_buffer[0] = usb_configuration;
|
|
datalen = 1;
|
|
data = reply_buffer;
|
|
goto send;
|
|
|
|
case 0x0080: // GET_STATUS (device)
|
|
reply_buffer[0] = 0;
|
|
reply_buffer[1] = 0;
|
|
datalen = 2;
|
|
data = reply_buffer;
|
|
goto send;
|
|
|
|
case 0x0082: // GET_STATUS (endpoint)
|
|
if ( setup.wIndex > NUM_ENDPOINTS )
|
|
{
|
|
// TODO: do we need to handle IN vs OUT here?
|
|
endpoint0_stall();
|
|
return;
|
|
}
|
|
reply_buffer[0] = 0;
|
|
reply_buffer[1] = 0;
|
|
if ( *(uint8_t *)(&USB0_ENDPT0 + setup.wIndex * 4) & 0x02 )
|
|
reply_buffer[0] = 1;
|
|
data = reply_buffer;
|
|
datalen = 2;
|
|
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_msg("CLEAR_FEATURE - Interface wValue(");
|
|
printHex( setup.wValue );
|
|
print(") wIndex(");
|
|
printHex( setup.wIndex );
|
|
print( ")" NL );
|
|
endpoint0_stall();
|
|
return;
|
|
|
|
case 0x0102: // CLEAR_FEATURE (endpoint)
|
|
i = setup.wIndex & 0x7F;
|
|
if ( i > NUM_ENDPOINTS || setup.wValue != 0 )
|
|
{
|
|
endpoint0_stall();
|
|
return;
|
|
}
|
|
(*(uint8_t *)(&USB0_ENDPT0 + setup.wIndex * 4)) &= ~0x02;
|
|
// TODO: do we need to clear the data toggle here?
|
|
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_msg("SET_FEATURE - Interface wValue(");
|
|
printHex( setup.wValue );
|
|
print(") wIndex(");
|
|
printHex( setup.wIndex );
|
|
print( ")" NL );
|
|
endpoint0_stall();
|
|
return;
|
|
|
|
case 0x0302: // SET_FEATURE (endpoint)
|
|
i = setup.wIndex & 0x7F;
|
|
if ( i > NUM_ENDPOINTS || setup.wValue != 0 )
|
|
{
|
|
// TODO: do we need to handle IN vs OUT here?
|
|
endpoint0_stall();
|
|
return;
|
|
}
|
|
(*(uint8_t *)(&USB0_ENDPT0 + setup.wIndex * 4)) |= 0x02;
|
|
// TODO: do we need to clear the data toggle here?
|
|
goto send;
|
|
|
|
case 0x0680: // GET_DESCRIPTOR
|
|
case 0x0681:
|
|
#ifdef UART_DEBUG
|
|
print("desc:");
|
|
printHex( setup.wValue );
|
|
print( NL );
|
|
#endif
|
|
for ( list = usb_descriptor_list; 1; list++ )
|
|
{
|
|
if ( list->addr == NULL )
|
|
break;
|
|
if ( setup.wValue == list->wValue && setup.wIndex == list->wIndex )
|
|
{
|
|
data = list->addr;
|
|
if ( (setup.wValue >> 8) == 3 )
|
|
{
|
|
// for string descriptors, use the descriptor's
|
|
// length field, allowing runtime configured
|
|
// length.
|
|
datalen = *(list->addr);
|
|
}
|
|
else
|
|
{
|
|
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 );
|
|
print(",");
|
|
printHex( datalen );
|
|
print(",");
|
|
printHex_op( data[0], 2 );
|
|
printHex_op( data[1], 2 );
|
|
printHex_op( data[2], 2 );
|
|
printHex_op( data[3], 2 );
|
|
printHex_op( data[4], 2 );
|
|
printHex_op( data[5], 2 );
|
|
print( NL );
|
|
#endif
|
|
goto send;
|
|
}
|
|
}
|
|
#ifdef UART_DEBUG
|
|
print( "desc: not found" NL );
|
|
#endif
|
|
endpoint0_stall();
|
|
return;
|
|
|
|
case 0x2221: // CDC_SET_CONTROL_LINE_STATE
|
|
usb_cdc_line_rtsdtr = setup.wValue;
|
|
//serial_print("set control line state\n");
|
|
goto send;
|
|
|
|
case 0x21A1: // CDC_GET_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;
|
|
|
|
case 0x0921: // HID SET_REPORT
|
|
// Interface
|
|
switch ( setup.wIndex & 0xFF )
|
|
{
|
|
// Keyboard Interface
|
|
case KEYBOARD_INTERFACE:
|
|
break;
|
|
// NKRO Keyboard Interface
|
|
case NKRO_KEYBOARD_INTERFACE:
|
|
break;
|
|
default:
|
|
warn_msg("Unknown interface - ");
|
|
printHex( setup.wIndex );
|
|
print( NL );
|
|
endpoint0_stall();
|
|
break;
|
|
}
|
|
|
|
goto send;
|
|
|
|
case 0x01A1: // HID GET_REPORT
|
|
#ifdef UART_DEBUG
|
|
print("GET_REPORT - ");
|
|
printHex( setup.wIndex );
|
|
print(NL);
|
|
#endif
|
|
// Search through descriptors returning necessary info
|
|
for ( list = usb_descriptor_list; 1; list++ )
|
|
{
|
|
if ( list->addr == NULL )
|
|
break;
|
|
if ( list->wValue != 0x2200 )
|
|
continue;
|
|
if ( setup.wIndex == list->wIndex )
|
|
{
|
|
data = list->addr;
|
|
datalen = list->length;
|
|
goto send;
|
|
}
|
|
}
|
|
endpoint0_stall();
|
|
return;
|
|
|
|
case 0x0A21: // HID SET_IDLE
|
|
#ifdef UART_DEBUG
|
|
print("SET_IDLE - ");
|
|
printHex( setup.wValue );
|
|
print(" - ");
|
|
printHex( setup.wValue >> 8 );
|
|
print(NL);
|
|
#endif
|
|
USBKeys_Idle_Config = (setup.wValue >> 8);
|
|
USBKeys_Idle_Expiry = 0;
|
|
goto send;
|
|
|
|
case 0x02A1: // HID GET_IDLE
|
|
#ifdef UART_DEBUG
|
|
print("SET_IDLE - ");
|
|
printHex( setup.wValue );
|
|
print(" - ");
|
|
printHex( USBKeys_Idle_Config );
|
|
print(NL);
|
|
#endif
|
|
reply_buffer[0] = USBKeys_Idle_Config;
|
|
datalen = 1;
|
|
goto send;
|
|
|
|
|
|
case 0x0B21: // HID SET_PROTOCOL
|
|
#ifdef UART_DEBUG
|
|
print("SET_PROTOCOL - ");
|
|
printHex( setup.wValue );
|
|
print(" - ");
|
|
printHex( setup.wValue & 0xFF );
|
|
print(NL);
|
|
#endif
|
|
USBKeys_Protocol = setup.wValue & 0xFF; // 0 - Boot Mode, 1 - NKRO Mode
|
|
goto send;
|
|
|
|
case 0x03A1: /// HID GET_PROTOCOL
|
|
#ifdef UART_DEBUG
|
|
print("GET_PROTOCOL - ");
|
|
printHex( setup.wValue );
|
|
print(" - ");
|
|
printHex( USBKeys_Protocol );
|
|
print(NL);
|
|
#endif
|
|
reply_buffer[0] = USBKeys_Protocol;
|
|
datalen = 1;
|
|
goto send;
|
|
|
|
// case 0xC940:
|
|
default:
|
|
#ifdef UART_DEBUG_UNKNOWN
|
|
print("UNKNOWN");
|
|
print(NL);
|
|
#endif
|
|
endpoint0_stall();
|
|
return;
|
|
}
|
|
|
|
send:
|
|
#ifdef UART_DEBUG
|
|
print("setup send ");
|
|
printHex32( (uint32_t)data );
|
|
print(",");
|
|
for ( uint8_t c = 0; c < datalen; c++ )
|
|
{
|
|
printHex( data[c] );
|
|
print(" ");
|
|
}
|
|
print(",");
|
|
printHex( datalen );
|
|
print( NL );
|
|
#endif
|
|
|
|
if ( datalen > setup.wLength )
|
|
datalen = setup.wLength;
|
|
|
|
size = datalen;
|
|
if ( size > EP0_SIZE )
|
|
size = EP0_SIZE;
|
|
|
|
endpoint0_transmit(data, size);
|
|
data += size;
|
|
datalen -= size;
|
|
|
|
// See if transmit has finished
|
|
if ( datalen == 0 && size < EP0_SIZE )
|
|
return;
|
|
|
|
size = datalen;
|
|
if ( size > EP0_SIZE )
|
|
size = EP0_SIZE;
|
|
endpoint0_transmit(data, size);
|
|
data += size;
|
|
datalen -= size;
|
|
|
|
// See if transmit has finished
|
|
if ( datalen == 0 && size < EP0_SIZE )
|
|
return;
|
|
|
|
// Save rest of transfer for later? XXX
|
|
ep0_tx_ptr = data;
|
|
ep0_tx_len = datalen;
|
|
}
|
|
|
|
|
|
//A bulk endpoint's toggle sequence is initialized to DATA0 when the endpoint
|
|
//experiences any configuration event (configuration events are explained in
|
|
//Sections 9.1.1.5 and 9.4.5).
|
|
|
|
//Configuring a device or changing an alternate setting causes all of the status
|
|
//and configuration values associated with endpoints in the affected interfaces
|
|
//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.
|
|
|
|
static void usb_control( uint32_t stat )
|
|
{
|
|
#ifdef UART_DEBUG
|
|
print("CONTROL - ");
|
|
#endif
|
|
bdt_t *b;
|
|
uint32_t pid, size;
|
|
uint8_t *buf;
|
|
const uint8_t *data;
|
|
|
|
b = stat2bufferdescriptor( stat );
|
|
pid = BDT_PID( b->desc );
|
|
buf = b->addr;
|
|
#ifdef UART_DEBUG
|
|
print("pid:");
|
|
printHex(pid);
|
|
print(", count:");
|
|
printHex32(b->desc);
|
|
print(" - ");
|
|
#endif
|
|
|
|
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);
|
|
|
|
// give the buffer back
|
|
b->desc = BDT_DESC( EP0_SIZE, DATA1 );
|
|
//table[index(0, RX, EVEN)].desc = BDT_DESC(EP0_SIZE, 1);
|
|
//table[index(0, RX, ODD)].desc = BDT_DESC(EP0_SIZE, 1);
|
|
|
|
// clear any leftover pending IN transactions
|
|
ep0_tx_ptr = NULL;
|
|
if ( ep0_tx_data_toggle )
|
|
{
|
|
}
|
|
//if (table[index(0, TX, EVEN)].desc & 0x80) {
|
|
//serial_print("leftover tx even\n");
|
|
//}
|
|
//if (table[index(0, TX, ODD)].desc & 0x80) {
|
|
//serial_print("leftover tx odd\n");
|
|
//}
|
|
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);
|
|
print(", wValue:");
|
|
printHex(setup.wValue);
|
|
print(", wIndex:");
|
|
printHex(setup.wIndex);
|
|
print(", len:");
|
|
printHex(setup.wLength);
|
|
print(" -- ");
|
|
printHex32(setup.word1);
|
|
print(" ");
|
|
printHex32(setup.word2);
|
|
print(NL);
|
|
#endif
|
|
// actually "do" the setup request
|
|
usb_setup();
|
|
// unfreeze the USB, now that we're ready
|
|
USB0_CTL = USB_CTL_USBENSOFEN; // clear TXSUSPENDTOKENBUSY bit
|
|
break;
|
|
|
|
case 0x01: // OUT transaction received from host
|
|
case 0x02:
|
|
#ifdef UART_DEBUG_UNKNOWN
|
|
print("PID=OUT 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(NL);
|
|
#endif
|
|
|
|
// CDC Interface
|
|
if ( setup.wRequestAndType == 0x2021 /*CDC_SET_LINE_CODING*/ )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
// 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:
|
|
// Only use 2nd byte, first byte is the report id
|
|
USBKeys_LEDs = buf[1];
|
|
endpoint0_transmit( NULL, 0 );
|
|
break;
|
|
default:
|
|
warn_msg("Unknown interface - ");
|
|
printHex( setup.wIndex );
|
|
print( NL );
|
|
break;
|
|
}
|
|
|
|
#ifdef UART_DEBUG
|
|
for ( size_t len = 0; len < setup.wLength; len++ )
|
|
{
|
|
printHex( buf[ len ] );
|
|
print(" ");
|
|
}
|
|
print( NL );
|
|
#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);
|
|
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);
|
|
data += size;
|
|
ep0_tx_len -= size;
|
|
ep0_tx_ptr = (ep0_tx_len > 0 || size == EP0_SIZE) ? data : NULL;
|
|
}
|
|
|
|
if ( setup.bRequest == 5 && setup.bmRequestType == 0 )
|
|
{
|
|
setup.bRequest = 0;
|
|
#ifdef UART_DEBUG
|
|
print("set address: ");
|
|
printHex(setup.wValue);
|
|
print(NL);
|
|
#endif
|
|
USB0_ADDR = setup.wValue;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
#ifdef UART_DEBUG
|
|
print("PID=unknown:");
|
|
printHex(pid);
|
|
print(NL);
|
|
#endif
|
|
break;
|
|
}
|
|
USB0_CTL = USB_CTL_USBENSOFEN; // clear TXSUSPENDTOKENBUSY bit
|
|
}
|
|
|
|
usb_packet_t *usb_rx( uint32_t endpoint )
|
|
{
|
|
//print("USB RX");
|
|
usb_packet_t *ret;
|
|
endpoint--;
|
|
if ( endpoint >= NUM_ENDPOINTS )
|
|
return NULL;
|
|
__disable_irq();
|
|
ret = rx_first[endpoint];
|
|
if ( ret )
|
|
rx_first[ endpoint ] = ret->next;
|
|
usb_rx_byte_count_data[ endpoint ] -= ret->len;
|
|
__enable_irq();
|
|
//serial_print("rx, epidx=");
|
|
//serial_phex(endpoint);
|
|
//serial_print(", packet=");
|
|
//serial_phex32(ret);
|
|
//serial_print("\n");
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t usb_queue_byte_count( const usb_packet_t *p )
|
|
{
|
|
uint32_t count=0;
|
|
|
|
__disable_irq();
|
|
for ( ; p; p = p->next )
|
|
{
|
|
count += p->len;
|
|
}
|
|
__enable_irq();
|
|
return count;
|
|
}
|
|
|
|
uint32_t usb_tx_byte_count( uint32_t endpoint )
|
|
{
|
|
endpoint--;
|
|
if ( endpoint >= NUM_ENDPOINTS )
|
|
return 0;
|
|
return usb_queue_byte_count( tx_first[ endpoint ] );
|
|
}
|
|
|
|
uint32_t usb_tx_packet_count( uint32_t endpoint )
|
|
{
|
|
const usb_packet_t *p;
|
|
uint32_t count=0;
|
|
|
|
endpoint--;
|
|
if ( endpoint >= NUM_ENDPOINTS )
|
|
return 0;
|
|
__disable_irq();
|
|
for ( p = tx_first[ endpoint ]; p; p = p->next )
|
|
count++;
|
|
__enable_irq();
|
|
return count;
|
|
}
|
|
|
|
|
|
// Called from usb_free, but only when usb_rx_memory_needed > 0, indicating
|
|
// receive endpoints are starving for memory. The intention is to give
|
|
// endpoints needing receive memory priority over the user's code, which is
|
|
// likely calling usb_malloc to obtain memory for transmitting. When the
|
|
// user is creating data very quickly, their consumption could starve reception
|
|
// without this prioritization. The packet buffer (input) is assigned to the
|
|
// first endpoint needing memory.
|
|
//
|
|
void usb_rx_memory( usb_packet_t *packet )
|
|
{
|
|
//print("USB RX MEMORY");
|
|
unsigned int i;
|
|
const uint8_t *cfg;
|
|
|
|
cfg = usb_endpoint_config_table;
|
|
//serial_print("rx_mem:");
|
|
__disable_irq();
|
|
for ( i = 1; i <= NUM_ENDPOINTS; i++ )
|
|
{
|
|
if ( *cfg++ & USB_ENDPT_EPRXEN )
|
|
{
|
|
if ( table[ index( i, RX, EVEN ) ].desc == 0 )
|
|
{
|
|
table[ index( i, RX, EVEN ) ].addr = packet->buf;
|
|
table[ index( i, RX, EVEN ) ].desc = BDT_DESC( 64, 0 );
|
|
usb_rx_memory_needed--;
|
|
__enable_irq();
|
|
//serial_phex(i);
|
|
//serial_print(",even\n");
|
|
return;
|
|
}
|
|
if ( table[ index( i, RX, ODD ) ].desc == 0 )
|
|
{
|
|
table[ index( i, RX, ODD ) ].addr = packet->buf;
|
|
table[ index( i, RX, ODD ) ].desc = BDT_DESC( 64, 1 );
|
|
usb_rx_memory_needed--;
|
|
__enable_irq();
|
|
//serial_phex(i);
|
|
//serial_print(",odd\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
__enable_irq();
|
|
// we should never reach this point. If we get here, it means
|
|
// usb_rx_memory_needed was set greater than zero, but no memory
|
|
// was actually needed.
|
|
usb_rx_memory_needed = 0;
|
|
usb_free( 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 )
|
|
{
|
|
// Update expiry counter
|
|
USBKeys_Idle_Expiry = systick_millis_count;
|
|
|
|
// If we have been sleeping, try to wake up host
|
|
if ( usb_dev_sleep )
|
|
{
|
|
// 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;
|
|
delay(10);
|
|
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
|
|
}
|
|
|
|
// 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;
|
|
|
|
endpoint--;
|
|
if ( endpoint >= NUM_ENDPOINTS )
|
|
return;
|
|
__disable_irq();
|
|
//serial_print("txstate=");
|
|
//serial_phex(tx_state[ endpoint ]);
|
|
//serial_print("\n");
|
|
switch ( tx_state[ endpoint ] )
|
|
{
|
|
case TX_STATE_BOTH_FREE_EVEN_FIRST:
|
|
next = TX_STATE_ODD_FREE;
|
|
break;
|
|
case TX_STATE_BOTH_FREE_ODD_FIRST:
|
|
b++;
|
|
next = TX_STATE_EVEN_FREE;
|
|
break;
|
|
case TX_STATE_EVEN_FREE:
|
|
next = TX_STATE_NONE_FREE_ODD_FIRST;
|
|
break;
|
|
case TX_STATE_ODD_FREE:
|
|
b++;
|
|
next = TX_STATE_NONE_FREE_EVEN_FIRST;
|
|
break;
|
|
default:
|
|
if (tx_first[ endpoint ] == NULL)
|
|
{
|
|
tx_first[ endpoint ] = packet;
|
|
}
|
|
else
|
|
{
|
|
tx_last[ endpoint ]->next = packet;
|
|
}
|
|
tx_last[ endpoint ] = packet;
|
|
__enable_irq();
|
|
return;
|
|
}
|
|
|
|
tx_state[ endpoint ] = next;
|
|
b->addr = packet->buf;
|
|
b->desc = BDT_DESC( packet->len, ((uint32_t)b & 8) ? DATA1 : DATA0 );
|
|
__enable_irq();
|
|
}
|
|
|
|
|
|
void usb_device_reload()
|
|
{
|
|
// MCHCK
|
|
// Kiibohd mk20dx256vlh7
|
|
#if defined(_mk20dx128vlf5_) || defined(_mk20dx256vlh7_)
|
|
// Copies variable into the VBAT register, must be identical to the variable in the bootloader to jump to the bootloader flash mode
|
|
for ( int pos = 0; pos < sizeof(sys_reset_to_loader_magic); pos++ )
|
|
(&VBAT)[ pos ] = sys_reset_to_loader_magic[ pos ];
|
|
SOFTWARE_RESET();
|
|
|
|
// Teensy 3.0 and 3.1
|
|
#else
|
|
asm volatile("bkpt");
|
|
#endif
|
|
}
|
|
|
|
|
|
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: ");
|
|
printHex( status );
|
|
print( NL );
|
|
*/
|
|
|
|
if ( (status & USB_INTEN_SOFTOKEN /* 04 */ ) )
|
|
{
|
|
if ( usb_configuration )
|
|
{
|
|
t = usb_reboot_timer;
|
|
if ( t )
|
|
{
|
|
usb_reboot_timer = --t;
|
|
if ( !t )
|
|
usb_device_reload();
|
|
}
|
|
|
|
// CDC Interface
|
|
t = usb_cdc_transmit_flush_timer;
|
|
if ( t )
|
|
{
|
|
usb_cdc_transmit_flush_timer = --t;
|
|
if ( t == 0 )
|
|
usb_serial_flush_callback();
|
|
}
|
|
|
|
}
|
|
USB0_ISTAT = USB_INTEN_SOFTOKEN;
|
|
}
|
|
|
|
if ( (status & USB_ISTAT_TOKDNE /* 08 */ ) )
|
|
{
|
|
uint8_t endpoint;
|
|
stat = USB0_STAT;
|
|
//serial_print("token: ep=");
|
|
//serial_phex(stat >> 4);
|
|
//serial_print(stat & 0x08 ? ",tx" : ",rx");
|
|
//serial_print(stat & 0x04 ? ",odd\n" : ",even\n");
|
|
endpoint = stat >> 4;
|
|
if ( endpoint == 0 )
|
|
{
|
|
usb_control( stat );
|
|
}
|
|
else
|
|
{
|
|
bdt_t *b = stat2bufferdescriptor(stat);
|
|
usb_packet_t *packet = (usb_packet_t *)((uint8_t *)(b->addr) - 8);
|
|
#if 0
|
|
serial_print("ep:");
|
|
serial_phex(endpoint);
|
|
serial_print(", pid:");
|
|
serial_phex(BDT_PID(b->desc));
|
|
serial_print(((uint32_t)b & 8) ? ", odd" : ", even");
|
|
serial_print(", count:");
|
|
serial_phex(b->desc >> 16);
|
|
serial_print("\n");
|
|
#endif
|
|
endpoint--; // endpoint is index to zero-based arrays
|
|
|
|
if ( stat & 0x08 )
|
|
{ // transmit
|
|
usb_free( packet );
|
|
packet = tx_first[ endpoint ];
|
|
if ( packet )
|
|
{
|
|
//serial_print("tx packet\n");
|
|
tx_first[endpoint] = packet->next;
|
|
b->addr = packet->buf;
|
|
switch ( tx_state[ endpoint ] )
|
|
{
|
|
case TX_STATE_BOTH_FREE_EVEN_FIRST:
|
|
tx_state[ endpoint ] = TX_STATE_ODD_FREE;
|
|
break;
|
|
case TX_STATE_BOTH_FREE_ODD_FIRST:
|
|
tx_state[ endpoint ] = TX_STATE_EVEN_FREE;
|
|
break;
|
|
case TX_STATE_EVEN_FREE:
|
|
tx_state[ endpoint ] = TX_STATE_NONE_FREE_ODD_FIRST;
|
|
break;
|
|
case TX_STATE_ODD_FREE:
|
|
tx_state[ endpoint ] = TX_STATE_NONE_FREE_EVEN_FIRST;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
b->desc = BDT_DESC( packet->len, ((uint32_t)b & 8) ? DATA1 : DATA0 );
|
|
} else {
|
|
//serial_print("tx no packet\n");
|
|
switch ( tx_state[ endpoint ] )
|
|
{
|
|
case TX_STATE_BOTH_FREE_EVEN_FIRST:
|
|
case TX_STATE_BOTH_FREE_ODD_FIRST:
|
|
break;
|
|
case TX_STATE_EVEN_FREE:
|
|
tx_state[ endpoint ] = TX_STATE_BOTH_FREE_EVEN_FIRST;
|
|
break;
|
|
case TX_STATE_ODD_FREE:
|
|
tx_state[ endpoint ] = TX_STATE_BOTH_FREE_ODD_FIRST;
|
|
break;
|
|
default:
|
|
tx_state[ endpoint ] = ((uint32_t)b & 8)
|
|
? TX_STATE_ODD_FREE
|
|
: TX_STATE_EVEN_FREE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // receive
|
|
packet->len = b->desc >> 16;
|
|
if ( packet->len > 0 )
|
|
{
|
|
packet->index = 0;
|
|
packet->next = NULL;
|
|
if ( rx_first[ endpoint ] == NULL )
|
|
{
|
|
//serial_print("rx 1st, epidx=");
|
|
//serial_phex(endpoint);
|
|
//serial_print(", packet=");
|
|
//serial_phex32((uint32_t)packet);
|
|
//serial_print("\n");
|
|
rx_first[ endpoint ] = packet;
|
|
}
|
|
else
|
|
{
|
|
//serial_print("rx Nth, epidx=");
|
|
//serial_phex(endpoint);
|
|
//serial_print(", packet=");
|
|
//serial_phex32((uint32_t)packet);
|
|
//serial_print("\n");
|
|
rx_last[ endpoint ]->next = packet;
|
|
}
|
|
rx_last[ endpoint ] = packet;
|
|
usb_rx_byte_count_data[ endpoint ] += packet->len;
|
|
// TODO: implement a per-endpoint maximum # of allocated packets
|
|
// so a flood of incoming data on 1 endpoint doesn't starve
|
|
// the others if the user isn't reading it regularly
|
|
packet = usb_malloc();
|
|
if ( packet )
|
|
{
|
|
b->addr = packet->buf;
|
|
b->desc = BDT_DESC( 64, ((uint32_t)b & 8) ? DATA1 : DATA0 );
|
|
}
|
|
else
|
|
{
|
|
//serial_print("starving ");
|
|
//serial_phex(endpoint + 1);
|
|
//serial_print(((uint32_t)b & 8) ? ",odd\n" : ",even\n");
|
|
b->desc = 0;
|
|
usb_rx_memory_needed++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
b->desc = BDT_DESC( 64, ((uint32_t)b & 8) ? DATA1 : DATA0 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
USB0_ISTAT = USB_ISTAT_TOKDNE;
|
|
goto restart;
|
|
}
|
|
|
|
|
|
if ( status & USB_ISTAT_USBRST /* 01 */ )
|
|
{
|
|
//serial_print("reset\n");
|
|
|
|
// initialize BDT toggle bits
|
|
USB0_CTL = USB_CTL_ODDRST;
|
|
ep0_tx_bdt_bank = 0;
|
|
|
|
// set up buffers to receive Setup and OUT packets
|
|
table[index( 0, RX, EVEN ) ].desc = BDT_DESC( EP0_SIZE, 0 );
|
|
table[index( 0, RX, EVEN ) ].addr = ep0_rx0_buf;
|
|
table[index( 0, RX, ODD ) ].desc = BDT_DESC( EP0_SIZE, 0 );
|
|
table[index( 0, RX, ODD ) ].addr = ep0_rx1_buf;
|
|
table[index( 0, TX, EVEN ) ].desc = 0;
|
|
table[index( 0, TX, ODD ) ].desc = 0;
|
|
|
|
// activate endpoint 0
|
|
USB0_ENDPT0 = USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK;
|
|
|
|
// clear all ending interrupts
|
|
USB0_ERRSTAT = 0xFF;
|
|
USB0_ISTAT = 0xFF;
|
|
|
|
// set the address to zero during enumeration
|
|
USB0_ADDR = 0;
|
|
|
|
// enable other interrupts
|
|
USB0_ERREN = 0xFF;
|
|
USB0_INTEN = USB_INTEN_TOKDNEEN |
|
|
USB_INTEN_SOFTOKEN |
|
|
USB_INTEN_STALLEN |
|
|
USB_INTEN_ERROREN |
|
|
USB_INTEN_USBRSTEN |
|
|
USB_INTEN_RESUMEEN |
|
|
USB_INTEN_SLEEPEN;
|
|
|
|
// is this necessary?
|
|
USB0_CTL = USB_CTL_USBENSOFEN;
|
|
return;
|
|
}
|
|
|
|
|
|
if ( (status & USB_ISTAT_STALL /* 80 */ ) )
|
|
{
|
|
//serial_print("stall:\n");
|
|
USB0_ENDPT0 = USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK;
|
|
USB0_ISTAT = USB_ISTAT_STALL;
|
|
}
|
|
if ( (status & USB_ISTAT_ERROR /* 02 */ ) )
|
|
{
|
|
uint8_t err = USB0_ERRSTAT;
|
|
USB0_ERRSTAT = err;
|
|
//serial_print("err:");
|
|
//serial_phex(err);
|
|
//serial_print("\n");
|
|
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 */ ) )
|
|
{
|
|
//info_print("Host has requested USB sleep/suspend state");
|
|
Output_update_usb_current( 100 ); // Set to 100 mA
|
|
usb_dev_sleep = 1;
|
|
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");
|
|
Output_update_usb_current( *usb_bMaxPower * 2 );
|
|
usb_dev_sleep = 0;
|
|
USB0_ISTAT |= USB_ISTAT_RESUME;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
uint8_t usb_init()
|
|
{
|
|
#ifdef UART_DEBUG
|
|
print("USB INIT"NL);
|
|
#endif
|
|
|
|
// Clear out endpoints table
|
|
for ( int i = 0; i <= NUM_ENDPOINTS * 4; i++ )
|
|
{
|
|
table[i].desc = 0;
|
|
table[i].addr = 0;
|
|
}
|
|
|
|
// this basically follows the flowchart in the Kinetis
|
|
// Quick Reference User Guide, Rev. 1, 03/2012, page 141
|
|
|
|
// assume 48 MHz clock already running
|
|
// SIM - enable clock
|
|
SIM_SCGC4 |= SIM_SCGC4_USBOTG;
|
|
|
|
// reset USB module
|
|
USB0_USBTRC0 = USB_USBTRC_USBRESET;
|
|
while ( (USB0_USBTRC0 & USB_USBTRC_USBRESET) != 0 ); // wait for reset to end
|
|
|
|
// set desc table base addr
|
|
USB0_BDTPAGE1 = ((uint32_t)table) >> 8;
|
|
USB0_BDTPAGE2 = ((uint32_t)table) >> 16;
|
|
USB0_BDTPAGE3 = ((uint32_t)table) >> 24;
|
|
|
|
// clear all ISR flags
|
|
USB0_ISTAT = 0xFF;
|
|
USB0_ERRSTAT = 0xFF;
|
|
USB0_OTGISTAT = 0xFF;
|
|
|
|
USB0_USBTRC0 |= 0x40; // undocumented bit
|
|
|
|
// enable USB
|
|
USB0_CTL = USB_CTL_USBENSOFEN;
|
|
USB0_USBCTRL = 0;
|
|
|
|
// enable reset interrupt
|
|
USB0_INTEN = USB_INTEN_USBRSTEN;
|
|
|
|
// enable interrupt in NVIC...
|
|
NVIC_SET_PRIORITY( IRQ_USBOTG, 112 );
|
|
NVIC_ENABLE_IRQ( IRQ_USBOTG );
|
|
|
|
// enable d+ pullup
|
|
USB0_CONTROL = USB_CONTROL_DPPULLUPNONOTG;
|
|
|
|
// Do not check for power negotiation delay until Get Configuration Descriptor
|
|
power_neg_delay = 0;
|
|
|
|
// During initialization host isn't sleeping
|
|
usb_dev_sleep = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// return 0 if the USB is not configured, or the configuration
|
|
// number selected by the HOST
|
|
uint8_t usb_configured()
|
|
{
|
|
return usb_configuration;
|
|
}
|
|
|