Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbhost/src/endpoint.c

    rf527f58 r296d22fc  
    11/*
    22 * Copyright (c) 2011 Jan Vesely
     3 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
    34 * All rights reserved.
    45 *
     
    3435 */
    3536
    36 #include <usb/host/endpoint.h>
    37 
    3837#include <assert.h>
     38#include <atomic.h>
     39#include <mem.h>
    3940#include <stdlib.h>
    40 #include <atomic.h>
    41 
    42 /** Allocate ad initialize endpoint_t structure.
    43  * @param address USB address.
    44  * @param endpoint USB endpoint number.
    45  * @param direction Communication direction.
    46  * @param type USB transfer type.
    47  * @param speed Communication speed.
    48  * @param max_packet_size Maximum size of data packets.
    49  * @param bw Required bandwidth.
    50  * @return Pointer to initialized endpoint_t structure, NULL on failure.
    51  */
    52 endpoint_t * endpoint_create(usb_address_t address, usb_endpoint_t endpoint,
    53     usb_direction_t direction, usb_transfer_type_t type, usb_speed_t speed,
    54     size_t max_packet_size, unsigned packets, size_t bw,
    55     usb_address_t tt_address, unsigned tt_p)
    56 {
    57         endpoint_t *instance = malloc(sizeof(endpoint_t));
    58         if (instance) {
    59                 atomic_set(&instance->refcnt, 0);
    60                 instance->address = address;
    61                 instance->endpoint = endpoint;
    62                 instance->direction = direction;
    63                 instance->transfer_type = type;
    64                 instance->speed = speed;
    65                 instance->max_packet_size = max_packet_size;
    66                 instance->packets = packets;
    67                 instance->bandwidth = bw;
    68                 instance->toggle = 0;
    69                 instance->active = false;
    70                 instance->tt.address = tt_address;
    71                 instance->tt.port = tt_p;
    72                 instance->hc_data.data = NULL;
    73                 instance->hc_data.toggle_get = NULL;
    74                 instance->hc_data.toggle_set = NULL;
    75                 link_initialize(&instance->link);
    76                 fibril_mutex_initialize(&instance->guard);
    77                 fibril_condvar_initialize(&instance->avail);
    78         }
    79         return instance;
    80 }
    81 
    82 /** Properly dispose of endpoint_t structure.
    83  * @param instance endpoint_t structure.
    84  */
    85 void endpoint_destroy(endpoint_t *instance)
    86 {
    87         assert(instance);
    88         assert(!instance->active);
    89         assert(instance->hc_data.data == NULL);
    90         free(instance);
    91 }
    92 
    93 void endpoint_add_ref(endpoint_t *instance)
    94 {
    95         atomic_inc(&instance->refcnt);
    96 }
    97 
    98 void endpoint_del_ref(endpoint_t *instance)
    99 {
    100         if (atomic_predec(&instance->refcnt) == 0)
    101                 endpoint_destroy(instance);
    102 }
    103 
    104 /** Set device specific data and hooks.
    105  * @param instance endpoint_t structure.
    106  * @param data device specific data.
    107  * @param toggle_get Hook to call when retrieving value of toggle bit.
    108  * @param toggle_set Hook to call when setting the value of toggle bit.
    109  */
    110 void endpoint_set_hc_data(endpoint_t *instance,
    111     void *data, int (*toggle_get)(void *), void (*toggle_set)(void *, int))
    112 {
    113         assert(instance);
    114         fibril_mutex_lock(&instance->guard);
    115         instance->hc_data.data = data;
    116         instance->hc_data.toggle_get = toggle_get;
    117         instance->hc_data.toggle_set = toggle_set;
    118         fibril_mutex_unlock(&instance->guard);
    119 }
    120 
    121 /** Clear device specific data and hooks.
    122  * @param instance endpoint_t structure.
    123  * @note This function does not free memory pointed to by data pointer.
    124  */
    125 void endpoint_clear_hc_data(endpoint_t *instance)
    126 {
    127         assert(instance);
    128         endpoint_set_hc_data(instance, NULL, NULL, NULL);
    129 }
    130 
    131 /** Mark the endpoint as active and block access for further fibrils.
    132  * @param instance endpoint_t structure.
    133  */
    134 void endpoint_use(endpoint_t *instance)
    135 {
    136         assert(instance);
    137         /* Add reference for active endpoint. */
    138         endpoint_add_ref(instance);
    139         fibril_mutex_lock(&instance->guard);
    140         while (instance->active)
    141                 fibril_condvar_wait(&instance->avail, &instance->guard);
    142         instance->active = true;
    143         fibril_mutex_unlock(&instance->guard);
    144 }
    145 
    146 /** Mark the endpoint as inactive and allow access for further fibrils.
    147  * @param instance endpoint_t structure.
    148  */
    149 void endpoint_release(endpoint_t *instance)
    150 {
    151         assert(instance);
    152         fibril_mutex_lock(&instance->guard);
    153         instance->active = false;
    154         fibril_mutex_unlock(&instance->guard);
    155         fibril_condvar_signal(&instance->avail);
    156         /* Drop reference for active endpoint. */
    157         endpoint_del_ref(instance);
    158 }
    159 
    160 /** Get the value of toggle bit.
    161  * @param instance endpoint_t structure.
    162  * @note Will use provided hook.
    163  */
    164 int endpoint_toggle_get(endpoint_t *instance)
    165 {
    166         assert(instance);
    167         fibril_mutex_lock(&instance->guard);
    168         if (instance->hc_data.toggle_get)
    169                 instance->toggle =
    170                     instance->hc_data.toggle_get(instance->hc_data.data);
    171         const int ret = instance->toggle;
    172         fibril_mutex_unlock(&instance->guard);
     41#include <str_error.h>
     42#include <usb/debug.h>
     43#include <usb/descriptor.h>
     44#include <usb/host/hcd.h>
     45#include <usb/host/utility.h>
     46
     47#include "usb_transfer_batch.h"
     48#include "bus.h"
     49
     50#include "endpoint.h"
     51
     52/**
     53 * Initialize provided endpoint structure.
     54 */
     55void endpoint_init(endpoint_t *ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
     56{
     57        memset(ep, 0, sizeof(endpoint_t));
     58
     59        assert(dev);
     60        ep->device = dev;
     61
     62        atomic_set(&ep->refcnt, 0);
     63        fibril_condvar_initialize(&ep->avail);
     64
     65        ep->endpoint = USB_ED_GET_EP(desc->endpoint);
     66        ep->direction = USB_ED_GET_DIR(desc->endpoint);
     67        ep->transfer_type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
     68        ep->max_packet_size = USB_ED_GET_MPS(desc->endpoint);
     69        ep->packets_per_uframe = USB_ED_GET_ADD_OPPS(desc->endpoint) + 1;
     70
     71        /** Direction both is our construct never present in descriptors */
     72        if (ep->transfer_type == USB_TRANSFER_CONTROL)
     73                ep->direction = USB_DIRECTION_BOTH;
     74
     75        ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
     76}
     77
     78/**
     79 * Get the bus endpoint belongs to.
     80 */
     81static inline const bus_ops_t *get_bus_ops(endpoint_t *ep)
     82{
     83        return ep->device->bus->ops;
     84}
     85
     86/**
     87 * Increase the reference count on endpoint.
     88 */
     89void endpoint_add_ref(endpoint_t *ep)
     90{
     91        atomic_inc(&ep->refcnt);
     92}
     93
     94/**
     95 * Call the desctruction callback. Default behavior is to free the memory directly.
     96 */
     97static inline void endpoint_destroy(endpoint_t *ep)
     98{
     99        const bus_ops_t *ops = get_bus_ops(ep);
     100        if (ops->endpoint_destroy) {
     101                ops->endpoint_destroy(ep);
     102        } else {
     103                assert(ep->active_batch == NULL);
     104
     105                /* Assume mostly the eps will be allocated by malloc. */
     106                free(ep);
     107        }
     108}
     109
     110/**
     111 * Decrease the reference count.
     112 */
     113void endpoint_del_ref(endpoint_t *ep)
     114{
     115        if (atomic_predec(&ep->refcnt) == 0) {
     116                endpoint_destroy(ep);
     117        }
     118}
     119
     120/**
     121 * Mark the endpoint as online. Supply a guard to be used for this endpoint
     122 * synchronization.
     123 */
     124void endpoint_set_online(endpoint_t *ep, fibril_mutex_t *guard)
     125{
     126        ep->guard = guard;
     127        ep->online = true;
     128}
     129
     130/**
     131 * Mark the endpoint as offline. All other fibrils waiting to activate this
     132 * endpoint will be interrupted.
     133 */
     134void endpoint_set_offline_locked(endpoint_t *ep)
     135{
     136        assert(ep);
     137        assert(fibril_mutex_is_locked(ep->guard));
     138
     139        ep->online = false;
     140        fibril_condvar_broadcast(&ep->avail);
     141}
     142
     143/**
     144 * Wait until a transfer finishes. Can be used even when the endpoint is
     145 * offline (and is interrupted by the endpoint going offline).
     146 */
     147void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
     148{
     149        assert(ep);
     150        assert(fibril_mutex_is_locked(ep->guard));
     151
     152        if (ep->active_batch == NULL)
     153                return;
     154
     155        fibril_condvar_wait_timeout(&ep->avail, ep->guard, timeout);
     156}
     157
     158/**
     159 * Mark the endpoint as active and block access for further fibrils. If the
     160 * endpoint is already active, it will block on ep->avail condvar.
     161 *
     162 * Call only under endpoint guard. After you activate the endpoint and release
     163 * the guard, you must assume that particular transfer is already
     164 * finished/aborted.
     165 *
     166 * Activation and deactivation is not done by the library to maximize
     167 * performance. The HC might want to prepare some memory buffers prior to
     168 * interfering with other world.
     169 *
     170 * @param batch Transfer batch this endpoint is blocked by.
     171 */
     172int endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
     173{
     174        assert(ep);
     175        assert(batch);
     176        assert(batch->ep == ep);
     177        assert(ep->guard);
     178        assert(fibril_mutex_is_locked(ep->guard));
     179
     180        while (ep->online && ep->active_batch != NULL)
     181                fibril_condvar_wait(&ep->avail, ep->guard);
     182
     183        if (!ep->online)
     184                return EINTR;
     185
     186        assert(ep->active_batch == NULL);
     187        ep->active_batch = batch;
     188        return EOK;
     189}
     190
     191/**
     192 * Mark the endpoint as inactive and allow access for further fibrils.
     193 */
     194void endpoint_deactivate_locked(endpoint_t *ep)
     195{
     196        assert(ep);
     197        assert(fibril_mutex_is_locked(ep->guard));
     198
     199        ep->active_batch = NULL;
     200        fibril_condvar_signal(&ep->avail);
     201}
     202
     203/**
     204 * Initiate a transfer on an endpoint. Creates a transfer batch, checks the
     205 * bandwidth requirements and schedules the batch.
     206 *
     207 * @param endpoint Endpoint for which to send the batch
     208 * @param target The target of the transfer.
     209 * @param direction A direction of the transfer.
     210 * @param data A pointer to the data buffer.
     211 * @param size Size of the data buffer.
     212 * @param setup_data Data to use in the setup stage (Control communication type)
     213 * @param on_complete Callback which is called after the batch is complete
     214 * @param arg Callback parameter.
     215 * @param name Communication identifier (for nicer output).
     216 */
     217int endpoint_send_batch(endpoint_t *ep, usb_target_t target,
     218    usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
     219    usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
     220{
     221        if (!ep)
     222                return EBADMEM;
     223
     224        if (ep->transfer_type == USB_TRANSFER_CONTROL) {
     225                usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, name,
     226                    target.address, target.endpoint, size, ep->max_packet_size,
     227                    setup_data);
     228        } else {
     229                usb_log_debug("%s %d:%d %zu/%zuB", name, target.address,
     230                    target.endpoint, size, ep->max_packet_size);
     231        }
     232
     233        device_t * const device = ep->device;
     234        if (!device) {
     235                usb_log_warning("Endpoint detached");
     236                return EAGAIN;
     237        }
     238
     239        const bus_ops_t *ops = device->bus->ops;
     240        if (!ops->batch_schedule) {
     241                usb_log_error("HCD does not implement scheduler.");
     242                return ENOTSUP;
     243        }
     244
     245        /** Limit transfers with reserved bandwidth to the amount reserved */
     246        if ((ep->transfer_type == USB_TRANSFER_INTERRUPT
     247            || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)
     248            && size > ep->max_transfer_size)
     249                return ENOSPC;
     250
     251        /* Offline devices don't schedule transfers other than on EP0. */
     252        if (!device->online && ep->endpoint > 0)
     253                return EAGAIN;
     254
     255        usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
     256        if (!batch) {
     257                usb_log_error("Failed to create transfer batch.");
     258                return ENOMEM;
     259        }
     260
     261        batch->target = target;
     262        batch->buffer = data;
     263        batch->buffer_size = size;
     264        batch->setup.packed = setup_data;
     265        batch->dir = direction;
     266        batch->on_complete = on_complete;
     267        batch->on_complete_data = arg;
     268
     269        const int ret = ops->batch_schedule(batch);
     270        if (ret != EOK) {
     271                usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
     272                usb_transfer_batch_destroy(batch);
     273        }
     274
    173275        return ret;
    174276}
    175277
    176 /** Set the value of toggle bit.
    177  * @param instance endpoint_t structure.
    178  * @note Will use provided hook.
    179  */
    180 void endpoint_toggle_set(endpoint_t *instance, int toggle)
    181 {
    182         assert(instance);
    183         assert(toggle == 0 || toggle == 1);
    184         fibril_mutex_lock(&instance->guard);
    185         instance->toggle = toggle;
    186         if (instance->hc_data.toggle_set)
    187                 instance->hc_data.toggle_set(instance->hc_data.data, toggle);
    188         fibril_mutex_unlock(&instance->guard);
    189 }
    190 
    191278/**
    192279 * @}
Note: See TracChangeset for help on using the changeset viewer.