Changeset 0892663a in mainline


Ignore:
Timestamp:
2018-01-11T04:14:37Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
9848c77
Parents:
bad4a05
git-author:
Ondřej Hlavatý <aearsis@…> (2018-01-11 03:59:03)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-01-11 04:14:37)
Message:

usbhost: device removal and off/onlining moved into the library

Also, it is just not possible to make generic transfer abortion. So the
current semantics of endpoint unregistering is also aborting the pending
transfer. As it is not yet implemented in XHCI, and the stub in UHCI is
missing the magic, it breaks offlining interrupt devices, such as mouse.

When finishing this commit, I came across the fact we need some more
synchronization above device. Guard can protect internal structures, but
it cannot synchronize multiple calls to offline, or offline & removal
between each other - they both need to allow driver to unregister
endpoints, and as such must release the guard.

Location:
uspace
Files:
14 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/ehci/ehci_bus.c

    rbad4a05 r0892663a  
    135135}
    136136
    137 static int ehci_device_online(device_t *device)
    138 {
    139         int err;
    140 
    141         /* Allow creation of new endpoints and transfers. */
    142         usb_log_info("Device(%d): Going online.", device->address);
    143         fibril_mutex_lock(&device->guard);
    144         device->online = true;
    145         fibril_mutex_unlock(&device->guard);
    146 
    147         if ((err = ddf_fun_online(device->fun))) {
    148                 return err;
    149         }
    150 
    151         return EOK;
    152 }
    153 
    154 static int ehci_device_offline(device_t *device)
    155 {
    156         int err;
    157 
    158         /* Tear down all drivers working with the device. */
    159         if ((err = ddf_fun_offline(device->fun))) {
    160                 return err;
    161         }
    162 
    163         /* At this point, all drivers are assumed to have already terminated
    164          * in a consistent way. The following code just cleans up hanging
    165          * transfers if there are any. */
    166 
    167         /* Block creation of new endpoints and transfers. */
    168         usb_log_info("Device(%d): Going offline.", device->address);
    169         fibril_mutex_lock(&device->guard);
    170         device->online = false;
    171         fibril_mutex_unlock(&device->guard);
    172 
    173         /* FIXME: Abort all transfers to all endpoints. */
    174 
    175         return EOK;
    176 }
    177 
    178137static const bus_ops_t ehci_bus_ops = {
    179138        .parent = &usb2_bus_ops,
     
    190149        .batch_destroy = ehci_destroy_batch,
    191150        .batch_schedule = ehci_hc_schedule,
    192         .device_online = ehci_device_online,
    193         .device_offline = ehci_device_offline,
    194151};
    195152
  • uspace/drv/bus/usb/uhci/hc.c

    rbad4a05 r0892663a  
    321321}
    322322
    323 static int device_online(device_t *device)
    324 {
    325         int err;
    326         hc_t *instance = bus_to_hc(device->bus);
    327         assert(instance);
    328 
    329         /* Allow creation of new endpoints and transfers. */
    330         usb_log_info("Device(%d): Going online.", device->address);
    331         fibril_mutex_lock(&device->guard);
    332         device->online = true;
    333         fibril_mutex_unlock(&device->guard);
    334 
    335         if ((err = ddf_fun_online(device->fun))) {
    336                 return err;
    337         }
    338 
    339         return EOK;
    340 }
    341 
    342 static int device_offline(device_t *device)
    343 {
    344         int err;
    345         hc_t *instance = bus_to_hc(device->bus);
    346         assert(instance);
    347 
    348         /* Tear down all drivers working with the device. */
    349         if ((err = ddf_fun_offline(device->fun))) {
    350                 return err;
    351         }
    352 
    353         /* At this point, all drivers are assumed to have already terminated
    354          * in a consistent way. The following code just cleans up hanging
    355          * transfers if there are any. */
    356 
    357         /* Block creation of new endpoints and transfers. */
    358         usb_log_info("Device(%d): Going offline.", device->address);
    359         fibril_mutex_lock(&device->guard);
    360         device->online = false;
    361         fibril_mutex_unlock(&device->guard);
    362 
    363         /* Abort all transfers to all endpoints. */
    364         transfer_list_abort_device(&instance->transfers_interrupt, device->address);
    365         transfer_list_abort_device(&instance->transfers_control_slow, device->address);
    366         transfer_list_abort_device(&instance->transfers_control_full, device->address);
    367         transfer_list_abort_device(&instance->transfers_bulk_full, device->address);
    368 
    369         return EOK;
     323static void endpoint_unregister(endpoint_t *ep)
     324{
     325        usb2_bus_ops.endpoint_unregister(ep);
     326
     327        fibril_mutex_lock(&ep->guard);
     328        if (ep->active_batch) {
     329                uhci_transfer_batch_t *ub = uhci_transfer_batch_get(ep->active_batch);
     330                uhci_transfer_batch_abort(ub);
     331
     332                assert(ep->active_batch == NULL);
     333        }
     334        fibril_mutex_unlock(&ep->guard);
    370335}
    371336
     
    379344        .status = hc_status,
    380345
     346        .endpoint_unregister = endpoint_unregister,
    381347        .endpoint_count_bw = bandwidth_count_usb11,
     348
    382349        .batch_create = create_transfer_batch,
    383350        .batch_schedule = hc_schedule,
    384351        .batch_destroy = destroy_transfer_batch,
    385 
    386         .device_online = device_online,
    387         .device_offline = device_offline,
    388352};
    389353
     
    522486static int hc_schedule(usb_transfer_batch_t *batch)
    523487{
     488        assert(batch);
    524489        hc_t *instance = bus_to_hc(endpoint_get_bus(batch->ep));
    525         assert(batch);
    526490
    527491        if (batch->target.address == uhci_rh_get_address(&instance->rh))
  • uspace/drv/bus/usb/uhci/transfer_list.c

    rbad4a05 r0892663a  
    194194        while (!list_empty(&instance->batch_list)) {
    195195                link_t * const current = list_first(&instance->batch_list);
    196                 uhci_transfer_batch_t *batch =
    197                     uhci_transfer_batch_from_link(current);
     196                uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
    198197                transfer_list_remove_batch(instance, batch);
    199                 endpoint_abort(batch->base.ep);
    200         }
    201         fibril_mutex_unlock(&instance->guard);
    202 }
    203 
    204 /** Walk the list and finish all batches of a specified device with EINTR.
    205  *
    206  * @param[in] instance List to use.
    207  * @param[in] address Address of the specified device. Other addresses are skipped.
    208  */
    209 void transfer_list_abort_device(transfer_list_t *instance, usb_address_t address)
    210 {
    211         fibril_mutex_lock(&instance->guard);
    212         link_t *current = list_first(&instance->batch_list);
    213         while (current && current != &instance->batch_list.head && !list_empty(&instance->batch_list)) {
    214                 link_t * const next = current->next;
    215                 uhci_transfer_batch_t *batch =
    216                     uhci_transfer_batch_from_link(current);
    217 
    218                 if (batch->base.target.address == address) {
    219                         transfer_list_remove_batch(instance, batch);
    220                         endpoint_abort(batch->base.ep);
    221                 }
    222 
    223                 current = next;
    224198        }
    225199        fibril_mutex_unlock(&instance->guard);
  • uspace/drv/bus/usb/uhci/transfer_list.h

    rbad4a05 r0892663a  
    6363void transfer_list_remove_finished(transfer_list_t *instance, list_t *done);
    6464void transfer_list_abort_all(transfer_list_t *instance);
    65 void transfer_list_abort_device(transfer_list_t *instance, usb_address_t address);
    6665
    6766#endif
  • uspace/drv/bus/usb/uhci/uhci_batch.c

    rbad4a05 r0892663a  
    6464}
    6565
     66/**
     67 * Abort a transfer that is currently running.
     68 * Call with endpoint guard locked.
     69 */
     70void uhci_transfer_batch_abort(uhci_transfer_batch_t *batch)
     71{
     72        assert(batch);
     73
     74        endpoint_t *ep = batch->base.ep;
     75        assert(ep);
     76        assert(fibril_mutex_is_locked(&ep->guard));
     77        assert(ep->active_batch == &batch->base);
     78
     79        /*
     80         * TODO: Do some magic here to remove the batch from schedule.
     81         */
     82
     83        /*
     84         * Wait for 2 frames. If the transfer was being processed,
     85         * it shall be marked as finished already after 1ms.
     86         */
     87        endpoint_wait_timeout_locked(ep, 2000);
     88        if (ep->active_batch != &batch->base)
     89                return;
     90
     91        /*
     92         * Now, we can be sure the transfer is not scheduled,
     93         * and as such will not be completed. We now own the batch.
     94         */
     95        endpoint_deactivate_locked(ep);
     96
     97        /* Leave the critical section for finishing the batch. */
     98        fibril_mutex_unlock(&ep->guard);
     99
     100        batch->base.error = EINTR;
     101        batch->base.transfered_size = 0;
     102        usb_transfer_batch_finish(&batch->base);
     103
     104        fibril_mutex_lock(&ep->guard);
     105}
     106
    66107/** Allocate memory and initialize internal data structure.
    67108 *
  • uspace/drv/bus/usb/uhci/uhci_batch.h

    rbad4a05 r0892663a  
    7070int uhci_transfer_batch_prepare(uhci_transfer_batch_t *);
    7171bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *);
    72 void uhci_transfer_batch_finish(uhci_transfer_batch_t *);
     72void uhci_transfer_batch_abort(uhci_transfer_batch_t *);
    7373void uhci_transfer_batch_destroy(uhci_transfer_batch_t *);
    7474
  • uspace/drv/bus/usb/xhci/bus.c

    rbad4a05 r0892663a  
    209209        xhci_device_t *xhci_dev = xhci_device_get(dev);
    210210
    211         /* Block creation of new endpoints and transfers. */
    212         usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
    213         fibril_mutex_lock(&dev->guard);
    214         dev->online = false;
    215         fibril_mutex_unlock(&dev->guard);
    216 
    217         /* Abort running transfers. */
    218         usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
    219         for (usb_endpoint_t i = 0; i < USB_ENDPOINT_MAX; ++i) {
    220                 xhci_endpoint_t *ep = xhci_device_get_endpoint(xhci_dev, i);
    221                 if (!ep)
    222                         continue;
    223 
    224                 endpoint_abort(&ep->base);
    225         }
    226 
    227         /* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
    228 
    229         /* Make DDF (and all drivers) forget about the device. */
    230         if ((err = ddf_fun_unbind(dev->fun))) {
    231                 usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
    232                     XHCI_DEV_ARGS(*xhci_dev), str_error(err));
    233         }
    234 
    235211        /* Disable the slot, dropping all endpoints. */
    236212        const uint32_t slot_id = xhci_dev->slot_id;
     
    241217
    242218        bus->devices_by_slot[slot_id] = NULL;
    243 
    244         /* Unregister remaining endpoints, freeing memory. */
    245         for (usb_endpoint_t i = 0; i < USB_ENDPOINT_MAX; ++i) {
    246                 if (!dev->endpoints[i])
    247                         continue;
    248 
    249                 bus_endpoint_remove(dev->endpoints[i]);
    250         }
    251 
    252         /* Destroy DDF device. */
    253         /* XXX: Not a good idea, this method should not destroy devices. */
    254         hcd_ddf_fun_destroy(dev);
    255219}
    256220
     
    273237        if ((err = hc_configure_device(bus->hc, dev->slot_id))) {
    274238                usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
    275         }
    276 
    277         /* Allow creation of new endpoints and transfers. */
    278         usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
    279         fibril_mutex_lock(&dev_base->guard);
    280         dev_base->online = true;
    281         fibril_mutex_unlock(&dev_base->guard);
    282 
    283         if ((err = ddf_fun_online(dev_base->fun))) {
    284239                return err;
    285240        }
     
    294249 * Bus callback.
    295250 */
    296 static int device_offline(device_t *dev_base)
     251static void device_offline(device_t *dev_base)
    297252{
    298253        int err;
     
    303258        xhci_device_t *dev = xhci_device_get(dev_base);
    304259        assert(dev);
    305 
    306         /* Tear down all drivers working with the device. */
    307         if ((err = ddf_fun_offline(dev_base->fun))) {
    308                 return err;
    309         }
    310 
    311         /* Block creation of new endpoints and transfers. */
    312         usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
    313         fibril_mutex_lock(&dev_base->guard);
    314         dev_base->online = false;
    315         fibril_mutex_unlock(&dev_base->guard);
    316 
    317         /* We will need the endpoint array later for DS deallocation. */
    318         endpoint_t *endpoints[USB_ENDPOINT_MAX];
    319         memcpy(endpoints, dev->base.endpoints, sizeof(endpoints));
    320 
    321         for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i) {
    322                 /* FIXME: Asserting here that the endpoint is not active. If not, EBUSY? */
    323                 dev->base.endpoints[i] = NULL;
    324         }
    325260
    326261        /* Issue one HC command to simultaneously drop all endpoints except zero. */
     
    329264                    XHCI_DEV_ARGS(*dev));
    330265        }
    331 
    332         /* Tear down TRB ring / PSA. */
    333         for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
    334                 if (!endpoints[i])
    335                         continue;
    336 
    337                 /* Bus reference */
    338                 endpoint_del_ref(endpoints[i]);
    339         }
    340 
    341         return EOK;
    342266}
    343267
     
    378302
    379303/**
    380  * Register an andpoint to the bus. Allocate its transfer ring(s) and inform
    381  * xHC about it.
     304 * Register an andpoint to the xHC.
    382305 *
    383306 * Bus callback.
    384307 */
    385308static int endpoint_register(endpoint_t *ep_base)
    386 {
    387         int err;
    388         xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
    389         xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
    390 
    391         xhci_device_t *dev = xhci_device_get(ep_base->device);
    392 
    393         usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
    394 
    395         xhci_ep_ctx_t ep_ctx;
    396         xhci_setup_endpoint_context(ep, &ep_ctx);
    397 
    398         if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
    399                 return err;
    400 
    401         return EOK;
    402 }
    403 
    404 /**
    405  * Unregister an endpoint. If the device is still available, inform the xHC
    406  * about it. Destroy resources allocated when registering.
    407  *
    408  * Bus callback.
    409  */
    410 static void endpoint_unregister(endpoint_t *ep_base)
    411309{
    412310        int err;
     
    415313        xhci_device_t *dev = xhci_device_get(ep_base->device);
    416314
    417         usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
     315        xhci_ep_ctx_t ep_ctx;
     316        xhci_setup_endpoint_context(ep, &ep_ctx);
     317
     318        if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
     319                return err;
     320
     321        return EOK;
     322}
     323
     324/**
     325 * Unregister an endpoint. If the device is still available, inform the xHC
     326 * about it.
     327 *
     328 * Bus callback.
     329 */
     330static void endpoint_unregister(endpoint_t *ep_base)
     331{
     332        int err;
     333        xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
     334        xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
     335        xhci_device_t *dev = xhci_device_get(ep_base->device);
    418336
    419337        /* If device slot is still available, drop the endpoint. */
  • uspace/lib/usbhost/include/usb/host/bus.h

    rbad4a05 r0892663a  
    105105        void (*device_remove)(device_t *);
    106106        int (*device_online)(device_t *);                       /**< Optional */
    107         int (*device_offline)(device_t *);                      /**< Optional */
     107        void (*device_offline)(device_t *);                     /**< Optional */
    108108        endpoint_t *(*endpoint_create)(device_t *, const usb_endpoint_descriptors_t *);
    109109
  • uspace/lib/usbhost/include/usb/host/endpoint.h

    rbad4a05 r0892663a  
    4444#include <fibril_synch.h>
    4545#include <stdbool.h>
     46#include <sys/time.h>
    4647#include <usb/usb.h>
    4748#include <usb/host/bus.h>
     
    9394extern void endpoint_del_ref(endpoint_t *);
    9495
    95 /* Pay atention to synchronization of batch access wrt to aborting & finishing from another fibril. */
    96 
    97 /* Set currently active batch. The common case is to activate in the same
    98  * critical section as scheduling to HW.
    99  */
     96extern void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t);
    10097extern void endpoint_activate_locked(endpoint_t *, usb_transfer_batch_t *);
    101 
    102 /* Deactivate the endpoint, allowing others to activate it again. Batch shall
    103  * already have an error set. */
    10498extern void endpoint_deactivate_locked(endpoint_t *);
    105 
    106 /* Abort the currenty active batch. */
    107 void endpoint_abort(endpoint_t *);
    10899
    109100/* Calculate bandwidth */
  • uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h

    rbad4a05 r0892663a  
    112112
    113113/** Batch finalization. */
    114 void usb_transfer_batch_abort(usb_transfer_batch_t *);
    115114void usb_transfer_batch_finish(usb_transfer_batch_t *);
    116115
  • uspace/lib/usbhost/src/bus.c

    rbad4a05 r0892663a  
    4343#include <mem.h>
    4444#include <stdio.h>
     45#include <str_error.h>
    4546#include <usb/debug.h>
    4647
     
    9798/**
    9899 * Invoke the device_enumerate bus operation.
     100 *
     101 * There's no need to synchronize here, because no one knows the device yet.
    99102 */
    100103int bus_device_enumerate(device_t *dev)
     
    106109                return ENOTSUP;
    107110
    108         return ops->device_enumerate(dev);
    109 }
    110 
    111 /**
    112  * Invoke the device_remove bus operation.
     111        if (dev->online) {
     112                fibril_mutex_unlock(&dev->guard);
     113                return EINVAL;
     114        }
     115
     116        const int r = ops->device_enumerate(dev);
     117        if (!r) {
     118                dev->online = true;
     119
     120                fibril_mutex_lock(&dev->hub->guard);
     121                list_append(&dev->link, &dev->hub->devices);
     122                fibril_mutex_unlock(&dev->hub->guard);
     123        }
     124
     125        return r;
     126}
     127
     128/**
     129 * Clean endpoints and children that could have been left behind after
     130 * asking the driver of device to offline/remove a device.
     131 *
     132 * Note that EP0's lifetime is shared with the device, and as such is not
     133 * touched.
     134 */
     135static void device_clean_ep_children(device_t *dev, const char *op)
     136{
     137        assert(fibril_mutex_is_locked(&dev->guard));
     138
     139        /* Unregister endpoints left behind. */
     140        for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i) {
     141                if (!dev->endpoints[i])
     142                        continue;
     143
     144                usb_log_warning("USB device '%s' driver left endpoint %u registered after %s.",
     145                    ddf_fun_get_name(dev->fun), i, op);
     146
     147                endpoint_t * const ep = dev->endpoints[i];
     148                endpoint_add_ref(ep);
     149               
     150                fibril_mutex_unlock(&dev->guard);
     151                bus_endpoint_remove(ep);
     152                fibril_mutex_lock(&dev->guard);
     153
     154                assert(dev->endpoints[i] == NULL);
     155        }
     156
     157        /* Remove also orphaned children. */
     158        while (!list_empty(&dev->devices)) {
     159                device_t * const child = list_get_instance(list_first(&dev->devices), device_t, link);
     160
     161                usb_log_warning("USB device '%s' driver left device '%s' behind after %s.",
     162                    ddf_fun_get_name(dev->fun), ddf_fun_get_name(child->fun), op);
     163                /*
     164                 * The child node won't disappear, because its parent's driver
     165                 * is already dead. And the child will need the guard to remove
     166                 * itself from the list.
     167                 */
     168                fibril_mutex_unlock(&dev->guard);
     169                bus_device_remove(child);
     170                fibril_mutex_lock(&dev->guard);
     171        }
     172        assert(list_empty(&dev->devices));
     173}
     174
     175/**
     176 * Resolve a USB device that is gone.
    113177 */
    114178void bus_device_remove(device_t *dev)
    115179{
    116180        assert(dev);
     181        assert(dev->fun == NULL);
    117182
    118183        const bus_ops_t *ops = BUS_OPS_LOOKUP(dev->bus->ops, device_remove);
    119184        assert(ops);
    120185
    121         return ops->device_remove(dev);
    122 }
    123 
    124 /**
    125  * Invoke the device_online bus operation.
     186        /* First, block new transfers and operations. */
     187        fibril_mutex_lock(&dev->guard);
     188        dev->online = false;
     189
     190        /* Unbinding will need guard unlocked. */
     191        fibril_mutex_unlock(&dev->guard);
     192
     193        /* Remove our device from our hub's children. */
     194        fibril_mutex_lock(&dev->hub->guard);
     195        list_remove(&dev->link);
     196        fibril_mutex_unlock(&dev->hub->guard);
     197
     198        /*
     199         * Unbind the DDF function. That will result in dev_gone called in
     200         * driver, which shall destroy its pipes and remove its children.
     201         */
     202        const int err = ddf_fun_unbind(dev->fun);
     203        if (err) {
     204                usb_log_error("Failed to unbind USB device '%s': %s",
     205                    ddf_fun_get_name(dev->fun), str_error(err));
     206                return;
     207        }
     208
     209        /* Remove what driver left behind */
     210        fibril_mutex_lock(&dev->guard);
     211        device_clean_ep_children(dev, "removing");
     212
     213        /* Tell the HC to release its resources. */
     214        ops->device_remove(dev);
     215
     216        /* Release the EP0 bus reference */
     217        endpoint_del_ref(dev->endpoints[0]);
     218
     219        /* Destroy the function, freeing also the device, unlocking mutex. */
     220        ddf_fun_destroy(dev->fun);
     221}
     222
     223/**
     224 * The user wants this device back online.
    126225 */
    127226int bus_device_online(device_t *dev)
    128227{
     228        int err;
    129229        assert(dev);
    130230
     231        fibril_mutex_lock(&dev->guard);
     232        if (dev->online) {
     233                fibril_mutex_unlock(&dev->guard);
     234                return EINVAL;
     235        }
     236
     237        /* First, tell the HC driver. */
    131238        const bus_ops_t *ops = BUS_OPS_LOOKUP(dev->bus->ops, device_online);
    132         if (!ops)
    133                 return ENOTSUP;
    134 
    135         return ops->device_online(dev);
    136 }
    137 
    138 /**
    139  * Invoke the device_offline bus operation.
     239        if (ops && (err = ops->device_online(dev))) {
     240                usb_log_warning("Host controller refused to make device '%s' online: %s",
     241                    ddf_fun_get_name(dev->fun), str_error(err));
     242                return err;
     243        }
     244
     245        /* Allow creation of new endpoints and communication with the device. */
     246        dev->online = true;
     247
     248        /* Onlining will need the guard */
     249        fibril_mutex_unlock(&dev->guard);
     250
     251        if ((err = ddf_fun_online(dev->fun))) {
     252                usb_log_warning("Failed to take device '%s' online: %s",
     253                    ddf_fun_get_name(dev->fun), str_error(err));
     254                return err;
     255        }
     256
     257        usb_log_info("USB Device '%s' offlined.", ddf_fun_get_name(dev->fun));
     258        return EOK;
     259}
     260
     261/**
     262 * The user requested to take this device offline.
    140263 */
    141264int bus_device_offline(device_t *dev)
    142265{
     266        int err;
    143267        assert(dev);
    144268
     269        /* Make sure we're the one who offlines this device */
     270        if (!dev->online)
     271                return ENOENT;
     272
     273        /*
     274         * XXX: If the device is removed/offlined just now, this can fail on
     275         * assertion. We most probably need some kind of enum status field to
     276         * make the synchronization work.
     277         */
     278       
     279        /* Tear down all drivers working with the device. */
     280        if ((err = ddf_fun_offline(dev->fun))) {
     281                return err;
     282        }
     283
     284        fibril_mutex_lock(&dev->guard);
     285        dev->online = false;
     286        device_clean_ep_children(dev, "offlining");
     287
     288        /* Tell also the HC driver. */
    145289        const bus_ops_t *ops = BUS_OPS_LOOKUP(dev->bus->ops, device_offline);
    146         if (!ops)
    147                 return ENOTSUP;
    148 
    149         return ops->device_offline(dev);
     290        if (ops)
     291                ops->device_offline(dev);
     292
     293        fibril_mutex_unlock(&dev->guard);
     294        usb_log_info("USB Device '%s' offlined.", ddf_fun_get_name(dev->fun));
     295        return EOK;
    150296}
    151297
     
    269415        fibril_mutex_unlock(&device->guard);
    270416
    271         /* Abort a transfer batch, if there was any */
    272         endpoint_abort(ep);
    273 
    274417        /* Bus reference */
    275418        endpoint_del_ref(ep);
  • uspace/lib/usbhost/src/ddf_helpers.c

    rbad4a05 r0892663a  
    336336        assert(device);
    337337
    338         hc_device_t *hcd = dev_to_hcd(device);
    339         assert(hcd);
    340         assert(hcd->bus);
     338        device_t *victim = NULL;
    341339
    342340        fibril_mutex_lock(&hub->guard);
    343 
    344         device_t *victim = NULL;
    345 
    346341        list_foreach(hub->devices, link, device_t, it) {
    347342                if (it->port == port) {
     
    350345                }
    351346        }
    352         if (victim) {
    353                 assert(victim->fun);
    354                 assert(victim->port == port);
    355                 assert(victim->hub == hub);
    356                 list_remove(&victim->link);
    357                 fibril_mutex_unlock(&hub->guard);
    358                 const int ret = ddf_fun_unbind(victim->fun);
    359                 if (ret == EOK) {
    360                         bus_device_remove(victim);
    361                         ddf_fun_destroy(victim->fun);
    362                 } else {
    363                         usb_log_warning("Failed to unbind device `%s': %s\n",
    364                             ddf_fun_get_name(victim->fun), str_error(ret));
    365                 }
    366                 return EOK;
    367         }
    368347        fibril_mutex_unlock(&hub->guard);
    369         return ENOENT;
     348
     349        if (!victim)
     350                return ENOENT;
     351
     352        assert(victim->fun);
     353        assert(victim->port == port);
     354        assert(victim->hub == hub);
     355
     356        bus_device_remove(victim);
     357        return EOK;
    370358}
    371359
     
    477465        }
    478466
    479         fibril_mutex_lock(&hub->guard);
    480         list_append(&dev->link, &hub->devices);
    481         fibril_mutex_unlock(&hub->guard);
    482 
    483467        return EOK;
    484468
  • uspace/lib/usbhost/src/endpoint.c

    rbad4a05 r0892663a  
    124124
    125125/**
     126 * Wait until the endpoint have no transfer scheduled.
     127 */
     128void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
     129{
     130        assert(fibril_mutex_is_locked(&ep->guard));
     131
     132        while (ep->active_batch != NULL)
     133                fibril_condvar_wait_timeout(&ep->avail, &ep->guard, timeout);
     134}
     135
     136/**
    126137 * Mark the endpoint as active and block access for further fibrils. If the
    127138 * endpoint is already active, it will block on ep->avail condvar.
     
    138149        assert(batch);
    139150        assert(batch->ep == ep);
    140         assert(fibril_mutex_is_locked(&ep->guard));
    141 
    142         while (ep->active_batch != NULL)
    143                 fibril_condvar_wait(&ep->avail, &ep->guard);
     151
     152        endpoint_wait_timeout_locked(ep, 0);
    144153        ep->active_batch = batch;
    145154}
     
    160169        ep->active_batch = NULL;
    161170        fibril_condvar_signal(&ep->avail);
    162 }
    163 
    164 /**
    165  * Abort an active batch on endpoint, if any.
    166  *
    167  * @param[in] ep endpoint_t structure.
    168  */
    169 void endpoint_abort(endpoint_t *ep)
    170 {
    171         assert(ep);
    172 
    173         fibril_mutex_lock(&ep->guard);
    174         usb_transfer_batch_t *batch = ep->active_batch;
    175         endpoint_deactivate_locked(ep);
    176         fibril_mutex_unlock(&ep->guard);
    177 
    178         if (batch)
    179                 usb_transfer_batch_abort(batch);
    180171}
    181172
  • uspace/lib/usbhost/src/usb_transfer_batch.c

    rbad4a05 r0892663a  
    128128
    129129/**
    130  * Finish a transfer batch as an aborted one.
    131  */
    132 void usb_transfer_batch_abort(usb_transfer_batch_t *batch)
    133 {
    134         assert(batch);
    135         assert(batch->ep);
    136 
    137         batch->error = EAGAIN;
    138         usb_transfer_batch_finish(batch);
    139 }
    140 
    141 /**
    142130 * @}
    143131 */
Note: See TracChangeset for help on using the changeset viewer.