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


Ignore:
Timestamp:
2018-02-28T16:37:50Z (6 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
1b20da0
Parents:
f5e5f73 (diff), b2dca8de (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.
git-author:
Jakub Jermar <jakub@…> (2018-02-28 16:06:42)
git-committer:
Jakub Jermar <jakub@…> (2018-02-28 16:37:50)
Message:

Merge github.com:helenos-xhci-team/helenos

This commit merges support for USB 3 and generally refactors, fixes,
extends and cleans up the existing USB framework.

Notable additions and features:

  • new host controller driver has been implemented to control various xHC models (among others, NEC Renesas uPD720200)
  • isochronous data transfer mode
  • support for explicit USB device removal
  • USB tablet driver
File:
1 edited

Legend:

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

    rf5e5f73 rdf6ded8  
    22 * Copyright (c) 2011 Vojtech Horky
    33 * Copyright (c) 2011 Jan Vesely
     4 * Copyright (c) 2018 Ondrej Hlavaty
    45 * All rights reserved.
    56 *
     
    4041#include <inttypes.h>
    4142#include <fibril_synch.h>
     43#include <usbhc_iface.h>
    4244
    4345#include <usb/debug.h>
     
    4749#include "status.h"
    4850
    49 /** Information for fibril for device discovery. */
    50 struct add_device_phase1 {
    51         usb_hub_dev_t *hub;
    52         usb_hub_port_t *port;
    53         usb_speed_t speed;
    54 };
    55 
    56 static errno_t usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub);
    57 static void usb_hub_port_reset_completed(usb_hub_port_t *port,
    58     usb_hub_dev_t *hub, usb_port_status_t status);
    59 static errno_t get_port_status(usb_hub_port_t *port, usb_port_status_t *status);
    60 static errno_t add_device_phase1_worker_fibril(void *arg);
    61 static errno_t create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub,
    62     usb_speed_t speed);
    63 
    64 errno_t usb_hub_port_fini(usb_hub_port_t *port, usb_hub_dev_t *hub)
     51#define port_log(lvl, port, fmt, ...) do { \
     52                usb_log_##lvl("(%p-%u): " fmt, \
     53                    (port->hub), (port->port_number), ##__VA_ARGS__); \
     54        } while (0)
     55
     56/** Initialize hub port information.
     57 *
     58 * @param port Port to be initialized.
     59 */
     60void usb_hub_port_init(usb_hub_port_t *port, usb_hub_dev_t *hub,
     61    unsigned int port_number)
    6562{
    6663        assert(port);
    67         if (port->device_attached)
    68                 return usb_hub_port_device_gone(port, hub);
     64        memset(port, 0, sizeof(*port));
     65        port->hub = hub;
     66        port->port_number = port_number;
     67        usb_port_init(&port->base);
     68}
     69
     70static inline usb_hub_port_t *get_hub_port(usb_port_t *port)
     71{
     72        assert(port);
     73        return (usb_hub_port_t *) port;
     74}
     75
     76/**
     77 * Inform the HC that the device on port is gone.
     78 */
     79static void remove_device(usb_port_t *port_base)
     80{
     81        usb_hub_port_t *port = get_hub_port(port_base);
     82
     83        async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
     84        if (!exch) {
     85                port_log(error, port, "Cannot remove the device, "
     86                    "failed creating exchange.");
     87                return;
     88        }
     89       
     90        const errno_t err = usbhc_device_remove(exch, port->port_number);
     91        if (err)
     92                port_log(error, port, "Failed to remove device: %s",
     93                    str_error(err));
     94
     95        usb_device_bus_exchange_end(exch);
     96}
     97
     98
     99static usb_speed_t get_port_speed(usb_hub_port_t *port, uint32_t status)
     100{
     101        assert(port);
     102        assert(port->hub);
     103
     104        return usb_port_speed(port->hub->speed, status);
     105}
     106
     107/**
     108 * Routine for adding a new device in USB2.
     109 */
     110static errno_t enumerate_device_usb2(usb_hub_port_t *port, async_exch_t *exch)
     111{
     112        errno_t err;
     113
     114        port_log(debug, port, "Requesting default address.");
     115        err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
     116        if (err != EOK) {
     117                port_log(error, port, "Failed to reserve default address: %s",
     118                    str_error(err));
     119                return err;
     120        }
     121
     122        /* Reservation of default address could have blocked */
     123        if (port->base.state != PORT_CONNECTING)
     124                goto out_address;
     125
     126        port_log(debug, port, "Resetting port.");
     127        if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
     128                            USB_HUB_FEATURE_PORT_RESET))) {
     129                port_log(warning, port, "Port reset request failed: %s",
     130                    str_error(err));
     131                goto out_address;
     132        }
     133
     134        if ((err = usb_port_wait_for_enabled(&port->base))) {
     135                port_log(error, port, "Failed to reset port: %s",
     136                    str_error(err));
     137                goto out_address;
     138        }
     139
     140        port_log(debug, port, "Enumerating device.");
     141        if ((err = usbhc_device_enumerate(exch, port->port_number,
     142                            port->speed))) {
     143                port_log(error, port, "Failed to enumerate device: %s",
     144                    str_error(err));
     145                /* Disable the port */
     146                usb_hub_clear_port_feature(port->hub, port->port_number,
     147                    USB2_HUB_FEATURE_PORT_ENABLE);
     148                goto out_address;
     149        }
     150
     151        port_log(debug, port, "Device enumerated");
     152
     153out_address:
     154        usb_hub_release_default_address(port->hub, exch);
     155        return err;
     156}
     157
     158/**
     159 * Routine for adding a new device in USB 3.
     160 */
     161static errno_t enumerate_device_usb3(usb_hub_port_t *port, async_exch_t *exch)
     162{
     163        errno_t err;
     164
     165        port_log(debug, port, "Issuing a warm reset.");
     166        if ((err = usb_hub_set_port_feature(port->hub, port->port_number,
     167                            USB3_HUB_FEATURE_BH_PORT_RESET))) {
     168                port_log(warning, port, "Port reset request failed: %s",
     169                    str_error(err));
     170                return err;
     171        }
     172
     173        if ((err = usb_port_wait_for_enabled(&port->base))) {
     174                port_log(error, port, "Failed to reset port: %s",
     175                    str_error(err));
     176                return err;
     177        }
     178
     179        port_log(debug, port, "Enumerating device.");
     180        if ((err = usbhc_device_enumerate(exch, port->port_number,
     181                            port->speed))) {
     182                port_log(error, port, "Failed to enumerate device: %s",
     183                    str_error(err));
     184                return err;
     185        }
     186
     187        port_log(debug, port, "Device enumerated");
    69188        return EOK;
    70189}
    71190
    72 /**
    73  * Clear feature on hub port.
    74  *
    75  * @param port Port structure.
    76  * @param feature Feature selector.
    77  * @return Operation result
    78  */
    79 errno_t usb_hub_port_clear_feature(
    80     usb_hub_port_t *port, usb_hub_class_feature_t feature)
    81 {
    82         assert(port);
    83         const usb_device_request_setup_packet_t clear_request = {
    84                 .request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE,
    85                 .request = USB_DEVREQ_CLEAR_FEATURE,
    86                 .value = feature,
    87                 .index = port->port_number,
    88                 .length = 0,
    89         };
    90         return usb_pipe_control_write(port->control_pipe, &clear_request,
    91             sizeof(clear_request), NULL, 0);
    92 }
    93 
    94 /**
    95  * Set feature on hub port.
    96  *
    97  * @param port Port structure.
    98  * @param feature Feature selector.
    99  * @return Operation result
    100  */
    101 errno_t usb_hub_port_set_feature(
    102     usb_hub_port_t *port, usb_hub_class_feature_t feature)
    103 {
    104         assert(port);
    105         const usb_device_request_setup_packet_t clear_request = {
    106                 .request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE,
    107                 .request = USB_DEVREQ_SET_FEATURE,
    108                 .index = port->port_number,
    109                 .value = feature,
    110                 .length = 0,
    111         };
    112         return usb_pipe_control_write(port->control_pipe, &clear_request,
    113             sizeof(clear_request), NULL, 0);
    114 }
    115 
    116 /**
    117  * Mark reset process as failed due to external reasons
    118  *
    119  * @param port Port structure
    120  */
    121 void usb_hub_port_reset_fail(usb_hub_port_t *port)
    122 {
    123         assert(port);
    124         fibril_mutex_lock(&port->mutex);
    125         if (port->reset_status == IN_RESET)
    126                 port->reset_status = RESET_FAIL;
    127         fibril_condvar_broadcast(&port->reset_cv);
    128         fibril_mutex_unlock(&port->mutex);
     191static errno_t enumerate_device(usb_port_t *port_base)
     192{
     193        usb_hub_port_t *port = get_hub_port(port_base);
     194
     195        port_log(debug, port, "Setting up new device.");
     196        async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
     197        if (!exch) {
     198                port_log(error, port, "Failed to create exchange.");
     199                return ENOMEM;
     200        }
     201
     202        const errno_t err = port->hub->speed == USB_SPEED_SUPER
     203                ? enumerate_device_usb3(port, exch)
     204                : enumerate_device_usb2(port, exch);
     205
     206        usb_device_bus_exchange_end(exch);
     207        return err;
     208}
     209
     210static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status)
     211{
     212        const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION);
     213        port_log(debug, port, "Connection change: device %s.", connected
     214            ? "attached" : "removed");
     215
     216        if (connected) {
     217                usb_port_connected(&port->base, &enumerate_device);
     218        } else {
     219                usb_port_disabled(&port->base, &remove_device);
     220        }
     221}
     222
     223static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
     224{
     225        const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
     226        if (enabled) {
     227                port_log(warning, port, "Port unexpectedly changed to enabled.");
     228        } else {
     229                usb_port_disabled(&port->base, &remove_device);
     230        }
     231}
     232
     233static void port_changed_overcurrent(usb_hub_port_t *port,
     234    usb_port_status_t status)
     235{
     236        const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
     237
     238        /* According to the USB specs:
     239         * 11.13.5 Over-current Reporting and Recovery
     240         * Hub device is responsible for putting port in power off
     241         * mode. USB system software is responsible for powering port
     242         * back on when the over-current condition is gone */
     243
     244        usb_port_disabled(&port->base, &remove_device);
     245
     246        if (!overcurrent) {
     247                const errno_t err = usb_hub_set_port_feature(port->hub,
     248                    port->port_number, USB_HUB_FEATURE_PORT_POWER);
     249                if (err)
     250                        port_log(error, port, "Failed to set port power "
     251                            "after OC: %s.", str_error(err));
     252        }
     253}
     254
     255static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
     256{
     257        const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
     258
     259        if (enabled) {
     260                port->speed = get_port_speed(port, status);
     261                usb_port_enabled(&port->base);
     262        } else
     263                usb_port_disabled(&port->base, &remove_device);
     264}
     265
     266typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
     267
     268static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
     269    change_handler_t handler, usb_port_status_t mask,
     270    usb_hub_class_feature_t feature)
     271{
     272        if ((*status & mask) == 0)
     273                return;
     274
     275        /* Clear the change so it won't come again */
     276        usb_hub_clear_port_feature(port->hub, port->port_number, feature);
     277
     278        if (handler)
     279                handler(port, *status);
     280
     281        /* Mark the change as resolved */
     282        *status &= ~mask;
    129283}
    130284
     
    136290 * @param hub hub representation
    137291 */
    138 void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_dev_t *hub)
     292void usb_hub_port_process_interrupt(usb_hub_port_t *port)
    139293{
    140294        assert(port);
    141         assert(hub);
    142         usb_log_debug2("(%p-%u): Interrupt.\n", hub, port->port_number);
     295        port_log(debug2, port, "Interrupt.");
    143296
    144297        usb_port_status_t status = 0;
    145         const errno_t opResult = get_port_status(port, &status);
    146         if (opResult != EOK) {
    147                 usb_log_error("(%p-%u): Failed to get port status: %s.\n", hub,
    148                     port->port_number, str_error(opResult));
     298        const errno_t err = usb_hub_get_port_status(port->hub,
     299            port->port_number, &status);
     300        if (err != EOK) {
     301                port_log(error, port, "Failed to get port status: %s.",
     302                    str_error(err));
    149303                return;
    150304        }
    151305
    152         /* Connection change */
    153         if (status & USB_HUB_PORT_C_STATUS_CONNECTION) {
    154                 const bool connected =
    155                     (status & USB_HUB_PORT_STATUS_CONNECTION) != 0;
    156                 usb_log_debug("(%p-%u): Connection change: device %s.\n", hub,
    157                     port->port_number, connected ? "attached" : "removed");
    158 
    159                 /* ACK the change */
    160                 const errno_t opResult = usb_hub_port_clear_feature(port,
    161                     USB_HUB_FEATURE_C_PORT_CONNECTION);
    162                 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));
    166                 }
    167 
    168                 if (connected) {
    169                         const errno_t opResult = create_add_device_fibril(port, hub,
    170                             usb_port_speed(status));
    171                         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));
    175                         }
    176                 } else {
    177                         /* Handle the case we were in reset */
    178                         // FIXME: usb_hub_port_reset_fail(port);
    179                         /* If enabled change was reported leave the removal
    180                          * to that handler, it shall ACK the change too. */
    181                         if (!(status & USB_HUB_PORT_C_STATUS_ENABLED)) {
    182                                 usb_hub_port_device_gone(port, hub);
    183                         }
    184                 }
    185         }
    186 
    187         /* Enable change, ports are automatically disabled on errors. */
    188         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,
    191                    port->port_number);
    192                 usb_hub_port_device_gone(port, hub);
    193                 const errno_t rc = usb_hub_port_clear_feature(port,
    194                         USB_HUB_FEATURE_C_PORT_ENABLE);
    195                 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));
    199                 }
    200 
    201         }
    202 
    203         /* Suspend change */
    204         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,
    207                     port->port_number);
    208                 const errno_t rc = usb_hub_port_clear_feature(port,
    209                         USB_HUB_FEATURE_C_PORT_SUSPEND);
    210                 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));
    214                 }
    215         }
    216 
    217         /* Over current */
    218         if (status & USB_HUB_PORT_C_STATUS_OC) {
    219                 usb_log_debug("(%p-%u): Port OC reported!.", hub,
    220                     port->port_number);
    221                 /* According to the USB specs:
    222                  * 11.13.5 Over-current Reporting and Recovery
    223                  * Hub device is responsible for putting port in power off
    224                  * mode. USB system software is responsible for powering port
    225                  * back on when the over-current condition is gone */
    226                 const errno_t rc = usb_hub_port_clear_feature(port,
    227                     USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
    228                 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));
    232                 }
    233                 if (!(status & ~USB_HUB_PORT_STATUS_OC)) {
    234                         const errno_t rc = usb_hub_port_set_feature(
    235                             port, USB_HUB_FEATURE_PORT_POWER);
    236                         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));
    240                         }
    241                 }
    242         }
    243 
    244         /* Port reset, set on port reset complete. */
    245         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,
    250             port->port_number, status);
    251 }
    252 
    253 /**
    254  * routine called when a device on port has been removed
    255  *
    256  * If the device on port had default address, it releases default address.
    257  * Otherwise does not do anything, because DDF does not allow to remove device
    258  * from it`s device tree.
    259  * @param port port structure
    260  * @param hub hub representation
    261  */
    262 errno_t usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub)
    263 {
    264         assert(port);
    265         assert(hub);
    266         async_exch_t *exch = usb_device_bus_exchange_begin(hub->usb_device);
    267         if (!exch)
    268                 return ENOMEM;
    269         const errno_t 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 
    275 }
    276 
    277 /**
    278  * Process port reset change
    279  *
    280  * After this change port should be enabled, unless some problem occurred.
    281  * This functions triggers second phase of enabling new device.
    282  * @param port Port structure
    283  * @param status Port status mask
    284  */
    285 void usb_hub_port_reset_completed(usb_hub_port_t *port, usb_hub_dev_t *hub,
    286     usb_port_status_t status)
    287 {
    288         assert(port);
    289         fibril_mutex_lock(&port->mutex);
    290         const bool enabled = (status & USB_HUB_PORT_STATUS_ENABLED) != 0;
    291         /* Finalize device adding. */
    292 
    293         if (enabled) {
    294                 port->reset_status = RESET_OK;
    295                 usb_log_debug("(%p-%u): Port reset complete.\n", hub,
    296                     port->port_number);
     306        check_port_change(port, &status, &port_changed_connection,
     307            USB_HUB_PORT_STATUS_C_CONNECTION,
     308            USB_HUB_FEATURE_C_PORT_CONNECTION);
     309
     310        check_port_change(port, &status, &port_changed_overcurrent,
     311            USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
     312
     313        check_port_change(port, &status, &port_changed_reset,
     314            USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
     315
     316        if (port->hub->speed <= USB_SPEED_HIGH) {
     317                check_port_change(port, &status, &port_changed_enabled,
     318                    USB2_HUB_PORT_STATUS_C_ENABLE,
     319                    USB2_HUB_FEATURE_C_PORT_ENABLE);
    297320        } 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);
    301         }
    302         fibril_condvar_broadcast(&port->reset_cv);
    303         fibril_mutex_unlock(&port->mutex);
    304 
    305         /* Clear the port reset change. */
    306         errno_t rc = usb_hub_port_clear_feature(port, USB_HUB_FEATURE_C_PORT_RESET);
    307         if (rc != EOK) {
    308                 usb_log_error("(%p-%u): Failed to clear port reset change: %s.",
    309                     hub, port->port_number, str_error(rc));
    310         }
    311 }
    312 
    313 /** Retrieve port status.
    314  *
    315  * @param[in] port Port structure
    316  * @param[out] status Where to store the port status.
    317  * @return Error code.
    318  */
    319 static errno_t get_port_status(usb_hub_port_t *port, usb_port_status_t *status)
    320 {
    321         assert(port);
    322         /* USB hub specific GET_PORT_STATUS request. See USB Spec 11.16.2.6
    323          * Generic GET_STATUS request cannot be used because of the difference
    324          * in status data size (2B vs. 4B)*/
    325         const usb_device_request_setup_packet_t request = {
    326                 .request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS,
    327                 .request = USB_HUB_REQUEST_GET_STATUS,
    328                 .value = 0,
    329                 .index = uint16_host2usb(port->port_number),
    330                 .length = sizeof(usb_port_status_t),
    331         };
    332         size_t recv_size;
    333         usb_port_status_t status_tmp;
    334 
    335         const errno_t rc = usb_pipe_control_read(port->control_pipe,
    336             &request, sizeof(usb_device_request_setup_packet_t),
    337             &status_tmp, sizeof(status_tmp), &recv_size);
    338         if (rc != EOK) {
    339                 return rc;
    340         }
    341 
    342         if (recv_size != sizeof (status_tmp)) {
    343                 return ELIMIT;
    344         }
    345 
    346         if (status != NULL) {
    347                 *status = status_tmp;
    348         }
    349 
    350         return EOK;
    351 }
    352 
    353 static errno_t port_enable(usb_hub_port_t *port, usb_hub_dev_t *hub, bool enable)
    354 {
    355         if (enable) {
    356                 errno_t 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);
    370                 return rc;
    371         } else {
    372                 return usb_hub_port_clear_feature(port,
    373                                 USB_HUB_FEATURE_PORT_ENABLE);
    374         }
    375 }
    376 
    377 /** Fibril for adding a new device.
    378  *
    379  * Separate fibril is needed because the port reset completion is announced
    380  * via interrupt pipe and thus we cannot block here.
    381  *
    382  * @param arg Pointer to struct add_device_phase1.
    383  * @return 0 Always.
    384  */
    385 errno_t add_device_phase1_worker_fibril(void *arg)
    386 {
    387         struct add_device_phase1 *params = arg;
    388         assert(params);
    389 
    390         errno_t 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;
    394         free(arg);
    395 
    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 errno_t 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 errno_t 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;
    466 }
    467 
    468 /** Start device adding when connection change is detected.
    469  *
    470  * This fires a new fibril to complete the device addition.
    471  *
    472  * @param hub Hub where the change occured.
    473  * @param port Port index (starting at 1).
    474  * @param speed Speed of the device.
    475  * @return Error code.
    476  */
    477 static errno_t create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub,
    478     usb_speed_t speed)
    479 {
    480         assert(hub);
    481         assert(port);
    482         struct add_device_phase1 *data
    483             = malloc(sizeof(struct add_device_phase1));
    484         if (data == NULL) {
    485                 return ENOMEM;
    486         }
    487         data->hub = hub;
    488         data->port = port;
    489         data->speed = speed;
    490 
    491         fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
    492         if (fibril == 0) {
    493                 free(data);
    494                 return ENOMEM;
    495         }
    496         fibril_mutex_lock(&hub->pending_ops_mutex);
    497         ++hub->pending_ops_count;
    498         fibril_mutex_unlock(&hub->pending_ops_mutex);
    499         fibril_add_ready(fibril);
    500 
    501         return EOK;
    502 }
     321                check_port_change(port, &status, &port_changed_reset,
     322                    USB3_HUB_PORT_STATUS_C_BH_RESET,
     323                    USB3_HUB_FEATURE_C_BH_PORT_RESET);
     324
     325                check_port_change(port, &status, NULL,
     326                    USB3_HUB_PORT_STATUS_C_LINK_STATE,
     327                    USB3_HUB_FEATURE_C_PORT_LINK_STATE);
     328        }
     329
     330        /* Check for changes we ignored */
     331        if (status & 0xffff0000) {
     332                port_log(debug, port, "Port status change igored. "
     333                    "Status: %#08" PRIx32, status);
     334        }
     335}
     336
    503337
    504338/**
Note: See TracChangeset for help on using the changeset viewer.