summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-04-27 19:57:12 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-15 21:44:48 -0700
commit0f2a79300a1471cf92ab43af165ea13555c8b0a5 (patch)
treea46c63777a6040708500aefdbc31e0a0404d2e4a /drivers/usb/host/xhci-ring.c
parent7206b00164a1c3ca533e01db285955617e1019f8 (diff)
USB: xhci: Root hub support.
Add functionality for getting port status and hub descriptor for xHCI root hubs. This is WIP because the USB 3.0 hub descriptor is different from the USB 2.0 hub descriptor. For now, we lie about the root hub descriptor because the changes won't effect how the core talks to the root hub. Later we will need to add the USB 3.0 hub descriptor for real hubs, and this code might change. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
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 c7e3c7142b9..9d6bb3d730c 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,
inc_deq(xhci, xhci->cmd_ring, false);
}
+static void handle_port_status(struct xhci_hcd *xhci,
+ union xhci_trb *event)
+{
+ u32 port_id;
+
+ /* Port status change events always have a successful completion code */
+ if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
+ xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
+ xhci->error_bitmask |= 1 << 8;
+ }
+ /* FIXME: core doesn't care about all port link state changes yet */
+ port_id = GET_PORT_ID(event->generic.field[0]);
+ xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
+
+ /* Update event ring dequeue pointer before dropping the lock */
+ inc_deq(xhci, xhci->event_ring, true);
+ set_hc_event_deq(xhci);
+
+ spin_unlock(&xhci->lock);
+ /* Pass this up to the core */
+ usb_hcd_poll_rh_status(xhci_to_hcd(xhci));
+ spin_lock(&xhci->lock);
+}
+
+/*
+ * This function handles all OS-owned events on the event ring. It may drop
+ * xhci->lock between event processing (e.g. to pass up port status changes).
+ */
void handle_event(struct xhci_hcd *xhci)
{
union xhci_trb *event;
+ int update_ptrs = 1;
if (!xhci->event_ring || !xhci->event_ring->dequeue) {
xhci->error_bitmask |= 1 << 1;
@@ -301,18 +330,24 @@ void handle_event(struct xhci_hcd *xhci)
return;
}
- /* FIXME: Only handles command completion events. */
+ /* FIXME: Handle more event types. */
switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) {
case TRB_TYPE(TRB_COMPLETION):
handle_cmd_completion(xhci, &event->event_cmd);
break;
+ case TRB_TYPE(TRB_PORT_STATUS):
+ handle_port_status(xhci, event);
+ update_ptrs = 0;
+ break;
default:
xhci->error_bitmask |= 1 << 3;
}
- /* Update SW and HC event ring dequeue pointer */
- inc_deq(xhci, xhci->event_ring, true);
- set_hc_event_deq(xhci);
+ if (update_ptrs) {
+ /* Update SW and HC event ring dequeue pointer */
+ inc_deq(xhci, xhci->event_ring, true);
+ set_hc_event_deq(xhci);
+ }
/* Are there more items on the event ring? */
handle_event(xhci);
}