Changeset df6ded8 in mainline for uspace/lib/drv/generic/remote_usb.c


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/drv/generic/remote_usb.c

    rf5e5f73 rdf6ded8  
    22 * Copyright (c) 2010 Vojtech Horky
    33 * Copyright (c) 2011 Jan Vesely
     4 * Copyright (c) 2018 Michal Staruch, Ondrej Hlavaty
    45 * All rights reserved.
    56 *
     
    5152usb_dev_session_t *usb_dev_connect_to_self(ddf_dev_t *dev)
    5253{
    53         return devman_parent_device_connect(ddf_dev_get_handle(dev), IPC_FLAG_BLOCKING);
     54        return devman_parent_device_connect(ddf_dev_get_handle(dev),
     55            IPC_FLAG_BLOCKING);
    5456}
    5557
     
    6163
    6264typedef enum {
    63         IPC_M_USB_GET_MY_INTERFACE,
    64         IPC_M_USB_GET_MY_DEVICE_HANDLE,
    65         IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
    66         IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
    67         IPC_M_USB_DEVICE_ENUMERATE,
    68         IPC_M_USB_DEVICE_REMOVE,
    69         IPC_M_USB_REGISTER_ENDPOINT,
    70         IPC_M_USB_UNREGISTER_ENDPOINT,
    71         IPC_M_USB_READ,
    72         IPC_M_USB_WRITE,
     65        IPC_M_USB_GET_MY_DESCRIPTION,
    7366} usb_iface_funcs_t;
    7467
     
    7972 * @return Error code.
    8073 */
    81 errno_t usb_get_my_interface(async_exch_t *exch, int *usb_iface)
    82 {
    83         if (!exch)
    84                 return EBADMEM;
    85         sysarg_t iface_no;
    86         const errno_t ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    87             IPC_M_USB_GET_MY_INTERFACE, &iface_no);
    88         if (ret == EOK && usb_iface)
    89                 *usb_iface = (int)iface_no;
    90         return ret;
    91 }
    92 
    93 /** Tell devman handle of the usb device function.
    94  *
    95  * @param[in]  exch   IPC communication exchange
    96  * @param[out] handle devman handle of the HC used by the target device.
    97  *
    98  * @return Error code.
    99  *
    100  */
    101 errno_t usb_get_my_device_handle(async_exch_t *exch, devman_handle_t *handle)
    102 {
    103         devman_handle_t h = 0;
    104         const errno_t ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    105             IPC_M_USB_GET_MY_DEVICE_HANDLE, &h);
    106         if (ret == EOK && handle)
    107                 *handle = (devman_handle_t)h;
    108         return ret;
    109 }
    110 
    111 /** Reserve default USB address.
    112  * @param[in] exch IPC communication exchange
    113  * @param[in] speed Communication speed of the newly attached device
    114  * @return Error code.
    115  */
    116 errno_t usb_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
    117 {
    118         if (!exch)
    119                 return EBADMEM;
    120         return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    121             IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
    122 }
    123 
    124 /** Release default USB address.
    125  *
    126  * @param[in] exch IPC communication exchange
    127  *
    128  * @return Error code.
    129  *
    130  */
    131 errno_t usb_release_default_address(async_exch_t *exch)
    132 {
    133         if (!exch)
    134                 return EBADMEM;
    135         return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    136             IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
    137 }
    138 
    139 /** Trigger USB device enumeration
    140  *
    141  * @param[in]  exch   IPC communication exchange
    142  * @param[out] handle Identifier of the newly added device (if successful)
    143  *
    144  * @return Error code.
    145  *
    146  */
    147 errno_t usb_device_enumerate(async_exch_t *exch, unsigned port)
    148 {
    149         if (!exch)
    150                 return EBADMEM;
    151         const errno_t ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    152             IPC_M_USB_DEVICE_ENUMERATE, port);
    153         return ret;
    154 }
    155 
    156 /** Trigger USB device enumeration
    157  *
    158  * @param[in] exch   IPC communication exchange
    159  * @param[in] handle Identifier of the device
    160  *
    161  * @return Error code.
    162  *
    163  */
    164 errno_t usb_device_remove(async_exch_t *exch, unsigned port)
    165 {
    166         if (!exch)
    167                 return EBADMEM;
    168         return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    169             IPC_M_USB_DEVICE_REMOVE, port);
    170 }
    171 
    172 static_assert(sizeof(sysarg_t) >= 4);
    173 
    174 typedef union {
    175         uint8_t arr[sizeof(sysarg_t)];
    176         sysarg_t arg;
    177 } pack8_t;
    178 
    179 errno_t usb_register_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
    180     usb_transfer_type_t type, usb_direction_t direction,
    181     size_t mps, unsigned packets, unsigned interval)
    182 {
    183         if (!exch)
    184                 return EBADMEM;
    185         pack8_t pack;
    186         pack.arr[0] = type;
    187         pack.arr[1] = direction;
    188         pack.arr[2] = interval;
    189         pack.arr[3] = packets;
    190 
    191         return async_req_4_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    192             IPC_M_USB_REGISTER_ENDPOINT, endpoint, pack.arg, mps);
    193 
    194 }
    195 
    196 errno_t usb_unregister_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
    197     usb_direction_t direction)
    198 {
    199         if (!exch)
    200                 return EBADMEM;
    201         return async_req_3_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
    202             IPC_M_USB_UNREGISTER_ENDPOINT, endpoint, direction);
    203 }
    204 
    205 errno_t usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
    206     void *data, size_t size, size_t *rec_size)
     74errno_t usb_get_my_description(async_exch_t *exch, usb_device_desc_t *desc)
    20775{
    20876        if (!exch)
    20977                return EBADMEM;
    21078
    211         if (size == 0 && setup == 0)
    212                 return EOK;
     79        usb_device_desc_t tmp_desc;
    21380
    214         /* Make call identifying target USB device and type of transfer. */
    215         aid_t opening_request = async_send_4(exch,
    216             DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
    217             (setup & UINT32_MAX), (setup >> 32), NULL);
    218 
    219         if (opening_request == 0) {
    220                 return ENOMEM;
    221         }
    222 
    223         /* Retrieve the data. */
    224         ipc_call_t data_request_call;
    225         aid_t data_request =
    226             async_data_read(exch, data, size, &data_request_call);
    227 
    228         if (data_request == 0) {
    229                 // FIXME: How to let the other side know that we want to abort?
    230                 async_forget(opening_request);
    231                 return ENOMEM;
    232         }
    233 
    234         /* Wait for the answer. */
    235         errno_t data_request_rc;
    236         errno_t opening_request_rc;
    237         async_wait_for(data_request, &data_request_rc);
    238         async_wait_for(opening_request, &opening_request_rc);
    239 
    240         if (data_request_rc != EOK) {
    241                 /* Prefer the return code of the opening request. */
    242                 if (opening_request_rc != EOK) {
    243                         return (errno_t) opening_request_rc;
    244                 } else {
    245                         return (errno_t) data_request_rc;
    246                 }
    247         }
    248         if (opening_request_rc != EOK) {
    249                 return (errno_t) opening_request_rc;
    250         }
    251 
    252         *rec_size = IPC_GET_ARG2(data_request_call);
    253         return EOK;
     81        const errno_t ret = async_req_1_5(exch, DEV_IFACE_ID(USB_DEV_IFACE),
     82            IPC_M_USB_GET_MY_DESCRIPTION,
     83            (sysarg_t *) &tmp_desc.address,
     84            (sysarg_t *) &tmp_desc.depth,
     85            (sysarg_t *) &tmp_desc.speed,
     86            &tmp_desc.handle,
     87            (sysarg_t *) &tmp_desc.iface);
     88        if (ret == EOK && desc)
     89                *desc = tmp_desc;
     90        return ret;
    25491}
    25592
    256 errno_t usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
    257     const void *data, size_t size)
    258 {
    259         if (!exch)
    260                 return EBADMEM;
    261 
    262         if (size == 0 && setup == 0)
    263                 return EOK;
    264 
    265         aid_t opening_request = async_send_5(exch,
    266             DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
    267             (setup & UINT32_MAX), (setup >> 32), NULL);
    268 
    269         if (opening_request == 0) {
    270                 return ENOMEM;
    271         }
    272 
    273         /* Send the data if any. */
    274         if (size > 0) {
    275                 const errno_t ret = async_data_write_start(exch, data, size);
    276                 if (ret != EOK) {
    277                         async_forget(opening_request);
    278                         return ret;
    279                 }
    280         }
    281 
    282         /* Wait for the answer. */
    283         errno_t opening_request_rc;
    284         async_wait_for(opening_request, &opening_request_rc);
    285 
    286         return (errno_t) opening_request_rc;
    287 }
    288 
    289 static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    290 static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    291 static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    292 static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    293 static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    294 static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    295 static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    296 static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
    297 static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
    298 static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
     93static void remote_usb_get_my_description(ddf_fun_t *, void *,
     94    ipc_callid_t, ipc_call_t *);
    29995
    30096/** Remote USB interface operations. */
    30197static const remote_iface_func_ptr_t remote_usb_iface_ops [] = {
    302         [IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
    303         [IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
    304         [IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
    305         [IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
    306         [IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
    307         [IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
    308         [IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
    309         [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
    310         [IPC_M_USB_READ] = remote_usb_read,
    311         [IPC_M_USB_WRITE] = remote_usb_write,
     98        [IPC_M_USB_GET_MY_DESCRIPTION] = remote_usb_get_my_description,
    31299};
    313100
     
    319106};
    320107
    321 void remote_usb_get_my_interface(ddf_fun_t *fun, void *iface,
     108void remote_usb_get_my_description(ddf_fun_t *fun, void *iface,
    322109    ipc_callid_t callid, ipc_call_t *call)
    323110{
    324111        const usb_iface_t *usb_iface = (usb_iface_t *) iface;
    325112
    326         if (usb_iface->get_my_interface == NULL) {
     113        if (usb_iface->get_my_description == NULL) {
    327114                async_answer_0(callid, ENOTSUP);
    328115                return;
    329116        }
    330117
    331         int iface_no;
    332         const errno_t ret = usb_iface->get_my_interface(fun, &iface_no);
     118        usb_device_desc_t desc;
     119        const errno_t ret = usb_iface->get_my_description(fun, &desc);
    333120        if (ret != EOK) {
    334121                async_answer_0(callid, ret);
    335122        } else {
    336                 async_answer_1(callid, EOK, iface_no);
     123                async_answer_5(callid, EOK,
     124                    (sysarg_t) desc.address,
     125                    (sysarg_t) desc.depth,
     126                    (sysarg_t) desc.speed,
     127                    desc.handle,
     128                    desc.iface);
    337129        }
    338130}
    339131
    340 void remote_usb_get_my_device_handle(ddf_fun_t *fun, void *iface,
    341     ipc_callid_t callid, ipc_call_t *call)
    342 {
    343         const usb_iface_t *usb_iface = (usb_iface_t *) iface;
    344 
    345         if (usb_iface->get_my_device_handle == NULL) {
    346                 async_answer_0(callid, ENOTSUP);
    347                 return;
    348         }
    349 
    350         devman_handle_t handle;
    351         const errno_t ret = usb_iface->get_my_device_handle(fun, &handle);
    352         if (ret != EOK) {
    353                 async_answer_0(callid, ret);
    354         }
    355 
    356         async_answer_1(callid, EOK, (sysarg_t) handle);
    357 }
    358 
    359 void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
    360     ipc_callid_t callid, ipc_call_t *call)
    361 {
    362         const usb_iface_t *usb_iface = (usb_iface_t *) iface;
    363 
    364         if (usb_iface->reserve_default_address == NULL) {
    365                 async_answer_0(callid, ENOTSUP);
    366                 return;
    367         }
    368 
    369         usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
    370         const errno_t ret = usb_iface->reserve_default_address(fun, speed);
    371         async_answer_0(callid, ret);
    372 }
    373 
    374 void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
    375     ipc_callid_t callid, ipc_call_t *call)
    376 {
    377         const usb_iface_t *usb_iface = (usb_iface_t *) iface;
    378 
    379         if (usb_iface->release_default_address == NULL) {
    380                 async_answer_0(callid, ENOTSUP);
    381                 return;
    382         }
    383 
    384         const errno_t ret = usb_iface->release_default_address(fun);
    385         async_answer_0(callid, ret);
    386 }
    387 
    388 static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
    389     ipc_callid_t callid, ipc_call_t *call)
    390 {
    391         const usb_iface_t *usb_iface = (usb_iface_t *) iface;
    392 
    393         if (usb_iface->device_enumerate == NULL) {
    394                 async_answer_0(callid, ENOTSUP);
    395                 return;
    396         }
    397 
    398         const unsigned port = DEV_IPC_GET_ARG1(*call);
    399         const errno_t ret = usb_iface->device_enumerate(fun, port);
    400         async_answer_0(callid, ret);
    401 }
    402 
    403 static void remote_usb_device_remove(ddf_fun_t *fun, void *iface,
    404     ipc_callid_t callid, ipc_call_t *call)
    405 {
    406         const usb_iface_t *usb_iface = (usb_iface_t *) iface;
    407 
    408         if (usb_iface->device_remove == NULL) {
    409                 async_answer_0(callid, ENOTSUP);
    410                 return;
    411         }
    412 
    413         const unsigned port = DEV_IPC_GET_ARG1(*call);
    414         const errno_t ret = usb_iface->device_remove(fun, port);
    415         async_answer_0(callid, ret);
    416 }
    417 
    418 static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
    419     ipc_callid_t callid, ipc_call_t *call)
    420 {
    421         usb_iface_t *usb_iface = (usb_iface_t *) iface;
    422 
    423         if (!usb_iface->register_endpoint) {
    424                 async_answer_0(callid, ENOTSUP);
    425                 return;
    426         }
    427 
    428         const usb_endpoint_t endpoint = DEV_IPC_GET_ARG1(*call);
    429         const pack8_t pack = { .arg = DEV_IPC_GET_ARG2(*call)};
    430         const size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
    431 
    432         const usb_transfer_type_t transfer_type = pack.arr[0];
    433         const usb_direction_t direction = pack.arr[1];
    434         unsigned packets = pack.arr[2];
    435         unsigned interval = pack.arr[3];
    436 
    437         const errno_t ret = usb_iface->register_endpoint(fun, endpoint,
    438             transfer_type, direction, max_packet_size, packets, interval);
    439 
    440         async_answer_0(callid, ret);
    441 }
    442 
    443 static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
    444     ipc_callid_t callid, ipc_call_t *call)
    445 {
    446         usb_iface_t *usb_iface = (usb_iface_t *) iface;
    447 
    448         if (!usb_iface->unregister_endpoint) {
    449                 async_answer_0(callid, ENOTSUP);
    450                 return;
    451         }
    452 
    453         usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG1(*call);
    454         usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG2(*call);
    455 
    456         errno_t rc = usb_iface->unregister_endpoint(fun, endpoint, direction);
    457 
    458         async_answer_0(callid, rc);
    459 }
    460 
    461 typedef struct {
    462         ipc_callid_t caller;
    463         ipc_callid_t data_caller;
    464         void *buffer;
    465 } async_transaction_t;
    466 
    467 static void async_transaction_destroy(async_transaction_t *trans)
    468 {
    469         if (trans == NULL) {
    470                 return;
    471         }
    472         if (trans->buffer != NULL) {
    473                 free(trans->buffer);
    474         }
    475 
    476         free(trans);
    477 }
    478 
    479 static async_transaction_t *async_transaction_create(ipc_callid_t caller)
    480 {
    481         async_transaction_t *trans = malloc(sizeof(async_transaction_t));
    482         if (trans == NULL) {
    483                 return NULL;
    484         }
    485 
    486         trans->caller = caller;
    487         trans->data_caller = 0;
    488         trans->buffer = NULL;
    489 
    490         return trans;
    491 }
    492 
    493 static void callback_out(errno_t outcome, void *arg)
    494 {
    495         async_transaction_t *trans = arg;
    496 
    497         async_answer_0(trans->caller, outcome);
    498 
    499         async_transaction_destroy(trans);
    500 }
    501 
    502 static void callback_in(errno_t outcome, size_t actual_size, void *arg)
    503 {
    504         async_transaction_t *trans = (async_transaction_t *)arg;
    505 
    506         if (outcome != EOK) {
    507                 async_answer_0(trans->caller, outcome);
    508                 if (trans->data_caller) {
    509                         async_answer_0(trans->data_caller, EINTR);
    510                 }
    511                 async_transaction_destroy(trans);
    512                 return;
    513         }
    514 
    515         if (trans->data_caller) {
    516                 async_data_read_finalize(trans->data_caller,
    517                     trans->buffer, actual_size);
    518         }
    519 
    520         async_answer_0(trans->caller, EOK);
    521 
    522         async_transaction_destroy(trans);
    523 }
    524 
    525 void remote_usb_read(
    526     ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
    527 {
    528         assert(fun);
    529         assert(iface);
    530         assert(call);
    531 
    532         const usb_iface_t *usb_iface = iface;
    533 
    534         if (!usb_iface->read) {
    535                 async_answer_0(callid, ENOTSUP);
    536                 return;
    537         }
    538 
    539         const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
    540         const uint64_t setup =
    541             ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
    542             (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
    543 
    544         async_transaction_t *trans = async_transaction_create(callid);
    545         if (trans == NULL) {
    546                 async_answer_0(callid, ENOMEM);
    547                 return;
    548         }
    549 
    550         size_t size = 0;
    551         if (!async_data_read_receive(&trans->data_caller, &size)) {
    552                 async_answer_0(callid, EPARTY);
    553                 async_transaction_destroy(trans);
    554                 return;
    555         }
    556 
    557         trans->buffer = malloc(size);
    558         if (trans->buffer == NULL) {
    559                 async_answer_0(trans->data_caller, ENOMEM);
    560                 async_answer_0(callid, ENOMEM);
    561                 async_transaction_destroy(trans);
    562                 return;
    563         }
    564 
    565         const errno_t rc = usb_iface->read(
    566             fun, ep, setup, trans->buffer, size, callback_in, trans);
    567 
    568         if (rc != EOK) {
    569                 async_answer_0(trans->data_caller, rc);
    570                 async_answer_0(callid, rc);
    571                 async_transaction_destroy(trans);
    572         }
    573 }
    574 
    575 void remote_usb_write(
    576     ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
    577 {
    578         assert(fun);
    579         assert(iface);
    580         assert(call);
    581 
    582         const usb_iface_t *usb_iface = iface;
    583 
    584         if (!usb_iface->write) {
    585                 async_answer_0(callid, ENOTSUP);
    586                 return;
    587         }
    588 
    589         const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
    590         const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
    591         const uint64_t setup =
    592             ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
    593             (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
    594 
    595         async_transaction_t *trans = async_transaction_create(callid);
    596         if (trans == NULL) {
    597                 async_answer_0(callid, ENOMEM);
    598                 return;
    599         }
    600 
    601         size_t size = 0;
    602         if (data_buffer_len > 0) {
    603                 const errno_t rc = async_data_write_accept(&trans->buffer, false,
    604                     1, data_buffer_len, 0, &size);
    605 
    606                 if (rc != EOK) {
    607                         async_answer_0(callid, rc);
    608                         async_transaction_destroy(trans);
    609                         return;
    610                 }
    611         }
    612 
    613         const errno_t rc = usb_iface->write(
    614             fun, ep, setup, trans->buffer, size, callback_out, trans);
    615 
    616         if (rc != EOK) {
    617                 async_answer_0(callid, rc);
    618                 async_transaction_destroy(trans);
    619         }
    620 }
    621132/**
    622133 * @}
Note: See TracChangeset for help on using the changeset viewer.