Changeset a9fcd73 in mainline for uspace/drv/bus/usb/xhci/rh.c


Ignore:
Timestamp:
2018-01-18T02:05:35Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
5bccec3
Parents:
94f8c363
Message:

xhci: rewrite RH to use usb/port

File:
1 edited

Legend:

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

    r94f8c363 ra9fcd73  
    4242#include <usb/host/dma_buffer.h>
    4343#include <usb/host/hcd.h>
     44#include <usb/port.h>
    4445
    4546#include "debug.h"
     
    6162        XHCI_REG_MASK(XHCI_PORT_CEC);
    6263
     64typedef struct rh_port {
     65        usb_port_t base;
     66        xhci_rh_t *rh;
     67        uint8_t major;
     68        xhci_port_regs_t *regs;
     69        xhci_device_t *device;
     70} rh_port_t;
     71
    6372/**
    6473 * Initialize the roothub subsystem.
     
    7180        rh->hc = hc;
    7281        rh->max_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
    73         rh->devices_by_port = (xhci_device_t **) calloc(rh->max_ports, sizeof(xhci_device_t *));
     82        rh->ports = calloc(rh->max_ports, sizeof(rh_port_t));
     83        if (!rh->ports)
     84                return ENOMEM;
    7485
    7586        const int err = bus_device_init(&rh->device.base, &rh->hc->bus.base);
    76         if (err)
     87        if (err) {
     88                free(rh->ports);
    7789                return err;
     90        }
     91
     92        for (unsigned i = 0; i < rh->max_ports; i++) {
     93                usb_port_init(&rh->ports[i].base);
     94                rh->ports[i].rh = rh;
     95                rh->ports[i].regs = &rh->hc->op_regs->portrs[i];
     96        }
    7897
    7998        /* Initialize route string */
     
    81100        rh->device.tier = 0;
    82101
    83         fibril_mutex_initialize(&rh->event_guard);
    84         fibril_condvar_initialize(&rh->event_ready);
    85         fibril_condvar_initialize(&rh->event_handled);
    86 
    87102        return EOK;
    88103}
     
    94109{
    95110        assert(rh);
    96         free(rh->devices_by_port);
     111        for (unsigned i = 0; i < rh->max_ports; i++)
     112                usb_port_fini(&rh->ports[i].base);
    97113        return EOK;
    98114}
    99115
    100 typedef struct rh_event {
    101         uint8_t port_id;
    102         uint32_t events;
    103         unsigned readers_to_go;
    104 } rh_event_t;
    105 
    106 static int rh_event_wait_timeout(xhci_rh_t *rh, uint8_t port_id, uint32_t mask, suseconds_t timeout)
    107 {
    108         int r;
    109         assert(fibril_mutex_is_locked(&rh->event_guard));
    110 
    111         ++rh->event_readers_waiting;
    112 
    113         do {
    114                 r = fibril_condvar_wait_timeout(&rh->event_ready, &rh->event_guard, timeout);
    115                 if (r != EOK)
    116                         break;
    117 
    118                 assert(rh->event);
    119                 if (--rh->event->readers_to_go == 0)
    120                         fibril_condvar_broadcast(&rh->event_handled);
    121         } while (rh->event->port_id != port_id || (rh->event->events & mask) != mask);
    122 
    123         if (r == EOK)
    124                 rh->event->events &= ~mask;
    125 
    126         --rh->event_readers_waiting;
    127 
    128         return r;
    129 }
    130 
    131 static void rh_event_run_handlers(xhci_rh_t *rh, uint8_t port_id, uint32_t *events)
    132 {
    133         assert(fibril_mutex_is_locked(&rh->event_guard));
    134 
    135         /* There can be different event running already */
    136         while (rh->event)
    137                 fibril_condvar_wait(&rh->event_handled, &rh->event_guard);
    138 
    139         rh_event_t event = {
    140                 .port_id = port_id,
    141                 .events = *events,
    142                 .readers_to_go = rh->event_readers_waiting,
    143         };
    144 
    145         rh->event = &event;
    146         fibril_condvar_broadcast(&rh->event_ready);
    147         while (event.readers_to_go)
    148                 fibril_condvar_wait(&rh->event_handled, &rh->event_guard);
    149         *events = event.events;
    150         rh->event = NULL;
    151 
    152         /* Wake other threads potentially waiting to post their event */
    153         fibril_condvar_broadcast(&rh->event_handled);
     116static rh_port_t *get_rh_port(usb_port_t *port)
     117{
     118        assert(port);
     119        return (rh_port_t *) port;
    154120}
    155121
     
    158124 * a virtual usbhub device for RH, this routine is called for devices directly.
    159125 */
    160 static int rh_setup_device(xhci_rh_t *rh, uint8_t port_id)
     126static int rh_enumerate_device(usb_port_t *usb_port)
    161127{
    162128        int err;
    163         assert(rh);
    164         assert(rh->devices_by_port[port_id - 1] == NULL);
    165 
    166         if (!XHCI_REG_RD(&rh->hc->op_regs->portrs[port_id - 1], XHCI_PORT_PED)) {
    167                 usb_log_error("Cannot setup RH device: port is disabled.");
    168                 return EIO;
    169         }
    170 
    171         const xhci_port_speed_t *port_speed = xhci_rh_get_port_speed(rh, port_id);
    172 
    173         device_t *dev = hcd_ddf_fun_create(&rh->hc->base, port_speed->usb_speed);
     129        rh_port_t *port = get_rh_port(usb_port);
     130
     131        if (port->major <= 2) {
     132                /* USB ports for lower speeds needs their port reset first. */
     133                XHCI_REG_SET(port->regs, XHCI_PORT_PR, 1);
     134                if ((err = usb_port_wait_for_enabled(&port->base)))
     135                        return err;
     136        }
     137
     138        device_t *dev = hcd_ddf_fun_create(&port->rh->hc->base, port->base.speed);
    174139        if (!dev) {
    175140                usb_log_error("Failed to create USB device function.");
     
    177142        }
    178143
    179         dev->hub = &rh->device.base;
    180         dev->port = port_id;
    181 
    182         xhci_device_t *xhci_dev = xhci_device_get(dev);
    183         xhci_dev->usb3 = port_speed->major == 3;
    184         xhci_dev->rh_port = port_id;
     144        dev->hub = &port->rh->device.base;
     145        dev->port = port - port->rh->ports + 1;
     146
     147        port->device = xhci_device_get(dev);
     148        port->device->rh_port = dev->port;
    185149
    186150        if ((err = bus_device_enumerate(dev))) {
     
    195159        if ((err = ddf_fun_bind(dev->fun))) {
    196160                usb_log_error("Failed to register device " XHCI_DEV_FMT " DDF function: %s.",
    197                     XHCI_DEV_ARGS(*xhci_dev), str_error(err));
     161                    XHCI_DEV_ARGS(*port->device), str_error(err));
    198162                goto err_usb_dev;
    199163        }
    200 
    201         rh->devices_by_port[port_id - 1] = xhci_dev;
    202164
    203165        return EOK;
     
    205167err_usb_dev:
    206168        hcd_ddf_fun_destroy(dev);
     169        port->device = NULL;
    207170        return err;
    208171}
    209172
    210 
    211 static int rh_port_reset_sync(xhci_rh_t *rh, uint8_t port_id)
    212 {
    213         xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port_id - 1];
    214 
    215         fibril_mutex_lock(&rh->event_guard);
    216         XHCI_REG_SET(regs, XHCI_PORT_PR, 1);
    217         const int r = rh_event_wait_timeout(rh, port_id, XHCI_REG_MASK(XHCI_PORT_PRC), 0);
    218         fibril_mutex_unlock(&rh->event_guard);
    219         return r;
    220 }
    221 
    222 /**
    223  * Handle a device connection. USB 3+ devices are set up directly, USB 2 and
    224  * below first need to have their port reset.
    225  */
    226 static int handle_connected_device(xhci_rh_t *rh, uint8_t port_id)
    227 {
    228         xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port_id - 1];
    229 
    230         uint8_t link_state = XHCI_REG_RD(regs, XHCI_PORT_PLS);
    231         const xhci_port_speed_t *speed = xhci_rh_get_port_speed(rh, port_id);
    232 
    233         usb_log_info("Detected new %.4s%u.%u device on port %u.", speed->name, speed->major, speed->minor, port_id);
    234 
    235         if (speed->major == 3) {
    236                 if (link_state == 0) {
    237                         /* USB3 is automatically advanced to enabled. */
    238                         return rh_setup_device(rh, port_id);
    239                 }
    240                 else if (link_state == 5) {
    241                         /* USB 3 failed to enable. */
    242                         usb_log_error("USB 3 port couldn't be enabled.");
    243                         return EAGAIN;
    244                 }
    245                 else {
    246                         usb_log_error("USB 3 port is in invalid state %u.", link_state);
    247                         return EINVAL;
    248                 }
    249         }
    250         else {
    251                 usb_log_debug("USB 2 device attached, issuing reset.");
    252                 const int err = rh_port_reset_sync(rh, port_id);
    253                 if (err)
    254                         return err;
    255 
    256                 rh_setup_device(rh, port_id);
    257                 return EOK;
    258         }
    259 }
    260 
    261173/**
    262174 * Deal with a detached device.
    263175 */
    264 static int handle_disconnected_device(xhci_rh_t *rh, uint8_t port_id)
    265 {
    266         assert(rh);
    267 
    268         /* Find XHCI device by the port. */
    269         xhci_device_t *dev = rh->devices_by_port[port_id - 1];
    270         if (!dev) {
    271                 /* Must be extraneous call. */
    272                 return EOK;
    273         }
    274 
    275         usb_log_info("Device " XHCI_DEV_FMT " at port %u has been disconnected.",
    276             XHCI_DEV_ARGS(*dev), port_id);
     176static void rh_remove_device(usb_port_t *usb_port)
     177{
     178        rh_port_t *port = get_rh_port(usb_port);
     179
     180        assert(port->device);
     181        usb_log_info("Device " XHCI_DEV_FMT " at port %zu has been disconnected.",
     182            XHCI_DEV_ARGS(*port->device), port - port->rh->ports + 1);
     183
     184        /* Remove device from XHCI bus. */
     185        bus_device_gone(&port->device->base);
    277186
    278187        /* Mark the device as detached. */
    279         rh->devices_by_port[port_id - 1] = NULL;
    280 
    281         /* Remove device from XHCI bus. */
    282         bus_device_gone(&dev->base);
    283 
    284         return EOK;
    285 }
    286 
    287 typedef int (*rh_event_handler_t)(xhci_rh_t *, uint8_t);
    288 
    289 typedef struct rh_event_args {
    290         xhci_rh_t *rh;
    291         uint8_t port_id;
    292         rh_event_handler_t handler;
    293 } rh_event_args_t;
    294 
    295 static int rh_event_handler_fibril(void *arg) {
    296         rh_event_args_t *rh_args = arg;
    297         xhci_rh_t *rh = rh_args->rh;
    298         uint8_t port_id = rh_args->port_id;
    299         rh_event_handler_t handler = rh_args->handler;
    300 
    301         free(rh_args);
    302 
    303         return handler(rh, port_id);
    304 }
    305 
    306 static fid_t handle_in_fibril(xhci_rh_t *rh, uint8_t port_id, rh_event_handler_t handler)
    307 {
    308         rh_event_args_t *args = malloc(sizeof(*args));
    309         *args = (rh_event_args_t) {
    310                 .rh = rh,
    311                 .port_id = port_id,
    312                 .handler = handler,
    313         };
    314 
    315         const fid_t fid = fibril_create(rh_event_handler_fibril, args);
    316         fibril_add_ready(fid);
    317         return fid;
     188        port->device = NULL;
    318189}
    319190
     
    323194void xhci_rh_handle_port_change(xhci_rh_t *rh, uint8_t port_id)
    324195{
    325         fibril_mutex_lock(&rh->event_guard);
    326         xhci_port_regs_t * const regs = &rh->hc->op_regs->portrs[port_id - 1];
    327 
    328         uint32_t events = XHCI_REG_RD_FIELD(&regs->portsc, 32) & port_events_mask;
    329 
    330         while (events) {
     196        rh_port_t * const port = &rh->ports[port_id - 1];
     197
     198        uint32_t status = XHCI_REG_RD_FIELD(&port->regs->portsc, 32);
     199
     200        while (status & port_events_mask) {
    331201                /*
    332202                 * The PED bit in xHCI has RW1C semantics, which means that
     
    334204                 * standard mechanisms of register handling fails here.
    335205                 */
    336                 uint32_t portsc = XHCI_REG_RD_FIELD(&regs->portsc, 32);
    337                 portsc &= ~(port_events_mask | XHCI_REG_MASK(XHCI_PORT_PED)); // Clear events + PED
    338                 portsc |= events; // Add back events to assert them
    339                 XHCI_REG_WR_FIELD(&regs->portsc, portsc, 32);
    340 
    341                 if (events & XHCI_REG_MASK(XHCI_PORT_CSC)) {
     206                XHCI_REG_WR_FIELD(&port->regs->portsc, status & ~XHCI_REG_MASK(XHCI_PORT_PED), 32);
     207
     208                if (status & XHCI_REG_MASK(XHCI_PORT_CSC)) {
    342209                        usb_log_info("Connected state changed on port %u.", port_id);
    343                         events &= ~XHCI_REG_MASK(XHCI_PORT_CSC);
    344 
    345                         bool connected = XHCI_REG_RD(regs, XHCI_PORT_CCS);
     210                        status &= ~XHCI_REG_MASK(XHCI_PORT_CSC);
     211
     212                        bool connected = !!(status & XHCI_REG_MASK(XHCI_PORT_CCS));
    346213                        if (connected) {
    347                                 handle_in_fibril(rh, port_id, handle_connected_device);
     214                                usb_port_connected(&port->base, &rh_enumerate_device);
    348215                        } else {
    349                                 handle_in_fibril(rh, port_id, handle_disconnected_device);
     216                                usb_port_disabled(&port->base, &rh_remove_device);
    350217                        }
    351218                }
    352219
    353                 if (events != 0)
    354                         rh_event_run_handlers(rh, port_id, &events);
    355 
    356                 if (events != 0)
    357                         usb_log_debug("RH port %u change not handled: 0x%x", port_id, events);
     220                if (status & XHCI_REG_MASK(XHCI_PORT_PRC)) {
     221                        status &= ~XHCI_REG_MASK(XHCI_PORT_PRC);
     222                        bool enabled = !!(status & XHCI_REG_MASK(XHCI_PORT_PED));
     223
     224                        if (enabled) {
     225                                unsigned psiv = (status & XHCI_REG_MASK(XHCI_PORT_PS)) >> XHCI_REG_SHIFT(XHCI_PORT_PS);
     226                                const usb_speed_t speed = rh->hc->speeds[psiv].usb_speed;
     227                                usb_port_enabled(&port->base, speed);
     228                        } else {
     229                                usb_port_disabled(&port->base, &rh_remove_device);
     230                        }
     231                }
     232
     233                status &= port_events_mask;
     234                if (status != 0)
     235                        usb_log_debug("RH port %u change not handled: 0x%x", port_id, status);
    358236               
    359237                /* Make sure that PSCEG is 0 before exiting the loop. */
    360                 events = XHCI_REG_RD_FIELD(&regs->portsc, 32) & port_events_mask;
    361         }
    362 
    363         fibril_mutex_unlock(&rh->event_guard);
    364 }
    365 
    366 /**
    367  * Get a port speed for a given port id.
    368  */
    369 const xhci_port_speed_t *xhci_rh_get_port_speed(xhci_rh_t *rh, uint8_t port)
    370 {
    371         xhci_port_regs_t *port_regs = &rh->hc->op_regs->portrs[port - 1];
    372 
    373         unsigned psiv = XHCI_REG_RD(port_regs, XHCI_PORT_PS);
    374         return &rh->hc->speeds[psiv];
     238                status = XHCI_REG_RD_FIELD(&port->regs->portsc, 32);
     239        }
     240}
     241
     242void xhci_rh_set_ports_protocol(xhci_rh_t *rh, unsigned offset, unsigned count, unsigned major)
     243{
     244        for (unsigned i = offset; i < offset + count; i++)
     245                rh->ports[i - 1].major = major;
    375246}
    376247
Note: See TracChangeset for help on using the changeset viewer.