usb: Refactor queue management
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user