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