Ignore:
File:
1 edited

Legend:

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

    raa148b3 rb7fd2a0  
    4040#include <inttypes.h>
    4141#include <fibril_synch.h>
    42 #include <usbhc_iface.h>
    4342
    4443#include <usb/debug.h>
     
    4847#include "status.h"
    4948
    50 #define port_log(lvl, port, fmt, ...) do { usb_log_##lvl("(%p-%u): " fmt, (port->hub), (port->port_number), ##__VA_ARGS__); } while (0)
    51 
    52 /** Initialize hub port information.
    53  *
    54  * @param port Port to be initialized.
    55  */
    56 void usb_hub_port_init(usb_hub_port_t *port, usb_hub_dev_t *hub, unsigned int port_number)
    57 {
    58         assert(port);
    59         memset(port, 0, sizeof(*port));
    60         port->hub = hub;
    61         port->port_number = port_number;
    62         usb_port_init(&port->base);
    63 }
    64 
    65 static inline usb_hub_port_t *get_hub_port(usb_port_t *port)
    66 {
    67         assert(port);
    68         return (usb_hub_port_t *) port;
    69 }
    70 
    71 /**
    72  * Inform the HC that the device on port is gone.
    73  */
    74 static void remove_device(usb_port_t *port_base)
    75 {
    76         usb_hub_port_t *port = get_hub_port(port_base);
    77 
    78         async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
    79         if (!exch) {
    80                 port_log(error, port, "Cannot remove the device, failed creating exchange.");
    81                 return;
    82         }
    83        
    84         const errno_t err = usbhc_device_remove(exch, port->port_number);
    85         if (err)
    86                 port_log(error, port, "Failed to remove device: %s", str_error(err));
    87 
    88         usb_device_bus_exchange_end(exch);
    89 }
    90 
    91 
    92 static usb_speed_t get_port_speed(usb_hub_port_t *port, uint32_t status)
    93 {
    94         assert(port);
    95         assert(port->hub);
    96 
    97         return usb_port_speed(port->hub->speed, status);
    98 }
    99 
    100 /**
    101  * Routine for adding a new device in USB2.
    102  */
    103 static errno_t enumerate_device_usb2(usb_hub_port_t *port, async_exch_t *exch)
    104 {
    105         errno_t err;
    106 
    107         port_log(debug, port, "Requesting default address.");
    108         err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
    109         if (err != EOK) {
    110                 port_log(error, port, "Failed to reserve default address: %s", str_error(err));
    111                 return err;
    112         }
    113 
    114         /* Reservation of default address could have blocked */
    115         if (port->base.state != PORT_CONNECTING)
    116                 goto out_address;
    117 
    118         port_log(debug, port, "Resetting port.");
    119         if ((err = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_RESET))) {
    120                 port_log(warning, port, "Port reset request failed: %s", str_error(err));
    121                 goto out_address;
    122         }
    123 
    124         if ((err = usb_port_wait_for_enabled(&port->base))) {
    125                 port_log(error, port, "Failed to reset port: %s", str_error(err));
    126                 goto out_address;
    127         }
    128 
    129         port_log(debug, port, "Enumerating device.");
    130         if ((err = usbhc_device_enumerate(exch, port->port_number, port->speed))) {
    131                 port_log(error, port, "Failed to enumerate device: %s", str_error(err));
    132                 /* Disable the port */
    133                 usb_hub_clear_port_feature(port->hub, port->port_number, USB2_HUB_FEATURE_PORT_ENABLE);
    134                 goto out_address;
    135         }
    136 
    137         port_log(debug, port, "Device enumerated");
    138 
    139 out_address:
    140         usb_hub_release_default_address(port->hub, exch);
    141         return err;
    142 }
    143 
    144 /**
    145  * Routine for adding a new device in USB 3.
    146  */
    147 static errno_t enumerate_device_usb3(usb_hub_port_t *port, async_exch_t *exch)
    148 {
    149         errno_t err;
    150 
    151         port_log(debug, port, "Issuing a warm reset.");
    152         if ((err = usb_hub_set_port_feature(port->hub, port->port_number, USB3_HUB_FEATURE_BH_PORT_RESET))) {
    153                 port_log(warning, port, "Port reset request failed: %s", str_error(err));
    154                 return err;
    155         }
    156 
    157         if ((err = usb_port_wait_for_enabled(&port->base))) {
    158                 port_log(error, port, "Failed to reset port: %s", str_error(err));
    159                 return err;
    160         }
    161 
    162         port_log(debug, port, "Enumerating device.");
    163         if ((err = usbhc_device_enumerate(exch, port->port_number, port->speed))) {
    164                 port_log(error, port, "Failed to enumerate device: %s", str_error(err));
    165                 return err;
    166         }
    167 
    168         port_log(debug, port, "Device enumerated");
     49/** Information for fibril for device discovery. */
     50struct add_device_phase1 {
     51        usb_hub_dev_t *hub;
     52        usb_hub_port_t *port;
     53        usb_speed_t speed;
     54};
     55
     56static errno_t usb_hub_port_device_gone(usb_hub_port_t *port, usb_hub_dev_t *hub);
     57static void usb_hub_port_reset_completed(usb_hub_port_t *port,
     58    usb_hub_dev_t *hub, usb_port_status_t status);
     59static errno_t get_port_status(usb_hub_port_t *port, usb_port_status_t *status);
     60static errno_t add_device_phase1_worker_fibril(void *arg);
     61static errno_t create_add_device_fibril(usb_hub_port_t *port, usb_hub_dev_t *hub,
     62    usb_speed_t speed);
     63
     64errno_t usb_hub_port_fini(usb_hub_port_t *port, usb_hub_dev_t *hub)
     65{
     66        assert(port);
     67        if (port->device_attached)
     68                return usb_hub_port_device_gone(port, hub);
    16969        return EOK;
    17070}
    17171
    172 static errno_t enumerate_device(usb_port_t *port_base)
    173 {
    174         usb_hub_port_t *port = get_hub_port(port_base);
    175 
    176         port_log(debug, port, "Setting up new device.");
    177         async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
    178         if (!exch) {
    179                 port_log(error, port, "Failed to create exchange.");
    180                 return ENOMEM;
    181         }
    182 
    183         const errno_t err = port->hub->speed == USB_SPEED_SUPER
    184                 ? enumerate_device_usb3(port, exch)
    185                 : enumerate_device_usb2(port, exch);
    186 
    187         usb_device_bus_exchange_end(exch);
    188         return err;
    189 }
    190 
    191 static void port_changed_connection(usb_hub_port_t *port, usb_port_status_t status)
    192 {
    193         const bool connected = !!(status & USB_HUB_PORT_STATUS_CONNECTION);
    194         port_log(debug, port, "Connection change: device %s.", connected ? "attached" : "removed");
    195 
    196         if (connected) {
    197                 usb_port_connected(&port->base, &enumerate_device);
    198         } else {
    199                 usb_port_disabled(&port->base, &remove_device);
    200         }
    201 }
    202 
    203 static void port_changed_enabled(usb_hub_port_t *port, usb_port_status_t status)
    204 {
    205         const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
    206         if (enabled) {
    207                 port_log(warning, port, "Port unexpectedly changed to enabled.");
    208         } else {
    209                 usb_port_disabled(&port->base, &remove_device);
    210         }
    211 }
    212 
    213 static void port_changed_overcurrent(usb_hub_port_t *port, usb_port_status_t status)
    214 {
    215         const bool overcurrent = !!(status & USB_HUB_PORT_STATUS_OC);
    216 
    217         /* According to the USB specs:
    218          * 11.13.5 Over-current Reporting and Recovery
    219          * Hub device is responsible for putting port in power off
    220          * mode. USB system software is responsible for powering port
    221          * back on when the over-current condition is gone */
    222 
    223         usb_port_disabled(&port->base, &remove_device);
    224 
    225         if (!overcurrent) {
    226                 const errno_t err = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_POWER);
    227                 if (err)
    228                         port_log(error, port, "Failed to set port power after OC: %s.", str_error(err));
    229         }
    230 }
    231 
    232 static void port_changed_reset(usb_hub_port_t *port, usb_port_status_t status)
    233 {
    234         const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLE);
    235 
    236         if (enabled) {
    237                 port->speed = get_port_speed(port, status);
    238                 usb_port_enabled(&port->base);
    239         } else
    240                 usb_port_disabled(&port->base, &remove_device);
    241 }
    242 
    243 typedef void (*change_handler_t)(usb_hub_port_t *, usb_port_status_t);
    244 
    245 static void check_port_change(usb_hub_port_t *port, usb_port_status_t *status,
    246     change_handler_t handler, usb_port_status_t mask, usb_hub_class_feature_t feature)
    247 {
    248         if ((*status & mask) == 0)
    249                 return;
    250 
    251         /* Clear the change so it won't come again */
    252         usb_hub_clear_port_feature(port->hub, port->port_number, feature);
    253 
    254         if (handler)
    255                 handler(port, *status);
    256 
    257         /* Mark the change as resolved */
    258         *status &= ~mask;
     72/**
     73 * Clear feature on hub port.
     74 *
     75 * @param port Port structure.
     76 * @param feature Feature selector.
     77 * @return Operation result
     78 */
     79errno_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 */
     101errno_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 */
     121void 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);
    259129}
    260130
     
    266136 * @param hub hub representation
    267137 */
    268 void usb_hub_port_process_interrupt(usb_hub_port_t *port)
    269 {
    270         assert(port);
    271         port_log(debug2, port, "Interrupt.");
     138void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_dev_t *hub)
     139{
     140        assert(port);
     141        assert(hub);
     142        usb_log_debug2("(%p-%u): Interrupt.\n", hub, port->port_number);
    272143
    273144        usb_port_status_t status = 0;
    274         const errno_t err = usb_hub_get_port_status(port->hub, port->port_number, &status);
    275         if (err != EOK) {
    276                 port_log(error, port, "Failed to get port status: %s.", str_error(err));
     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));
    277149                return;
    278150        }
    279151
    280         check_port_change(port, &status, &port_changed_connection,
    281             USB_HUB_PORT_STATUS_C_CONNECTION, USB_HUB_FEATURE_C_PORT_CONNECTION);
    282 
    283         check_port_change(port, &status, &port_changed_overcurrent,
    284             USB_HUB_PORT_STATUS_C_OC, USB_HUB_FEATURE_C_PORT_OVER_CURRENT);
    285 
    286         check_port_change(port, &status, &port_changed_reset,
    287             USB_HUB_PORT_STATUS_C_RESET, USB_HUB_FEATURE_C_PORT_RESET);
    288 
    289         if (port->hub->speed <= USB_SPEED_HIGH) {
    290                 check_port_change(port, &status, &port_changed_enabled,
    291                     USB2_HUB_PORT_STATUS_C_ENABLE, USB2_HUB_FEATURE_C_PORT_ENABLE);
     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 */
     262errno_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 */
     285void 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);
    292297        } else {
    293                 check_port_change(port, &status, &port_changed_reset,
    294                     USB3_HUB_PORT_STATUS_C_BH_RESET, USB3_HUB_FEATURE_C_BH_PORT_RESET);
    295 
    296                 check_port_change(port, &status, NULL,
    297                     USB3_HUB_PORT_STATUS_C_LINK_STATE, USB3_HUB_FEATURE_C_PORT_LINK_STATE);
    298         }
    299 
    300         /* Check for changes we ignored */
    301         if (status & 0xffff0000) {
    302                 port_log(debug, port, "Port status change igored. Status: %#08" PRIx32, status);
    303         }
    304 }
    305 
     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 */
     319static 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
     353static 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 */
     385errno_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        }
     456out:
     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 */
     477static 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}
    306503
    307504/**
Note: See TracChangeset for help on using the changeset viewer.