Changeset 0892663a in mainline for uspace/lib/usbhost/src/bus.c


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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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);
Note: See TracChangeset for help on using the changeset viewer.