Ignore:
File:
1 edited

Legend:

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

    rb7fd2a0 r1d758fc  
    3636#include <usb/dev/request.h>
    3737#include <usb/usb.h>
    38 #include <usb_iface.h>
     38#include <usb/dma_buffer.h>
    3939
    4040#include <assert.h>
     41#include <bitops.h>
    4142#include <async.h>
     43#include <as.h>
    4244#include <errno.h>
    4345#include <mem.h>
     
    5153        assert(pipe != NULL);
    5254
    53         if (!pipe->auto_reset_halt || (pipe->endpoint_no != 0)) {
     55        if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
    5456                return;
    5557        }
     
    5759        /* Prevent infinite recursion. */
    5860        pipe->auto_reset_halt = false;
    59         usb_request_clear_endpoint_halt(pipe, 0);
     61        usb_pipe_clear_halt(pipe, pipe);
    6062        pipe->auto_reset_halt = true;
     63}
     64
     65/* Helper structure to avoid passing loads of arguments through */
     66typedef struct {
     67        usb_pipe_t *pipe;
     68        usb_direction_t dir;
     69        bool is_control;        // Only for checking purposes
     70
     71        usbhc_iface_transfer_request_t req;
     72
     73        size_t transferred_size;
     74} transfer_t;
     75
     76/**
     77 * Issue a transfer in a separate exchange.
     78 */
     79static errno_t transfer_common(transfer_t *t)
     80{
     81        if (!t->pipe)
     82                return EBADMEM;
     83
     84        /* Only control writes make sense without buffer */
     85        if ((t->dir != USB_DIRECTION_OUT || !t->is_control) && t->req.size == 0)
     86                return EINVAL;
     87
     88        /* Nonzero size requires buffer */
     89        if (!dma_buffer_is_set(&t->req.buffer) && t->req.size != 0)
     90                return EINVAL;
     91
     92        /* Check expected direction */
     93        if (t->pipe->desc.direction != USB_DIRECTION_BOTH &&
     94            t->pipe->desc.direction != t->dir)
     95                return EBADF;
     96
     97        /* Check expected transfer type */
     98        if ((t->pipe->desc.transfer_type == USB_TRANSFER_CONTROL) != t->is_control)
     99                return EBADF;
     100
     101        async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
     102        if (!exch)
     103                return ENOMEM;
     104
     105        t->req.dir = t->dir;
     106        t->req.endpoint = t->pipe->desc.endpoint_no;
     107
     108        const errno_t rc = usbhc_transfer(exch, &t->req, &t->transferred_size);
     109
     110        async_exchange_end(exch);
     111
     112        if (rc == ESTALL)
     113                clear_self_endpoint_halt(t->pipe);
     114
     115        return rc;
     116}
     117
     118/**
     119 * Setup the transfer request inside transfer according to dma buffer provided.
     120 *
     121 * TODO: The buffer could have been allocated as a more strict one. Currently,
     122 * we assume that the policy is just the requested one.
     123 */
     124static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
     125{
     126        t->req.buffer.virt = base;
     127        t->req.buffer.policy = t->pipe->desc.transfer_buffer_policy;
     128        t->req.offset = ptr - base;
     129        t->req.size = size;
     130}
     131
     132/**
     133 * Compatibility wrapper for reads/writes without preallocated buffer.
     134 */
     135static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
     136{
     137        if (size == 0) {
     138                setup_dma_buffer(t, NULL, NULL, 0);
     139                return transfer_common(t);
     140        }
     141
     142        void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
     143        setup_dma_buffer(t, dma_buf, dma_buf, size);
     144
     145        if (t->dir == USB_DIRECTION_OUT)
     146                memcpy(dma_buf, buf, size);
     147
     148        const errno_t err = transfer_common(t);
     149
     150        if (!err && t->dir == USB_DIRECTION_IN)
     151                memcpy(buf, dma_buf, t->transferred_size);
     152
     153        usb_pipe_free_buffer(t->pipe, dma_buf);
     154        return err;
     155}
     156
     157static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
     158{
     159        if ((setup == NULL) || (setup_size != 8))
     160                return EINVAL;
     161       
     162        memcpy(&t->req.setup, setup, 8);
     163        return EOK;
    61164}
    62165
     
    70173 * @param[out] data_buffer Buffer for incoming data.
    71174 * @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.
     175 * @param[out] data_transferred_size Number of bytes that were actually
     176 *                                  transferred during the DATA stage.
    74177 * @return Error code.
    75178 */
    76179errno_t usb_pipe_control_read(usb_pipe_t *pipe,
    77180    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;
     181    void *buffer, size_t buffer_size, size_t *transferred_size)
     182{
     183        errno_t err;
     184        transfer_t transfer = {
     185                .pipe = pipe,
     186                .dir = USB_DIRECTION_IN,
     187                .is_control = true,
     188        };
     189
     190        if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
     191                return err;
     192
     193        if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
     194                return err;
     195
     196        if (transferred_size)
     197                *transferred_size = transfer.transferred_size;
     198
     199        return EOK;
    113200}
    114201
     
    129216{
    130217        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;
     218        errno_t err;
     219        transfer_t transfer = {
     220                .pipe = pipe,
     221                .dir = USB_DIRECTION_OUT,
     222                .is_control = true,
     223        };
     224
     225        if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
     226                return err;
     227
     228        return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
     229}
     230
     231/**
     232 * Allocate a buffer for data transmission, that satisfies the constraints
     233 * imposed by the host controller.
     234 *
     235 * @param[in] pipe Pipe for which the buffer is allocated
     236 * @param[in] size Size of the required buffer
     237 */
     238void *usb_pipe_alloc_buffer(usb_pipe_t *pipe, size_t size)
     239{
     240        dma_buffer_t buf;
     241        if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
     242                return NULL;
     243
     244        return buf.virt;
     245}
     246
     247void usb_pipe_free_buffer(usb_pipe_t *pipe, void *buffer)
     248{
     249        dma_buffer_t buf;
     250        buf.virt = buffer;
     251        dma_buffer_free(&buf);
    162252}
    163253
     
    167257 * @param[out] buffer Buffer where to store the data.
    168258 * @param[in] size Size of the buffer (in bytes).
    169  * @param[out] size_transfered Number of bytes that were actually transfered.
     259 * @param[out] size_transferred Number of bytes that were actually transferred.
    170260 * @return Error code.
    171261 */
    172262errno_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;
     263    void *buffer, size_t size, size_t *size_transferred)
     264{
     265        assert(pipe);
     266        errno_t err;
     267        transfer_t transfer = {
     268                .pipe = pipe,
     269                .dir = USB_DIRECTION_IN,
     270        };
     271
     272        if ((err = transfer_wrap_dma(&transfer, buffer, size)))
     273                return err;
     274
     275        if (size_transferred)
     276                *size_transferred = transfer.transferred_size;
     277
     278        return EOK;
    209279}
    210280
     
    219289{
    220290        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;
     291        transfer_t transfer = {
     292                .pipe = pipe,
     293                .dir = USB_DIRECTION_OUT,
     294        };
     295
     296        return transfer_wrap_dma(&transfer, (void *) buffer, size);
     297}
     298
     299/**
     300 * Request a read (in) transfer on an endpoint pipe, declaring that buffer
     301 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
     302 *
     303 * @param[in] pipe Pipe used for the transfer.
     304 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
     305 * @param[in] size Size of the buffer (in bytes).
     306 * @param[out] size_transferred Number of bytes that were actually transferred.
     307 * @return Error code.
     308 */
     309errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
     310    size_t *size_transferred)
     311{
     312        assert(pipe);
     313        errno_t err;
     314        transfer_t transfer = {
     315                .pipe = pipe,
     316                .dir = USB_DIRECTION_IN,
     317        };
     318
     319        setup_dma_buffer(&transfer, base, ptr, size);
     320
     321        if ((err = transfer_common(&transfer)))
     322                return err;
     323
     324        if (size_transferred)
     325                *size_transferred = transfer.transferred_size;
     326
     327        return EOK;
     328}
     329
     330/**
     331 * Request a write (out) transfer on an endpoint pipe, declaring that buffer
     332 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
     333 *
     334 * @param[in] pipe Pipe used for the transfer.
     335 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
     336 * @param[in] size Size of the buffer (in bytes).
     337 * @return Error code.
     338 */
     339errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void* ptr,  size_t size)
     340{
     341        assert(pipe);
     342        transfer_t transfer = {
     343                .pipe = pipe,
     344                .dir = USB_DIRECTION_OUT,
     345        };
     346
     347        setup_dma_buffer(&transfer, base, ptr, size);
     348
     349        return transfer_common(&transfer);
    243350}
    244351
     
    246353 *
    247354 * @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;
     355 * @param bus_session Endpoint pipe to be initialized.
     356 * @return Error code.
     357 */
     358errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
     359{
     360        assert(pipe);
     361
    265362        pipe->auto_reset_halt = false;
    266363        pipe->bus_session = bus_session;
     
    269366}
    270367
    271 /** Initialize USB endpoint pipe as the default zero control pipe.
     368static const usb_pipe_desc_t default_control_pipe = {
     369        .endpoint_no = 0,
     370        .transfer_type = USB_TRANSFER_CONTROL,
     371        .direction = USB_DIRECTION_BOTH,
     372        .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
     373        .transfer_buffer_policy = DMA_POLICY_STRICT,
     374};
     375
     376/** Initialize USB default control pipe.
     377 *
     378 * This one is special because it must not be registered, it is registered automatically.
    272379 *
    273380 * @param pipe Endpoint pipe to be initialized.
    274  * @return Error code.
    275  */
    276 errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe,
    277     usb_dev_session_t *bus_session)
    278 {
    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 
     381 * @param bus_session Endpoint pipe to be initialized.
     382 * @return Error code.
     383 */
     384errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
     385{
     386        const errno_t ret = usb_pipe_initialize(pipe, bus_session);
     387        if (ret)
     388                return ret;
     389
     390        pipe->desc = default_control_pipe;
    284391        pipe->auto_reset_halt = true;
    285392
    286         return rc;
     393        return EOK;
    287394}
    288395
     
    290397 *
    291398 * @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)
     399 * @param ep_desc Matched endpoint descriptor
     400 * @param comp_desc Matched superspeed companion descriptro, if any
     401 * @return Error code.
     402 */
     403errno_t usb_pipe_register(usb_pipe_t *pipe, const usb_standard_endpoint_descriptor_t *ep_desc, const usb_superspeed_endpoint_companion_descriptor_t *comp_desc)
     404{
     405        assert(pipe);
     406        assert(pipe->bus_session);
     407        assert(ep_desc);
     408
     409        async_exch_t *exch = async_exchange_begin(pipe->bus_session);
     410        if (!exch)
     411                return ENOMEM;
     412
     413        usb_endpoint_descriptors_t descriptors;
     414
     415#define COPY(field) descriptors.endpoint.field = ep_desc->field
     416        COPY(endpoint_address);
     417        COPY(attributes);
     418        COPY(max_packet_size);
     419        COPY(poll_interval);
     420#undef COPY
     421
     422#define COPY(field) descriptors.companion.field = comp_desc->field
     423        if (comp_desc) {
     424                COPY(max_burst);
     425                COPY(attributes);
     426                COPY(bytes_per_interval);
     427        }
     428#undef COPY
     429
     430        const errno_t ret = usbhc_register_endpoint(exch, &pipe->desc, &descriptors);
     431        async_exchange_end(exch);
     432        return ret;
     433}
     434
     435/** Revert endpoint registration with the host controller.
     436 *
     437 * @param pipe Pipe to be unregistered.
     438 * @return Error code.
     439 */
     440errno_t usb_pipe_unregister(usb_pipe_t *pipe)
    296441{
    297442        assert(pipe);
     
    300445        if (!exch)
    301446                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);
     447
     448        const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
     449
    305450        async_exchange_end(exch);
    306451        return ret;
    307452}
    308453
    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 
    327454/**
    328455 * @}
Note: See TracChangeset for help on using the changeset viewer.