Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbdev/src/pipes.c

    rb7fd2a0 re773f58  
    22 * Copyright (c) 2011 Vojtech Horky
    33 * Copyright (c) 2011 Jan Vesely
     4 * Copyright (c) 2018 Ondrej Hlavaty, Michal Staruch
    45 * All rights reserved.
    56 *
     
    3637#include <usb/dev/request.h>
    3738#include <usb/usb.h>
    38 #include <usb_iface.h>
     39#include <usb/dma_buffer.h>
    3940
    4041#include <assert.h>
     42#include <bitops.h>
    4143#include <async.h>
     44#include <as.h>
    4245#include <errno.h>
    4346#include <mem.h>
     
    5154        assert(pipe != NULL);
    5255
    53         if (!pipe->auto_reset_halt || (pipe->endpoint_no != 0)) {
     56        if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
    5457                return;
    5558        }
     
    5760        /* Prevent infinite recursion. */
    5861        pipe->auto_reset_halt = false;
    59         usb_request_clear_endpoint_halt(pipe, 0);
     62        usb_pipe_clear_halt(pipe, pipe);
    6063        pipe->auto_reset_halt = true;
     64}
     65
     66/* Helper structure to avoid passing loads of arguments through */
     67typedef struct {
     68        usb_pipe_t *pipe;
     69        usb_direction_t dir;
     70        bool is_control;        // Only for checking purposes
     71
     72        usbhc_iface_transfer_request_t req;
     73
     74        size_t transferred_size;
     75} transfer_t;
     76
     77/**
     78 * Issue a transfer in a separate exchange.
     79 */
     80static errno_t transfer_common(transfer_t *t)
     81{
     82        if (!t->pipe)
     83                return EBADMEM;
     84
     85        /* Only control writes make sense without buffer */
     86        if ((t->dir != USB_DIRECTION_OUT || !t->is_control) && t->req.size == 0)
     87                return EINVAL;
     88
     89        /* Nonzero size requires buffer */
     90        if (!dma_buffer_is_set(&t->req.buffer) && t->req.size != 0)
     91                return EINVAL;
     92
     93        /* Check expected direction */
     94        if (t->pipe->desc.direction != USB_DIRECTION_BOTH &&
     95            t->pipe->desc.direction != t->dir)
     96                return EBADF;
     97
     98        /* Check expected transfer type */
     99        if ((t->pipe->desc.transfer_type == USB_TRANSFER_CONTROL) != t->is_control)
     100                return EBADF;
     101
     102        async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
     103        if (!exch)
     104                return ENOMEM;
     105
     106        t->req.dir = t->dir;
     107        t->req.endpoint = t->pipe->desc.endpoint_no;
     108
     109        const errno_t rc = usbhc_transfer(exch, &t->req, &t->transferred_size);
     110
     111        async_exchange_end(exch);
     112
     113        if (rc == ESTALL)
     114                clear_self_endpoint_halt(t->pipe);
     115
     116        return rc;
     117}
     118
     119/**
     120 * Setup the transfer request inside transfer according to dma buffer provided.
     121 *
     122 * TODO: The buffer could have been allocated as a more strict one. Currently,
     123 * we assume that the policy is just the requested one.
     124 */
     125static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
     126{
     127        t->req.buffer.virt = base;
     128        t->req.buffer.policy = t->pipe->desc.transfer_buffer_policy;
     129        t->req.offset = ptr - base;
     130        t->req.size = size;
     131}
     132
     133/**
     134 * Compatibility wrapper for reads/writes without preallocated buffer.
     135 */
     136static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
     137{
     138        if (size == 0) {
     139                setup_dma_buffer(t, NULL, NULL, 0);
     140                return transfer_common(t);
     141        }
     142
     143        void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
     144        setup_dma_buffer(t, dma_buf, dma_buf, size);
     145
     146        if (t->dir == USB_DIRECTION_OUT)
     147                memcpy(dma_buf, buf, size);
     148
     149        const errno_t err = transfer_common(t);
     150
     151        if (!err && t->dir == USB_DIRECTION_IN)
     152                memcpy(buf, dma_buf, t->transferred_size);
     153
     154        usb_pipe_free_buffer(t->pipe, dma_buf);
     155        return err;
     156}
     157
     158static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
     159{
     160        if ((setup == NULL) || (setup_size != 8))
     161                return EINVAL;
     162       
     163        memcpy(&t->req.setup, setup, 8);
     164        return EOK;
    61165}
    62166
     
    70174 * @param[out] data_buffer Buffer for incoming data.
    71175 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
    72  * @param[out] data_transfered_size Number of bytes that were actually
    73  *                                  transfered during the DATA stage.
     176 * @param[out] data_transferred_size Number of bytes that were actually
     177 *                                  transferred during the DATA stage.
    74178 * @return Error code.
    75179 */
    76180errno_t usb_pipe_control_read(usb_pipe_t *pipe,
    77181    const void *setup_buffer, size_t setup_buffer_size,
    78     void *buffer, size_t buffer_size, size_t *transfered_size)
    79 {
    80         assert(pipe);
    81 
    82         if ((setup_buffer == NULL) || (setup_buffer_size != 8)) {
    83                 return EINVAL;
    84         }
    85 
    86         if ((buffer == NULL) || (buffer_size == 0)) {
    87                 return EINVAL;
    88         }
    89 
    90         if ((pipe->direction != USB_DIRECTION_BOTH)
    91             || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
    92                 return EBADF;
    93         }
    94 
    95         uint64_t setup_packet;
    96         memcpy(&setup_packet, setup_buffer, 8);
    97 
    98         async_exch_t *exch = async_exchange_begin(pipe->bus_session);
    99         size_t act_size = 0;
    100         const errno_t rc = usb_read(exch, pipe->endpoint_no, setup_packet, buffer,
    101             buffer_size, &act_size);
    102         async_exchange_end(exch);
    103 
    104         if (rc == ESTALL) {
    105                 clear_self_endpoint_halt(pipe);
    106         }
    107 
    108         if (rc == EOK && transfered_size != NULL) {
    109                 *transfered_size = act_size;
    110         }
    111 
    112         return rc;
     182    void *buffer, size_t buffer_size, size_t *transferred_size)
     183{
     184        errno_t err;
     185        transfer_t transfer = {
     186                .pipe = pipe,
     187                .dir = USB_DIRECTION_IN,
     188                .is_control = true,
     189        };
     190
     191        if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
     192                return err;
     193
     194        if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
     195                return err;
     196
     197        if (transferred_size)
     198                *transferred_size = transfer.transferred_size;
     199
     200        return EOK;
    113201}
    114202
     
    129217{
    130218        assert(pipe);
    131 
    132         if ((setup_buffer == NULL) || (setup_buffer_size != 8)) {
    133                 return EINVAL;
    134         }
    135 
    136         if ((buffer == NULL) && (buffer_size > 0)) {
    137                 return EINVAL;
    138         }
    139 
    140         if ((buffer != NULL) && (buffer_size == 0)) {
    141                 return EINVAL;
    142         }
    143 
    144         if ((pipe->direction != USB_DIRECTION_BOTH)
    145             || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
    146                 return EBADF;
    147         }
    148 
    149         uint64_t setup_packet;
    150         memcpy(&setup_packet, setup_buffer, 8);
    151 
    152         async_exch_t *exch = async_exchange_begin(pipe->bus_session);
    153         const errno_t rc = usb_write(exch,
    154             pipe->endpoint_no, setup_packet, buffer, buffer_size);
    155         async_exchange_end(exch);
    156 
    157         if (rc == ESTALL) {
    158                 clear_self_endpoint_halt(pipe);
    159         }
    160 
    161         return rc;
     219        errno_t err;
     220        transfer_t transfer = {
     221                .pipe = pipe,
     222                .dir = USB_DIRECTION_OUT,
     223                .is_control = true,
     224        };
     225
     226        if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
     227                return err;
     228
     229        return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
     230}
     231
     232/**
     233 * Allocate a buffer for data transmission, that satisfies the constraints
     234 * imposed by the host controller.
     235 *
     236 * @param[in] pipe Pipe for which the buffer is allocated
     237 * @param[in] size Size of the required buffer
     238 */
     239void *usb_pipe_alloc_buffer(usb_pipe_t *pipe, size_t size)
     240{
     241        dma_buffer_t buf;
     242        if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
     243                return NULL;
     244
     245        return buf.virt;
     246}
     247
     248void usb_pipe_free_buffer(usb_pipe_t *pipe, void *buffer)
     249{
     250        dma_buffer_t buf;
     251        buf.virt = buffer;
     252        dma_buffer_free(&buf);
    162253}
    163254
     
    167258 * @param[out] buffer Buffer where to store the data.
    168259 * @param[in] size Size of the buffer (in bytes).
    169  * @param[out] size_transfered Number of bytes that were actually transfered.
     260 * @param[out] size_transferred Number of bytes that were actually transferred.
    170261 * @return Error code.
    171262 */
    172263errno_t usb_pipe_read(usb_pipe_t *pipe,
    173     void *buffer, size_t size, size_t *size_transfered)
    174 {
    175         assert(pipe);
    176 
    177         if (buffer == NULL) {
    178                 return EINVAL;
    179         }
    180 
    181         if (size == 0) {
    182                 return EINVAL;
    183         }
    184 
    185         if (pipe->direction != USB_DIRECTION_IN) {
    186                 return EBADF;
    187         }
    188 
    189         if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
    190                 return EBADF;
    191         }
    192 
    193         /* Isochronous transfer are not supported (yet) */
    194         if (pipe->transfer_type != USB_TRANSFER_INTERRUPT &&
    195             pipe->transfer_type != USB_TRANSFER_BULK)
    196             return ENOTSUP;
    197 
    198         async_exch_t *exch = async_exchange_begin(pipe->bus_session);
    199         size_t act_size = 0;
    200         const errno_t rc =
    201             usb_read(exch, pipe->endpoint_no, 0, buffer, size, &act_size);
    202         async_exchange_end(exch);
    203 
    204         if (rc == EOK && size_transfered != NULL) {
    205                 *size_transfered = act_size;
    206         }
    207 
    208         return rc;
     264    void *buffer, size_t size, size_t *size_transferred)
     265{
     266        assert(pipe);
     267        errno_t err;
     268        transfer_t transfer = {
     269                .pipe = pipe,
     270                .dir = USB_DIRECTION_IN,
     271        };
     272
     273        if ((err = transfer_wrap_dma(&transfer, buffer, size)))
     274                return err;
     275
     276        if (size_transferred)
     277                *size_transferred = transfer.transferred_size;
     278
     279        return EOK;
    209280}
    210281
     
    219290{
    220291        assert(pipe);
    221 
    222         if (buffer == NULL || size == 0) {
    223                 return EINVAL;
    224         }
    225 
    226         if (pipe->direction != USB_DIRECTION_OUT) {
    227                 return EBADF;
    228         }
    229 
    230         if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
    231                 return EBADF;
    232         }
    233 
    234         /* Isochronous transfer are not supported (yet) */
    235         if (pipe->transfer_type != USB_TRANSFER_INTERRUPT &&
    236             pipe->transfer_type != USB_TRANSFER_BULK)
    237             return ENOTSUP;
    238 
    239         async_exch_t *exch = async_exchange_begin(pipe->bus_session);
    240         const errno_t rc = usb_write(exch, pipe->endpoint_no, 0, buffer, size);
    241         async_exchange_end(exch);
    242         return rc;
     292        transfer_t transfer = {
     293                .pipe = pipe,
     294                .dir = USB_DIRECTION_OUT,
     295        };
     296
     297        return transfer_wrap_dma(&transfer, (void *) buffer, size);
     298}
     299
     300/**
     301 * Request a read (in) transfer on an endpoint pipe, declaring that buffer
     302 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
     303 *
     304 * @param[in] pipe Pipe used for the transfer.
     305 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
     306 * @param[in] size Size of the buffer (in bytes).
     307 * @param[out] size_transferred Number of bytes that were actually transferred.
     308 * @return Error code.
     309 */
     310errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
     311    size_t *size_transferred)
     312{
     313        assert(pipe);
     314        errno_t err;
     315        transfer_t transfer = {
     316                .pipe = pipe,
     317                .dir = USB_DIRECTION_IN,
     318        };
     319
     320        setup_dma_buffer(&transfer, base, ptr, size);
     321
     322        if ((err = transfer_common(&transfer)))
     323                return err;
     324
     325        if (size_transferred)
     326                *size_transferred = transfer.transferred_size;
     327
     328        return EOK;
     329}
     330
     331/**
     332 * Request a write (out) transfer on an endpoint pipe, declaring that buffer
     333 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
     334 *
     335 * @param[in] pipe Pipe used for the transfer.
     336 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
     337 * @param[in] size Size of the buffer (in bytes).
     338 * @return Error code.
     339 */
     340errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size)
     341{
     342        assert(pipe);
     343        transfer_t transfer = {
     344                .pipe = pipe,
     345                .dir = USB_DIRECTION_OUT,
     346        };
     347
     348        setup_dma_buffer(&transfer, base, ptr, size);
     349
     350        return transfer_common(&transfer);
    243351}
    244352
     
    246354 *
    247355 * @param pipe Endpoint pipe to be initialized.
    248  * @param endpoint_no Endpoint number (in USB 1.1 in range 0 to 15).
    249  * @param transfer_type Transfer type (e.g. interrupt or bulk).
    250  * @param max_packet_size Maximum packet size in bytes.
    251  * @param direction Endpoint direction (in/out).
    252  * @return Error code.
    253  */
    254 errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_endpoint_t endpoint_no,
    255     usb_transfer_type_t transfer_type, size_t max_packet_size,
    256     usb_direction_t direction, unsigned packets, usb_dev_session_t *bus_session)
    257 {
    258         assert(pipe);
    259 
    260         pipe->endpoint_no = endpoint_no;
    261         pipe->transfer_type = transfer_type;
    262         pipe->packets = packets;
    263         pipe->max_packet_size = max_packet_size;
    264         pipe->direction = direction;
     356 * @param bus_session Endpoint pipe to be initialized.
     357 * @return Error code.
     358 */
     359errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
     360{
     361        assert(pipe);
     362
    265363        pipe->auto_reset_halt = false;
    266364        pipe->bus_session = bus_session;
     
    269367}
    270368
    271 /** Initialize USB endpoint pipe as the default zero control pipe.
     369static const usb_pipe_desc_t default_control_pipe = {
     370        .endpoint_no = 0,
     371        .transfer_type = USB_TRANSFER_CONTROL,
     372        .direction = USB_DIRECTION_BOTH,
     373        .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
     374        .transfer_buffer_policy = DMA_POLICY_STRICT,
     375};
     376
     377/** Initialize USB default control pipe.
     378 *
     379 * This one is special because it must not be registered, it is registered
     380 * automatically.
    272381 *
    273382 * @param pipe Endpoint pipe to be initialized.
     383 * @param bus_session Endpoint pipe to be initialized.
    274384 * @return Error code.
    275385 */
     
    277387    usb_dev_session_t *bus_session)
    278388{
    279         assert(pipe);
    280 
    281         const errno_t rc = usb_pipe_initialize(pipe, 0, USB_TRANSFER_CONTROL,
    282             CTRL_PIPE_MIN_PACKET_SIZE, USB_DIRECTION_BOTH, 1, bus_session);
    283 
     389        const errno_t ret = usb_pipe_initialize(pipe, bus_session);
     390        if (ret)
     391                return ret;
     392
     393        pipe->desc = default_control_pipe;
    284394        pipe->auto_reset_halt = true;
    285395
    286         return rc;
     396        return EOK;
    287397}
    288398
     
    290400 *
    291401 * @param pipe Pipe to be registered.
    292  * @param interval Polling interval.
    293  * @return Error code.
    294  */
    295 errno_t usb_pipe_register(usb_pipe_t *pipe, unsigned interval)
     402 * @param ep_desc Matched endpoint descriptor
     403 * @param comp_desc Matched superspeed companion descriptro, if any
     404 * @return Error code.
     405 */
     406errno_t usb_pipe_register(usb_pipe_t *pipe,
     407    const usb_standard_endpoint_descriptor_t *ep_desc,
     408    const usb_superspeed_endpoint_companion_descriptor_t *comp_desc)
     409{
     410        assert(pipe);
     411        assert(pipe->bus_session);
     412        assert(ep_desc);
     413
     414        async_exch_t *exch = async_exchange_begin(pipe->bus_session);
     415        if (!exch)
     416                return ENOMEM;
     417
     418        usb_endpoint_descriptors_t descriptors = { 0 };
     419
     420#define COPY(field) descriptors.endpoint.field = ep_desc->field
     421        COPY(endpoint_address);
     422        COPY(attributes);
     423        COPY(max_packet_size);
     424        COPY(poll_interval);
     425#undef COPY
     426
     427#define COPY(field) descriptors.companion.field = comp_desc->field
     428        if (comp_desc) {
     429                COPY(max_burst);
     430                COPY(attributes);
     431                COPY(bytes_per_interval);
     432        }
     433#undef COPY
     434
     435        const errno_t ret = usbhc_register_endpoint(exch,
     436            &pipe->desc, &descriptors);
     437        async_exchange_end(exch);
     438        return ret;
     439}
     440
     441/** Revert endpoint registration with the host controller.
     442 *
     443 * @param pipe Pipe to be unregistered.
     444 * @return Error code.
     445 */
     446errno_t usb_pipe_unregister(usb_pipe_t *pipe)
    296447{
    297448        assert(pipe);
     
    300451        if (!exch)
    301452                return ENOMEM;
    302         const errno_t ret = usb_register_endpoint(exch, pipe->endpoint_no,
    303             pipe->transfer_type, pipe->direction, pipe->max_packet_size,
    304             pipe->packets, interval);
     453
     454        const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
     455
    305456        async_exchange_end(exch);
    306457        return ret;
    307458}
    308459
    309 /** Revert endpoint registration with the host controller.
    310  *
    311  * @param pipe Pipe to be unregistered.
    312  * @return Error code.
    313  */
    314 errno_t usb_pipe_unregister(usb_pipe_t *pipe)
    315 {
    316         assert(pipe);
    317         assert(pipe->bus_session);
    318         async_exch_t *exch = async_exchange_begin(pipe->bus_session);
    319         if (!exch)
    320                 return ENOMEM;
    321         const errno_t ret = usb_unregister_endpoint(exch, pipe->endpoint_no,
    322             pipe->direction);
    323         async_exchange_end(exch);
    324         return ret;
    325 }
    326 
    327460/**
    328461 * @}
Note: See TracChangeset for help on using the changeset viewer.