Changeset 1d758fc in mainline for uspace/lib/usbhost


Ignore:
Timestamp:
2018-02-12T10:11:47Z (7 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
5fe3f954
Parents:
2f762a7
git-author:
Ondřej Hlavatý <aearsis@…> (2018-02-05 03:28:50)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-02-12 10:11:47)
Message:

usb: rethinking DMA buffers

Location:
uspace/lib/usbhost
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbhost/include/usb/host/bus.h

    r2f762a7 r1d758fc  
    153153int bus_device_offline(device_t *);
    154154
    155 int bus_device_send_batch(device_t *, usb_target_t,
    156     usb_direction_t direction, char *, size_t, uint64_t,
    157     usbhc_iface_transfer_callback_t, void *, const char *);
     155/**
     156 * A proforma to USB transfer batch. As opposed to transfer batch, which is
     157 * supposed to be a dynamic structrure, this one is static and descriptive only.
     158 * Its fields are copied to the final batch.
     159 */
     160typedef struct transfer_request {
     161        usb_target_t target;
     162        usb_direction_t dir;
     163
     164        dma_buffer_t buffer;
     165        size_t offset, size;
     166        uint64_t setup;
     167
     168        usbhc_iface_transfer_callback_t on_complete;
     169        void *arg;
     170
     171        const char *name;
     172} transfer_request_t;
     173
     174int bus_issue_transfer(device_t *, const transfer_request_t *);
    158175
    159176errno_t bus_device_send_batch_sync(device_t *, usb_target_t,
  • uspace/lib/usbhost/include/usb/host/endpoint.h

    r2f762a7 r1d758fc  
    5151typedef struct bus bus_t;
    5252typedef struct device device_t;
     53typedef struct transfer_request transfer_request_t;
    5354typedef struct usb_transfer_batch usb_transfer_batch_t;
    5455
     
    9899        /** Maximum size of one transfer */
    99100        size_t max_transfer_size;
    100         /** Policy for transfer buffers */
    101         dma_policy_t transfer_buffer_policy;
     101
     102        /* Policies for transfer buffers */
     103        dma_policy_t transfer_buffer_policy;            /**< A hint for optimal performance. */
     104        dma_policy_t required_transfer_buffer_policy;   /**< Enforced by the library. */
    102105
    103106        /**
     
    122125extern void endpoint_deactivate_locked(endpoint_t *);
    123126
    124 int endpoint_send_batch(endpoint_t *, usb_target_t, usb_direction_t,
    125     char *, size_t, uint64_t, usbhc_iface_transfer_callback_t, void *,
    126     const char *);
     127int endpoint_send_batch(endpoint_t *, const transfer_request_t *);
    127128
    128129static inline bus_t *endpoint_get_bus(endpoint_t *ep)
  • uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h

    r2f762a7 r1d758fc  
    7070        } setup;
    7171
     72        /** DMA buffer with enforced policy */
     73        dma_buffer_t dma_buffer;
     74        /** Size of memory buffer */
     75        size_t offset, size;
     76
    7277        /**
    7378         * In case a bounce buffer is allocated, the original buffer must to be
    7479         * stored to be filled after the IN transaction is finished.
    7580         */
    76         char *buffer;
    77         /** Size of memory buffer */
    78         size_t buffer_size;
    79 
    80         /** DMA buffer with enforced policy */
    81         dma_buffer_t dma_buffer;
     81        char *original_buffer;
    8282        bool is_bounced;
    8383
     
    107107        usb_str_transfer_type_short((batch).ep->transfer_type), \
    108108        usb_str_direction((batch).dir), \
    109         (batch).buffer_size, (batch).ep->max_packet_size
     109        (batch).size, (batch).ep->max_packet_size
    110110
    111111/** Wrapper for bus operation. */
     
    115115void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *);
    116116
     117/** Buffer handling */
     118bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *);
    117119errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *);
    118 /** Buffer preparation */
    119 errno_t usb_transfer_batch_prepare_buffer(usb_transfer_batch_t *, char *);
    120120
    121121/** Batch finalization. */
  • uspace/lib/usbhost/src/bus.c

    r2f762a7 r1d758fc  
    4646#include <str_error.h>
    4747#include <usb/debug.h>
     48#include <usb/dma_buffer.h>
    4849
    4950#include "endpoint.h"
     
    389390                endpoint_init(ep, device, desc);
    390391        }
     392
     393        assert((ep->required_transfer_buffer_policy & ~ep->transfer_buffer_policy) == 0);
    391394
    392395        /* Bus reference */
     
    557560
    558561/**
    559  * Initiate a transfer on the bus. Finds the target endpoint, creates
    560  * a transfer batch and schedules it.
    561  *
    562  * @param device Device for which to send the batch
    563  * @param target The target of the transfer.
    564  * @param direction A direction of the transfer.
    565  * @param data A pointer to the data buffer.
    566  * @param size Size of the data buffer.
    567  * @param setup_data Data to use in the setup stage (Control communication type)
    568  * @param on_complete Callback which is called after the batch is complete
    569  * @param arg Callback parameter.
    570  * @param name Communication identifier (for nicer output).
     562 * Assert some conditions on transfer request. As the request is an entity of
     563 * HC driver only, we can force these conditions harder. Invalid values from
     564 * devices shall be caught on DDF interface already.
     565 */
     566static void check_request(const transfer_request_t *request)
     567{
     568        assert(usb_target_is_valid(&request->target));
     569        assert(request->dir != USB_DIRECTION_BOTH);
     570        /* Non-zero offset => size is non-zero */
     571        assert(request->offset == 0 || request->size != 0);
     572        /* Non-zero size => buffer is set */
     573        assert(request->size == 0 || dma_buffer_is_set(&request->buffer));
     574        /* Non-null arg => callback is set */
     575        assert(request->arg == NULL || request->on_complete != NULL);
     576        assert(request->name);
     577}
     578
     579/**
     580 * Initiate a transfer with given device.
     581 *
    571582 * @return Error code.
    572583 */
    573 int bus_device_send_batch(device_t *device, usb_target_t target,
    574     usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
    575     usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
    576 {
    577         assert(device->address == target.address);
     584int bus_issue_transfer(device_t *device, const transfer_request_t *request)
     585{
     586        assert(device);
     587        assert(request);
     588
     589        check_request(request);
     590        assert(device->address == request->target.address);
    578591
    579592        /* Temporary reference */
    580         endpoint_t *ep = bus_find_endpoint(device, target.endpoint, direction);
     593        endpoint_t *ep = bus_find_endpoint(device, request->target.endpoint, request->dir);
    581594        if (ep == NULL) {
    582595                usb_log_error("Endpoint(%d:%d) not registered for %s.",
    583                     device->address, target.endpoint, name);
     596                    device->address, request->target.endpoint, request->name);
    584597                return ENOENT;
    585598        }
     
    587600        assert(ep->device == device);
    588601
    589         /*
    590          * This method is already callable from HC only, so we can force these
    591          * conditions harder.
    592          * Invalid values from devices shall be caught on DDF interface already.
    593          */
    594         assert(usb_target_is_valid(&target));
    595         assert(direction != USB_DIRECTION_BOTH);
    596         assert(size == 0 || data != NULL);
    597         assert(arg == NULL || on_complete != NULL);
    598         assert(name);
    599 
    600         const int err = endpoint_send_batch(ep, target, direction,
    601             data, size, setup_data, on_complete, arg, name);
     602        const int err = endpoint_send_batch(ep, request);
    602603
    603604        /* Temporary reference */
     
    650651    const char *name, size_t *transferred_size)
    651652{
     653        int err;
    652654        sync_data_t sd = { .done = false };
    653655        fibril_mutex_initialize(&sd.done_mtx);
    654656        fibril_condvar_initialize(&sd.done_cv);
    655657
    656         const int ret = bus_device_send_batch(device, target, direction,
    657             data, size, setup_data, sync_transfer_complete, &sd, name);
    658         if (ret != EOK)
    659                 return ret;
     658        transfer_request_t request = {
     659                .target = target,
     660                .dir = direction,
     661                .offset = ((uintptr_t) data) % PAGE_SIZE,
     662                .size = size,
     663                .setup = setup_data,
     664                .on_complete = sync_transfer_complete,
     665                .arg = &sd,
     666                .name = name,
     667        };
     668
     669        if (data &&
     670            (err = dma_buffer_lock(&request.buffer, data - request.offset, size)))
     671                return err;
     672
     673        if ((err = bus_issue_transfer(device, &request))) {
     674                dma_buffer_unlock(&request.buffer, size);
     675                return err;
     676        }
    660677
    661678        /*
     
    668685        fibril_mutex_unlock(&sd.done_mtx);
    669686
     687        dma_buffer_unlock(&request.buffer, size);
     688
    670689        if (transferred_size)
    671690                *transferred_size = sd.transferred_size;
  • uspace/lib/usbhost/src/ddf_helpers.c

    r2f762a7 r1d758fc  
    4646#include <usb/descriptor.h>
    4747#include <usb/usb.h>
     48#include <usb/dma_buffer.h>
    4849#include <usb_iface.h>
    4950#include <usbhc_iface.h>
     
    271272 * @return Error code.
    272273 */
    273 static errno_t transfer(ddf_fun_t *fun, const usbhc_iface_transfer_request_t *req,
     274static errno_t transfer(ddf_fun_t *fun,
     275    const usbhc_iface_transfer_request_t *ifreq,
    274276    usbhc_iface_transfer_callback_t callback, void *arg)
    275277{
     
    280282        const usb_target_t target = {{
    281283                .address = dev->address,
    282                 .endpoint = req->endpoint,
    283                 .stream = req->stream,
     284                .endpoint = ifreq->endpoint,
     285                .stream = ifreq->stream,
    284286        }};
    285287
     
    287289                return EINVAL;
    288290
    289         if (req->size > 0 && req->base == NULL)
     291        if (ifreq->offset > 0 && ifreq->size == 0)
     292                return EINVAL;
     293
     294        if (ifreq->size > 0 && !dma_buffer_is_set(&ifreq->buffer))
    290295                return EBADMEM;
    291296
     
    293298                return EBADMEM;
    294299
    295         const char *name = (req->dir == USB_DIRECTION_IN) ? "READ" : "WRITE";
    296 
    297         char *buffer = req->base + req->offset;
    298 
    299         return bus_device_send_batch(dev, target, req->dir,
    300             buffer, req->size, req->setup,
    301             callback, arg, name);
     300        const transfer_request_t request = {
     301                .target = target,
     302                .dir = ifreq->dir,
     303                .buffer = ifreq->buffer,
     304                .offset = ifreq->offset,
     305                .size = ifreq->size,
     306                .setup = ifreq->setup,
     307                .on_complete = callback,
     308                .arg = arg,
     309                .name = (ifreq->dir == USB_DIRECTION_IN) ? "READ" : "WRITE",
     310        };
     311
     312        return bus_issue_transfer(dev, &request);
    302313}
    303314
  • uspace/lib/usbhost/src/endpoint.c

    r2f762a7 r1d758fc  
    7575        ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
    7676        ep->transfer_buffer_policy = DMA_POLICY_STRICT;
     77        ep->required_transfer_buffer_policy = DMA_POLICY_STRICT;
    7778}
    7879
     
    207208 *
    208209 * @param endpoint Endpoint for which to send the batch
    209  * @param target The target of the transfer.
    210  * @param direction A direction of the transfer.
    211  * @param data A pointer to the data buffer.
    212  * @param size Size of the data buffer.
    213  * @param setup_data Data to use in the setup stage (Control communication type)
    214  * @param on_complete Callback which is called after the batch is complete
    215  * @param arg Callback parameter.
    216  * @param name Communication identifier (for nicer output).
    217  */
    218 errno_t endpoint_send_batch(endpoint_t *ep, usb_target_t target,
    219     usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
    220     usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
    221 {
    222         if (!ep)
    223                 return EBADMEM;
     210 */
     211errno_t endpoint_send_batch(endpoint_t *ep, const transfer_request_t *req)
     212{
     213        assert(ep);
     214        assert(req);
    224215
    225216        if (ep->transfer_type == USB_TRANSFER_CONTROL) {
    226                 usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, name,
    227                     target.address, target.endpoint, size, ep->max_packet_size,
    228                     setup_data);
     217                usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, req->name,
     218                    req->target.address, req->target.endpoint,
     219                    req->size, ep->max_packet_size,
     220                    req->setup);
    229221        } else {
    230                 usb_log_debug("%s %d:%d %zu/%zuB", name, target.address,
    231                     target.endpoint, size, ep->max_packet_size);
     222                usb_log_debug("%s %d:%d %zu/%zuB", req->name,
     223                    req->target.address, req->target.endpoint,
     224                    req->size, ep->max_packet_size);
    232225        }
    233226
     
    244237        }
    245238
     239        size_t size = req->size;
    246240        /*
    247241         * Limit transfers with reserved bandwidth to the amount reserved.
    248242         * OUT transfers are rejected, IN can be just trimmed in advance.
    249243         */
    250         if ((ep->transfer_type == USB_TRANSFER_INTERRUPT || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) && size > ep->max_transfer_size) {
    251                 if (direction == USB_DIRECTION_OUT)
     244        if (size > ep->max_transfer_size &&
     245            (ep->transfer_type == USB_TRANSFER_INTERRUPT
     246             || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)) {
     247                if (req->dir == USB_DIRECTION_OUT)
    252248                        return ENOSPC;
    253249                else
    254250                        size = ep->max_transfer_size;
    255 
    256251        }
    257252
     
    266261        }
    267262
    268         batch->target = target;
    269         batch->setup.packed = setup_data;
    270         batch->dir = direction;
    271         batch->buffer_size = size;
    272 
    273         errno_t err;
    274         if ((err = usb_transfer_batch_prepare_buffer(batch, data))) {
    275                 usb_log_warning("Failed to prepare buffer for batch: %s", str_error(err));
    276                 usb_transfer_batch_destroy(batch);
    277                 return err;
    278         }
    279 
    280         batch->on_complete = on_complete;
    281         batch->on_complete_data = arg;
     263        batch->target = req->target;
     264        batch->setup.packed = req->setup;
     265        batch->dir = req->dir;
     266        batch->size = size;
     267        batch->offset = req->offset;
     268        batch->dma_buffer = req->buffer;
     269
     270        dma_buffer_acquire(&batch->dma_buffer);
     271
     272        if (batch->offset != 0) {
     273                usb_log_debug("A transfer with nonzero offset requested.");
     274                usb_transfer_batch_bounce(batch);
     275        }
     276
     277        if (usb_transfer_batch_bounce_required(batch))
     278                usb_transfer_batch_bounce(batch);
     279
     280        batch->on_complete = req->on_complete;
     281        batch->on_complete_data = req->arg;
    282282
    283283        const int ret = ops->batch_schedule(batch);
  • uspace/lib/usbhost/src/usb_transfer_batch.c

    r2f762a7 r1d758fc  
    103103}
    104104
     105bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *batch)
     106{
     107        if (!batch->size)
     108                return false;
     109
     110        unsigned flags = batch->dma_buffer.policy & DMA_POLICY_FLAGS_MASK;
     111        unsigned required_flags =
     112            batch->ep->required_transfer_buffer_policy & DMA_POLICY_FLAGS_MASK;
     113
     114        if (required_flags & ~flags)
     115                return true;
     116
     117        size_t chunk_mask = dma_policy_chunk_mask(batch->dma_buffer.policy);
     118        size_t required_chunk_mask =
     119             dma_policy_chunk_mask(batch->ep->required_transfer_buffer_policy);
     120
     121        /* If the chunks are at least as large as required, we're good */
     122        if ((required_chunk_mask & ~chunk_mask) == 0)
     123                return false;
     124
     125        size_t start_chunk = batch->offset & ~chunk_mask;
     126        size_t end_chunk = (batch->offset + batch->size - 1) & ~chunk_mask;
     127
     128        /* The requested area crosses a chunk boundary */
     129        if (start_chunk != end_chunk)
     130                return true;
     131
     132        return false;
     133}
     134
    105135errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *batch)
    106136{
     
    108138        assert(!batch->is_bounced);
    109139
    110         if (dma_buffer_is_set(&batch->dma_buffer))
    111                 dma_buffer_unlock(&batch->dma_buffer, batch->buffer_size);
     140        dma_buffer_release(&batch->dma_buffer);
     141
     142        batch->original_buffer = batch->dma_buffer.virt + batch->offset;
    112143
    113144        usb_log_debug("Batch(%p): Buffer cannot be used directly, "
     
    115146
    116147        const errno_t err = dma_buffer_alloc_policy(&batch->dma_buffer,
    117             batch->buffer_size, batch->ep->transfer_buffer_policy);
     148            batch->size, batch->ep->transfer_buffer_policy);
    118149        if (err)
    119150                return err;
     
    121152        /* Copy the data out */
    122153        if (batch->dir == USB_DIRECTION_OUT)
    123                 memcpy(batch->dma_buffer.virt, batch->buffer, batch->buffer_size);
     154                memcpy(batch->dma_buffer.virt,
     155                    batch->original_buffer,
     156                    batch->size);
    124157
    125158        batch->is_bounced = true;
     159        batch->offset = 0;
     160
    126161        return err;
    127 }
    128 
    129 /**
    130  * Prepare a DMA buffer according to endpoint policy.
    131  *
    132  * If the buffer is suitable to be used directly, it is. Otherwise, a bounce
    133  * buffer is created.
    134  */
    135 errno_t usb_transfer_batch_prepare_buffer(usb_transfer_batch_t *batch, char *buf)
    136 {
    137         /* Empty transfers do not need a buffer */
    138         if (batch->buffer_size == 0)
    139                 return EOK;
    140 
    141         batch->buffer = buf;
    142 
    143         const dma_policy_t policy = batch->ep->transfer_buffer_policy;
    144 
    145         /*
    146          * We don't have enough information (yet, WIP) to know if we can skip
    147          * the bounce, so check the conditions carefully.
    148          */
    149         if (!dma_buffer_check_policy(buf, batch->buffer_size, policy))
    150                 return usb_transfer_batch_bounce(batch);
    151 
    152         /* Fill the buffer with virtual address and lock it for DMA */
    153         return dma_buffer_lock(&batch->dma_buffer, buf, batch->buffer_size);
    154162}
    155163
     
    167175            batch, USB_TRANSFER_BATCH_ARGS(*batch));
    168176
    169         if (batch->error == EOK && batch->buffer_size > 0) {
    170                 if (!batch->is_bounced) {
    171                         /* Unlock the buffer for DMA */
    172                         dma_buffer_unlock(&batch->dma_buffer,
    173                             batch->buffer_size);
     177        if (batch->error == EOK && batch->size > 0) {
     178                if (batch->is_bounced) {
     179                        /* We we're forced to use bounce buffer, copy it back */
     180                        if (batch->dir == USB_DIRECTION_IN)
     181                        memcpy(batch->original_buffer,
     182                            batch->dma_buffer.virt,
     183                            batch->transferred_size);
     184
     185                        dma_buffer_free(&batch->dma_buffer);
    174186                }
    175187                else {
    176                         /* We we're forced to use bounce buffer, copy it back */
    177                         if (batch->dir == USB_DIRECTION_IN)
    178                                 memcpy(batch->buffer,
    179                                     batch->dma_buffer.virt,
    180                                     batch->transferred_size);
    181 
    182                         dma_buffer_free(&batch->dma_buffer);
     188                        dma_buffer_release(&batch->dma_buffer);
    183189                }
    184190        }
Note: See TracChangeset for help on using the changeset viewer.