HelenOS sources
This source file includes following definitions.
- uhci_transfer_batch_destroy
- uhci_transfer_batch_create
- uhci_transfer_batch_prepare
- uhci_transfer_batch_check_completed
- batch_data
- batch_control
#include <assert.h>
#include <errno.h>
#include <macros.h>
#include <mem.h>
#include <stdlib.h>
#include <usb/usb.h>
#include <usb/debug.h>
#include <usb/host/endpoint.h>
#include <usb/host/utils/malloc32.h>
#include "uhci_batch.h"
#include "hc.h"
#include "hw_struct/transfer_descriptor.h"
#define DEFAULT_ERROR_COUNT 3
static void (*const batch_setup[])(uhci_transfer_batch_t *);
void uhci_transfer_batch_destroy(uhci_transfer_batch_t *uhci_batch)
{
assert(uhci_batch);
dma_buffer_free(&uhci_batch->uhci_dma_buffer);
free(uhci_batch);
}
uhci_transfer_batch_t *uhci_transfer_batch_create(endpoint_t *ep)
{
uhci_transfer_batch_t *uhci_batch =
calloc(1, sizeof(uhci_transfer_batch_t));
if (!uhci_batch) {
usb_log_error("Failed to allocate UHCI batch.");
return NULL;
}
usb_transfer_batch_init(&uhci_batch->base, ep);
link_initialize(&uhci_batch->link);
return uhci_batch;
}
int uhci_transfer_batch_prepare(uhci_transfer_batch_t *uhci_batch)
{
static_assert((sizeof(td_t) % 16) == 0, "");
usb_transfer_batch_t *usb_batch = &uhci_batch->base;
uhci_batch->td_count = (usb_batch->size + usb_batch->ep->max_packet_size - 1) /
usb_batch->ep->max_packet_size;
if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
uhci_batch->td_count += 2;
}
const size_t setup_size = (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) ?
USB_SETUP_PACKET_SIZE :
0;
const size_t total_size = (sizeof(td_t) * uhci_batch->td_count) +
sizeof(qh_t) + setup_size;
if (dma_buffer_alloc(&uhci_batch->uhci_dma_buffer, total_size)) {
usb_log_error("Failed to allocate UHCI buffer.");
return ENOMEM;
}
memset(uhci_batch->uhci_dma_buffer.virt, 0, total_size);
uhci_batch->tds = uhci_batch->uhci_dma_buffer.virt;
uhci_batch->qh = (qh_t *) &uhci_batch->tds[uhci_batch->td_count];
qh_init(uhci_batch->qh);
qh_set_element_td(uhci_batch->qh, &uhci_batch->tds[0]);
void *setup_buffer = uhci_transfer_batch_setup_buffer(uhci_batch);
assert(setup_buffer == (void *) (uhci_batch->qh + 1));
memcpy(setup_buffer, usb_batch->setup.buffer, setup_size);
usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
" memory structures ready.", usb_batch,
USB_TRANSFER_BATCH_ARGS(*usb_batch));
assert(batch_setup[usb_batch->ep->transfer_type]);
batch_setup[usb_batch->ep->transfer_type](uhci_batch);
return EOK;
}
bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *uhci_batch)
{
assert(uhci_batch);
usb_transfer_batch_t *batch = &uhci_batch->base;
usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
" checking %zu transfer(s) for completion.",
uhci_batch, USB_TRANSFER_BATCH_ARGS(*batch),
uhci_batch->td_count);
batch->transferred_size = 0;
uhci_endpoint_t *uhci_ep = (uhci_endpoint_t *) batch->ep;
for (size_t i = 0; i < uhci_batch->td_count; ++i) {
if (td_is_active(&uhci_batch->tds[i])) {
return false;
}
batch->error = td_status(&uhci_batch->tds[i]);
if (batch->error != EOK) {
assert(batch->ep != NULL);
usb_log_debug("Batch %p found error TD(%zu->%p):%"
PRIx32 ".", uhci_batch, i,
&uhci_batch->tds[i], uhci_batch->tds[i].status);
td_print_status(&uhci_batch->tds[i]);
uhci_ep->toggle = td_toggle(&uhci_batch->tds[i]);
goto substract_ret;
}
batch->transferred_size +=
td_act_size(&uhci_batch->tds[i]);
if (td_is_short(&uhci_batch->tds[i]))
goto substract_ret;
}
substract_ret:
if (batch->transferred_size > 0 && batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
assert(batch->transferred_size >= USB_SETUP_PACKET_SIZE);
batch->transferred_size -= USB_SETUP_PACKET_SIZE;
}
assert(batch->transferred_size <= batch->size);
return true;
}
static const usb_packet_id direction_pids[] = {
[USB_DIRECTION_IN] = USB_PID_IN,
[USB_DIRECTION_OUT] = USB_PID_OUT,
};
static void batch_data(uhci_transfer_batch_t *uhci_batch)
{
assert(uhci_batch);
usb_direction_t dir = uhci_batch->base.dir;
assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
const usb_packet_id pid = direction_pids[dir];
const bool low_speed =
uhci_batch->base.ep->device->speed == USB_SPEED_LOW;
const size_t mps = uhci_batch->base.ep->max_packet_size;
uhci_endpoint_t *uhci_ep = (uhci_endpoint_t *) uhci_batch->base.ep;
int toggle = uhci_ep->toggle;
assert(toggle == 0 || toggle == 1);
size_t td = 0;
size_t remain_size = uhci_batch->base.size;
char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
while (remain_size > 0) {
const size_t packet_size = min(remain_size, mps);
const td_t *next_td = (td + 1 < uhci_batch->td_count) ?
&uhci_batch->tds[td + 1] : NULL;
assert(td < uhci_batch->td_count);
td_init(
&uhci_batch->tds[td], DEFAULT_ERROR_COUNT, packet_size,
toggle, false, low_speed, uhci_batch->base.target, pid, buffer, next_td);
++td;
toggle = 1 - toggle;
buffer += packet_size;
remain_size -= packet_size;
}
td_set_ioc(&uhci_batch->tds[td - 1]);
uhci_ep->toggle = toggle;
usb_log_debug2(
"Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.",
uhci_batch,
usb_str_transfer_type(uhci_batch->base.ep->transfer_type),
usb_str_direction(uhci_batch->base.ep->direction),
USB_TRANSFER_BATCH_ARGS(uhci_batch->base));
}
static void batch_control(uhci_transfer_batch_t *uhci_batch)
{
assert(uhci_batch);
usb_direction_t dir = uhci_batch->base.dir;
assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
assert(uhci_batch->td_count >= 2);
static const usb_packet_id status_stage_pids[] = {
[USB_DIRECTION_IN] = USB_PID_OUT,
[USB_DIRECTION_OUT] = USB_PID_IN,
};
const usb_packet_id data_stage_pid = direction_pids[dir];
const usb_packet_id status_stage_pid = status_stage_pids[dir];
const bool low_speed =
uhci_batch->base.ep->device->speed == USB_SPEED_LOW;
const size_t mps = uhci_batch->base.ep->max_packet_size;
const usb_target_t target = uhci_batch->base.target;
td_init(
&uhci_batch->tds[0], DEFAULT_ERROR_COUNT,
USB_SETUP_PACKET_SIZE, 0, false,
low_speed, target, USB_PID_SETUP,
uhci_transfer_batch_setup_buffer(uhci_batch), &uhci_batch->tds[1]);
size_t td = 1;
unsigned toggle = 1;
size_t remain_size = uhci_batch->base.size;
char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
while (remain_size > 0) {
const size_t packet_size = min(remain_size, mps);
td_init(
&uhci_batch->tds[td], DEFAULT_ERROR_COUNT, packet_size,
toggle, false, low_speed, target, data_stage_pid,
buffer, &uhci_batch->tds[td + 1]);
++td;
toggle = 1 - toggle;
buffer += packet_size;
remain_size -= packet_size;
assert(td < uhci_batch->td_count);
}
assert(td == uhci_batch->td_count - 1);
td_init(
&uhci_batch->tds[td], DEFAULT_ERROR_COUNT, 0, 1, false, low_speed,
target, status_stage_pid, NULL, NULL);
td_set_ioc(&uhci_batch->tds[td]);
usb_log_debug2("Control last TD status: %x.",
uhci_batch->tds[td].status);
}
static void (*const batch_setup[])(uhci_transfer_batch_t *) =
{
[USB_TRANSFER_CONTROL] = batch_control,
[USB_TRANSFER_BULK] = batch_data,
[USB_TRANSFER_INTERRUPT] = batch_data,
[USB_TRANSFER_ISOCHRONOUS] = NULL,
};
HelenOS homepage, sources at GitHub