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


Ignore:
Timestamp:
2018-01-16T03:45:38Z (7 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
c952abc4
Parents:
d2c3dcd
git-author:
Ondřej Hlavatý <aearsis@…> (2018-01-15 20:49:15)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-01-16 03:45:38)
Message:

usbhub: rewrite port handling

The state space of a usb hub port is a bit more complex than what was
there originally. Got rid of the active operations counting, and
replaced that with finite state machine. Fixes a lot of race conditions
and lack of synchronization when connect and disconnect events come very
fast.

File:
1 edited

Legend:

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

    rd2c3dcd rc4e84ed6  
    8686    usb_hub_status_t status);
    8787static void usb_hub_global_interrupt(const usb_hub_dev_t *hub_dev);
    88 static void usb_hub_polling_terminated_callback(usb_device_t *device,
    89     bool was_error, void *data);
    9088
    9189static bool usb_hub_polling_error_callback(usb_device_t *dev, int err_code, void *arg)
     
    9391        assert(dev);
    9492        assert(arg);
    95         usb_hub_dev_t *hub = arg;
    96 
    97         usb_log_error("Device %s polling error: %s", usb_device_get_name(dev),
    98             str_error(err_code));
    99 
    100         /* Continue polling until the device is about to be removed. */
    101         return hub->running;
     93
     94        usb_log_error("Device %s polling error: %s", usb_device_get_name(dev), str_error(err_code));
     95
     96        return true;
    10297}
    10398
     
    121116        }
    122117        hub_dev->usb_device = usb_dev;
    123         hub_dev->pending_ops_count = 0;
    124         hub_dev->running = false;
    125         fibril_mutex_initialize(&hub_dev->pending_ops_mutex);
    126         fibril_condvar_initialize(&hub_dev->pending_ops_cv);
    127118
    128119        /* Set hub's first configuration. (There should be only one) */
     
    178169        polling->buffer = malloc(polling->request_size);
    179170        polling->on_data = hub_port_changes_callback;
    180         polling->on_polling_end = usb_hub_polling_terminated_callback;
    181171        polling->on_error = usb_hub_polling_error_callback;
    182172        polling->arg = hub_dev;
     
    194184        }
    195185
    196         hub_dev->running = true;
    197186        usb_log_info("Controlling hub '%s' (%p: %zu ports).",
    198187            usb_device_get_name(hub_dev->usb_device), hub_dev,
     
    204193static int usb_hub_cleanup(usb_hub_dev_t *hub)
    205194{
    206         assert(!hub->running);
    207 
    208195        free(hub->polling.buffer);
    209196        usb_polling_fini(&hub->polling);
    210197
    211198        for (size_t port = 0; port < hub->port_count; ++port) {
    212                 const int ret = usb_hub_port_fini(&hub->ports[port], hub);
    213                 if (ret != EOK)
    214                         return ret;
     199                usb_hub_port_fini(&hub->ports[port]);
    215200        }
    216201        free(hub->ports);
     
    343328            descriptor.port_count);
    344329        hub_dev->port_count = descriptor.port_count;
     330        hub_dev->control_pipe = control_pipe;
    345331
    346332        hub_dev->ports = calloc(hub_dev->port_count, sizeof(usb_hub_port_t));
     
    350336
    351337        for (size_t port = 0; port < hub_dev->port_count; ++port) {
    352                 usb_hub_port_init(
    353                     &hub_dev->ports[port], port + 1, control_pipe);
     338                usb_hub_port_init(&hub_dev->ports[port], hub_dev, port + 1);
    354339        }
    355340
     
    370355        for (unsigned int port = 0; port < hub_dev->port_count; ++port) {
    371356                usb_log_debug("(%p): Powering port %u.", hub_dev, port);
    372                 const int ret = usb_hub_port_set_feature(
    373                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
     357                const int ret = usb_hub_set_port_feature(hub_dev, port, USB_HUB_FEATURE_PORT_POWER);
    374358
    375359                if (ret != EOK) {
     
    459443        /* Over-current condition is gone, it is safe to turn the ports on. */
    460444        for (size_t port = 0; port < hub_dev->port_count; ++port) {
    461                 const int ret = usb_hub_port_set_feature(
    462                     &hub_dev->ports[port], USB_HUB_FEATURE_PORT_POWER);
     445                const int ret = usb_hub_set_port_feature(hub_dev, port, USB_HUB_FEATURE_PORT_POWER);
    463446                if (ret != EOK) {
    464447                        usb_log_warning("(%p-%u): HUB OVER-CURRENT GONE: Cannot"
     
    470453                }
    471454        }
    472 
     455}
     456
     457/**
     458 * Set feature on the real hub port.
     459 *
     460 * @param port Port structure.
     461 * @param feature Feature selector.
     462 */
     463int usb_hub_set_port_feature(const usb_hub_dev_t *hub, size_t port_number, usb_hub_class_feature_t feature)
     464{
     465        assert(hub);
     466        const usb_device_request_setup_packet_t clear_request = {
     467                .request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE,
     468                .request = USB_DEVREQ_SET_FEATURE,
     469                .index = uint16_host2usb(port_number),
     470                .value = feature,
     471                .length = 0,
     472        };
     473        return usb_pipe_control_write(hub->control_pipe, &clear_request,
     474            sizeof(clear_request), NULL, 0);
     475}
     476
     477/**
     478 * Clear feature on the real hub port.
     479 *
     480 * @param port Port structure.
     481 * @param feature Feature selector.
     482 */
     483int usb_hub_clear_port_feature(const usb_hub_dev_t *hub, size_t port_number, usb_hub_class_feature_t feature)
     484{
     485        assert(hub);
     486        const usb_device_request_setup_packet_t clear_request = {
     487                .request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE,
     488                .request = USB_DEVREQ_CLEAR_FEATURE,
     489                .value = feature,
     490                .index = uint16_host2usb(port_number),
     491                .length = 0,
     492        };
     493        return usb_pipe_control_write(hub->control_pipe,
     494            &clear_request, sizeof(clear_request), NULL, 0);
     495}
     496
     497/**
     498 * Retrieve port status.
     499 *
     500 * @param[in] port Port structure
     501 * @param[out] status Where to store the port status.
     502 * @return Error code.
     503 */
     504int usb_hub_get_port_status(const usb_hub_dev_t *hub, size_t port_number, usb_port_status_t *status)
     505{
     506        assert(hub);
     507        assert(status);
     508
     509        /* USB hub specific GET_PORT_STATUS request. See USB Spec 11.16.2.6
     510         * Generic GET_STATUS request cannot be used because of the difference
     511         * in status data size (2B vs. 4B)*/
     512        const usb_device_request_setup_packet_t request = {
     513                .request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS,
     514                .request = USB_HUB_REQUEST_GET_STATUS,
     515                .value = 0,
     516                .index = uint16_host2usb(port_number),
     517                .length = sizeof(usb_port_status_t),
     518        };
     519        size_t recv_size;
     520
     521        const int rc = usb_pipe_control_read(hub->control_pipe,
     522            &request, sizeof(usb_device_request_setup_packet_t),
     523            status, sizeof(*status), &recv_size);
     524        if (rc != EOK)
     525                return rc;
     526
     527        if (recv_size != sizeof(*status))
     528                return ELIMIT;
     529
     530        return EOK;
    473531}
    474532
     
    545603
    546604/**
    547  * callback called from hub polling fibril when the fibril terminates
    548  *
    549  * Does not perform cleanup, just marks the hub as not running.
    550  * @param device usb device afected
    551  * @param was_error indicates that the fibril is stoped due to an error
    552  * @param data pointer to usb_hub_dev_t structure
    553  */
    554 static void usb_hub_polling_terminated_callback(usb_device_t *device,
    555     bool was_error, void *data)
    556 {
    557         usb_hub_dev_t *hub = data;
    558         assert(hub);
    559 
    560         fibril_mutex_lock(&hub->pending_ops_mutex);
    561 
    562         /* The device is dead. However there might be some pending operations
    563          * that we need to wait for.
    564          * One of them is device adding in progress.
    565          * The respective fibril is probably waiting for status change
    566          * in port reset (port enable) callback.
    567          * Such change would never come (otherwise we would not be here).
    568          * Thus, we would flush all pending port resets.
    569          */
    570         if (hub->pending_ops_count > 0) {
    571                 for (size_t port = 0; port < hub->port_count; ++port) {
    572                         usb_hub_port_reset_fail(&hub->ports[port]);
    573                 }
    574         }
    575         /* And now wait for them. */
    576         while (hub->pending_ops_count > 0) {
    577                 fibril_condvar_wait(&hub->pending_ops_cv,
    578                     &hub->pending_ops_mutex);
    579         }
    580         fibril_mutex_unlock(&hub->pending_ops_mutex);
    581         hub->running = false;
    582 }
    583 
    584 /**
    585605 * @}
    586606 */
Note: See TracChangeset for help on using the changeset viewer.