Tons of new USB code. It should eventually migrate into common, or even libopencm3, once it's not a steaming pile of crap...
This commit is contained in:
603
firmware/usb_performance/usb.c
Normal file
603
firmware/usb_performance/usb.c
Normal file
@ -0,0 +1,603 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "usb.h"
|
||||||
|
#include "usb_type.h"
|
||||||
|
#include "usb_standard_request.h"
|
||||||
|
|
||||||
|
#include <libopencm3/lpc43xx/creg.h>
|
||||||
|
#include <libopencm3/lpc43xx/nvic.h>
|
||||||
|
#include <libopencm3/lpc43xx/rgu.h>
|
||||||
|
#include <libopencm3/lpc43xx/usb.h>
|
||||||
|
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
}
|
77
firmware/usb_performance/usb.h
Normal file
77
firmware/usb_performance/usb.h
Normal file
@ -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 <libopencm3/lpc43xx/usb.h>
|
||||||
|
|
||||||
|
#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__
|
169
firmware/usb_performance/usb_descriptor.c
Normal file
169
firmware/usb_performance/usb_descriptor.c
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
};
|
12
firmware/usb_performance/usb_descriptor.h
Normal file
12
firmware/usb_performance/usb_descriptor.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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[];
|
63
firmware/usb_performance/usb_request.c
Normal file
63
firmware/usb_performance/usb_request.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "usb_request.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
45
firmware/usb_performance/usb_request.h
Normal file
45
firmware/usb_performance/usb_request.h
Normal file
@ -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__
|
257
firmware/usb_performance/usb_standard_request.c
Normal file
257
firmware/usb_performance/usb_standard_request.c
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
24
firmware/usb_performance/usb_standard_request.h
Normal file
24
firmware/usb_performance/usb_standard_request.h
Normal file
@ -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__
|
101
firmware/usb_performance/usb_type.h
Normal file
101
firmware/usb_performance/usb_type.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#ifndef __USB_TYPE_H__
|
||||||
|
#define __USB_TYPE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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__
|
Reference in New Issue
Block a user