HelenOS sources
This source file includes following definitions.
- transfer_list_init
- transfer_list_fini
- transfer_list_set_next
- transfer_list_add_batch
- uhci_reset_toggle
- transfer_list_check_finished
- transfer_list_abort_all
- transfer_list_remove_batch
#include <assert.h>
#include <errno.h>
#include <barrier.h>
#include <stdint.h>
#include <usb/debug.h>
#include <usb/host/usb_transfer_batch.h>
#include <usb/host/utils/malloc32.h>
#include <usb/host/utility.h>
#include "hw_struct/link_pointer.h"
#include "transfer_list.h"
#include "hc.h"
errno_t transfer_list_init(transfer_list_t *instance, const char *name)
{
assert(instance);
instance->name = name;
instance->queue_head = malloc32(sizeof(qh_t));
if (!instance->queue_head) {
usb_log_error("Failed to allocate queue head.");
return ENOMEM;
}
const uint32_t queue_head_pa = addr_to_phys(instance->queue_head);
usb_log_debug2("Transfer list %s setup with QH: %p (%#" PRIx32 " ).",
name, instance->queue_head, queue_head_pa);
qh_init(instance->queue_head);
list_initialize(&instance->batch_list);
fibril_mutex_initialize(&instance->guard);
return EOK;
}
void transfer_list_fini(transfer_list_t *instance)
{
assert(instance);
free32(instance->queue_head);
}
void transfer_list_set_next(transfer_list_t *instance, transfer_list_t *next)
{
assert(instance);
assert(instance->queue_head);
assert(next);
qh_set_next_qh(instance->queue_head, next->queue_head);
}
int transfer_list_add_batch(
transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch)
{
assert(instance);
assert(uhci_batch);
endpoint_t *ep = uhci_batch->base.ep;
fibril_mutex_lock(&instance->guard);
const int err = endpoint_activate_locked(ep, &uhci_batch->base);
if (err) {
fibril_mutex_unlock(&instance->guard);
return err;
}
usb_log_debug2("Batch %p adding to queue %s.",
uhci_batch, instance->name);
qh_t *last_qh = instance->queue_head;
if (!list_empty(&instance->batch_list)) {
last_qh = uhci_transfer_batch_from_link(
list_last(&instance->batch_list))->qh;
}
const uint32_t pa = addr_to_phys(uhci_batch->qh);
assert((pa & LINK_POINTER_ADDRESS_MASK) == pa);
write_barrier();
uhci_batch->qh->next = last_qh->next;
qh_set_next_qh(last_qh, uhci_batch->qh);
write_barrier();
list_append(&uhci_batch->link, &instance->batch_list);
usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
" scheduled in queue %s.", uhci_batch,
USB_TRANSFER_BATCH_ARGS(uhci_batch->base), instance->name);
fibril_mutex_unlock(&instance->guard);
return EOK;
}
static void uhci_reset_toggle(endpoint_t *ep)
{
uhci_endpoint_t *uhci_ep = (uhci_endpoint_t *) ep;
uhci_ep->toggle = 0;
}
void transfer_list_check_finished(transfer_list_t *instance)
{
assert(instance);
fibril_mutex_lock(&instance->guard);
list_foreach_safe(instance->batch_list, current, next) {
uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
if (uhci_transfer_batch_check_completed(batch)) {
assert(batch->base.ep->active_batch == &batch->base);
endpoint_deactivate_locked(batch->base.ep);
hc_reset_toggles(&batch->base, &uhci_reset_toggle);
transfer_list_remove_batch(instance, batch);
usb_transfer_batch_finish(&batch->base);
}
}
fibril_mutex_unlock(&instance->guard);
}
void transfer_list_abort_all(transfer_list_t *instance)
{
fibril_mutex_lock(&instance->guard);
while (!list_empty(&instance->batch_list)) {
link_t *const current = list_first(&instance->batch_list);
uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
transfer_list_remove_batch(instance, batch);
}
fibril_mutex_unlock(&instance->guard);
}
void transfer_list_remove_batch(
transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch)
{
assert(instance);
assert(instance->queue_head);
assert(uhci_batch);
assert(uhci_batch->qh);
assert(fibril_mutex_is_locked(&instance->guard));
assert(!list_empty(&instance->batch_list));
usb_log_debug2("Batch %p removing from queue %s.",
uhci_batch, instance->name);
const char *qpos = "FIRST";
qh_t *prev_qh = instance->queue_head;
if (list_first(&instance->batch_list) != &uhci_batch->link) {
prev_qh =
uhci_transfer_batch_from_link(uhci_batch->link.prev)->qh;
qpos = "NOT FIRST";
}
assert((prev_qh->next & LINK_POINTER_ADDRESS_MASK) ==
addr_to_phys(uhci_batch->qh));
prev_qh->next = uhci_batch->qh->next;
write_barrier();
list_remove(&uhci_batch->link);
usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " removed (%s) "
"from %s, next: %x.", uhci_batch,
USB_TRANSFER_BATCH_ARGS(uhci_batch->base),
qpos, instance->name, uhci_batch->qh->next);
}
HelenOS homepage, sources at GitHub