HelenOS sources
This source file includes following definitions.
- endpoint_init
- get_bus_ops
- endpoint_add_ref
- endpoint_destroy
- endpoint_del_ref
- endpoint_set_online
- endpoint_set_offline_locked
- endpoint_wait_timeout_locked
- endpoint_activate_locked
- endpoint_deactivate_locked
- endpoint_send_batch
#include <assert.h>
#include <mem.h>
#include <stdlib.h>
#include <str_error.h>
#include <usb/debug.h>
#include <usb/descriptor.h>
#include <usb/host/hcd.h>
#include <usb/host/utility.h>
#include "usb_transfer_batch.h"
#include "bus.h"
#include "endpoint.h"
void endpoint_init(endpoint_t *ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
{
memset(ep, 0, sizeof(endpoint_t));
assert(dev);
ep->device = dev;
refcount_init(&ep->refcnt);
fibril_condvar_initialize(&ep->avail);
ep->endpoint = USB_ED_GET_EP(desc->endpoint);
ep->direction = USB_ED_GET_DIR(desc->endpoint);
ep->transfer_type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
ep->max_packet_size = USB_ED_GET_MPS(desc->endpoint);
ep->packets_per_uframe = USB_ED_GET_ADD_OPPS(desc->endpoint) + 1;
if (ep->transfer_type == USB_TRANSFER_CONTROL)
ep->direction = USB_DIRECTION_BOTH;
ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
ep->transfer_buffer_policy = DMA_POLICY_STRICT;
ep->required_transfer_buffer_policy = DMA_POLICY_STRICT;
}
static inline const bus_ops_t *get_bus_ops(endpoint_t *ep)
{
return ep->device->bus->ops;
}
void endpoint_add_ref(endpoint_t *ep)
{
refcount_up(&ep->refcnt);
}
static inline void endpoint_destroy(endpoint_t *ep)
{
const bus_ops_t *ops = get_bus_ops(ep);
if (ops->endpoint_destroy) {
ops->endpoint_destroy(ep);
} else {
assert(ep->active_batch == NULL);
free(ep);
}
}
void endpoint_del_ref(endpoint_t *ep)
{
if (refcount_down(&ep->refcnt))
endpoint_destroy(ep);
}
void endpoint_set_online(endpoint_t *ep, fibril_mutex_t *guard)
{
ep->guard = guard;
ep->online = true;
}
void endpoint_set_offline_locked(endpoint_t *ep)
{
assert(ep);
assert(fibril_mutex_is_locked(ep->guard));
ep->online = false;
fibril_condvar_broadcast(&ep->avail);
}
void endpoint_wait_timeout_locked(endpoint_t *ep, usec_t timeout)
{
assert(ep);
assert(fibril_mutex_is_locked(ep->guard));
if (ep->active_batch == NULL)
return;
fibril_condvar_wait_timeout(&ep->avail, ep->guard, timeout);
}
int endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
{
assert(ep);
assert(batch);
assert(batch->ep == ep);
assert(ep->guard);
assert(fibril_mutex_is_locked(ep->guard));
while (ep->online && ep->active_batch != NULL)
fibril_condvar_wait(&ep->avail, ep->guard);
if (!ep->online)
return EINTR;
assert(ep->active_batch == NULL);
ep->active_batch = batch;
return EOK;
}
void endpoint_deactivate_locked(endpoint_t *ep)
{
assert(ep);
assert(fibril_mutex_is_locked(ep->guard));
ep->active_batch = NULL;
fibril_condvar_signal(&ep->avail);
}
errno_t endpoint_send_batch(endpoint_t *ep, const transfer_request_t *req)
{
assert(ep);
assert(req);
if (ep->transfer_type == USB_TRANSFER_CONTROL) {
usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, req->name,
req->target.address, req->target.endpoint,
req->size, ep->max_packet_size,
req->setup);
} else {
usb_log_debug("%s %d:%d %zu/%zuB", req->name,
req->target.address, req->target.endpoint,
req->size, ep->max_packet_size);
}
device_t *const device = ep->device;
if (!device) {
usb_log_warning("Endpoint detached");
return EAGAIN;
}
const bus_ops_t *ops = device->bus->ops;
if (!ops->batch_schedule) {
usb_log_error("HCD does not implement scheduler.");
return ENOTSUP;
}
size_t size = req->size;
if (size > ep->max_transfer_size &&
(ep->transfer_type == USB_TRANSFER_INTERRUPT ||
ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)) {
if (req->dir == USB_DIRECTION_OUT)
return ENOSPC;
else
size = ep->max_transfer_size;
}
if (!device->online && ep->endpoint > 0)
return EAGAIN;
usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
if (!batch) {
usb_log_error("Failed to create transfer batch.");
return ENOMEM;
}
batch->target = req->target;
batch->setup.packed = req->setup;
batch->dir = req->dir;
batch->size = size;
batch->offset = req->offset;
batch->dma_buffer = req->buffer;
dma_buffer_acquire(&batch->dma_buffer);
if (batch->offset != 0) {
usb_log_debug("A transfer with nonzero offset requested.");
usb_transfer_batch_bounce(batch);
}
if (usb_transfer_batch_bounce_required(batch))
usb_transfer_batch_bounce(batch);
batch->on_complete = req->on_complete;
batch->on_complete_data = req->arg;
const int ret = ops->batch_schedule(batch);
if (ret != EOK) {
usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
usb_transfer_batch_destroy(batch);
}
return ret;
}
HelenOS homepage, sources at GitHub