Mercurial > louis > kiibohd-controller
view Output/pjrcUSB/arm/usb_dev.c @ 308:ab4515606277
Fix whitespace
Use a consistent standard - Tabs in front for indenting, spaces after for anything else. This way everything stays nice and lined up while also letting users change there prefered indent level. Most of the new files from Haata where already in this format.
author | Rowan Decker <Smasher816@gmail.com> |
---|---|
date | Sun, 08 Mar 2015 18:40:01 -0700 |
parents | d5bf41d7f7ef |
children | 4f47971c45c2 |
line wrap: on
line source
/* Teensyduino Core Library * http://www.pjrc.com/teensy/ * Copyright (c) 2013 PJRC.COM, LLC. * Modifications by Jacob Alexander (2013-2014) * * 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> // 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]; // ----- Functions ----- static void endpoint0_stall() { 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; } 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; switch ( setup.wRequestAndType ) { case 0x0500: // SET_ADDRESS break; case 0x0900: // SET_CONFIGURATION #ifdef UART_DEBUG print("CONFIGURE - "); #endif usb_configuration = setup.wValue; reg = &USB0_ENDPT1; cfg = usb_endpoint_config_table; // 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; } break; case 0x0880: // GET_CONFIGURATION reply_buffer[0] = usb_configuration; datalen = 1; data = reply_buffer; break; case 0x0080: // GET_STATUS (device) reply_buffer[0] = 0; reply_buffer[1] = 0; datalen = 2; data = reply_buffer; break; 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; break; case 0x0102: // CLEAR_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? break; 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? break; 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; } #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"); endpoint0_stall(); return; 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"); endpoint0_stall(); return; // Cannot stall here (causes issues) case 0x0921: // HID SET_REPORT #ifdef UART_DEBUG print("SET_REPORT - "); printHex( setup.wValue ); print(" - "); printHex( setup.wValue & 0xFF ); print( NL ); #endif USBKeys_LEDs = setup.wValue & 0xFF; endpoint0_stall(); return; case 0x01A1: // HID GET_REPORT #ifdef UART_DEBUG print("GET_REPORT - "); printHex( USBKeys_LEDs ); print(NL); #endif data = (uint8_t*)&USBKeys_LEDs; datalen = 1; goto send; case 0x0A21: // HID SET_IDLE #ifdef UART_DEBUG print("SET_IDLE - "); printHex( setup.wValue ); print(NL); #endif USBKeys_Idle_Config = (setup.wValue >> 8); USBKeys_Idle_Count = 0; endpoint0_stall(); return; 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 endpoint0_stall(); return; // case 0xC940: default: #ifdef UART_DEBUG_UNKNOWN print("UNKNOWN"); #endif endpoint0_stall(); return; } send: #ifdef UART_DEBUG print("setup send "); printHex32((uint32_t)data); 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(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 print("PID=OUT"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 Interface if ( setup.word1 == 0x02000921 && setup.word2 == ( (1<<16) | KEYBOARD_INTERFACE ) ) { USBKeys_LEDs = buf[0]; endpoint0_transmit( NULL, 0 ); } // NKRO Keyboard Interface if ( setup.word1 == 0x02000921 && setup.word2 == ( (1<<16) | NKRO_KEYBOARD_INTERFACE ) ) { USBKeys_LEDs = buf[0]; endpoint0_transmit( NULL, 0 ); } // 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 ) { 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 #if defined(_mk20dx128vlf5_) // MCHCK Kiibohd Variant // Check to see if PTA3 (has a pull-up) is connected to GND (usually via jumper) // Only allow reload if the jumper is present (security) GPIOA_PDDR &= ~(1<<3); // Input PORTA_PCR3 = PORT_PCR_PFE | PORT_PCR_MUX(1); // Internal pull-up // Check for jumper if ( GPIOA_PDIR & (1<<3) ) { print( NL ); warn_print("Security jumper not present, cancelling firmware reload..."); info_msg("Replace jumper on middle 2 pins, or manually press the firmware reload button."); } else { // 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_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; } if ( (status & USB_ISTAT_SLEEP /* 10 */ ) ) { //serial_print("sleep\n"); USB0_ISTAT = USB_ISTAT_SLEEP; } } uint8_t usb_init() { #ifdef UART_DEBUG print("USB INIT"NL); #endif // If no USB cable is attached, do not initialize usb // XXX Test -HaaTa //if ( USB0_OTGISTAT & USB_OTGSTAT_ID ) // return 0; // 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; 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; }