Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/usbhub/port.c

    r58563585 rffa96c2  
    2727 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2828 */
    29 
    3029/** @addtogroup drvusbhub
    3130 * @{
     
    4241
    4342#include <usb/debug.h>
     43#include <usb/dev/hub.h>
    4444
    4545#include "port.h"
     
    5656static int usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub);
    5757static void usb_hub_port_reset_completed(usb_hub_port_t *port,
    58     usb_hub_dev_t *hub, usb_port_status_t status);
     58    usb_port_status_t status);
    5959static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status);
     60static int enable_port_callback(void *arg);
    6061static int add_device_phase1_worker_fibril(void *arg);
    6162static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub,
     
    6566{
    6667        assert(port);
    67         if (port->device_attached)
     68        if (port->attached_device.fun)
    6869                return usb_hub_port_device_gone(port, hub);
    6970        return EOK;
     
    123124        assert(port);
    124125        fibril_mutex_lock(&port->mutex);
    125         if (port->reset_status == IN_RESET)
    126                 port->reset_status = RESET_FAIL;
     126        port->reset_completed = true;
     127        port->reset_okay = false;
    127128        fibril_condvar_broadcast(&port->reset_cv);
    128129        fibril_mutex_unlock(&port->mutex);
     
    140141        assert(port);
    141142        assert(hub);
    142         usb_log_debug2("(%p-%u): Interrupt.\n", hub, port->port_number);
     143        usb_log_debug("Interrupt at port %zu\n", port->port_number);
    143144
    144145        usb_port_status_t status = 0;
    145146        const int opResult = get_port_status(port, &status);
    146147        if (opResult != EOK) {
    147                 usb_log_error("(%p-%u): Failed to get port status: %s.\n", hub,
     148                usb_log_error("Failed to get port %zu status: %s.\n",
    148149                    port->port_number, str_error(opResult));
    149150                return;
     
    154155                const bool connected =
    155156                    (status & USB_HUB_PORT_STATUS_CONNECTION) != 0;
    156                 usb_log_debug("(%p-%u): Connection change: device %s.\n", hub,
     157                usb_log_debug("Connection change on port %zu: device %s.\n",
    157158                    port->port_number, connected ? "attached" : "removed");
    158159
     
    161162                    USB_HUB_FEATURE_C_PORT_CONNECTION);
    162163                if (opResult != EOK) {
    163                         usb_log_warning("(%p-%u): Failed to clear "
    164                             "port-change-connection flag: %s.\n", hub,
    165                             port->port_number, str_error(opResult));
     164                        usb_log_warning("Failed to clear port-change-connection"
     165                            " flag: %s.\n", str_error(opResult));
    166166                }
    167167
     
    170170                            usb_port_speed(status));
    171171                        if (opResult != EOK) {
    172                                 usb_log_error("(%p-%u): Cannot handle change on"
    173                                    " port: %s.\n", hub, port->port_number,
    174                                    str_error(opResult));
     172                                usb_log_error(
     173                                    "Cannot handle change on port %zu: %s.\n",
     174                                    port->port_number, str_error(opResult));
    175175                        }
    176176                } else {
    177                         /* Handle the case we were in reset */
    178                         // FIXME: usb_hub_port_reset_fail(port);
    179177                        /* If enabled change was reported leave the removal
    180178                         * to that handler, it shall ACK the change too. */
     
    187185        /* Enable change, ports are automatically disabled on errors. */
    188186        if (status & USB_HUB_PORT_C_STATUS_ENABLED) {
    189                 // TODO: maybe HS reset failed?
    190                 usb_log_info("(%p-%u): Port disabled because of errors.\n", hub,
     187                usb_log_info("Port %zu, disabled because of errors.\n",
    191188                   port->port_number);
    192189                usb_hub_port_device_gone(port, hub);
     
    194191                        USB_HUB_FEATURE_C_PORT_ENABLE);
    195192                if (rc != EOK) {
    196                         usb_log_error("(%p-%u): Failed to clear port enable "
    197                             "change feature: %s.", hub, port->port_number,
    198                             str_error(rc));
     193                        usb_log_error(
     194                            "Failed to clear port %zu enable change feature: "
     195                            "%s.\n", port->port_number, str_error(rc));
    199196                }
    200197
     
    203200        /* Suspend change */
    204201        if (status & USB_HUB_PORT_C_STATUS_SUSPEND) {
    205                 usb_log_error("(%p-%u): Port went to suspend state, this should"
    206                     " NOT happen as we do not support suspend state!", hub,
     202                usb_log_error("Port %zu went to suspend state, this should"
     203                    "NOT happen as we do not support suspend state!",
    207204                    port->port_number);
    208205                const int rc = usb_hub_port_clear_feature(port,
    209206                        USB_HUB_FEATURE_C_PORT_SUSPEND);
    210207                if (rc != EOK) {
    211                         usb_log_error("(%p-%u): Failed to clear port suspend "
    212                             "change feature: %s.", hub, port->port_number,
    213                             str_error(rc));
     208                        usb_log_error(
     209                            "Failed to clear port %zu suspend change feature: "
     210                            "%s.\n", port->port_number, str_error(rc));
    214211                }
    215212        }
     
    217214        /* Over current */
    218215        if (status & USB_HUB_PORT_C_STATUS_OC) {
    219                 usb_log_debug("(%p-%u): Port OC reported!.", hub,
    220                     port->port_number);
    221216                /* According to the USB specs:
    222217                 * 11.13.5 Over-current Reporting and Recovery
     
    227222                    USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
    228223                if (rc != EOK) {
    229                         usb_log_error("(%p-%u): Failed to clear port OC change "
    230                             "feature: %s.\n", hub, port->port_number,
    231                             str_error(rc));
     224                        usb_log_error(
     225                            "Failed to clear port %zu OC change feature: %s.\n",
     226                            port->port_number, str_error(rc));
    232227                }
    233228                if (!(status & ~USB_HUB_PORT_STATUS_OC)) {
     
    235230                            port, USB_HUB_FEATURE_PORT_POWER);
    236231                        if (rc != EOK) {
    237                                 usb_log_error("(%p-%u): Failed to set port "
    238                                     "power after OC: %s.", hub,
    239                                     port->port_number, str_error(rc));
     232                                usb_log_error(
     233                                    "Failed to set port %zu power after OC:"
     234                                    " %s.\n", port->port_number, str_error(rc));
    240235                        }
    241236                }
     
    244239        /* Port reset, set on port reset complete. */
    245240        if (status & USB_HUB_PORT_C_STATUS_RESET) {
    246                 usb_hub_port_reset_completed(port, hub, status);
    247         }
    248 
    249         usb_log_debug2("(%p-%u): Port status %#08" PRIx32, hub,
     241                usb_hub_port_reset_completed(port, status);
     242        }
     243
     244        usb_log_debug("Port %zu status 0x%08" PRIx32 "\n",
    250245            port->port_number, status);
    251246}
     
    264259        assert(port);
    265260        assert(hub);
    266         async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device);
    267         if (!exch)
    268                 return ENOMEM;
    269         const int rc = usb_device_remove(exch, port->port_number);
    270         usb_device_bus_exchange_end(exch);
    271         if (rc == EOK)
    272                 port->device_attached = false;
    273         return rc;
    274 
     261        if (port->attached_device.address < 0) {
     262                usb_log_warning(
     263                    "Device on port %zu removed before being registered.\n",
     264                    port->port_number);
     265
     266                /*
     267                 * Device was removed before port reset completed.
     268                 * We will announce a failed port reset to unblock the
     269                 * port reset callback from new device wrapper.
     270                 */
     271                usb_hub_port_reset_fail(port);
     272                return EOK;
     273        }
     274
     275        fibril_mutex_lock(&port->mutex);
     276        assert(port->attached_device.fun);
     277        usb_log_debug("Removing device on port %zu.\n", port->port_number);
     278        int ret = ddf_fun_unbind(port->attached_device.fun);
     279        if (ret != EOK) {
     280                usb_log_error("Failed to unbind child function on port"
     281                    " %zu: %s.\n", port->port_number, str_error(ret));
     282                fibril_mutex_unlock(&port->mutex);
     283                return ret;
     284        }
     285
     286        ddf_fun_destroy(port->attached_device.fun);
     287        port->attached_device.fun = NULL;
     288
     289        ret = usb_hub_unregister_device(&hub->usb_device->hc_conn,
     290            &port->attached_device);
     291        if (ret != EOK) {
     292                usb_log_warning("Failed to unregister address of the "
     293                    "removed device: %s.\n", str_error(ret));
     294        }
     295
     296        port->attached_device.address = -1;
     297        fibril_mutex_unlock(&port->mutex);
     298        usb_log_info("Removed device on port %zu.\n", port->port_number);
     299        return EOK;
    275300}
    276301
     
    283308 * @param status Port status mask
    284309 */
    285 void usb_hub_port_reset_completed(usb_hub_port_t *port, usb_hub_dev_t *hub,
     310void usb_hub_port_reset_completed(usb_hub_port_t *port,
    286311    usb_port_status_t status)
    287312{
    288313        assert(port);
    289314        fibril_mutex_lock(&port->mutex);
    290         const bool enabled = (status & USB_HUB_PORT_STATUS_ENABLED) != 0;
    291315        /* Finalize device adding. */
    292 
    293         if (enabled) {
    294                 port->reset_status = RESET_OK;
    295                 usb_log_debug("(%p-%u): Port reset complete.\n", hub,
     316        port->reset_completed = true;
     317        port->reset_okay = (status & USB_HUB_PORT_STATUS_ENABLED) != 0;
     318
     319        if (port->reset_okay) {
     320                usb_log_debug("Port %zu reset complete.\n", port->port_number);
     321        } else {
     322                usb_log_warning(
     323                    "Port %zu reset complete but port not enabled.\n",
    296324                    port->port_number);
    297         } else {
    298                 port->reset_status = RESET_FAIL;
    299                 usb_log_warning("(%p-%u): Port reset complete but port not "
    300                     "enabled.", hub, port->port_number);
    301325        }
    302326        fibril_condvar_broadcast(&port->reset_cv);
     
    306330        int rc = usb_hub_port_clear_feature(port, USB_HUB_FEATURE_C_PORT_RESET);
    307331        if (rc != EOK) {
    308                 usb_log_error("(%p-%u): Failed to clear port reset change: %s.",
    309                     hub, port->port_number, str_error(rc));
     332                usb_log_error(
     333                    "Failed to clear port %zu reset change feature: %s.\n",
     334                    port->port_number, str_error(rc));
    310335        }
    311336}
     
    351376}
    352377
    353 static int port_enable(usb_hub_port_t *port, usb_hub_dev_t *hub, bool enable)
    354 {
    355         if (enable) {
    356                 int rc =
    357                     usb_hub_port_set_feature(port, USB_HUB_FEATURE_PORT_RESET);
    358                 if (rc != EOK) {
    359                         usb_log_error("(%p-%u): Port reset request failed: %s.",
    360                             hub, port->port_number, str_error(rc));
    361                         return rc;
    362                 }
    363                 /* Wait until reset completes. */
    364                 fibril_mutex_lock(&port->mutex);
    365                 port->reset_status = IN_RESET;
    366                 while (port->reset_status == IN_RESET)
    367                         fibril_condvar_wait(&port->reset_cv, &port->mutex);
    368                 rc = port->reset_status == RESET_OK ? EOK : ESTALL;
    369                 fibril_mutex_unlock(&port->mutex);
     378/** Callback for enabling a specific port.
     379 *
     380 * We wait on a CV until port is reseted.
     381 * That is announced via change on interrupt pipe.
     382 *
     383 * @param port_no Port number (starting at 1).
     384 * @param arg Custom argument, points to @c usb_hub_dev_t.
     385 * @return Error code.
     386 */
     387static int enable_port_callback(void *arg)
     388{
     389        usb_hub_port_t *port = arg;
     390        assert(port);
     391        const int rc =
     392            usb_hub_port_set_feature(port, USB_HUB_FEATURE_PORT_RESET);
     393        if (rc != EOK) {
     394                usb_log_warning("Port reset failed: %s.\n", str_error(rc));
    370395                return rc;
    371         } else {
    372                 return usb_hub_port_clear_feature(port,
    373                                 USB_HUB_FEATURE_PORT_ENABLE);
    374         }
     396        }
     397
     398        /*
     399         * Wait until reset completes.
     400         */
     401        fibril_mutex_lock(&port->mutex);
     402        while (!port->reset_completed) {
     403                fibril_condvar_wait(&port->reset_cv, &port->mutex);
     404        }
     405        fibril_mutex_unlock(&port->mutex);
     406
     407        return port->reset_okay ? EOK : ESTALL;
    375408}
    376409
     
    385418int add_device_phase1_worker_fibril(void *arg)
    386419{
    387         struct add_device_phase1 *params = arg;
    388         assert(params);
    389 
    390         int ret = EOK;
    391         usb_hub_dev_t *hub = params->hub;
    392         usb_hub_port_t *port = params->port;
    393         const usb_speed_t speed = params->speed;
     420        struct add_device_phase1 *data = arg;
     421        assert(data);
     422
     423        usb_address_t new_address;
     424        ddf_fun_t *child_fun;
     425
     426        child_fun = ddf_fun_create(data->hub->usb_device->ddf_dev,
     427            fun_inner, NULL);
     428        if (child_fun == NULL)
     429                return ENOMEM;
     430
     431        const int rc = usb_hc_new_device_wrapper(data->hub->usb_device->ddf_dev,
     432            child_fun, &data->hub->usb_device->hc_conn, data->speed,
     433            enable_port_callback, data->port, &new_address, NULL);
     434
     435        if (rc == EOK) {
     436                fibril_mutex_lock(&data->port->mutex);
     437                data->port->attached_device.fun = child_fun;
     438                data->port->attached_device.address = new_address;
     439                fibril_mutex_unlock(&data->port->mutex);
     440
     441                usb_log_info("Detected new device on `%s' (port %zu), "
     442                    "address %d (handle %" PRIun ").\n",
     443                    ddf_dev_get_name(data->hub->usb_device->ddf_dev),
     444                    data->port->port_number, new_address,
     445                    ddf_fun_get_handle(child_fun));
     446        } else {
     447                ddf_fun_destroy(child_fun);
     448                usb_log_error("Failed registering device on port %zu: %s.\n",
     449                    data->port->port_number, str_error(rc));
     450        }
     451
     452
     453        fibril_mutex_lock(&data->hub->pending_ops_mutex);
     454        assert(data->hub->pending_ops_count > 0);
     455        --data->hub->pending_ops_count;
     456        fibril_condvar_signal(&data->hub->pending_ops_cv);
     457        fibril_mutex_unlock(&data->hub->pending_ops_mutex);
     458
    394459        free(arg);
    395460
    396         usb_log_debug("(%p-%u): New device sequence.", hub, port->port_number);
    397 
    398         async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device);
    399         if (!exch) {
    400                 usb_log_error("(%p-%u): Failed to begin bus exchange.", hub,
    401                     port->port_number);
    402                 ret = ENOMEM;
    403                 goto out;
    404         }
    405 
    406         /* Reserve default address */
    407         while ((ret = usb_reserve_default_address(exch, speed)) == ENOENT) {
    408                 async_usleep(1000000);
    409         }
    410         if (ret != EOK) {
    411                 usb_log_error("(%p-%u): Failed to reserve default address: %s",
    412                     hub, port->port_number, str_error(ret));
    413                 goto out;
    414         }
    415 
    416         usb_log_debug("(%p-%u): Got default address reseting port.", hub,
    417             port->port_number);
    418         /* Reset port */
    419         ret = port_enable(port, hub, true);
    420         if (ret != EOK) {
    421                 usb_log_error("(%p-%u): Failed to reset port.", hub,
    422                     port->port_number);
    423                 if (usb_release_default_address(exch) != EOK)
    424                         usb_log_warning("(%p-%u): Failed to release default "
    425                             "address.", hub, port->port_number);
    426                 ret = EIO;
    427                 goto out;
    428         }
    429         usb_log_debug("(%p-%u): Port reset, enumerating device", hub,
    430             port->port_number);
    431 
    432         ret = usb_device_enumerate(exch, port->port_number);
    433         if (ret != EOK) {
    434                 usb_log_error("(%p-%u): Failed to enumerate device: %s", hub,
    435                     port->port_number, str_error(ret));
    436                 const int ret = port_enable(port, hub, false);
    437                 if (ret != EOK) {
    438                         usb_log_warning("(%p-%u)Failed to disable port (%s), "
    439                             "NOT releasing default address.", hub,
    440                             port->port_number, str_error(ret));
    441                 } else {
    442                         const int ret = usb_release_default_address(exch);
    443                         if (ret != EOK)
    444                                 usb_log_warning("(%p-%u): Failed to release "
    445                                     "default address: %s", hub,
    446                                     port->port_number, str_error(ret));
    447                 }
    448         } else {
    449                 usb_log_debug("(%p-%u): Device enumerated", hub,
    450                     port->port_number);
    451                 port->device_attached = true;
    452                 if (usb_release_default_address(exch) != EOK)
    453                         usb_log_warning("(%p-%u): Failed to release default "
    454                             "address", hub, port->port_number);
    455         }
    456 out:
    457         usb_device_bus_exchange_end(exch);
    458 
    459         fibril_mutex_lock(&hub->pending_ops_mutex);
    460         assert(hub->pending_ops_count > 0);
    461         --hub->pending_ops_count;
    462         fibril_condvar_signal(&hub->pending_ops_cv);
    463         fibril_mutex_unlock(&hub->pending_ops_mutex);
    464 
    465         return ret;
     461        return rc;
    466462}
    467463
     
    489485        data->speed = speed;
    490486
     487        fibril_mutex_lock(&port->mutex);
     488        port->reset_completed = false;
     489        fibril_mutex_unlock(&port->mutex);
     490
    491491        fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
    492492        if (fibril == 0) {
Note: See TracChangeset for help on using the changeset viewer.