Ignore:
File:
1 edited

Legend:

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

    r8351f9a4 rffa96c2  
    4141
    4242#include <usb/debug.h>
     43#include <usb/dev/hub.h>
    4344
    4445#include "port.h"
     
    5556static int usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub);
    5657static void usb_hub_port_reset_completed(usb_hub_port_t *port,
    57     usb_hub_dev_t *hub, usb_port_status_t status);
     58    usb_port_status_t status);
    5859static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status);
     60static int enable_port_callback(void *arg);
    5961static int add_device_phase1_worker_fibril(void *arg);
    6062static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub,
     
    6466{
    6567        assert(port);
    66         if (port->device_attached)
     68        if (port->attached_device.fun)
    6769                return usb_hub_port_device_gone(port, hub);
    6870        return EOK;
     
    122124        assert(port);
    123125        fibril_mutex_lock(&port->mutex);
    124         if (port->reset_status == IN_RESET)
    125                 port->reset_status = RESET_FAIL;
     126        port->reset_completed = true;
     127        port->reset_okay = false;
    126128        fibril_condvar_broadcast(&port->reset_cv);
    127129        fibril_mutex_unlock(&port->mutex);
     
    139141        assert(port);
    140142        assert(hub);
    141         usb_log_debug2("(%p-%u): Interrupt.\n", hub, port->port_number);
     143        usb_log_debug("Interrupt at port %zu\n", port->port_number);
    142144
    143145        usb_port_status_t status = 0;
    144146        const int opResult = get_port_status(port, &status);
    145147        if (opResult != EOK) {
    146                 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",
    147149                    port->port_number, str_error(opResult));
    148150                return;
     
    153155                const bool connected =
    154156                    (status & USB_HUB_PORT_STATUS_CONNECTION) != 0;
    155                 usb_log_debug("(%p-%u): Connection change: device %s.\n", hub,
     157                usb_log_debug("Connection change on port %zu: device %s.\n",
    156158                    port->port_number, connected ? "attached" : "removed");
    157159
     
    160162                    USB_HUB_FEATURE_C_PORT_CONNECTION);
    161163                if (opResult != EOK) {
    162                         usb_log_warning("(%p-%u): Failed to clear "
    163                             "port-change-connection flag: %s.\n", hub,
    164                             port->port_number, str_error(opResult));
     164                        usb_log_warning("Failed to clear port-change-connection"
     165                            " flag: %s.\n", str_error(opResult));
    165166                }
    166167
     
    169170                            usb_port_speed(status));
    170171                        if (opResult != EOK) {
    171                                 usb_log_error("(%p-%u): Cannot handle change on"
    172                                    " port: %s.\n", hub, port->port_number,
    173                                    str_error(opResult));
     172                                usb_log_error(
     173                                    "Cannot handle change on port %zu: %s.\n",
     174                                    port->port_number, str_error(opResult));
    174175                        }
    175176                } else {
    176                         /* Handle the case we were in reset */
    177                         //usb_hub_port_reset_fail(port);
    178177                        /* If enabled change was reported leave the removal
    179178                         * to that handler, it shall ACK the change too. */
     
    186185        /* Enable change, ports are automatically disabled on errors. */
    187186        if (status & USB_HUB_PORT_C_STATUS_ENABLED) {
    188                 //TODO: maybe HS reset failed?
    189                 usb_log_info("(%p-%u): Port disabled because of errors.\n", hub,
     187                usb_log_info("Port %zu, disabled because of errors.\n",
    190188                   port->port_number);
    191189                usb_hub_port_device_gone(port, hub);
     
    193191                        USB_HUB_FEATURE_C_PORT_ENABLE);
    194192                if (rc != EOK) {
    195                         usb_log_error("(%p-%u): Failed to clear port enable "
    196                             "change feature: %s.", hub, port->port_number,
    197                             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));
    198196                }
    199197
     
    202200        /* Suspend change */
    203201        if (status & USB_HUB_PORT_C_STATUS_SUSPEND) {
    204                 usb_log_error("(%p-%u): Port went to suspend state, this should"
    205                     " 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!",
    206204                    port->port_number);
    207205                const int rc = usb_hub_port_clear_feature(port,
    208206                        USB_HUB_FEATURE_C_PORT_SUSPEND);
    209207                if (rc != EOK) {
    210                         usb_log_error("(%p-%u): Failed to clear port suspend "
    211                             "change feature: %s.", hub, port->port_number,
    212                             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));
    213211                }
    214212        }
     
    216214        /* Over current */
    217215        if (status & USB_HUB_PORT_C_STATUS_OC) {
    218                 usb_log_debug("(%p-%u): Port OC reported!.", hub,
    219                     port->port_number);
    220216                /* According to the USB specs:
    221217                 * 11.13.5 Over-current Reporting and Recovery
     
    226222                    USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
    227223                if (rc != EOK) {
    228                         usb_log_error("(%p-%u): Failed to clear port OC change "
    229                             "feature: %s.\n", hub, port->port_number,
    230                             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));
    231227                }
    232228                if (!(status & ~USB_HUB_PORT_STATUS_OC)) {
     
    234230                            port, USB_HUB_FEATURE_PORT_POWER);
    235231                        if (rc != EOK) {
    236                                 usb_log_error("(%p-%u): Failed to set port "
    237                                     "power after OC: %s.", hub,
    238                                     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));
    239235                        }
    240236                }
     
    243239        /* Port reset, set on port reset complete. */
    244240        if (status & USB_HUB_PORT_C_STATUS_RESET) {
    245                 usb_hub_port_reset_completed(port, hub, status);
    246         }
    247 
    248         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",
    249245            port->port_number, status);
    250246}
     
    263259        assert(port);
    264260        assert(hub);
    265         async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device);
    266         if (!exch)
    267                 return ENOMEM;
    268         const int rc = usb_device_remove(exch, port->port_number);
    269         usb_device_bus_exchange_end(exch);
    270         if (rc == EOK)
    271                 port->device_attached = false;
    272         return rc;
    273 
     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;
    274300}
    275301
     
    282308 * @param status Port status mask
    283309 */
    284 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,
    285311    usb_port_status_t status)
    286312{
    287313        assert(port);
    288314        fibril_mutex_lock(&port->mutex);
    289         const bool enabled = (status & USB_HUB_PORT_STATUS_ENABLED) != 0;
    290315        /* Finalize device adding. */
    291 
    292         if (enabled) {
    293                 port->reset_status = RESET_OK;
    294                 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",
    295324                    port->port_number);
    296         } else {
    297                 port->reset_status = RESET_FAIL;
    298                 usb_log_warning("(%p-%u): Port reset complete but port not "
    299                     "enabled.", hub, port->port_number);
    300325        }
    301326        fibril_condvar_broadcast(&port->reset_cv);
     
    305330        int rc = usb_hub_port_clear_feature(port, USB_HUB_FEATURE_C_PORT_RESET);
    306331        if (rc != EOK) {
    307                 usb_log_error("(%p-%u): Failed to clear port reset change: %s.",
    308                     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));
    309335        }
    310336}
     
    350376}
    351377
    352 static int port_enable(usb_hub_port_t *port, usb_hub_dev_t *hub, bool enable)
    353 {
    354         if (enable) {
    355                 int rc =
    356                     usb_hub_port_set_feature(port, USB_HUB_FEATURE_PORT_RESET);
    357                 if (rc != EOK) {
    358                         usb_log_error("(%p-%u): Port reset request failed: %s.",
    359                             hub, port->port_number, str_error(rc));
    360                         return rc;
    361                 }
    362                 /* Wait until reset completes. */
    363                 fibril_mutex_lock(&port->mutex);
    364                 port->reset_status = IN_RESET;
    365                 while (port->reset_status == IN_RESET)
    366                         fibril_condvar_wait(&port->reset_cv, &port->mutex);
    367                 rc = port->reset_status == RESET_OK ? EOK : ESTALL;
    368                 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));
    369395                return rc;
    370         } else {
    371                 return usb_hub_port_clear_feature(port,
    372                                 USB_HUB_FEATURE_PORT_ENABLE);
    373         }
     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;
    374408}
    375409
     
    384418int add_device_phase1_worker_fibril(void *arg)
    385419{
    386         struct add_device_phase1 *params = arg;
    387         assert(params);
    388 
    389         int ret = EOK;
    390         usb_hub_dev_t *hub = params->hub;
    391         usb_hub_port_t *port = params->port;
    392         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
    393459        free(arg);
    394460
    395         usb_log_debug("(%p-%u): New device sequence.", hub, port->port_number);
    396 
    397         async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device);
    398         if (!exch) {
    399                 usb_log_error("(%p-%u): Failed to begin bus exchange.", hub,
    400                     port->port_number);
    401                 ret = ENOMEM;
    402                 goto out;
    403         }
    404 
    405         /* Reserve default address */
    406         while ((ret = usb_reserve_default_address(exch, speed)) == ENOENT) {
    407                 async_usleep(1000000);
    408         }
    409         if (ret != EOK) {
    410                 usb_log_error("(%p-%u): Failed to reserve default address: %s",
    411                     hub, port->port_number, str_error(ret));
    412                 goto out;
    413         }
    414 
    415         usb_log_debug("(%p-%u): Got default address reseting port.", hub,
    416             port->port_number);
    417         /* Reset port */
    418         ret = port_enable(port, hub, true);
    419         if (ret != EOK) {
    420                 usb_log_error("(%p-%u): Failed to reset port.", hub,
    421                     port->port_number);
    422                 if (usb_release_default_address(exch) != EOK)
    423                         usb_log_warning("(%p-%u): Failed to release default "
    424                             "address.", hub, port->port_number);
    425                 ret = EIO;
    426                 goto out;
    427         }
    428         usb_log_debug("(%p-%u): Port reset, enumerating device", hub,
    429             port->port_number);
    430 
    431         ret = usb_device_enumerate(exch, port->port_number);
    432         if (ret != EOK) {
    433                 usb_log_error("(%p-%u): Failed to enumerate device: %s", hub,
    434                     port->port_number, str_error(ret));
    435                 const int ret = port_enable(port, hub, false);
    436                 if (ret != EOK) {
    437                         usb_log_warning("(%p-%u)Failed to disable port (%s), "
    438                             "NOT releasing default address.", hub,
    439                             port->port_number, str_error(ret));
    440                 } else {
    441                         const int ret = usb_release_default_address(exch);
    442                         if (ret != EOK)
    443                                 usb_log_warning("(%p-%u): Failed to release "
    444                                     "default address: %s", hub,
    445                                     port->port_number, str_error(ret));
    446                 }
    447         } else {
    448                 usb_log_debug("(%p-%u): Device enumerated", hub,
    449                     port->port_number);
    450                 port->device_attached = true;
    451                 if (usb_release_default_address(exch) != EOK)
    452                         usb_log_warning("(%p-%u): Failed to release default "
    453                             "address", hub, port->port_number);
    454         }
    455 out:
    456         usb_device_bus_exchange_end(exch);
    457 
    458         fibril_mutex_lock(&hub->pending_ops_mutex);
    459         assert(hub->pending_ops_count > 0);
    460         --hub->pending_ops_count;
    461         fibril_condvar_signal(&hub->pending_ops_cv);
    462         fibril_mutex_unlock(&hub->pending_ops_mutex);
    463 
    464         return ret;
     461        return rc;
    465462}
    466463
     
    488485        data->speed = speed;
    489486
     487        fibril_mutex_lock(&port->mutex);
     488        port->reset_completed = false;
     489        fibril_mutex_unlock(&port->mutex);
     490
    490491        fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
    491492        if (fibril == 0) {
Note: See TracChangeset for help on using the changeset viewer.