usb: Refactor queue management

This commit is contained in:
Ben Gamari
2013-07-02 22:39:26 -04:00
parent e6bf90af23
commit 6142c828df
5 changed files with 166 additions and 41 deletions

View File

@ -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

View File

@ -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

View File

@ -22,32 +22,145 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#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;
}

View File

@ -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__

View File

@ -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;