Changeset fb154e13 in mainline


Ignore:
Timestamp:
2018-01-12T22:48:57Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
0e7380f
Parents:
7242ba21
Message:

xhci: revised roothub event handling

According to the xHCI specification, Port Status Change Event is
generated per port. Also, the PCD bit can be safely ignored. Added
mutual exclusion to roothub event handling to avoid duplicate device
adding.

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

Legend:

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

    r7242ba21 rfb154e13  
    476476         * not cause an interrupt.
    477477         */
    478         xhci_rh_handle_port_change(&hc->rh);
     478        for (uint8_t port = 1; port <= hc->rh.max_ports; ++port)
     479                xhci_rh_handle_port_change(&hc->rh, port);
    479480
    480481        return EOK;
     
    508509}
    509510
     511static int handle_port_status_change_event(xhci_hc_t *hc, xhci_trb_t *trb)
     512{
     513        uint8_t port_id = XHCI_QWORD_EXTRACT(trb->parameter, 31, 24);
     514        usb_log_debug("Port status change event detected for port %u.", port_id);
     515        xhci_rh_handle_port_change(&hc->rh, port_id);
     516        return EOK;
     517}
     518
    510519typedef int (*event_handler) (xhci_hc_t *, xhci_trb_t *trb);
    511520
    512521static event_handler event_handlers [] = {
    513522        [XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT] = &xhci_handle_command_completion,
    514         [XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &xhci_rh_handle_port_status_change_event,
     523        [XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &handle_port_status_change_event,
    515524        [XHCI_TRB_TYPE_TRANSFER_EVENT] = &xhci_handle_transfer_event,
    516525        [XHCI_TRB_TYPE_MFINDEX_WRAP_EVENT] = &xhci_handle_mfindex_wrap_event,
     
    578587        status = xhci2host(32, status);
    579588
    580         if (status & XHCI_REG_MASK(XHCI_OP_PCD)) {
    581                 usb_log_debug2("Root hub interrupt.");
    582                 xhci_rh_handle_port_change(&hc->rh);
    583                 status &= ~XHCI_REG_MASK(XHCI_OP_PCD);
    584         }
    585 
    586589        if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
    587590                usb_log_error("Host controller error occured. Bad things gonna happen...");
     
    599602                status &= ~XHCI_REG_MASK(XHCI_OP_SRE);
    600603        }
     604
     605        /* According to Note on p. 302, we may safely ignore the PCD bit. */
     606        status &= ~XHCI_REG_MASK(XHCI_OP_PCD);
    601607
    602608        if (status) {
  • uspace/drv/bus/usb/xhci/rh.c

    r7242ba21 rfb154e13  
    9898}
    9999
    100 static int rh_event_wait_timeout(xhci_rh_t *rh, suseconds_t timeout)
    101 {
     100static int rh_event_wait_timeout(xhci_rh_t *rh, uint8_t port_id, uint32_t mask, suseconds_t timeout)
     101{
     102        int r;
    102103        assert(fibril_mutex_is_locked(&rh->event_guard));
    103104
    104105        ++rh->event_readers_waiting;
    105         const int r = fibril_condvar_wait_timeout(&rh->event_ready, &rh->event_guard, timeout);
     106
     107        do {
     108                r = fibril_condvar_wait_timeout(&rh->event_ready, &rh->event_guard, timeout);
     109                if (r != EOK)
     110                        break;
     111        } while (rh->event.port_id != port_id || (rh->event.events & mask) != mask);
     112
     113        if (r == EOK)
     114                rh->event.events &= ~mask;
     115
    106116        --rh->event_readers_waiting;
    107117        if (--rh->event_readers_to_go == 0)
    108118                fibril_condvar_broadcast(&rh->event_handled);
     119
    109120        return r;
    110121}
     
    112123static void rh_event_run_handlers(xhci_rh_t *rh)
    113124{
    114         fibril_mutex_lock(&rh->event_guard);
     125        assert(fibril_mutex_is_locked(&rh->event_guard));
    115126        assert(rh->event_readers_to_go == 0);
    116127
     
    119130        while (rh->event_readers_to_go)
    120131                fibril_condvar_wait(&rh->event_handled, &rh->event_guard);
    121         fibril_mutex_unlock(&rh->event_guard);
    122132}
    123133
     
    178188static int rh_port_reset_sync(xhci_rh_t *rh, uint8_t port_id)
    179189{
    180         int r;
    181190        xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port_id - 1];
    182191
    183192        fibril_mutex_lock(&rh->event_guard);
    184193        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        const int r = rh_event_wait_timeout(rh, port_id, XHCI_REG_MASK(XHCI_PORT_PRC), 0);
    194195        fibril_mutex_unlock(&rh->event_guard);
    195196
     
    261262        /* Remove device from XHCI bus. */
    262263        bus_device_gone(&dev->base);
    263 
    264         return EOK;
    265 }
    266 
    267 /**
    268  * Handle an incoming Port Change Detected Event.
    269  */
    270 int xhci_rh_handle_port_status_change_event(xhci_hc_t *hc, xhci_trb_t *trb)
    271 {
    272         uint8_t port_id = XHCI_QWORD_EXTRACT(trb->parameter, 31, 24);
    273         usb_log_debug("Port status change event detected for port %u.", port_id);
    274 
    275         /**
    276          * We can't be sure that the port change this event announces is the
    277          * only port change that happened (see section 4.19.2 of the xHCI
    278          * specification). Therefore, we just check all ports for changes.
    279          */
    280         xhci_rh_handle_port_change(&hc->rh);
    281264
    282265        return EOK;
     
    317300
    318301/**
    319  * Handle all changes on all ports.
    320  */
    321 void xhci_rh_handle_port_change(xhci_rh_t *rh)
    322 {
    323         for (uint8_t i = 1; i <= rh->max_ports; ++i) {
    324                 xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[i - 1];
    325 
    326                 uint32_t events = XHCI_REG_RD_FIELD(&regs->portsc, 32);
    327                 XHCI_REG_WR_FIELD(&regs->portsc, events, 32);
    328 
    329                 events &= port_change_mask;
     302 * Handle all changes on specified port.
     303 */
     304void xhci_rh_handle_port_change(xhci_rh_t *rh, uint8_t port_id)
     305{
     306        fibril_mutex_lock(&rh->event_guard);
     307        xhci_port_regs_t * const regs = &rh->hc->op_regs->portrs[port_id - 1];
     308
     309        uint32_t events = XHCI_REG_RD_FIELD(&regs->portsc, 32) & port_change_mask;
     310
     311        while (events) {
     312                XHCI_REG_SET_FIELD(&regs->portsc, events, 32);
    330313
    331314                if (events & XHCI_REG_MASK(XHCI_PORT_CSC)) {
    332                         usb_log_info("Connected state changed on port %u.", i);
     315                        usb_log_info("Connected state changed on port %u.", port_id);
    333316                        events &= ~XHCI_REG_MASK(XHCI_PORT_CSC);
    334317
    335318                        bool connected = XHCI_REG_RD(regs, XHCI_PORT_CCS);
    336319                        if (connected) {
    337                                 handle_in_fibril(rh, i, handle_connected_device);
     320                                handle_in_fibril(rh, port_id, handle_connected_device);
    338321                        } else {
    339                                 handle_in_fibril(rh, i, handle_disconnected_device);
     322                                handle_in_fibril(rh, port_id, handle_disconnected_device);
    340323                        }
    341324                }
    342325
    343326                if (events != 0) {
    344                         rh->event.port_id = i;
     327                        rh->event.port_id = port_id;
    345328                        rh->event.events = events;
    346329                        rh_event_run_handlers(rh);
    347330                }
    348         }
    349 
    350         /**
    351          * Theory:
    352          *
    353          * Although more events could have happened while processing, the PCD
    354          * bit in USBSTS will be set on every change. Because the PCD is
    355          * cleared even before the interrupt is cleared, it is safe to assume
    356          * that this handler will be called again.
    357          *
    358          * But because we could have handled the event in previous run of this
    359          * handler, it is not an error when no event is detected.
    360          *
    361          * Reality:
    362          *
    363          * The PCD bit is never set. TODO Check why the interrupt never carries
    364          * the PCD flag. Possibly repeat the checking until we're sure the
    365          * PSCEG is 0 - check section 4.19.2 of the xHCI spec.
    366          */
     331
     332                if (rh->event.events != 0)
     333                        usb_log_debug("RH port %u change not handled: 0x%x", port_id, rh->event.events);
     334               
     335                /* Make sure that PSCEG is 0 before exiting the loop. */
     336                events = XHCI_REG_RD_FIELD(&regs->portsc, 32) & port_change_mask;
     337        }
     338
     339        fibril_mutex_unlock(&rh->event_guard);
    367340}
    368341
  • uspace/drv/bus/usb/xhci/rh.h

    r7242ba21 rfb154e13  
    9090const xhci_port_speed_t *xhci_rh_get_port_speed(xhci_rh_t *, uint8_t);
    9191
    92 int xhci_rh_handle_port_status_change_event(xhci_hc_t *, xhci_trb_t *);
    93 void xhci_rh_handle_port_change(xhci_rh_t *);
    94 
    95 int xhci_rh_address_device(xhci_rh_t *rh, device_t *dev, xhci_bus_t *bus);
     92void xhci_rh_handle_port_change(xhci_rh_t *, uint8_t);
    9693
    9794#endif
Note: See TracChangeset for help on using the changeset viewer.