diff --git a/firmware/usb_performance/usb.c b/firmware/usb_performance/usb.c new file mode 100644 index 00000000..a47c47c2 --- /dev/null +++ b/firmware/usb_performance/usb.c @@ -0,0 +1,603 @@ +#include +#include + +#include "usb.h" +#include "usb_type.h" +#include "usb_standard_request.h" + +#include +#include +#include +#include + +usb_device_t* usb_device_usb0 = 0; + +usb_queue_head_t usb_qh[12] ATTR_SECTION(".usb_qh"); +usb_transfer_descriptor_t usb_td[12] ATTR_SECTION(".usb_td"); + +#define USB_QH_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) +#define USB_TD_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) + +usb_queue_head_t* usb_queue_head( + const uint_fast8_t endpoint_address +) { + return &usb_qh[USB_QH_INDEX(endpoint_address)]; +} + +usb_transfer_descriptor_t* usb_transfer_descriptor( + const uint_fast8_t endpoint_address +) { + return &usb_td[USB_TD_INDEX(endpoint_address)]; +} + +static usb_endpoint_t* usb_endpoint_from_address( + const uint_fast8_t endpoint_address +) { + return (usb_endpoint_t*)usb_queue_head(endpoint_address)->_reserved_0; +} + +static uint_fast8_t usb_endpoint_address( + const usb_transfer_direction_t direction, + const uint_fast8_t number +) { + return ((direction == USB_TRANSFER_DIRECTION_IN) ? 0x80 : 0x00) + number; +} + +static bool usb_endpoint_is_in(const uint_fast8_t endpoint_address) { + return (endpoint_address & 0x80) ? true : false; +} + +static uint_fast8_t usb_endpoint_number(const uint_fast8_t endpoint_address) { + return (endpoint_address & 0xF); +} + +void usb_peripheral_reset() { + RESET_CTRL0 = RESET_CTRL0_USB0_RST; + RESET_CTRL0 = 0; + + while( (RESET_ACTIVE_STATUS0 & RESET_CTRL0_USB0_RST) == 0 ); +} + +static void usb_phy_enable() { + CREG_CREG0 &= ~CREG_CREG0_USB0PHY; +} + +static void usb_wait_for_endpoint_priming_to_finish(const uint32_t mask) { + // Wait until controller has parsed new transfer descriptors and prepared + // receive buffers. + while( USB0_ENDPTPRIME & mask ); +} + +static void usb_flush_endpoints(const uint32_t mask) { + // Clear any primed buffers. If a packet is in progress, that transfer + // will continue until completion. + USB0_ENDPTFLUSH = mask; +} + +static void usb_wait_for_endpoint_flushing_to_finish(const uint32_t mask) { + // Wait until controller has flushed all endpoints / cleared any primed + // buffers. + while( USB0_ENDPTFLUSH & mask ); +} + +static void usb_flush_primed_endpoints(const uint32_t mask) { + usb_wait_for_endpoint_priming_to_finish(mask); + usb_flush_endpoints(mask); + usb_wait_for_endpoint_flushing_to_finish(mask); +} + +static void usb_flush_all_primed_endpoints() { + usb_flush_primed_endpoints(0xFFFFFFFF); +} + +static void usb_endpoint_set_type( + const usb_endpoint_t* const endpoint, + const usb_transfer_type_t transfer_type +) { + // NOTE: UM10503 section 23.6.24 "Endpoint 1 to 5 control registers" says + // that the disabled side of an endpoint must be set to a non-control type + // (e.g. bulk, interrupt, or iso). + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + USB0_ENDPTCTRL(endpoint_number) + = ( USB0_ENDPTCTRL(endpoint_number) + & ~(USB0_ENDPTCTRL_TXT1_0_MASK | USB0_ENDPTCTRL_RXT_MASK) + ) + | ( USB0_ENDPTCTRL_TXT1_0(transfer_type) + | USB0_ENDPTCTRL_RXT(transfer_type) + ); +} + +static void usb_endpoint_enable( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + USB0_ENDPTCTRL(endpoint_number) |= (USB0_ENDPTCTRL_TXE | USB0_ENDPTCTRL_TXR); + } else { + USB0_ENDPTCTRL(endpoint_number) |= (USB0_ENDPTCTRL_RXE | USB0_ENDPTCTRL_RXR); + } +} + +void usb_endpoint_disable( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + USB0_ENDPTCTRL(endpoint_number) &= ~(USB0_ENDPTCTRL_TXE); + } else { + USB0_ENDPTCTRL(endpoint_number) &= ~(USB0_ENDPTCTRL_RXE); + } +} + +void usb_endpoint_prime( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const first_td +) { + usb_queue_head_t* const qh = usb_queue_head(endpoint->address); + + qh->next_dtd_pointer = first_td; + qh->total_bytes + &= ~( USB_TD_DTD_TOKEN_STATUS_ACTIVE + | USB_TD_DTD_TOKEN_STATUS_HALTED + ) + ; + + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + USB0_ENDPTPRIME = USB0_ENDPTPRIME_PETB(1 << endpoint_number); + } else { + USB0_ENDPTPRIME = USB0_ENDPTPRIME_PERB(1 << endpoint_number); + } +} + +static bool usb_endpoint_is_priming( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + return USB0_ENDPTPRIME & USB0_ENDPTPRIME_PETB(1 << endpoint_number); + } else { + return USB0_ENDPTPRIME & USB0_ENDPTPRIME_PERB(1 << endpoint_number); + } +} + +void usb_endpoint_flush( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + usb_flush_primed_endpoints(USB0_ENDPTFLUSH_FETB(1 << endpoint_number)); + } else { + usb_flush_primed_endpoints(USB0_ENDPTFLUSH_FERB(1 << endpoint_number)); + } +} + +static bool usb_endpoint_is_flushing( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + return USB0_ENDPTFLUSH & USB0_ENDPTFLUSH_FETB(1 << endpoint_number); + } else { + return USB0_ENDPTFLUSH & USB0_ENDPTFLUSH_FERB(1 << endpoint_number); + } +} + +bool usb_endpoint_is_ready( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + return USB0_ENDPTSTAT & USB0_ENDPTSTAT_ETBR(1 << endpoint_number); + } else { + return USB0_ENDPTSTAT & USB0_ENDPTSTAT_ERBR(1 << endpoint_number); + } +} + +void usb_endpoint_stall( + const usb_endpoint_t* const endpoint +) { + // Endpoint is to be stalled as a pair -- both OUT and IN. + // See UM10503 section 23.10.5.2 "Stalling" + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + USB0_ENDPTCTRL(endpoint_number) |= (USB0_ENDPTCTRL_RXS | USB0_ENDPTCTRL_TXS); + + // TODO: Also need to reset data toggle in both directions? +} + +static void usb_controller_run() { + USB0_USBCMD_D |= USB0_USBCMD_D_RS; +} + +static void usb_controller_stop() { + USB0_USBCMD_D &= ~USB0_USBCMD_D_RS; +} + +static uint_fast8_t usb_controller_is_resetting() { + return (USB0_USBCMD_D & USB0_USBCMD_D_RST) != 0; +} + +static void usb_controller_set_device_mode() { + // Set USB0 peripheral mode + USB0_USBMODE_D = USB0_USBMODE_D_CM1_0(2); + + // Set device-related OTG flags + // OTG termination: controls pull-down on USB_DM + // VBUS_Discharge: VBUS discharges through resistor + USB0_OTGSC = USB0_OTGSC_OT | USB0_OTGSC_VD; +} + +usb_speed_t usb_speed( + const usb_device_t* const device +) { + if( device == usb_device_usb0 ) { + switch( USB0_PORTSC1_D & USB0_PORTSC1_D_PSPD_MASK ) { + case USB0_PORTSC1_D_PSPD(0): + return USB_SPEED_FULL; + + case USB0_PORTSC1_D_PSPD(2): + return USB_SPEED_HIGH; + + default: + // TODO: What to do/return here? Is this even possible? + return USB_SPEED_FULL; + } + } else { + // TODO: This should not be possible with a more class-like + // implementation. + return USB_SPEED_FULL; + } +} + +static void usb_clear_status(const uint32_t status) { + USB0_USBSTS_D = status; +} + +static uint32_t usb_get_status() { + // Mask status flags with enabled flag interrupts. + const uint32_t status = USB0_USBSTS_D & USB0_USBINTR_D; + + // Clear flags that were just read, leaving alone any flags that + // were just set (after the read). It's important to read and + // reset flags atomically! :-) + usb_clear_status(status); + + return status; +} + +static void usb_clear_endpoint_setup_status(const uint32_t endpoint_setup_status) { + USB0_ENDPTSETUPSTAT = endpoint_setup_status; +} + +static uint32_t usb_get_endpoint_setup_status() { + return USB0_ENDPTSETUPSTAT; +} + +static void usb_clear_endpoint_complete(const uint32_t endpoint_complete) { + USB0_ENDPTCOMPLETE = endpoint_complete; +} + +static uint32_t usb_get_endpoint_complete() { + return USB0_ENDPTCOMPLETE; +} + +static void usb_disable_all_endpoints() { + // Endpoint 0 is always enabled. TODO: So why set ENDPTCTRL0? + USB0_ENDPTCTRL0 &= ~(USB0_ENDPTCTRL0_RXE | USB0_ENDPTCTRL0_TXE); + USB0_ENDPTCTRL1 &= ~(USB0_ENDPTCTRL1_RXE | USB0_ENDPTCTRL1_TXE); + USB0_ENDPTCTRL2 &= ~(USB0_ENDPTCTRL2_RXE | USB0_ENDPTCTRL2_TXE); + USB0_ENDPTCTRL3 &= ~(USB0_ENDPTCTRL3_RXE | USB0_ENDPTCTRL3_TXE); + USB0_ENDPTCTRL4 &= ~(USB0_ENDPTCTRL4_RXE | USB0_ENDPTCTRL4_TXE); + USB0_ENDPTCTRL5 &= ~(USB0_ENDPTCTRL5_RXE | USB0_ENDPTCTRL5_TXE); +} + +static void usb_clear_all_pending_interrupts() { + USB0_ENDPTNAK = ~0; + USB0_ENDPTNAKEN = 0; + USB0_USBSTS_D = ~0; + USB0_ENDPTSETUPSTAT = USB0_ENDPTSETUPSTAT; + USB0_ENDPTCOMPLETE = USB0_ENDPTCOMPLETE; +} + +void usb_set_address_immediate( + const usb_device_t* const device, + const uint_fast8_t address +) { + if( device == usb_device_usb0 ) { + USB0_DEVICEADDR = USB0_DEVICEADDR_USBADR(address); + } +} + +void usb_set_address_deferred( + const usb_device_t* const device, + const uint_fast8_t address +) { + if( device == usb_device_usb0 ) { + USB0_DEVICEADDR + = USB0_DEVICEADDR_USBADR(address) + | USB0_DEVICEADDR_USBADRA + ; + } +} + +static void usb_reset_all_endpoints() { + usb_disable_all_endpoints(); + usb_clear_all_pending_interrupts(); + usb_flush_all_primed_endpoints(); +} + +static void usb_controller_reset() { + // TODO: Good to disable some USB interrupts to avoid priming new + // new endpoints before the controller is reset? + usb_reset_all_endpoints(); + usb_controller_stop(); + + // Reset controller. Resets internal pipelines, timers, counters, state + // machines to initial values. Not recommended when device is in attached + // state -- effect on attached host is undefined. Detach first by flushing + // all primed endpoints and stopping controller. + USB0_USBCMD_D = USB0_USBCMD_D_RST; + + while( usb_controller_is_resetting() ); +} + +static void usb_bus_reset(usb_device_t* const device) { + // According to UM10503 v1.4 section 23.10.3 "Bus reset": + usb_reset_all_endpoints(); + usb_set_address_immediate(device, 0); + usb_set_configuration(device, 0); + + // TODO: Enable endpoint 0, which might not actually be necessary, + // as the datasheet claims it can't be disabled. + + //wait_ms(3); + // + //if( USB0_PORTSC1 & USB0_PORTSC1_PR ) { + // // Port still is in the reset state. + //} else { + // usb_hardware_reset(); + //} +} + +static void usb_interrupt_enable( + usb_device_t* const device +) { + if( device == usb_device_usb0 ) { + nvic_enable_irq(NVIC_M4_USB0_IRQ); + } +} + +void usb_device_init( + const uint_fast8_t device_ordinal, + usb_device_t* const device +) { + if( device_ordinal == 0 ) { + usb_device_usb0 = device; + + usb_phy_enable(); + usb_controller_reset(); + usb_controller_set_device_mode(); + + // Set interrupt threshold interval to 0 + USB0_USBCMD_D &= ~USB0_USBCMD_D_ITC_MASK; + + // Configure endpoint list address + USB0_ENDPOINTLISTADDR = (uint32_t)usb_qh; + + // Enable interrupts + USB0_USBINTR_D = + USB0_USBINTR_D_UE + | USB0_USBINTR_D_UEE + | USB0_USBINTR_D_PCE + | USB0_USBINTR_D_URE + //| USB0_USBINTR_D_SRE + | USB0_USBINTR_D_SLE + //| USB0_USBINTR_D_NAKE + ; + } +} + +void usb_run( + usb_device_t* const device +) { + usb_interrupt_enable(device); + usb_controller_run(device); +} + +static void copy_setup(usb_setup_t* const dst, const volatile uint8_t* const src) { + dst->request_type = src[0]; + dst->request = src[1]; + dst->value_l = src[2]; + dst->value_h = src[3]; + dst->index_l = src[4]; + dst->index_h = src[5]; + dst->length_l = src[6]; + dst->length_h = src[7]; +} + +void usb_endpoint_init( + const usb_endpoint_t* const endpoint +) { + usb_endpoint_flush(endpoint); + + uint_fast16_t max_packet_size = endpoint->device->descriptor[7]; + usb_transfer_type_t transfer_type = USB_TRANSFER_TYPE_CONTROL; + const uint8_t* const endpoint_descriptor = usb_endpoint_descriptor(endpoint); + if( endpoint_descriptor ) { + max_packet_size = usb_endpoint_descriptor_max_packet_size(endpoint_descriptor); + transfer_type = usb_endpoint_descriptor_transfer_type(endpoint_descriptor); + } + + // TODO: There are more capabilities to adjust based on the endpoint + // descriptor. + usb_queue_head_t* const qh = usb_queue_head(endpoint->address); + qh->capabilities + = USB_QH_CAPABILITIES_MULT(0) + | USB_QH_CAPABILITIES_ZLT + | USB_QH_CAPABILITIES_MPL(max_packet_size) + | ((transfer_type == USB_TRANSFER_TYPE_CONTROL) ? USB_QH_CAPABILITIES_IOS : 0) + ; + qh->current_dtd_pointer = 0; + qh->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; + qh->total_bytes + = USB_TD_DTD_TOKEN_TOTAL_BYTES(0) + | USB_TD_DTD_TOKEN_MULTO(0) + ; + qh->buffer_pointer_page[0] = 0; + qh->buffer_pointer_page[1] = 0; + qh->buffer_pointer_page[2] = 0; + qh->buffer_pointer_page[3] = 0; + qh->buffer_pointer_page[4] = 0; + + // This is how we look up an endpoint structure from an endpoint address: + qh->_reserved_0 = (uint32_t)endpoint; + + // TODO: Should NAK be enabled? I'm kinda squishy on this... + //USB0_ENDPTNAKEN |= + // USB0_ENDPTNAKEN_EPRNE(1 << endpoint_out->number); + + usb_endpoint_set_type(endpoint, transfer_type); + + usb_endpoint_enable(endpoint); +} + +void usb_endpoint_schedule( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length +) { + usb_transfer_descriptor_t* const td = usb_transfer_descriptor(endpoint->address); + + // Ensure that OUT endpoint is ready to be primed. + // It may have been flushed due to an aborted transaction. + // TODO: This should be preceded by a flush? + while( usb_endpoint_is_ready(endpoint) ); + + // Configure a transfer. + td->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; + td->total_bytes = + USB_TD_DTD_TOKEN_TOTAL_BYTES(maximum_length) + | USB_TD_DTD_TOKEN_IOC + | USB_TD_DTD_TOKEN_MULTO(0) + | USB_TD_DTD_TOKEN_STATUS_ACTIVE + ; + td->buffer_pointer_page[0] = (uint32_t)data; + td->buffer_pointer_page[1] = ((uint32_t)data + 0x1000) & 0xfffff000; + td->buffer_pointer_page[2] = ((uint32_t)data + 0x2000) & 0xfffff000; + td->buffer_pointer_page[3] = ((uint32_t)data + 0x3000) & 0xfffff000; + td->buffer_pointer_page[4] = ((uint32_t)data + 0x4000) & 0xfffff000; + + usb_endpoint_prime(endpoint, td); +} + +void usb_endpoint_schedule_ack( + const usb_endpoint_t* const endpoint +) { + usb_endpoint_schedule(endpoint, 0, 0); +} + +static void usb_check_for_setup_events() { + const uint32_t endptsetupstat = usb_get_endpoint_setup_status(); + if( endptsetupstat ) { + for( uint_fast8_t i=0; i<6; i++ ) { + const uint32_t endptsetupstat_bit = USB0_ENDPTSETUPSTAT_ENDPTSETUPSTAT(1 << i); + if( endptsetupstat & endptsetupstat_bit ) { + usb_endpoint_t* const endpoint = + usb_endpoint_from_address( + usb_endpoint_address(USB_TRANSFER_DIRECTION_OUT, i) + ); + if( endpoint && endpoint->setup_complete ) { + copy_setup(&endpoint->setup, usb_queue_head(endpoint->address)->setup); + usb_clear_endpoint_setup_status(endptsetupstat_bit); + endpoint->setup_complete(endpoint); + } else { + usb_clear_endpoint_setup_status(endptsetupstat_bit); + } + } + } + } +} + +static void usb_check_for_transfer_events() { + const uint32_t endptcomplete = usb_get_endpoint_complete(); + if( endptcomplete ) { + for( uint_fast8_t i=0; i<6; i++ ) { + + const uint32_t endptcomplete_out_bit = USB0_ENDPTCOMPLETE_ERCE(1 << i); + if( endptcomplete & endptcomplete_out_bit ) { + usb_clear_endpoint_complete(endptcomplete_out_bit); + usb_endpoint_t* const endpoint = + usb_endpoint_from_address( + usb_endpoint_address(USB_TRANSFER_DIRECTION_OUT, i) + ); + if( endpoint && endpoint->transfer_complete ) { + endpoint->transfer_complete(endpoint); + } + } + + const uint32_t endptcomplete_in_bit = USB0_ENDPTCOMPLETE_ETCE(1 << i); + if( endptcomplete & endptcomplete_in_bit ) { + usb_clear_endpoint_complete(endptcomplete_in_bit); + usb_endpoint_t* const endpoint = + usb_endpoint_from_address( + usb_endpoint_address(USB_TRANSFER_DIRECTION_IN, i) + ); + if( endpoint && endpoint->transfer_complete ) { + endpoint->transfer_complete(endpoint); + } + } + } + } +} + +void usb0_irqhandler() { + const uint32_t status = usb_get_status(); + + if( status == 0 ) { + // Nothing to do. + return; + } + + if( status & USB0_USBSTS_D_UI ) { + // USB: + // - Completed transaction transfer descriptor has IOC set. + // - Short packet detected. + // - SETUP packet received. + + usb_check_for_setup_events(); + usb_check_for_transfer_events(); + + // TODO: Reset ignored ENDPTSETUPSTAT and ENDPTCOMPLETE flags? + } + + if( status & USB0_USBSTS_D_SRI ) { + // Start Of Frame received. + } + + if( status & USB0_USBSTS_D_PCI ) { + // Port change detect: + // Port controller entered full- or high-speed operational state. + } + + if( status & USB0_USBSTS_D_SLI ) { + // Device controller suspend. + } + + if( status & USB0_USBSTS_D_URI ) { + // USB reset received. + usb_bus_reset(usb_device_usb0); + } + + if( status & USB0_USBSTS_D_UEI ) { + // USB error: + // Completion of a USB transaction resulted in an error condition. + // Set along with USBINT if the TD on which the error interrupt + // occurred also had its interrupt on complete (IOC) bit set. + // The device controller detects resume signalling only. + } + + if( status & USB0_USBSTS_D_NAKI ) { + // Both the TX/RX endpoint NAK bit and corresponding TX/RX endpoint + // NAK enable bit are set. + } +} diff --git a/firmware/usb_performance/usb.h b/firmware/usb_performance/usb.h new file mode 100644 index 00000000..86c1683d --- /dev/null +++ b/firmware/usb_performance/usb.h @@ -0,0 +1,77 @@ +#ifndef __USB_H__ +#define __USB_H__ + +// TODO: Refactor to support high performance operations without having to +// expose usb_transfer_descriptor_t. Or usb_endpoint_prime(). Or, or, or... +#include + +#include "usb_type.h" + +#define ATTR_ALIGNED(x) __attribute__ ((aligned(x))) +#define ATTR_SECTION(x) __attribute__ ((section(x))) + +void usb_peripheral_reset(); + +void usb_device_init( + const uint_fast8_t device_ordinal, + usb_device_t* const device +); + +void usb_run( + usb_device_t* const device +); + +void usb_run_tasks( + const usb_device_t* const device +); + +usb_speed_t usb_speed( + const usb_device_t* const device +); + +void usb_set_address_immediate( + const usb_device_t* const device, + const uint_fast8_t address +); + +void usb_set_address_deferred( + const usb_device_t* const device, + const uint_fast8_t address +); + +void usb_endpoint_init( + const usb_endpoint_t* const endpoint +); + +void usb_endpoint_schedule( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length +); + +void usb_endpoint_schedule_ack( + const usb_endpoint_t* const endpoint +); + +void usb_endpoint_stall( + const usb_endpoint_t* const endpoint +); + +void usb_endpoint_disable( + const usb_endpoint_t* const endpoint +); + +void usb_endpoint_flush( + const usb_endpoint_t* const endpoint +); + +bool usb_endpoint_is_ready( + const usb_endpoint_t* const endpoint +); + +void usb_endpoint_prime( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const first_td +); + +#endif//__USB_H__ diff --git a/firmware/usb_performance/usb_descriptor.c b/firmware/usb_performance/usb_descriptor.c new file mode 100644 index 00000000..704978a3 --- /dev/null +++ b/firmware/usb_performance/usb_descriptor.c @@ -0,0 +1,169 @@ +#include + +#include "usb_type.h" + +#define USB_VENDOR_ID (0x1D50) +#define USB_PRODUCT_ID (0x604B) + +#define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) + +#define USB_MAX_PACKET0 (64) +#define USB_MAX_PACKET_BULK_FS (64) +#define USB_MAX_PACKET_BULK_HS (512) + +#define USB_BULK_IN_EP_ADDR (0x81) +#define USB_BULK_OUT_EP_ADDR (0x02) + +#define USB_STRING_LANGID (0x0409) + +uint8_t usb_descriptor_device[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(USB_PRODUCT_ID), // idProduct + USB_WORD(0x0100), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x00, // iSerialNumber + 0x01 // bNumConfigurations +}; + +uint8_t usb_descriptor_device_qualifier[] = { + 10, // bLength + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 64, // bMaxPacketSize0 + 0x01, // bNumOtherSpeedConfigurations + 0x00 // bReserved +}; + +uint8_t usb_descriptor_configuration_full_speed[] = { + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0xFF, // bInterfaceClass: vendor-specific + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 0, // TERMINATOR +}; + +uint8_t usb_descriptor_configuration_high_speed[] = { + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0xFF, // bInterfaceClass: vendor-specific + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 0, // TERMINATOR +}; + +uint8_t usb_descriptor_string_languages[] = { + 0x04, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + USB_WORD(USB_STRING_LANGID), // wLANGID +}; + +uint8_t usb_descriptor_string_manufacturer[] = { + 40, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'G', 0x00, + 'r', 0x00, + 'e', 0x00, + 'a', 0x00, + 't', 0x00, + ' ', 0x00, + 'S', 0x00, + 'c', 0x00, + 'o', 0x00, + 't', 0x00, + 't', 0x00, + ' ', 0x00, + 'G', 0x00, + 'a', 0x00, + 'd', 0x00, + 'g', 0x00, + 'e', 0x00, + 't', 0x00, + 's', 0x00, +}; + +uint8_t usb_descriptor_string_product[] = { + 14, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'H', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + 'R', 0x00, + 'F', 0x00, +}; + +uint8_t* const usb_descriptor_strings[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product, + + 0, // TERMINATOR +}; diff --git a/firmware/usb_performance/usb_descriptor.h b/firmware/usb_performance/usb_descriptor.h new file mode 100644 index 00000000..91026296 --- /dev/null +++ b/firmware/usb_performance/usb_descriptor.h @@ -0,0 +1,12 @@ +#include + +extern uint8_t usb_descriptor_device[]; +extern uint8_t usb_descriptor_device_qualifier[]; +extern uint8_t usb_descriptor_configuration_full_speed[]; +extern uint8_t usb_descriptor_configuration_high_speed[]; +extern uint8_t usb_descriptor_string_languages[]; +extern uint8_t usb_descriptor_string_manufacturer[]; +extern uint8_t usb_descriptor_string_product[]; +extern uint8_t usb_descriptor_string_serial_number[]; + +extern uint8_t* usb_descriptor_strings[]; diff --git a/firmware/usb_performance/usb_request.c b/firmware/usb_performance/usb_request.c new file mode 100644 index 00000000..08aacbc6 --- /dev/null +++ b/firmware/usb_performance/usb_request.c @@ -0,0 +1,63 @@ +#include "usb_request.h" + +#include + +static void usb_request( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +) { + usb_request_handler_fn handler = 0; + + switch( endpoint->setup.request_type & USB_SETUP_REQUEST_TYPE_mask ) { + case USB_SETUP_REQUEST_TYPE_STANDARD: + handler = usb_request_handlers.standard; + break; + + case USB_SETUP_REQUEST_TYPE_CLASS: + handler = usb_request_handlers.class; + break; + + case USB_SETUP_REQUEST_TYPE_VENDOR: + handler = usb_request_handlers.vendor; + break; + + case USB_SETUP_REQUEST_TYPE_RESERVED: + handler = usb_request_handlers.reserved; + break; + } + + if( handler ) { + handler(endpoint, stage); + } +} + +void usb_setup_complete( + usb_endpoint_t* const endpoint +) { + usb_request(endpoint, USB_TRANSFER_STAGE_SETUP); +} + +void usb_control_out_complete( + usb_endpoint_t* const endpoint +) { + const bool device_to_host = + endpoint->setup.request_type >> USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_shift; + if( device_to_host ) { + usb_request(endpoint, USB_TRANSFER_STAGE_STATUS); + } else { + usb_request(endpoint, USB_TRANSFER_STAGE_DATA); + } +} + +void usb_control_in_complete( + usb_endpoint_t* const endpoint +) { + const bool device_to_host = + endpoint->setup.request_type >> USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_shift; + if( device_to_host ) { + usb_request(endpoint, USB_TRANSFER_STAGE_DATA); + } else { + usb_request(endpoint, USB_TRANSFER_STAGE_STATUS); + } +} + diff --git a/firmware/usb_performance/usb_request.h b/firmware/usb_performance/usb_request.h new file mode 100644 index 00000000..51833b02 --- /dev/null +++ b/firmware/usb_performance/usb_request.h @@ -0,0 +1,45 @@ +#ifndef __USB_REQUEST_H__ +#define __USB_REQUEST_H__ + +#include "usb_type.h" + +typedef enum { + USB_RESPONSE_NONE, + USB_RESPONSE_IN, + USB_RESPONSE_OUT, + USB_RESPONSE_STALL, +} usb_endpoint_type_t; + +typedef enum { + USB_TRANSFER_STAGE_SETUP, + USB_TRANSFER_STAGE_DATA, + USB_TRANSFER_STAGE_STATUS, +} usb_transfer_stage_t; + +typedef void (*usb_request_handler_fn)( + usb_endpoint_t* const response, + const usb_transfer_stage_t stage +); + +typedef struct { + usb_request_handler_fn standard; + usb_request_handler_fn class; + usb_request_handler_fn vendor; + usb_request_handler_fn reserved; +} usb_request_handlers_t; + +extern const usb_request_handlers_t usb_request_handlers; + +void usb_setup_complete( + usb_endpoint_t* const endpoint +); + +void usb_control_in_complete( + usb_endpoint_t* const endpoint +); + +void usb_control_out_complete( + usb_endpoint_t* const endpoint +); + +#endif//__USB_REQUEST_H__ diff --git a/firmware/usb_performance/usb_standard_request.c b/firmware/usb_performance/usb_standard_request.c new file mode 100644 index 00000000..18330b13 --- /dev/null +++ b/firmware/usb_performance/usb_standard_request.c @@ -0,0 +1,257 @@ +#include + +#include "usb_standard_request.h" + +#include "usb.h" +#include "usb_type.h" +#include "usb_descriptor.h" + +const uint8_t* usb_endpoint_descriptor( + const usb_endpoint_t* const endpoint +) { + const usb_configuration_t* const configuration = endpoint->device->configuration; + if( configuration ) { + const uint8_t* descriptor = configuration->descriptor; + while( descriptor[0] != 0 ) { + if( descriptor[1] == USB_DESCRIPTOR_TYPE_ENDPOINT ) { + if( descriptor[2] == endpoint->address ) { + return descriptor; + } + } + descriptor += descriptor[0]; + } + } + + return 0; +} + +uint_fast16_t usb_endpoint_descriptor_max_packet_size( + const uint8_t* const endpoint_descriptor +) { + return (endpoint_descriptor[5] << 8) | endpoint_descriptor[4]; +} + +usb_transfer_type_t usb_endpoint_descriptor_transfer_type( + const uint8_t* const endpoint_descriptor +) { + return (endpoint_descriptor[3] & 0x3); +} + +extern bool usb_set_configuration( + usb_device_t* const device, + const uint_fast8_t configuration_number +); + +static void usb_send_descriptor( + usb_endpoint_t* const endpoint, + uint8_t* const descriptor_data +) { + const uint32_t setup_length = (endpoint->setup.length_h << 8) | endpoint->setup.length_l; + uint32_t descriptor_length = descriptor_data[0]; + if( descriptor_data[1] == USB_DESCRIPTOR_TYPE_CONFIGURATION ) { + descriptor_length = (descriptor_data[3] << 8) | descriptor_data[2]; + } + usb_endpoint_schedule( + endpoint->in, + descriptor_data, + (setup_length > descriptor_length) ? descriptor_length : setup_length + ); +} + +static void usb_send_descriptor_string( + usb_endpoint_t* const endpoint +) { + uint_fast8_t index = endpoint->setup.value_l; + for( uint_fast8_t i=0; usb_descriptor_strings[i] != 0; i++ ) { + if( i == index ) { + usb_send_descriptor(endpoint, usb_descriptor_strings[i]); + return; + } + } + + usb_endpoint_stall(endpoint); +} + +static void usb_standard_request_get_descriptor_setup( + usb_endpoint_t* const endpoint +) { + switch( endpoint->setup.value_h ) { + case USB_DESCRIPTOR_TYPE_DEVICE: + usb_send_descriptor(endpoint, usb_descriptor_device); + break; + + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + // TODO: Duplicated code. Refactor. + if( usb_speed(endpoint->device) == USB_SPEED_HIGH ) { + usb_send_descriptor(endpoint, usb_descriptor_configuration_high_speed); + } else { + usb_send_descriptor(endpoint, usb_descriptor_configuration_full_speed); + } + break; + + case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: + usb_send_descriptor(endpoint, usb_descriptor_device_qualifier); + break; + + case USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION: + // TODO: Duplicated code. Refactor. + if( usb_speed(endpoint->device) == USB_SPEED_HIGH ) { + usb_send_descriptor(endpoint, usb_descriptor_configuration_full_speed); + } else { + usb_send_descriptor(endpoint, usb_descriptor_configuration_high_speed); + } + break; + + case USB_DESCRIPTOR_TYPE_STRING: + usb_send_descriptor_string(endpoint); + break; + + case USB_DESCRIPTOR_TYPE_INTERFACE: + case USB_DESCRIPTOR_TYPE_ENDPOINT: + default: + usb_endpoint_stall(endpoint); + break; + } +} + +static void usb_standard_request_get_descriptor( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +) { + switch( stage ) { + case USB_TRANSFER_STAGE_SETUP: + usb_standard_request_get_descriptor_setup(endpoint); + usb_endpoint_schedule_ack(endpoint->out); + break; + + case USB_TRANSFER_STAGE_DATA: + break; + + case USB_TRANSFER_STAGE_STATUS: + break; + + } +} + +/*********************************************************************/ + +static void usb_standard_request_set_address( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +) { + switch( stage ) { + case USB_TRANSFER_STAGE_SETUP: + usb_set_address_deferred(endpoint->device, endpoint->setup.value_l); + usb_endpoint_schedule_ack(endpoint->in); + break; + + case USB_TRANSFER_STAGE_DATA: + break; + + case USB_TRANSFER_STAGE_STATUS: + /* NOTE: Not necessary to set address here, as DEVICEADR.USBADRA bit + * will cause controller to automatically perform set address + * operation on IN ACK. + */ + break; + + default: + break; + } +} + +/*********************************************************************/ + +static void usb_standard_request_set_configuration_setup( + usb_endpoint_t* const endpoint +) { + const uint8_t usb_configuration = endpoint->setup.value_l; + if( usb_set_configuration(endpoint->device, usb_configuration) ) { + if( usb_configuration == 0 ) { + // TODO: Should this be done immediately? + usb_set_address_immediate(endpoint->device, 0); + } + usb_endpoint_schedule_ack(endpoint->in); + } else { + usb_endpoint_stall(endpoint); + } +} + +static void usb_standard_request_set_configuration( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +) { + switch( stage ) { + case USB_TRANSFER_STAGE_SETUP: + usb_standard_request_set_configuration_setup(endpoint); + break; + + case USB_TRANSFER_STAGE_DATA: + break; + + case USB_TRANSFER_STAGE_STATUS: + break; + + } +} + +/*********************************************************************/ + +static void usb_standard_request_get_configuration_setup( + usb_endpoint_t* const endpoint +) { + if( (endpoint->setup.length_h == 0) && (endpoint->setup.length_l == 1) ) { + endpoint->buffer[0] = 0; + if( endpoint->device->configuration ) { + endpoint->buffer[0] = endpoint->device->configuration->number; + } + usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); + } else { + usb_endpoint_stall(endpoint); + } +} + +static void usb_standard_request_get_configuration( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +) { + switch( stage ) { + case USB_TRANSFER_STAGE_SETUP: + usb_standard_request_get_configuration_setup(endpoint); + usb_endpoint_schedule_ack(endpoint->out); + break; + + case USB_TRANSFER_STAGE_DATA: + break; + + case USB_TRANSFER_STAGE_STATUS: + break; + + } +} + +/*********************************************************************/ + +void usb_standard_request( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +) { + switch( endpoint->setup.request ) { + case USB_STANDARD_REQUEST_GET_DESCRIPTOR: + usb_standard_request_get_descriptor(endpoint, stage); + break; + + case USB_STANDARD_REQUEST_SET_ADDRESS: + usb_standard_request_set_address(endpoint, stage); + break; + + case USB_STANDARD_REQUEST_SET_CONFIGURATION: + usb_standard_request_set_configuration(endpoint, stage); + break; + + case USB_STANDARD_REQUEST_GET_CONFIGURATION: + usb_standard_request_get_configuration(endpoint, stage); + break; + + } +} diff --git a/firmware/usb_performance/usb_standard_request.h b/firmware/usb_performance/usb_standard_request.h new file mode 100644 index 00000000..87479942 --- /dev/null +++ b/firmware/usb_performance/usb_standard_request.h @@ -0,0 +1,24 @@ +#ifndef __USB_STANDARD_REQUEST_H__ +#define __USB_STANDARD_REQUEST_H__ + +#include "usb_type.h" +#include "usb_request.h" + +void usb_standard_request( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage +); + +const uint8_t* usb_endpoint_descriptor( + const usb_endpoint_t* const endpoint +); + +uint_fast16_t usb_endpoint_descriptor_max_packet_size( + const uint8_t* const endpoint_descriptor +); + +usb_transfer_type_t usb_endpoint_descriptor_transfer_type( + const uint8_t* const endpoint_descriptor +); + +#endif//__USB_STANDARD_REQUEST_H__ diff --git a/firmware/usb_performance/usb_type.h b/firmware/usb_performance/usb_type.h new file mode 100644 index 00000000..0393c1d0 --- /dev/null +++ b/firmware/usb_performance/usb_type.h @@ -0,0 +1,101 @@ +#ifndef __USB_TYPE_H__ +#define __USB_TYPE_H__ + +#include +#include + +typedef struct { + uint8_t request_type; + uint8_t request; + uint8_t value_l; + uint8_t value_h; + uint8_t index_l; + uint8_t index_h; + uint8_t length_l; + uint8_t length_h; +} usb_setup_t; + +typedef enum { + USB_STANDARD_REQUEST_GET_STATUS = 0, + USB_STANDARD_REQUEST_CLEAR_FEATURE = 1, + USB_STANDARD_REQUEST_SET_FEATURE = 3, + USB_STANDARD_REQUEST_SET_ADDRESS = 5, + USB_STANDARD_REQUEST_GET_DESCRIPTOR = 6, + USB_STANDARD_REQUEST_SET_DESCRIPTOR = 7, + USB_STANDARD_REQUEST_GET_CONFIGURATION = 8, + USB_STANDARD_REQUEST_SET_CONFIGURATION = 9, + USB_STANDARD_REQUEST_GET_INTERFACE = 10, + USB_STANDARD_REQUEST_SET_INTERFACE = 11, + USB_STANDARD_REQUEST_SYNCH_FRAME = 12, +} usb_standard_request_t; + +typedef enum { + USB_SETUP_REQUEST_TYPE_shift = 5, + USB_SETUP_REQUEST_TYPE_mask = 3 << USB_SETUP_REQUEST_TYPE_shift, + + USB_SETUP_REQUEST_TYPE_STANDARD = 0 << USB_SETUP_REQUEST_TYPE_shift, + USB_SETUP_REQUEST_TYPE_CLASS = 1 << USB_SETUP_REQUEST_TYPE_shift, + USB_SETUP_REQUEST_TYPE_VENDOR = 2 << USB_SETUP_REQUEST_TYPE_shift, + USB_SETUP_REQUEST_TYPE_RESERVED = 3 << USB_SETUP_REQUEST_TYPE_shift, + + USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_shift = 7, + USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_mask = 1 << USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_shift, + USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_HOST_TO_DEVICE = 0 << USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_shift, + USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_DEVICE_TO_HOST = 1 << USB_SETUP_REQUEST_TYPE_DATA_TRANSFER_DIRECTION_shift, +} usb_setup_request_type_t; + +typedef enum { + USB_TRANSFER_DIRECTION_OUT = 0, + USB_TRANSFER_DIRECTION_IN = 1, +} usb_transfer_direction_t; + +typedef enum { + USB_DESCRIPTOR_TYPE_DEVICE = 1, + USB_DESCRIPTOR_TYPE_CONFIGURATION = 2, + USB_DESCRIPTOR_TYPE_STRING = 3, + USB_DESCRIPTOR_TYPE_INTERFACE = 4, + USB_DESCRIPTOR_TYPE_ENDPOINT = 5, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER = 6, + USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION = 7, + USB_DESCRIPTOR_TYPE_INTERFACE_POWER = 8, +} usb_descriptor_type_t; + +typedef enum { + USB_TRANSFER_TYPE_CONTROL = 0, + USB_TRANSFER_TYPE_ISOCHRONOUS = 1, + USB_TRANSFER_TYPE_BULK = 2, + USB_TRANSFER_TYPE_INTERRUPT = 3, +} usb_transfer_type_t; + +typedef enum { + USB_SPEED_LOW = 0, + USB_SPEED_FULL = 1, + USB_SPEED_HIGH = 2, + USB_SPEED_SUPER = 3, +} usb_speed_t; + +typedef struct { + const uint8_t* const descriptor; + const uint32_t number; + const usb_speed_t speed; +} usb_configuration_t; + +typedef struct { + const uint8_t* const descriptor; + usb_configuration_t* (*configurations)[]; + const usb_configuration_t* configuration; +} usb_device_t; + +typedef struct usb_endpoint_t usb_endpoint_t; +struct usb_endpoint_t { + usb_setup_t setup; + uint8_t buffer[8]; // Buffer for use during IN stage. + const uint_fast8_t address; + usb_device_t* const device; + usb_endpoint_t* const in; + usb_endpoint_t* const out; + void (*setup_complete)(usb_endpoint_t* const endpoint); + void (*transfer_complete)(usb_endpoint_t* const endpoint); +}; + +#endif//__USB_TYPE_H__