diff options
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c7e3c7142b9d..9d6bb3d730c4 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c | |||
@@ -284,9 +284,38 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, | |||
284 | inc_deq(xhci, xhci->cmd_ring, false); | 284 | inc_deq(xhci, xhci->cmd_ring, false); |
285 | } | 285 | } |
286 | 286 | ||
287 | static void handle_port_status(struct xhci_hcd *xhci, | ||
288 | union xhci_trb *event) | ||
289 | { | ||
290 | u32 port_id; | ||
291 | |||
292 | /* Port status change events always have a successful completion code */ | ||
293 | if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { | ||
294 | xhci_warn(xhci, "WARN: xHC returned failed port status event\n"); | ||
295 | xhci->error_bitmask |= 1 << 8; | ||
296 | } | ||
297 | /* FIXME: core doesn't care about all port link state changes yet */ | ||
298 | port_id = GET_PORT_ID(event->generic.field[0]); | ||
299 | xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id); | ||
300 | |||
301 | /* Update event ring dequeue pointer before dropping the lock */ | ||
302 | inc_deq(xhci, xhci->event_ring, true); | ||
303 | set_hc_event_deq(xhci); | ||
304 | |||
305 | spin_unlock(&xhci->lock); | ||
306 | /* Pass this up to the core */ | ||
307 | usb_hcd_poll_rh_status(xhci_to_hcd(xhci)); | ||
308 | spin_lock(&xhci->lock); | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * This function handles all OS-owned events on the event ring. It may drop | ||
313 | * xhci->lock between event processing (e.g. to pass up port status changes). | ||
314 | */ | ||
287 | void handle_event(struct xhci_hcd *xhci) | 315 | void handle_event(struct xhci_hcd *xhci) |
288 | { | 316 | { |
289 | union xhci_trb *event; | 317 | union xhci_trb *event; |
318 | int update_ptrs = 1; | ||
290 | 319 | ||
291 | if (!xhci->event_ring || !xhci->event_ring->dequeue) { | 320 | if (!xhci->event_ring || !xhci->event_ring->dequeue) { |
292 | xhci->error_bitmask |= 1 << 1; | 321 | xhci->error_bitmask |= 1 << 1; |
@@ -301,18 +330,24 @@ void handle_event(struct xhci_hcd *xhci) | |||
301 | return; | 330 | return; |
302 | } | 331 | } |
303 | 332 | ||
304 | /* FIXME: Only handles command completion events. */ | 333 | /* FIXME: Handle more event types. */ |
305 | switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) { | 334 | switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) { |
306 | case TRB_TYPE(TRB_COMPLETION): | 335 | case TRB_TYPE(TRB_COMPLETION): |
307 | handle_cmd_completion(xhci, &event->event_cmd); | 336 | handle_cmd_completion(xhci, &event->event_cmd); |
308 | break; | 337 | break; |
338 | case TRB_TYPE(TRB_PORT_STATUS): | ||
339 | handle_port_status(xhci, event); | ||
340 | update_ptrs = 0; | ||
341 | break; | ||
309 | default: | 342 | default: |
310 | xhci->error_bitmask |= 1 << 3; | 343 | xhci->error_bitmask |= 1 << 3; |
311 | } | 344 | } |
312 | 345 | ||
313 | /* Update SW and HC event ring dequeue pointer */ | 346 | if (update_ptrs) { |
314 | inc_deq(xhci, xhci->event_ring, true); | 347 | /* Update SW and HC event ring dequeue pointer */ |
315 | set_hc_event_deq(xhci); | 348 | inc_deq(xhci, xhci->event_ring, true); |
349 | set_hc_event_deq(xhci); | ||
350 | } | ||
316 | /* Are there more items on the event ring? */ | 351 | /* Are there more items on the event ring? */ |
317 | handle_event(xhci); | 352 | handle_event(xhci); |
318 | } | 353 | } |