aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c43
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
287static 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 */
287void handle_event(struct xhci_hcd *xhci) 315void 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}