Changeset a9fcd73 in mainline
- Timestamp:
- 2018-01-18T02:05:35Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 5bccec3
- Parents:
- 94f8c363
- Location:
- uspace/drv/bus/usb/xhci
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/xhci/hc.c
r94f8c363 ra9fcd73 109 109 return ENOTSUP; 110 110 } 111 112 unsigned offset = XHCI_REG_RD(ec, XHCI_EC_SP_CP_OFF); 113 unsigned count = XHCI_REG_RD(ec, XHCI_EC_SP_CP_COUNT); 114 xhci_rh_set_ports_protocol(&hc->rh, offset, count, major); 111 115 112 116 // "Implied" speed … … 229 233 hc->ist = (ist & 0x10 >> 1) * (ist & 0xf); 230 234 231 if ((err = hc_parse_ec(hc))) { 232 pio_disable(hc->reg_base, RNGSZ(hc->mmio_range)); 233 return err; 234 } 235 236 return EOK; 235 if ((err = xhci_rh_init(&hc->rh, hc))) 236 goto err_pio; 237 238 if ((err = hc_parse_ec(hc))) 239 goto err_rh; 240 241 return EOK; 242 243 err_rh: 244 xhci_rh_fini(&hc->rh); 245 err_pio: 246 pio_disable(hc->reg_base, RNGSZ(hc->mmio_range)); 247 return err; 237 248 } 238 249 … … 260 271 goto err_cmd; 261 272 262 if ((err = xhci_rh_init(&hc->rh, hc))) 263 goto err_bus; 264 265 return EOK; 266 267 err_bus: 268 xhci_bus_fini(&hc->bus); 273 return EOK; 274 269 275 err_cmd: 270 276 xhci_fini_commands(hc); -
uspace/drv/bus/usb/xhci/rh.c
r94f8c363 ra9fcd73 42 42 #include <usb/host/dma_buffer.h> 43 43 #include <usb/host/hcd.h> 44 #include <usb/port.h> 44 45 45 46 #include "debug.h" … … 61 62 XHCI_REG_MASK(XHCI_PORT_CEC); 62 63 64 typedef 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 63 72 /** 64 73 * Initialize the roothub subsystem. … … 71 80 rh->hc = hc; 72 81 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; 74 85 75 86 const int err = bus_device_init(&rh->device.base, &rh->hc->bus.base); 76 if (err) 87 if (err) { 88 free(rh->ports); 77 89 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 } 78 97 79 98 /* Initialize route string */ … … 81 100 rh->device.tier = 0; 82 101 83 fibril_mutex_initialize(&rh->event_guard);84 fibril_condvar_initialize(&rh->event_ready);85 fibril_condvar_initialize(&rh->event_handled);86 87 102 return EOK; 88 103 } … … 94 109 { 95 110 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); 97 113 return EOK; 98 114 } 99 115 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); 116 static rh_port_t *get_rh_port(usb_port_t *port) 117 { 118 assert(port); 119 return (rh_port_t *) port; 154 120 } 155 121 … … 158 124 * a virtual usbhub device for RH, this routine is called for devices directly. 159 125 */ 160 static int rh_ setup_device(xhci_rh_t *rh, uint8_t port_id)126 static int rh_enumerate_device(usb_port_t *usb_port) 161 127 { 162 128 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); 174 139 if (!dev) { 175 140 usb_log_error("Failed to create USB device function."); … … 177 142 } 178 143 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; 185 149 186 150 if ((err = bus_device_enumerate(dev))) { … … 195 159 if ((err = ddf_fun_bind(dev->fun))) { 196 160 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)); 198 162 goto err_usb_dev; 199 163 } 200 201 rh->devices_by_port[port_id - 1] = xhci_dev;202 164 203 165 return EOK; … … 205 167 err_usb_dev: 206 168 hcd_ddf_fun_destroy(dev); 169 port->device = NULL; 207 170 return err; 208 171 } 209 172 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 and224 * 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 261 173 /** 262 174 * Deal with a detached device. 263 175 */ 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); 176 static 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); 277 186 278 187 /* 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; 318 189 } 319 190 … … 323 194 void xhci_rh_handle_port_change(xhci_rh_t *rh, uint8_t port_id) 324 195 { 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(®s->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) { 331 201 /* 332 202 * The PED bit in xHCI has RW1C semantics, which means that … … 334 204 * standard mechanisms of register handling fails here. 335 205 */ 336 uint32_t portsc = XHCI_REG_RD_FIELD(®s->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(®s->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)) { 342 209 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)); 346 213 if (connected) { 347 handle_in_fibril(rh, port_id, handle_connected_device);214 usb_port_connected(&port->base, &rh_enumerate_device); 348 215 } else { 349 handle_in_fibril(rh, port_id, handle_disconnected_device);216 usb_port_disabled(&port->base, &rh_remove_device); 350 217 } 351 218 } 352 219 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); 358 236 359 237 /* Make sure that PSCEG is 0 before exiting the loop. */ 360 events = XHCI_REG_RD_FIELD(®s->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 242 void 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; 375 246 } 376 247 -
uspace/drv/bus/usb/xhci/rh.h
r94f8c363 ra9fcd73 58 58 typedef struct hcd_roothub hcd_roothub_t; 59 59 typedef struct xhci_bus xhci_bus_t; 60 typedef struct rh_ event rh_event_t;60 typedef struct rh_port rh_port_t; 61 61 62 62 /* XHCI root hub instance */ … … 68 68 xhci_device_t device; 69 69 70 /* * Interrupt transfer waiting for an actual interrupt to occur*/71 usb_transfer_batch_t *unfinished_interrupt_transfer;70 /* Number of hub ports. */ 71 size_t max_ports; 72 72 73 /* Number of hub ports. */ 74 uint8_t max_ports; 75 76 /* Device pointers connected to RH ports or NULL. (size is `max_ports`) */ 77 xhci_device_t **devices_by_port; 78 79 /* Roothub events. */ 80 fibril_mutex_t event_guard; 81 fibril_condvar_t event_ready, event_handled; 82 unsigned event_readers_waiting; 83 rh_event_t *event; 73 /* Array of port structures. (size is `max_ports`) */ 74 rh_port_t *ports; 84 75 } xhci_rh_t; 85 76 86 77 int xhci_rh_init(xhci_rh_t *, xhci_hc_t *); 87 78 int xhci_rh_fini(xhci_rh_t *); 88 const xhci_port_speed_t *xhci_rh_get_port_speed(xhci_rh_t *, uint8_t);89 79 90 80 void xhci_rh_handle_port_change(xhci_rh_t *, uint8_t); 81 void xhci_rh_set_ports_protocol(xhci_rh_t *, unsigned, unsigned, unsigned); 91 82 92 83 #endif
Note:
See TracChangeset
for help on using the changeset viewer.