Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset 49e62998 in mainline


Ignore:
Timestamp:
2018-01-11T23:06:57Z (4 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master
Children:
50be3c6
Parents:
f3baab1
git-author:
Ondřej Hlavatý <aearsis@…> (2018-01-11 23:01:08)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-01-11 23:06:57)
Message:

xhci: defer roothub events processing

There are two new generic mechanisms, which are used in only one
instance.

First one is a simple wrapper for roothub event handlers that should run
in separate fibril. The usage can be seen at line 337, for example. The
mechanism just passes arguments to a newly created fibril.

The second is a bit more complex, and its purpose is to broadcast roothub
events to all fibrils that could wait for it. It's used to wait for port
reset complete at USB 2 ports. See rh_port_reset_sync.

Sorry for naming both mechanisms "rh_event", which could create
a confusion.

Location:
uspace/drv/bus/usb/xhci
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/rh.c

    rf3baab1 r49e62998  
    8181        rh->device.tier = 0;
    8282
     83        fibril_mutex_initialize(&rh->event_guard);
     84        fibril_condvar_initialize(&rh->event_ready);
     85        fibril_condvar_initialize(&rh->event_handled);
     86
    8387        return EOK;
    8488}
     
    9296        free(rh->devices_by_port);
    9397        return EOK;
     98}
     99
     100static int rh_event_wait_timeout(xhci_rh_t *rh, suseconds_t timeout)
     101{
     102        assert(fibril_mutex_is_locked(&rh->event_guard));
     103
     104        ++rh->event_readers_waiting;
     105        const int r = fibril_condvar_wait_timeout(&rh->event_ready, &rh->event_guard, timeout);
     106        --rh->event_readers_waiting;
     107        if (--rh->event_readers_to_go == 0)
     108                fibril_condvar_broadcast(&rh->event_handled);
     109        return r;
     110}
     111
     112static void rh_event_run_handlers(xhci_rh_t *rh)
     113{
     114        fibril_mutex_lock(&rh->event_guard);
     115        assert(rh->event_readers_to_go == 0);
     116
     117        rh->event_readers_to_go = rh->event_readers_waiting;
     118        fibril_condvar_broadcast(&rh->event_ready);
     119        while (rh->event_readers_to_go)
     120                fibril_condvar_wait(&rh->event_handled, &rh->event_guard);
     121        fibril_mutex_unlock(&rh->event_guard);
    94122}
    95123
     
    147175}
    148176
     177
     178static int rh_port_reset_sync(xhci_rh_t *rh, uint8_t port_id)
     179{
     180        int r;
     181        xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port_id - 1];
     182
     183        fibril_mutex_lock(&rh->event_guard);
     184        XHCI_REG_SET(regs, XHCI_PORT_PR, 1);
     185
     186        while (true) {
     187                r = rh_event_wait_timeout(rh, 0);
     188                if (r != EOK)
     189                        break;
     190                if (rh->event.port_id == port_id
     191                    && rh->event.events & XHCI_REG_MASK(XHCI_PORT_PRC))
     192                        break;
     193        }
     194        fibril_mutex_unlock(&rh->event_guard);
     195
     196        return r;
     197}
     198
    149199/**
    150200 * Handle a device connection. USB 3+ devices are set up directly, USB 2 and
     
    177227        else {
    178228                usb_log_debug("USB 2 device attached, issuing reset.");
    179                 xhci_rh_reset_port(rh, port_id);
     229                const int err = rh_port_reset_sync(rh, port_id);
     230                if (err)
     231                        return err;
     232
     233                rh_setup_device(rh, port_id);
    180234                return EOK;
    181235        }
     
    229283}
    230284
     285typedef int (*rh_event_handler_t)(xhci_rh_t *, uint8_t);
     286
     287typedef struct rh_event_args {
     288        xhci_rh_t *rh;
     289        uint8_t port_id;
     290        rh_event_handler_t handler;
     291} rh_event_args_t;
     292
     293static int rh_event_handler_fibril(void *arg) {
     294        rh_event_args_t *rh_args = arg;
     295        xhci_rh_t *rh = rh_args->rh;
     296        uint8_t port_id = rh_args->port_id;
     297        rh_event_handler_t handler = rh_args->handler;
     298
     299        free(rh_args);
     300
     301        return handler(rh, port_id);
     302}
     303
     304static fid_t handle_in_fibril(xhci_rh_t *rh, uint8_t port_id, rh_event_handler_t handler)
     305{
     306        rh_event_args_t *args = malloc(sizeof(*args));
     307        *args = (rh_event_args_t) {
     308                .rh = rh,
     309                .port_id = port_id,
     310                .handler = handler,
     311        };
     312
     313        const fid_t fid = fibril_create(rh_event_handler_fibril, args);
     314        fibril_add_ready(fid);
     315        return fid;
     316}
     317
    231318/**
    232319 * Handle all changes on all ports.
     
    248335                        bool connected = XHCI_REG_RD(regs, XHCI_PORT_CCS);
    249336                        if (connected) {
    250                                 handle_connected_device(rh, i);
     337                                handle_in_fibril(rh, i, handle_connected_device);
    251338                        } else {
    252                                 handle_disconnected_device(rh, i);
     339                                handle_in_fibril(rh, i, handle_disconnected_device);
    253340                        }
    254341                }
    255342
    256                 if (events & XHCI_REG_MASK(XHCI_PORT_PEC)) {
    257                         usb_log_info("Port enabled changed on port %u.", i);
    258                         events &= ~XHCI_REG_MASK(XHCI_PORT_PEC);
    259                 }
    260 
    261                 if (events & XHCI_REG_MASK(XHCI_PORT_WRC)) {
    262                         usb_log_info("Warm port reset on port %u completed.", i);
    263                         events &= ~XHCI_REG_MASK(XHCI_PORT_WRC);
    264                 }
    265 
    266                 if (events & XHCI_REG_MASK(XHCI_PORT_OCC)) {
    267                         usb_log_info("Over-current change on port %u.", i);
    268                         events &= ~XHCI_REG_MASK(XHCI_PORT_OCC);
    269                 }
    270 
    271                 if (events & XHCI_REG_MASK(XHCI_PORT_PRC)) {
    272                         usb_log_info("Port reset on port %u completed.", i);
    273                         events &= ~XHCI_REG_MASK(XHCI_PORT_PRC);
    274 
    275                         const xhci_port_speed_t *speed = xhci_rh_get_port_speed(rh, i);
    276                         if (speed->major != 3) {
    277                                 /* FIXME: We probably don't want to do this
    278                                  * every time USB2 port is reset. This is a
    279                                  * temporary workaround. */
    280                                 rh_setup_device(rh, i);
    281                         }
    282                 }
    283 
    284                 if (events & XHCI_REG_MASK(XHCI_PORT_PLC)) {
    285                         usb_log_info("Port link state changed on port %u.", i);
    286                         events &= ~XHCI_REG_MASK(XHCI_PORT_PLC);
    287                 }
    288 
    289                 if (events & XHCI_REG_MASK(XHCI_PORT_CEC)) {
    290                         usb_log_info("Port %u failed to configure link.", i);
    291                         events &= ~XHCI_REG_MASK(XHCI_PORT_CEC);
    292                 }
    293 
    294                 if (events) {
    295                         usb_log_warning("Port change (0x%08x) ignored on port %u.", events, i);
     343                if (events != 0) {
     344                        rh->event.port_id = i;
     345                        rh->event.events = events;
     346                        rh_event_run_handlers(rh);
    296347                }
    297348        }
     
    333384{
    334385        usb_log_debug2("Resetting port %u.", port);
    335         xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port-1];
    336         XHCI_REG_SET(regs, XHCI_PORT_PR, 1);
    337386
    338387        return EOK;
  • uspace/drv/bus/usb/xhci/rh.h

    rf3baab1 r49e62998  
    7575        /* Device pointers connected to RH ports or NULL. (size is `max_ports`) */
    7676        xhci_device_t **devices_by_port;
     77
     78        /* Roothub events. */
     79        fibril_mutex_t event_guard;
     80        fibril_condvar_t event_ready, event_handled;
     81        unsigned event_readers_waiting, event_readers_to_go;
     82        struct {
     83                uint8_t port_id;
     84                uint32_t events;
     85        } event;
    7786} xhci_rh_t;
    7887
     
    8089int xhci_rh_fini(xhci_rh_t *);
    8190const xhci_port_speed_t *xhci_rh_get_port_speed(xhci_rh_t *, uint8_t);
    82 int xhci_rh_reset_port(xhci_rh_t *, uint8_t);
    8391
    8492int xhci_rh_handle_port_status_change_event(xhci_hc_t *, xhci_trb_t *);
Note: See TracChangeset for help on using the changeset viewer.