Ignore:
Timestamp:
2018-02-28T16:37:50Z (6 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
1b20da0
Parents:
f5e5f73 (diff), b2dca8de (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Jakub Jermar <jakub@…> (2018-02-28 16:06:42)
git-committer:
Jakub Jermar <jakub@…> (2018-02-28 16:37:50)
Message:

Merge github.com:helenos-xhci-team/helenos

This commit merges support for USB 3 and generally refactors, fixes,
extends and cleans up the existing USB framework.

Notable additions and features:

  • new host controller driver has been implemented to control various xHC models (among others, NEC Renesas uPD720200)
  • isochronous data transfer mode
  • support for explicit USB device removal
  • USB tablet driver
File:
1 edited

Legend:

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

    rf5e5f73 rdf6ded8  
    11/*
    22 * Copyright (c) 2011 Jan Vesely
     3 * Copyright (c) 2018 Ondrej Hlavaty
    34 * All rights reserved.
    45 *
     
    3334 */
    3435
    35 #include <usb/host/usb_transfer_batch.h>
    36 #include <usb/debug.h>
    37 
    3836#include <assert.h>
    3937#include <errno.h>
    40 #include <macros.h>
    41 #include <mem.h>
    4238#include <stdlib.h>
    43 #include <usbhc_iface.h>
    44 
    45 /** Allocate and initialize usb_transfer_batch structure.
    46  * @param ep endpoint used by the transfer batch.
    47  * @param buffer data to send/recieve.
    48  * @param buffer_size Size of data buffer.
    49  * @param setup_buffer Data to send in SETUP stage of control transfer.
    50  * @param func_in callback on IN transfer completion.
    51  * @param func_out callback on OUT transfer completion.
    52  * @param fun DDF function (passed to callback function).
    53  * @param arg Argument to pass to the callback function.
    54  * @param private_data driver specific per batch data.
    55  * @param private_data_dtor Function to properly destroy private_data.
    56  * @return Pointer to valid usb_transfer_batch_t structure, NULL on failure.
    57  */
    58 usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep, char *buffer,
    59     size_t buffer_size,
    60     uint64_t setup_buffer,
    61     usbhc_iface_transfer_in_callback_t func_in,
    62     usbhc_iface_transfer_out_callback_t func_out,
    63     void *arg)
    64 {
    65         if (func_in == NULL && func_out == NULL)
    66                 return NULL;
    67         if (func_in != NULL && func_out != NULL)
    68                 return NULL;
    69 
    70         usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
    71         if (instance) {
    72                 instance->ep = ep;
    73                 instance->callback_in = func_in;
    74                 instance->callback_out = func_out;
    75                 instance->arg = arg;
    76                 instance->buffer = buffer;
    77                 instance->buffer_size = buffer_size;
    78                 instance->setup_size = 0;
    79                 instance->transfered_size = 0;
    80                 instance->error = EOK;
    81                 if (ep && ep->transfer_type == USB_TRANSFER_CONTROL) {
    82                         memcpy(instance->setup_buffer, &setup_buffer,
    83                             USB_SETUP_PACKET_SIZE);
    84                         instance->setup_size = USB_SETUP_PACKET_SIZE;
     39#include <str_error.h>
     40#include <usb/debug.h>
     41
     42#include "endpoint.h"
     43#include "bus.h"
     44
     45#include "usb_transfer_batch.h"
     46
     47/**
     48 * Create a batch on a given endpoint.
     49 *
     50 * If the bus callback is not defined, it just creates a default batch.
     51 */
     52usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep)
     53{
     54        assert(ep);
     55
     56        bus_t *bus = endpoint_get_bus(ep);
     57
     58        if (!bus->ops->batch_create) {
     59                usb_transfer_batch_t *batch = calloc(1, sizeof(usb_transfer_batch_t));
     60                if (!batch)
     61                        return NULL;
     62                usb_transfer_batch_init(batch, ep);
     63                return batch;
     64        }
     65
     66        return bus->ops->batch_create(ep);
     67}
     68
     69/**
     70 * Initialize given batch structure.
     71 */
     72void usb_transfer_batch_init(usb_transfer_batch_t *batch, endpoint_t *ep)
     73{
     74        assert(ep);
     75        /* Batch reference */
     76        endpoint_add_ref(ep);
     77        batch->ep = ep;
     78}
     79
     80/**
     81 * Destroy the batch. If there's no bus callback, just free it.
     82 */
     83void usb_transfer_batch_destroy(usb_transfer_batch_t *batch)
     84{
     85        assert(batch);
     86        assert(batch->ep);
     87
     88        bus_t *bus = endpoint_get_bus(batch->ep);
     89        endpoint_t *ep = batch->ep;
     90
     91        if (bus->ops) {
     92                usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " destroying.",
     93                    batch, USB_TRANSFER_BATCH_ARGS(*batch));
     94                bus->ops->batch_destroy(batch);
     95        }
     96        else {
     97                usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.",
     98                    batch, USB_TRANSFER_BATCH_ARGS(*batch));
     99                free(batch);
     100        }
     101
     102        /* Batch reference */
     103        endpoint_del_ref(ep);
     104}
     105
     106bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *batch)
     107{
     108        if (!batch->size)
     109                return false;
     110
     111        unsigned flags = batch->dma_buffer.policy & DMA_POLICY_FLAGS_MASK;
     112        unsigned required_flags =
     113            batch->ep->required_transfer_buffer_policy & DMA_POLICY_FLAGS_MASK;
     114
     115        if (required_flags & ~flags)
     116                return true;
     117
     118        size_t chunk_mask = dma_policy_chunk_mask(batch->dma_buffer.policy);
     119        size_t required_chunk_mask =
     120             dma_policy_chunk_mask(batch->ep->required_transfer_buffer_policy);
     121
     122        /* If the chunks are at least as large as required, we're good */
     123        if ((required_chunk_mask & ~chunk_mask) == 0)
     124                return false;
     125
     126        size_t start_chunk = batch->offset & ~chunk_mask;
     127        size_t end_chunk = (batch->offset + batch->size - 1) & ~chunk_mask;
     128
     129        /* The requested area crosses a chunk boundary */
     130        if (start_chunk != end_chunk)
     131                return true;
     132
     133        return false;
     134}
     135
     136errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *batch)
     137{
     138        assert(batch);
     139        assert(!batch->is_bounced);
     140
     141        dma_buffer_release(&batch->dma_buffer);
     142
     143        batch->original_buffer = batch->dma_buffer.virt + batch->offset;
     144
     145        usb_log_debug("Batch(%p): Buffer cannot be used directly, "
     146            "falling back to bounce buffer!", batch);
     147
     148        const errno_t err = dma_buffer_alloc_policy(&batch->dma_buffer,
     149            batch->size, batch->ep->transfer_buffer_policy);
     150        if (err)
     151                return err;
     152
     153        /* Copy the data out */
     154        if (batch->dir == USB_DIRECTION_OUT)
     155                memcpy(batch->dma_buffer.virt,
     156                    batch->original_buffer,
     157                    batch->size);
     158
     159        batch->is_bounced = true;
     160        batch->offset = 0;
     161
     162        return err;
     163}
     164
     165/**
     166 * Finish a transfer batch: call handler, destroy batch, release endpoint.
     167 *
     168 * Call only after the batch have been scheduled && completed!
     169 */
     170void usb_transfer_batch_finish(usb_transfer_batch_t *batch)
     171{
     172        assert(batch);
     173        assert(batch->ep);
     174
     175        usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.",
     176            batch, USB_TRANSFER_BATCH_ARGS(*batch));
     177
     178        if (batch->error == EOK && batch->size > 0) {
     179                if (batch->is_bounced) {
     180                        /* We we're forced to use bounce buffer, copy it back */
     181                        if (batch->dir == USB_DIRECTION_IN)
     182                        memcpy(batch->original_buffer,
     183                            batch->dma_buffer.virt,
     184                            batch->transferred_size);
     185
     186                        dma_buffer_free(&batch->dma_buffer);
    85187                }
    86                 if (instance->ep)
    87                         endpoint_use(instance->ep);
    88         }
    89         return instance;
    90 }
    91 
    92 /** Correctly dispose all used data structures.
    93  *
    94  * @param[in] instance Batch structure to use.
    95  */
    96 void usb_transfer_batch_destroy(usb_transfer_batch_t *instance)
    97 {
    98         if (!instance)
    99                 return;
    100         usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
    101             instance, USB_TRANSFER_BATCH_ARGS(*instance));
    102         if (instance->ep) {
    103                 endpoint_release(instance->ep);
    104         }
    105         free(instance);
    106 }
    107 
    108 /** Prepare data and call the right callback.
    109  *
    110  * @param[in] instance Batch structure to use.
    111  * @param[in] data Data to copy to the output buffer.
    112  * @param[in] size Size of @p data.
    113  * @param[in] error Error value to use.
    114  */
    115 void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
    116     const void *data, size_t size, errno_t error)
    117 {
    118         assert(instance);
    119         usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.\n",
    120             instance, USB_TRANSFER_BATCH_ARGS(*instance));
    121 
    122         /* NOTE: Only one of these pointers should be set. */
    123         if (instance->callback_out) {
    124                 instance->callback_out(error, instance->arg);
    125         }
    126 
    127         if (instance->callback_in) {
    128                 /* We care about the data and there are some to copy */
    129                 const size_t safe_size = min(size, instance->buffer_size);
    130                 if (data) {
    131                         memcpy(instance->buffer, data, safe_size);
     188                else {
     189                        dma_buffer_release(&batch->dma_buffer);
    132190                }
    133                 instance->callback_in(error, safe_size, instance->arg);
    134         }
    135 }
     191        }
     192
     193        if (batch->on_complete) {
     194                const int err = batch->on_complete(batch->on_complete_data, batch->error, batch->transferred_size);
     195                if (err)
     196                        usb_log_warning("Batch %p failed to complete: %s",
     197                            batch, str_error(err));
     198        }
     199
     200        usb_transfer_batch_destroy(batch);
     201}
     202
    136203/**
    137204 * @}
Note: See TracChangeset for help on using the changeset viewer.