Changeset b4b534ac in mainline for uspace/drv/bus/usb/usbhub/port.c


Ignore:
Timestamp:
2016-07-22T08:24:47Z (8 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
f76d2c2
Parents:
5b18137 (diff), 8351f9a4 (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.
Message:

Merge from lp:~jan.vesely/helenos/usb

File:
1 edited

Legend:

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

    r5b18137 rb4b534ac  
    4141
    4242#include <usb/debug.h>
    43 #include <usb/dev/hub.h>
    4443
    4544#include "port.h"
     
    5655static int usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub);
    5756static void usb_hub_port_reset_completed(usb_hub_port_t *port,
    58     usb_port_status_t status);
     57    usb_hub_dev_t *hub, usb_port_status_t status);
    5958static int get_port_status(usb_hub_port_t *port, usb_port_status_t *status);
    60 static int enable_port_callback(void *arg);
    6159static int add_device_phase1_worker_fibril(void *arg);
    6260static int create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub,
     
    6664{
    6765        assert(port);
    68         if (port->attached_device.fun)
     66        if (port->device_attached)
    6967                return usb_hub_port_device_gone(port, hub);
    7068        return EOK;
     
    124122        assert(port);
    125123        fibril_mutex_lock(&port->mutex);
    126         port->reset_completed = true;
    127         port->reset_okay = false;
     124        if (port->reset_status == IN_RESET)
     125                port->reset_status = RESET_FAIL;
    128126        fibril_condvar_broadcast(&port->reset_cv);
    129127        fibril_mutex_unlock(&port->mutex);
     
    141139        assert(port);
    142140        assert(hub);
    143         usb_log_debug("Interrupt at port %zu\n", port->port_number);
     141        usb_log_debug2("(%p-%u): Interrupt.\n", hub, port->port_number);
    144142
    145143        usb_port_status_t status = 0;
    146144        const int opResult = get_port_status(port, &status);
    147145        if (opResult != EOK) {
    148                 usb_log_error("Failed to get port %zu status: %s.\n",
     146                usb_log_error("(%p-%u): Failed to get port status: %s.\n", hub,
    149147                    port->port_number, str_error(opResult));
    150148                return;
     
    155153                const bool connected =
    156154                    (status & USB_HUB_PORT_STATUS_CONNECTION) != 0;
    157                 usb_log_debug("Connection change on port %zu: device %s.\n",
     155                usb_log_debug("(%p-%u): Connection change: device %s.\n", hub,
    158156                    port->port_number, connected ? "attached" : "removed");
    159157
     
    162160                    USB_HUB_FEATURE_C_PORT_CONNECTION);
    163161                if (opResult != EOK) {
    164                         usb_log_warning("Failed to clear port-change-connection"
    165                             " flag: %s.\n", str_error(opResult));
     162                        usb_log_warning("(%p-%u): Failed to clear "
     163                            "port-change-connection flag: %s.\n", hub,
     164                            port->port_number, str_error(opResult));
    166165                }
    167166
     
    170169                            usb_port_speed(status));
    171170                        if (opResult != EOK) {
    172                                 usb_log_error(
    173                                     "Cannot handle change on port %zu: %s.\n",
    174                                     port->port_number, str_error(opResult));
     171                                usb_log_error("(%p-%u): Cannot handle change on"
     172                                   " port: %s.\n", hub, port->port_number,
     173                                   str_error(opResult));
    175174                        }
    176175                } else {
     176                        /* Handle the case we were in reset */
     177                        //usb_hub_port_reset_fail(port);
    177178                        /* If enabled change was reported leave the removal
    178179                         * to that handler, it shall ACK the change too. */
     
    185186        /* Enable change, ports are automatically disabled on errors. */
    186187        if (status & USB_HUB_PORT_C_STATUS_ENABLED) {
    187                 usb_log_info("Port %zu, disabled because of errors.\n",
     188                //TODO: maybe HS reset failed?
     189                usb_log_info("(%p-%u): Port disabled because of errors.\n", hub,
    188190                   port->port_number);
    189191                usb_hub_port_device_gone(port, hub);
     
    191193                        USB_HUB_FEATURE_C_PORT_ENABLE);
    192194                if (rc != EOK) {
    193                         usb_log_error(
    194                             "Failed to clear port %zu enable change feature: "
    195                             "%s.\n", port->port_number, str_error(rc));
     195                        usb_log_error("(%p-%u): Failed to clear port enable "
     196                            "change feature: %s.", hub, port->port_number,
     197                            str_error(rc));
    196198                }
    197199
     
    200202        /* Suspend change */
    201203        if (status & USB_HUB_PORT_C_STATUS_SUSPEND) {
    202                 usb_log_error("Port %zu went to suspend state, this should"
    203                     "NOT happen as we do not support suspend state!",
     204                usb_log_error("(%p-%u): Port went to suspend state, this should"
     205                    " NOT happen as we do not support suspend state!", hub,
    204206                    port->port_number);
    205207                const int rc = usb_hub_port_clear_feature(port,
    206208                        USB_HUB_FEATURE_C_PORT_SUSPEND);
    207209                if (rc != EOK) {
    208                         usb_log_error(
    209                             "Failed to clear port %zu suspend change feature: "
    210                             "%s.\n", port->port_number, str_error(rc));
     210                        usb_log_error("(%p-%u): Failed to clear port suspend "
     211                            "change feature: %s.", hub, port->port_number,
     212                            str_error(rc));
    211213                }
    212214        }
     
    214216        /* Over current */
    215217        if (status & USB_HUB_PORT_C_STATUS_OC) {
     218                usb_log_debug("(%p-%u): Port OC reported!.", hub,
     219                    port->port_number);
    216220                /* According to the USB specs:
    217221                 * 11.13.5 Over-current Reporting and Recovery
     
    222226                    USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
    223227                if (rc != EOK) {
    224                         usb_log_error(
    225                             "Failed to clear port %zu OC change feature: %s.\n",
    226                             port->port_number, str_error(rc));
     228                        usb_log_error("(%p-%u): Failed to clear port OC change "
     229                            "feature: %s.\n", hub, port->port_number,
     230                            str_error(rc));
    227231                }
    228232                if (!(status & ~USB_HUB_PORT_STATUS_OC)) {
     
    230234                            port, USB_HUB_FEATURE_PORT_POWER);
    231235                        if (rc != EOK) {
    232                                 usb_log_error(
    233                                     "Failed to set port %zu power after OC:"
    234                                     " %s.\n", port->port_number, str_error(rc));
     236                                usb_log_error("(%p-%u): Failed to set port "
     237                                    "power after OC: %s.", hub,
     238                                    port->port_number, str_error(rc));
    235239                        }
    236240                }
     
    239243        /* Port reset, set on port reset complete. */
    240244        if (status & USB_HUB_PORT_C_STATUS_RESET) {
    241                 usb_hub_port_reset_completed(port, status);
    242         }
    243 
    244         usb_log_debug("Port %zu status 0x%08" PRIx32 "\n",
     245                usb_hub_port_reset_completed(port, hub, status);
     246        }
     247
     248        usb_log_debug2("(%p-%u): Port status %#08" PRIx32, hub,
    245249            port->port_number, status);
    246250}
     
    259263        assert(port);
    260264        assert(hub);
    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;
     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
    300274}
    301275
     
    308282 * @param status Port status mask
    309283 */
    310 void usb_hub_port_reset_completed(usb_hub_port_t *port,
     284void usb_hub_port_reset_completed(usb_hub_port_t *port, usb_hub_dev_t *hub,
    311285    usb_port_status_t status)
    312286{
    313287        assert(port);
    314288        fibril_mutex_lock(&port->mutex);
     289        const bool enabled = (status & USB_HUB_PORT_STATUS_ENABLED) != 0;
    315290        /* Finalize device adding. */
    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);
     291
     292        if (enabled) {
     293                port->reset_status = RESET_OK;
     294                usb_log_debug("(%p-%u): Port reset complete.\n", hub,
     295                    port->port_number);
    321296        } else {
    322                 usb_log_warning(
    323                     "Port %zu reset complete but port not enabled.\n",
    324                     port->port_number);
     297                port->reset_status = RESET_FAIL;
     298                usb_log_warning("(%p-%u): Port reset complete but port not "
     299                    "enabled.", hub, port->port_number);
    325300        }
    326301        fibril_condvar_broadcast(&port->reset_cv);
     
    330305        int rc = usb_hub_port_clear_feature(port, USB_HUB_FEATURE_C_PORT_RESET);
    331306        if (rc != EOK) {
    332                 usb_log_error(
    333                     "Failed to clear port %zu reset change feature: %s.\n",
    334                     port->port_number, str_error(rc));
     307                usb_log_error("(%p-%u): Failed to clear port reset change: %s.",
     308                    hub, port->port_number, str_error(rc));
    335309        }
    336310}
     
    376350}
    377351
    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  */
    387 static 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));
     352static 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);
    395369                return rc;
    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;
     370        } else {
     371                return usb_hub_port_clear_feature(port,
     372                                USB_HUB_FEATURE_PORT_ENABLE);
     373        }
    408374}
    409375
     
    418384int add_device_phase1_worker_fibril(void *arg)
    419385{
    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));
     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;
     393        free(arg);
     394
     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                }
    446447        } 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 
    459         free(arg);
    460 
    461         return rc;
     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        }
     455out:
     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;
    462465}
    463466
     
    485488        data->speed = speed;
    486489
    487         fibril_mutex_lock(&port->mutex);
    488         port->reset_completed = false;
    489         fibril_mutex_unlock(&port->mutex);
    490 
    491490        fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
    492491        if (fibril == 0) {
Note: See TracChangeset for help on using the changeset viewer.