From 6142c828df49eebf4db418cb963dad964f20ab3b Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Tue, 2 Jul 2013 22:39:26 -0400 Subject: [PATCH] usb: Refactor queue management --- firmware/hackrf_usb/usb.c | 27 ++-- firmware/hackrf_usb/usb.h | 7 +- firmware/hackrf_usb/usb_queue.c | 139 +++++++++++++++++++-- firmware/hackrf_usb/usb_queue.h | 22 +++- firmware/hackrf_usb/usb_standard_request.c | 12 +- 5 files changed, 166 insertions(+), 41 deletions(-) diff --git a/firmware/hackrf_usb/usb.c b/firmware/hackrf_usb/usb.c index c057b5c3..14a94397 100644 --- a/firmware/hackrf_usb/usb.c +++ b/firmware/hackrf_usb/usb.c @@ -188,38 +188,27 @@ void usb_endpoint_prime( } } +// Schedule an already filled-in transfer descriptor for execution on +// the given endpoint, waiting until the endpoint has finished. void usb_endpoint_schedule_wait( const usb_endpoint_t* const endpoint, - usb_transfer_descriptor_t* const td, - void* const data, - const uint32_t maximum_length + usb_transfer_descriptor_t* const td ) { // Ensure that 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); } -// Schedule an already filled-up transfer descriptor for execution on -// the given endpoint. Note that this requires that one knows the tail -// of the endpoint's TD queue -void usb_endpoint_append_td( +// Schedule an already filled-in transfer descriptor for execution on +// the given endpoint, appending to the end of the endpoint's queue if +// there are pending TDs. Note that this requires that one knows the +// tail of the endpoint's TD queue +void usb_endpoint_schedule_append( const usb_endpoint_t* const endpoint, usb_transfer_descriptor_t* const tail_td, usb_transfer_descriptor_t* const new_td diff --git a/firmware/hackrf_usb/usb.h b/firmware/hackrf_usb/usb.h index 7d64f358..a6fa6a92 100644 --- a/firmware/hackrf_usb/usb.h +++ b/firmware/hackrf_usb/usb.h @@ -82,7 +82,12 @@ void usb_endpoint_prime( usb_transfer_descriptor_t* const first_td ); -void usb_endpoint_append_td( +void usb_endpoint_schedule_wait( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const td +); + +void usb_endpoint_schedule_append( const usb_endpoint_t* const endpoint, usb_transfer_descriptor_t* const tail_td, usb_transfer_descriptor_t* const new_td diff --git a/firmware/hackrf_usb/usb_queue.c b/firmware/hackrf_usb/usb_queue.c index 5792bcae..0fc833d1 100644 --- a/firmware/hackrf_usb/usb_queue.c +++ b/firmware/hackrf_usb/usb_queue.c @@ -22,32 +22,145 @@ #include #include +#include #include "usb.h" +#include "usb_queue.h" -usb_transfer_descriptor_t usb_td[12] ATTR_ALIGNED(64); +struct _usb_transfer_t { + struct _usb_transfer_t* next; + usb_transfer_descriptor_t td ATTR_ALIGNED(64); + unsigned int actual_length; + usb_endpoint_t* endpoint; + bool finished; + transfer_completion_cb completion_cb; +}; -#define USB_TD_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) +usb_transfer_t transfer_pool[16]; -usb_transfer_descriptor_t* usb_transfer_descriptor( - const uint_fast8_t endpoint_address -) { - return &usb_td[USB_TD_INDEX(endpoint_address)]; +// Available transfer list +usb_transfer_t* free_transfers; + +#define USB_ENDPOINT_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) + +// Pending transfer heads +usb_transfer_t* endpoint_transfers[12] = {}; + +void init_transfers() { + usb_transfer_t* t = &transfer_pool[0]; + free_transfers = t; + for (unsigned int i=0; i < sizeof(transfer_pool) / sizeof(usb_transfer_t); i++, t++) { + t->next = t+1; + } + t->next = NULL; } -void usb_endpoint_schedule( +static bool usb_endpoint_is_in(const uint_fast8_t endpoint_address) { + return (endpoint_address & 0x80) ? true : false; +} + +#if 0 +static usb_transfer_t* usb_transfer( + const uint_fast8_t endpoint_address +) { + return endpoint_transfers[USB_ENDPOINT_INDEX(endpoint_address)]; +} +#endif + +static void fill_in_transfer(usb_transfer_t* transfer, + void* const data, + const uint32_t maximum_length +) { + usb_transfer_descriptor_t* const td = &transfer->td; + + // Configure a transfer. + 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; +} + +static usb_transfer_t* allocate_transfer() +{ + while (free_transfers == NULL); + //disable_irqs(); // FIXME + usb_transfer_t* const transfer = free_transfers; + free_transfers = transfer->next; + //enable_irqs(); + transfer->finished = false; + return transfer; +} + +static void endpoint_add_transfer( + const usb_endpoint_t* const endpoint, + usb_transfer_t* const transfer +) { + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + //FIXME disable_irqs(); + usb_transfer_t* t = endpoint_transfers[index]; + transfer->next = NULL; + for (; t->next != NULL; t = t->next); + t->next = transfer; + //enable_irqs(); +} + +static usb_transfer_t* endpoint_pop_transfer( + const usb_endpoint_t* const endpoint +) { + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + //FIXME disable_irqs(); + usb_transfer_t* transfer = endpoint_transfers[index]; + endpoint_transfers[index] = transfer->next; + //enable_irqs(); + return transfer; +} + +void usb_transfer_schedule_wait( const usb_endpoint_t* const endpoint, void* const data, const uint32_t maximum_length ) { - usb_endpoint_schedule_wait(endpoint, - usb_transfer_descriptor(endpoint->address), - data, - maximum_length); + usb_transfer_t* const transfer = allocate_transfer(); + fill_in_transfer(transfer, data, maximum_length); + endpoint_add_transfer(endpoint, transfer); + usb_endpoint_schedule_wait(endpoint, &transfer->td); +} + +void usb_transfer_schedule_append( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length +) { + usb_transfer_t* const transfer = allocate_transfer(); + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + fill_in_transfer(transfer, data, maximum_length); + // TODO: disable_interrupts(); + usb_transfer_t* tail = endpoint_transfers[index]; + for (; tail->next != NULL; tail = tail->next); + endpoint_add_transfer(endpoint, transfer); + usb_endpoint_schedule_append(endpoint, &tail->td, &transfer->td); + //enable_interrupts(); } -void usb_endpoint_schedule_ack( +void usb_transfer_schedule_ack( const usb_endpoint_t* const endpoint ) { - usb_endpoint_schedule(endpoint, 0, 0); + usb_transfer_schedule_wait(endpoint, 0, 0); +} + +void transfer_complete(const usb_endpoint_t* const endpoint) +{ + usb_transfer_t* transfer = endpoint_pop_transfer(endpoint); + unsigned int transferred = transfer->actual_length - transfer->td.total_bytes; + if (transfer->completion_cb) + transfer->completion_cb(transfer, transferred); + else if (usb_endpoint_is_in(transfer->endpoint->address)) + transfer->finished = true; } diff --git a/firmware/hackrf_usb/usb_queue.h b/firmware/hackrf_usb/usb_queue.h index f07b8146..5e10b33e 100644 --- a/firmware/hackrf_usb/usb_queue.h +++ b/firmware/hackrf_usb/usb_queue.h @@ -27,20 +27,38 @@ #include "usb_type.h" +typedef struct _usb_transfer_t usb_transfer_t; + +typedef void (*transfer_completion_cb)(struct _usb_transfer_t*, unsigned int); + void usb_endpoint_schedule( const usb_endpoint_t* const endpoint, void* const data, const uint32_t maximum_length ); -void usb_endpoint_schedule_ack( +void usb_transfer_schedule_wait( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length +); + +void usb_transfer_schedule_append( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length +); + +void usb_transfer_schedule_ack( const usb_endpoint_t* const endpoint ); -void usb_endpoint_schedule( +void usb_transfer_schedule( const usb_endpoint_t* const endpoint, void* const data, const uint32_t maximum_length ); +void init_transfers(void); + #endif//__USB_QUEUE_H__ diff --git a/firmware/hackrf_usb/usb_standard_request.c b/firmware/hackrf_usb/usb_standard_request.c index 59991ef1..db9a0940 100644 --- a/firmware/hackrf_usb/usb_standard_request.c +++ b/firmware/hackrf_usb/usb_standard_request.c @@ -115,12 +115,12 @@ static usb_request_status_t usb_send_descriptor( if( descriptor_data[1] == USB_DESCRIPTOR_TYPE_CONFIGURATION ) { descriptor_length = (descriptor_data[3] << 8) | descriptor_data[2]; } - usb_endpoint_schedule( + usb_transfer_schedule_wait( endpoint->in, descriptor_data, (setup_length > descriptor_length) ? descriptor_length : setup_length ); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } @@ -196,7 +196,7 @@ static usb_request_status_t usb_standard_request_set_address_setup( usb_endpoint_t* const endpoint ) { usb_set_address_deferred(endpoint->device, endpoint->setup.value_l); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } @@ -232,7 +232,7 @@ static usb_request_status_t usb_standard_request_set_configuration_setup( // TODO: Should this be done immediately? usb_set_address_immediate(endpoint->device, 0); } - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } else { return USB_REQUEST_STATUS_STALL; @@ -266,8 +266,8 @@ static usb_request_status_t usb_standard_request_get_configuration_setup( if( endpoint->device->configuration ) { endpoint->buffer[0] = endpoint->device->configuration->number; } - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule_wait(endpoint->in, &endpoint->buffer, 1); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } else { return USB_REQUEST_STATUS_STALL;