Skip to contentSkip to conversation

xhci features and fixes for usb-next

PATCH New Mathias Nyman 1 participant 18 messages Aug 6

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

cover xhci features and fixes for usb-next

Hi Greg

Several xhci improvements, clenups and fixes for usb-next

Thanks
Mathias

Breno Leitao (1):
usb: xhci: bail out of setup if the controller is inaccessible

Lucas De Marchi (3):
xhci: dbgtty: Fix unregister on tty_register_driver() failure
xhci: dbgtty: Fix unregister on tty_alloc_driver() failure
xhci: dbgtty: Drop extra call to idr_destroy()

Mathias Nyman (6):
xhci: fix frame id calculation and checks for isoc URBs
xhci: Set frame ID field of isoc TRB when starting an isoch stream
xhci: include all root port children in recovery prevention on link
error
xhci: prevent endpoint recovery after roothub disconnect
xhci: avoid xHC endpoint changes after disconnect or link error.
xhci: move dequeue to next valid td instead of past cancelled one

Michal Pecio (1):
usb: xhci: Handle bogus TRB pointers in Missed Service Error events

Niklas Neronin (5):
usb: xhci: standardize multi bit-field macros
usb: xhci: use 64-bit Addressing Capability macro
usb: xhci: remove redundant function wrapper
usb: xhci: remove redundant 'xhci' pointer from endpoint struct
usb: xhci: replace Unicode quotes with ASCII apostrophes

Semih Baskan (1):
usb: xhci: Handle USB3 port events when there is one roothub

 drivers/usb/dwc3/host.c         |   5 +-
 drivers/usb/host/xhci-caps.h    |  41 +--
 drivers/usb/host/xhci-dbgtty.c  |  25 +-
 drivers/usb/host/xhci-debugfs.c |   3 +-
 drivers/usb/host/xhci-histb.c   |   2 +-
 drivers/usb/host/xhci-hub.c     |   6 +-
 drivers/usb/host/xhci-mem.c     |   7 +-
 drivers/usb/host/xhci-mtk.c     |   4 +-
 drivers/usb/host/xhci-pci.c     |   4 +-
 drivers/usb/host/xhci-plat.c    |   2 +-
 drivers/usb/host/xhci-ring.c    | 492 ++++++++++++++++----------------
 drivers/usb/host/xhci-tegra.c   |   5 +-
 drivers/usb/host/xhci.c         |  43 +--
 drivers/usb/host/xhci.h         |  21 +-
 14 files changed, 344 insertions(+), 316 deletions(-)
Signature · 1 line
2.43.0
#1 lore raw

PATCH 1/17 xhci: fix frame id calculation and checks for isoc URBs

Check if the expected frame IDs for a isochronous URB submitted mid stream is within the valid frame time window that xHC controller is capable of queuing TDs.

The range only needs to be checked once per URB as the isoc TDs of an URB are queued in one go with spinlock held and interrupts disabled.

Calculate the valid frame window start and end frame id in frames instead of microframes to better match how xhci specification section 4.11.2.5 does it.

Don't add frame id gaps or change scheduling to SIA mid stream if the start frame is outside the valid frame winow.
Only print a debug message.
Some devices can't handle gaps in isochronous transfers.

Calculate a valid start frame for the first URB of a stream, and align it to a full frame, or to interval start if interval is longer than a frame

Set urb->start_frame value for every URB

---
 drivers/usb/host/xhci-mem.c  |   1 +
 drivers/usb/host/xhci-ring.c | 182 +++++++++++++++--------------------
 drivers/usb/host/xhci.h      |   7 +-
 3 files changed, 86 insertions(+), 104 deletions(-)
drivers/usb/host/xhci-mem.c +1 -0
index a5e7f363922f..2d7a61b3aaf2 100644
@@ -1493,6 +1493,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
 		return -ENOMEM;
 
 	virt_dev->eps[ep_index].skip = false;
+	virt_dev->eps[ep_index].next_uframe = -1;
 	ep_ring = virt_dev->eps[ep_index].new_ring;
 	xhci_ring_init(xhci, ep_ring);
 
drivers/usb/host/xhci-ring.c +79 -103

243 lines not on this page · raw message

drivers/usb/host/xhci.h +6 -1
index 2d3941b5e1e3..8b07f7808ad7 100644
@@ -290,6 +290,11 @@ struct xhci_run_regs {
 	struct xhci_intr_reg	ir_set[1024];
 };
 
+/* Bits [13:3] of the microframe index equals the 1ms frame index */
+#define MFINDEX_TO_FRAME(p)	(((p) >> 3) & 0x7ff)
+#define MAX_FRAMES		2048
+#define MAX_UFRAMES		(MAX_FRAMES * 8)
+
 /**
  * struct doorbell_array
  *
@@ -699,7 +704,7 @@ struct xhci_virt_ep {
 	struct list_head	bw_endpoint_list;
 	unsigned long		stop_time;
 	/* Isoch Frame ID checking storage */
-	int			next_frame_id;
+	int			next_uframe;
 	/* Use new Isoch TRB layout needed for extended TBC support */
 	bool			use_extended_tbc;
 	/* set if this endpoint is controlled via sideband access*/
Signature · 1 line
2.43.0
#2 lore raw

PATCH 2/17 xhci: Set frame ID field of isoc TRB when starting an isoch stream

In reply to Mathias Nyman

The frame id field can be set for the first TD of the first isoc URB to schedule the start of an isoc stream even in host doesn't support CFC (Contiguous Frame ID Capability)

Set the frame ID TRB field of the first isoc TD unless URB has the schedule immediately 'URB_ISO_ASAP' transfer flag set.

---
 drivers/usb/host/xhci-ring.c | 31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)
drivers/usb/host/xhci-ring.c +24 -7
index bc998692589d..3ab0d1b68d85 100644
@@ -3955,6 +3955,23 @@ static int xhci_ist_microframes(struct xhci_hcd *xhci)
 	return ist;
 }
 
+
+static bool xhci_isoc_td_uses_frame_id(struct xhci_hcd *xhci, struct urb *urb,
+				       struct xhci_virt_ep *ep, int i)
+{
+	if (urb->transfer_flags & URB_ISO_ASAP)
+		return false;
+
+	if (xhci->hcc_params & HCC_CFC)
+		return true;
+
+	/* set frame id for first TD of first URB in stream */
+	if (ep->next_uframe == -1 && i == 0)
+		return true;
+
+	return false;
+}
+
 /*
  * Check if frame is in the valid frame window, including start and end.
  * If start > end then assume window wrapped around at a limit the frame
@@ -4066,7 +4083,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 	int i, j;
 	bool more_trbs_coming;
 	struct xhci_virt_ep *xep;
-	int frame_id;
 	int uinterval = urb->interval;
 	int start_uframe;
 
@@ -4122,12 +4138,13 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		}
 		td = &urb_priv->td[i];
 
-		/* use SIA as default, if frame id is used overwrite it */
-		sia_frame_id = TRB_SIA;
-		if (!(urb->transfer_flags & URB_ISO_ASAP) && (xhci->hcc_params & HCC_CFC)) {
-			frame_id = (start_uframe + i * uinterval) / 8;
-			frame_id %= MAX_FRAMES;
-			sia_frame_id = TRB_FRAME_ID(frame_id);
+
+		/* Choose SIA or frame ID based scheduling for this TD */
+		if (xhci_isoc_td_uses_frame_id(xhci, urb, xep, i)) {
+			sia_frame_id = (start_uframe + i * uinterval) / 8;
+			sia_frame_id = TRB_FRAME_ID(sia_frame_id % MAX_FRAMES);
+		} else {
+			sia_frame_id = TRB_SIA;
 		}
 
 		/*
Signature · 1 line
2.43.0
#3 lore raw

PATCH 3/17 xhci: include all root port children in recovery prevention on link error

In reply to Mathias Nyman

Driver already prevents useless transfer retry and endpoint recovery for devices directly connected to a root port with link errors.

These devices are either disconnecting or will be reset. Link is gone.

Move the flag indicating link error from the xhci device structure to the root port strucure, allowing all child devices behind hubs to easily check for root port link errors, avoiding useless transfer retries and endpoint recovery.

This extends the previous endpoint recovery prevention in commit b8c3b718087b ("usb: xhci: Don't try to recover an endpoint if port is in error state.")
Only root port link errors can be detected early by xhci driver, not link errors between external hubs and their children.

---
 drivers/usb/host/xhci-ring.c | 27 +++++++++++++++------------
 drivers/usb/host/xhci.c      |  4 +---
 drivers/usb/host/xhci.h      |  9 +--------
 3 files changed, 17 insertions(+), 23 deletions(-)
drivers/usb/host/xhci-ring.c +15 -12
index 3ab0d1b68d85..544749b607a4 100644
@@ -993,7 +993,7 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
 	 * Avoid resetting endpoint if link is inactive. Can cause host hang.
 	 * Device will be reset soon to recover the link so don't do anything
 	 */
-	if (ep->vdev->flags & VDEV_PORT_ERROR)
+	if (ep->vdev->rhub_port->link_inactive)
 		return -ENODEV;
 
 	/* add td to cancelled list and let reset ep handler take care of it */
@@ -1992,13 +1992,15 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
 static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
 {
 	struct xhci_virt_device *vdev = NULL;
-	struct usb_hcd *hcd;
-	u32 port_id;
-	u32 portsc, cmd_reg;
-	unsigned int hcd_portnum;
 	struct xhci_bus_state *bus_state;
-	bool bogus_port_status = false;
 	struct xhci_port *port;
+	struct usb_hcd *hcd;
+	bool bogus_port_status = false;
+	unsigned int hcd_portnum;
+	u32 cmd_reg;
+	u32 port_id;
+	u32 portsc;
+	u32 pls;
 
 	/* Port status change events always have a successful completion code */
 	if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS)
@@ -2035,6 +2037,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
 	bus_state = &port->rhub->bus_state;
 	hcd_portnum = port->hcd_portnum;
 	portsc = xhci_portsc_readl(port);
+	pls = portsc & PORT_PLS_MASK;
 
 	xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n",
 		 hcd->self.busnum, hcd_portnum + 1, port_id, portsc);
@@ -2046,12 +2049,12 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
 		usb_hcd_resume_root_hub(hcd);
 	}
 
-	if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) {
-		if (!(portsc & PORT_RESET))
-			vdev->flags |= VDEV_PORT_ERROR;
-	} else if (vdev && portsc & PORT_RC) {
-		vdev->flags &= ~VDEV_PORT_ERROR;
-	}
+	/*
+	 * Tag broken links to avoid retries while hub driver sorts it out.
+	 * Link status is not relible while port is in reset.
+	 */
+	if (!(portsc & PORT_RESET))
+		port->link_inactive = (pls == XDEV_INACTIVE);
 
 	if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
 		xhci_dbg(xhci, "port resume event for port %d\n", port_id);
drivers/usb/host/xhci.c +1 -3
index 091c82ca8ee2..6f830a43963f 100644
@@ -1664,7 +1664,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
 		goto free_priv;
 	}
 
-	if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) {
+	if (xhci->devs[slot_id]->rhub_port->link_inactive) {
 		xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n");
 		ret = -ENODEV;
 		goto free_priv;
@@ -4035,7 +4035,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
 				xhci_get_slot_state(xhci, virt_dev->out_ctx));
 		xhci_dbg(xhci, "Not freeing device rings.\n");
 		/* Don't treat this as an error.  May change my mind later. */
-		virt_dev->flags = 0;
 		ret = 0;
 		goto command_cleanup;
 	case COMP_SUCCESS:
@@ -4087,7 +4086,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
 	}
 	/* If necessary, update the number of active TTs on this root port */
 	xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
-	virt_dev->flags = 0;
 	ret = 0;
 
 command_cleanup:
drivers/usb/host/xhci.h +1 -8
index 8b07f7808ad7..7c8467581076 100644
@@ -758,14 +758,6 @@ struct xhci_virt_device {
 	struct xhci_port		*rhub_port;
 	struct xhci_interval_bw_table	*bw_table;
 	struct xhci_tt_bw_info		*tt_info;
-	/*
-	 * flags for state tracking based on events and issued commands.
-	 * Software can not rely on states from output contexts because of
-	 * latency between events and xHC updating output context values.
-	 * See xhci 1.1 section 4.8.3 for more details
-	 */
-	unsigned long			flags;
-#define VDEV_PORT_ERROR			BIT(0) /* Port error, link inactive */
 
 	/* The current max exit latency for the enabled USB3 link states. */
 	u16				current_mel;
@@ -1485,6 +1477,7 @@ struct xhci_port {
 	int			hcd_portnum;
 	struct xhci_hub		*rhub;
 	struct xhci_port_cap	*port_cap;
+	unsigned int		link_inactive:1;
 	unsigned int		lpm_incapable:1;
 	unsigned long		resume_timestamp;
 	bool			rexit_active;
Signature · 1 line
2.43.0
#4 lore raw

PATCH 4/17 xhci: prevent endpoint recovery after roothub disconnect

In reply to Mathias Nyman

Prevent transfer retry and endpoint recovery if the device or its parent disconnected from the roothub. Just like link error case.

There is a suspicion some xHC controllers may stop processing endpoint related commands after the last USB device disconnects from the host.

Disconnect often causes transaction errors, xhci driver tries to (soft) reset and restart the endpoint to recover it.
Hub driver again will cancel all pending URBs once disconnect is detected, stopping the endpoint right after (soft) reset restarted it. xHC controller sometimes fail to complete the stop endpoint command, leading to driver timing out, and tearing down xhci

Prevent extra endpoint (soft) reset after xhci driver is aware of the parent roothub port disconnect.

---
 drivers/usb/host/xhci-ring.c | 12 ++++++++----
 drivers/usb/host/xhci.h      |  1 +
 2 files changed, 9 insertions(+), 4 deletions(-)
drivers/usb/host/xhci-ring.c +8 -4
index 544749b607a4..656ed6470e4a 100644
@@ -986,14 +986,16 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
 				struct xhci_td *td,
 				enum xhci_ep_reset_type reset_type)
 {
+	struct xhci_port *rhub_port = ep->vdev->rhub_port;
 	unsigned int slot_id = ep->vdev->slot_id;
 	int err;
 
 	/*
-	 * Avoid resetting endpoint if link is inactive. Can cause host hang.
-	 * Device will be reset soon to recover the link so don't do anything
+	 * Avoid resetting endpoint if link is inactive or device disonnected.
+	 * Can cause host hang.
+	 * Device will be reset to recover an inactive link, so don't do anything
 	 */
-	if (ep->vdev->rhub_port->link_inactive)
+	if (rhub_port->link_inactive || !rhub_port->connected)
 		return -ENODEV;
 
 	/* add td to cancelled list and let reset ep handler take care of it */
@@ -2053,8 +2055,10 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
 	 * Tag broken links to avoid retries while hub driver sorts it out.
 	 * Link status is not relible while port is in reset.
 	 */
-	if (!(portsc & PORT_RESET))
+	if (!(portsc & PORT_RESET)) {
 		port->link_inactive = (pls == XDEV_INACTIVE);
+		port->connected = !!(portsc & PORT_CONNECT);
+	}
 
 	if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
 		xhci_dbg(xhci, "port resume event for port %d\n", port_id);
drivers/usb/host/xhci.h +1 -0
index 7c8467581076..defc5ff5aa55 100644
@@ -1478,6 +1478,7 @@ struct xhci_port {
 	struct xhci_hub		*rhub;
 	struct xhci_port_cap	*port_cap;
 	unsigned int		link_inactive:1;
+	unsigned int		connected:1;
 	unsigned int		lpm_incapable:1;
 	unsigned long		resume_timestamp;
 	bool			rexit_active;
Signature · 1 line
2.43.0
#5 lore raw

PATCH 5/17 xhci: avoid xHC endpoint changes after disconnect or link error.

In reply to Mathias Nyman

Avoid all extra endpoint state changes after the roothub link is lost due to disconnect or link error, and endpoint is known to be in a non-running state.

Rapid endpoint state changes involving endpoint reset, restart, and stopping the endpoint have caused xHC failures to complete stop endpoint command. xhci driver sees this as a fatal flaw and tears down xhci.

These endpoint state changes are normally part of recovery from transaction errors or URB cancel.
In this case recovery is not needed.

Add an endpoint state called EP_DROP_PENDING.
Set ep->ep_state |= EP_DROP_PENDING when an endpoint is found in a halted or stopped non-running state, and the roothub link is lost. Prevent endpoint from restarting.

URB cancel doesn't need to stop the endpoint if EP_DROP_PENDONG is set. URBs can be given back directly.
Endpoint is, and will remain stopped until it's dropped.

---
 drivers/usb/host/xhci-ring.c | 19 ++++++++++++++++---
 drivers/usb/host/xhci.c      |  5 ++++-
 drivers/usb/host/xhci.h      |  1 +
 3 files changed, 21 insertions(+), 4 deletions(-)
drivers/usb/host/xhci-ring.c +16 -3
index 656ed6470e4a..51008bad16cd 100644
@@ -561,8 +561,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
 	 * pointer command pending because the device can choose to start any
 	 * stream once the endpoint is on the HW schedule.
 	 */
-	if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) ||
-	    (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT))
+	if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED |
+			EP_CLEARING_TT | EP_DROP_PENDING))
 		return;
 
 	trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
@@ -995,8 +995,10 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
 	 * Can cause host hang.
 	 * Device will be reset to recover an inactive link, so don't do anything
 	 */
-	if (rhub_port->link_inactive || !rhub_port->connected)
+	if (rhub_port->link_inactive || !rhub_port->connected) {
+		ep->ep_state |= EP_DROP_PENDING;
 		return -ENODEV;
+	}
 
 	/* add td to cancelled list and let reset ep handler take care of it */
 	if (reset_type == EP_HARD_RESET) {
@@ -1066,6 +1068,13 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
 				  td->urb, td->urb->stream_id);
 			continue;
 		}
+
+		/* device disconnected or link error, ep will be dropped */
+		if (ep->ep_state & EP_DROP_PENDING) {
+			td->cancel_status = TD_CLEARED;
+			continue;
+		}
+
 		/*
 		 * If a ring stopped on the TD we need to cancel then we have to
 		 * move the xHC endpoint ring dequeue pointer past this TD.
@@ -1296,6 +1305,10 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
 		}
 	}
 
+	/* link is inactive or disconnected, ep is not running and shouldn't be restarted */
+	if (ep->vdev->rhub_port->link_inactive || !ep->vdev->rhub_port->connected)
+		ep->ep_state |= EP_DROP_PENDING;
+
 	/* will queue a set TR deq if stopped on a cancelled, uncleared TD */
 	xhci_invalidate_cancelled_tds(ep);
 	ep->ep_state &= ~EP_STOP_CMD_PENDING;
drivers/usb/host/xhci.c +4 -1
index 6f830a43963f..71c25c2e71b9 100644
@@ -1852,7 +1852,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 	}
 
 	/* In this case no commands are pending but the endpoint is stopped */
-	if (ep->ep_state & EP_CLEARING_TT) {
+	if (ep->ep_state & (EP_CLEARING_TT | EP_DROP_PENDING)) {
 		/* and cancelled TDs can be given back right away */
 		xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n",
 				urb->dev->slot_id, ep_index, ep->ep_state);
@@ -4089,6 +4089,9 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
 	ret = 0;
 
 command_cleanup:
+	for (i = 0; i < EP_CTX_PER_DEV; i++)
+		virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING;
+
 	xhci_free_command(xhci, reset_device_cmd);
 	return ret;
 }
drivers/usb/host/xhci.h +1 -0
index defc5ff5aa55..670533dc1d97 100644
@@ -682,6 +682,7 @@ struct xhci_virt_ep {
 #define EP_SOFT_CLEAR_TOGGLE	BIT(7)
 /* usb_hub_clear_tt_buffer is in progress */
 #define EP_CLEARING_TT		BIT(8)
+#define EP_DROP_PENDING		BIT(9) /* port disconnect or link error, don't restart */
 	/* ----  Related to URB cancellation ---- */
 	struct list_head	cancelled_td_list;
 	struct xhci_hcd		*xhci;
Signature · 1 line
2.43.0
#6 lore raw

PATCH 6/17 xhci: move dequeue to next valid td instead of past cancelled one

In reply to Mathias Nyman

If a ring stops on a TD that is about to be cancelled then the xHC ring hardware dequeue pointer needs to move past the TD to flush TRBs from xHC cache.

The TRB after the cancelled TD might be a no-op TRB, or a link TRB. Moving the dequeue to a link TRB has caused isses on some hosts, and moving it to a no-op TRB can be an issue for control endpoints as xhci specification 4.8.3 'Endpoint Context State" states that The Default Control Endpoint shall return to the Running state when the Doorbell is rung for the next Setup Stage TD sent to the endpoint.

Solve this by always moving the dequeue pointer to the next valid TD. If ring is empty and there are no queued TDs then move the dequeue pointer to the enqueue pointer.
If enqueue points to a link TRB on a empty ring then propagate enqueue to next segment before pointing dequeue to it.

Note that this patch ended up almost identical to a simplifiaction patch done earlier by Michal Pecio, see link. That patch was not added due to a potential, somewhat theoretical issue of moving dequeue backwards. Turns out improving cancelled control transfers end up with the same code, and is now worth taking.
Code is very likely subconsciously based the patch by Michal Pecio.

---
 drivers/usb/host/xhci-ring.c | 140 ++++++++++++++++-------------------
 1 file changed, 63 insertions(+), 77 deletions(-)
drivers/usb/host/xhci-ring.c +63 -77
index 51008bad16cd..c868bf4deaef 100644
@@ -686,112 +686,100 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev,
 	return le64_to_cpu(ep_ctx->deq);
 }
 
-static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
-				unsigned int slot_id, unsigned int ep_index,
-				unsigned int stream_id, struct xhci_td *td)
+/*
+ * Move the endpoint dequeue pointer to the next queued TD on ring->td_list or
+ * to enqueue if no TDs are queued (empty ring)
+ * All cancelled TDs on ring->td_list should be moved to ep->cancelled_td_list
+ * before calling this function
+ */
+static int xhci_move_deq_to_next_td(struct xhci_hcd *xhci,
+				    struct xhci_virt_ep *ep,
+				    unsigned int stream_id)
 {
-	struct xhci_virt_device *dev = xhci->devs[slot_id];
-	struct xhci_virt_ep *ep = &dev->eps[ep_index];
-	struct xhci_ring *ep_ring;
 	struct xhci_command *cmd;
-	struct xhci_segment *new_seg;
-	union xhci_trb *new_deq;
-	int new_cycle;
+	struct xhci_ring *ring;
+	struct xhci_td *td;
 	dma_addr_t addr;
-	u64 hw_dequeue;
-	bool hw_dequeue_found = false;
-	bool td_last_trb_found = false;
+	int new_cycle;
 	u32 trb_sct = 0;
-	int ret;
+	int ret = 0;
 
-	ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
-			ep_index, stream_id);
-	if (!ep_ring) {
+	ring = xhci_virt_ep_to_ring(xhci, ep, stream_id);
+	if (!ring) {
 		xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n",
 			  stream_id);
 		return -ENODEV;
 	}
 
-	hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK;
-	new_seg = ep_ring->deq_seg;
-	new_deq = ep_ring->dequeue;
-	new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE;
-
-	/*
-	 * Walk the ring until both the next TRB and hw_dequeue are found (don't
-	 * move hw_dequeue back if it went forward due to a HW bug). Cycle state
-	 * is loaded from a known good TRB, track later toggles to maintain it.
-	 */
-	do {
-		if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq)
-		    == (dma_addr_t)hw_dequeue) {
-			hw_dequeue_found = true;
-			if (td_last_trb_found)
-				break;
-		}
-		if (new_deq == td->end_trb)
-			td_last_trb_found = true;
-
-		if (td_last_trb_found && trb_is_link(new_deq) &&
-		    link_trb_toggles_cycle(new_deq))
-			new_cycle ^= 0x1;
-
-		next_trb(&new_seg, &new_deq);
-
-		/* Search wrapped around, bail out */
-		if (new_deq == ep->ring->dequeue) {
-			xhci_err(xhci, "Error: Failed finding new dequeue state\n");
-			return -EINVAL;
-		}
-
-	} while (!hw_dequeue_found || !td_last_trb_found);
-
-	/* Don't update the ring cycle state for the producer (us). */
-	addr = xhci_trb_virt_to_dma(new_seg, new_deq);
-	if (addr == 0) {
-		xhci_warn(xhci, "Can't find dma of new dequeue ptr\n");
-		xhci_warn(xhci, "deq seg = %p, deq ptr = %p\n", new_seg, new_deq);
-		return -EINVAL;
-	}
-
 	if ((ep->ep_state & SET_DEQ_PENDING)) {
-		xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n",
-			  &addr);
+		xhci_warn(xhci, "Set TR Deq already pending\n");
 		return -EBUSY;
 	}
 
 	/* This function gets called from contexts where it cannot sleep */
 	cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
 	if (!cmd) {
-		xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr);
+		xhci_warn(xhci, "Can't alloc Set TR Deq cmd\n");
 		return -ENOMEM;
 	}
 
+	/*
+	 * Move dequeue to the beginning of next td, or to enqueue if ring is
+	 * empty. Avoid moving dequeue to a link trb (empty ring) as it causes
+	 * issues on some hosts. In that case advance the enqueue to next segment
+	 * before moving dequeue to it
+	 */
+
+	if (list_empty(&ring->td_list)) {
+		if (trb_is_link(ring->enqueue))
+			inc_enq_past_link(xhci, ring, 0);
+		ep->queued_deq_seg = ring->enq_seg;
+		ep->queued_deq_ptr = ring->enqueue;
+		new_cycle = ring->cycle_state;
+	} else {
+		td = list_first_entry(&ring->td_list, struct xhci_td, td_list);
+		ep->queued_deq_seg = td->start_seg;
+		ep->queued_deq_ptr = td->start_trb;
+		new_cycle = le32_to_cpu(td->start_trb->generic.field[3]) & TRB_CYCLE;
+	}
+
+	addr = xhci_trb_virt_to_dma(ep->queued_deq_seg, ep->queued_deq_ptr);
+	if (addr == 0) {
+		xhci_warn(xhci, "Can't find new dequeue dma of seg %p, ptr %p\n",
+			  ep->queued_deq_seg, ep->queued_deq_ptr);
+		ret = -EINVAL;
+		goto err_out;
+	}
+
 	if (stream_id)
 		trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
 	ret = queue_command(xhci, cmd,
 		lower_32_bits(addr) | trb_sct | new_cycle,
 		upper_32_bits(addr),
-		STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) |
-		EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
-	if (ret < 0) {
-		xhci_free_command(xhci, cmd);
-		return ret;
-	}
-	ep->queued_deq_seg = new_seg;
-	ep->queued_deq_ptr = new_deq;
+		STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(ep->vdev->slot_id) |
+		EP_INDEX_FOR_TRB(ep->ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
+	if (ret < 0)
+		goto err_out;
 
 	xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
 		       "Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle);
 
-	/* Stop the TD queueing code from ringing the doorbell until
-	 * this command completes.  The HC won't set the dequeue pointer
-	 * if the ring is running, and ringing the doorbell starts the
-	 * ring running.
+	/*
+	 * Stop the TD queueing code from ringing the doorbell until this
+	 * command completes. The HC won't set the dequeue pointer if the ring
+	 * is running, and ringing the doorbell starts the ring.
 	 */
 	ep->ep_state |= SET_DEQ_PENDING;
 	xhci_ring_cmd_db(xhci);
+
 	return 0;
+
+err_out:
+	xhci_free_command(xhci, cmd);
+	ep->queued_deq_seg = NULL;
+	ep->queued_deq_ptr = NULL;
+
+	return ret;
 }
 
 /* flip_cycle means flip the cycle bit of all but the first and last TRB.
@@ -1043,7 +1031,6 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
 	struct xhci_td		*cached_td = NULL;
 	struct xhci_ring	*ring;
 	u64			hw_deq;
-	unsigned int		slot_id = ep->vdev->slot_id;
 	int			err;
 
 	/*
@@ -1126,9 +1113,8 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
 	if (!cached_td)
 		return 0;
 
-	err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index,
-					cached_td->urb->stream_id,
-					cached_td);
+	err = xhci_move_deq_to_next_td(xhci, ep, cached_td->urb->stream_id);
+
 	if (err) {
 		/* Failed to move past cached td, just set cached TDs to no-op */
 		list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
Signature · 1 line
2.43.0
#7 lore raw

PATCH 7/17 xhci: dbgtty: Fix unregister on tty_register_driver() failure

In reply to Mathias Nyman

From: Lucas De Marchi <[email protected]>

If tty_register_driver() fails, it drops the reference, but fails to set the global dbc_tty_driver to NULL, causing the unregister to be called again when module exits.

On module unload dbc_tty_exit() only gates its cleanup on the driver pointer being non-NULL, so it operates on the already-freed driver:

    module_init(xhci_hcd_init)
      xhci_hcd_init()
        xhci_dbc_init()                       [return value ignored]
          dbc_tty_init()
            tty_register_driver() fails
              tty_driver_kref_put()           -> driver freed
              (dbc_tty_driver left dangling)
    ...
    module_exit(xhci_hcd_fini)
      xhci_hcd_fini()
        xhci_dbc_exit()
          dbc_tty_exit()
            if (dbc_tty_driver)               -> true (dangling)
              tty_unregister_driver()         -> use-after-free
Fixes 4521f1613940 ("xhci: dbctty: split dbc tty driver registration and unregistration functions.")
Cc Mathias Nyman <[email protected]>
Cc Greg Kroah-Hartman <[email protected]>
---
 drivers/usb/host/xhci-dbgtty.c | 1 +
 1 file changed, 1 insertion(+)
drivers/usb/host/xhci-dbgtty.c +1 -0
index 2e7384c6b6ec..01decd46f313 100644
@@ -651,6 +651,7 @@ int dbc_tty_init(void)
 		pr_err("Can't register dbc tty driver\n");
 		tty_driver_kref_put(dbc_tty_driver);
 		idr_destroy(&dbc_tty_minors);
+		dbc_tty_driver = NULL;
 	}
 
 	return ret;
Signature · 1 line
2.43.0
#8 lore raw

PATCH 8/17 xhci: dbgtty: Fix unregister on tty_alloc_driver() failure

In reply to Mathias Nyman

From: Lucas De Marchi <[email protected]>

Make sure to set dbc_tty_driver to NULL to match the check in dbc_tty_exit(). For that, make detached error handling path common to the other branch in the same function.

Fixes 4521f1613940 ("xhci: dbctty: split dbc tty driver registration and unregistration functions.")
Cc Mathias Nyman <[email protected]>
Cc Greg Kroah-Hartman <[email protected]>
---
 drivers/usb/host/xhci-dbgtty.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/usb/host/xhci-dbgtty.c +11 -5
index 01decd46f313..7e44e62d6ea9 100644
@@ -628,8 +628,8 @@ int dbc_tty_init(void)
 	dbc_tty_driver = tty_alloc_driver(64, TTY_DRIVER_REAL_RAW |
 					  TTY_DRIVER_DYNAMIC_DEV);
 	if (IS_ERR(dbc_tty_driver)) {
-		idr_destroy(&dbc_tty_minors);
-		return PTR_ERR(dbc_tty_driver);
+		ret = PTR_ERR(dbc_tty_driver);
+		goto fail;
 	}
 
 	dbc_tty_driver->driver_name = "dbc_serial";
@@ -649,11 +649,17 @@ int dbc_tty_init(void)
 	ret = tty_register_driver(dbc_tty_driver);
 	if (ret) {
 		pr_err("Can't register dbc tty driver\n");
-		tty_driver_kref_put(dbc_tty_driver);
-		idr_destroy(&dbc_tty_minors);
-		dbc_tty_driver = NULL;
+		goto fail_put;
 	}
 
+	return ret;
+
+fail_put:
+	tty_driver_kref_put(dbc_tty_driver);
+fail:
+	idr_destroy(&dbc_tty_minors);
+	dbc_tty_driver = NULL;
+
 	return ret;
 }
 
Signature · 1 line
2.43.0
#9 lore raw

PATCH 9/17 xhci: dbgtty: Drop extra call to idr_destroy()

In reply to Mathias Nyman

From: Lucas De Marchi <[email protected]>

idr_destroy() is already called on error paths in dbc_tty_init(). Do not call it again on exit. For symmetry with the init side, also use IS_ERR_OR_NULL() to gate the exit steps.

---
 drivers/usb/host/xhci-dbgtty.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/usb/host/xhci-dbgtty.c +5 -5
index 7e44e62d6ea9..3d51e8d82659 100644
@@ -665,11 +665,11 @@ int dbc_tty_init(void)
 
 void dbc_tty_exit(void)
 {
-	if (dbc_tty_driver) {
-		tty_unregister_driver(dbc_tty_driver);
-		tty_driver_kref_put(dbc_tty_driver);
-		dbc_tty_driver = NULL;
-	}
+	if (IS_ERR_OR_NULL(dbc_tty_driver))
+		return;
 
+	tty_unregister_driver(dbc_tty_driver);
+	tty_driver_kref_put(dbc_tty_driver);
 	idr_destroy(&dbc_tty_minors);
+	dbc_tty_driver = NULL;
 }
Signature · 1 line
2.43.0
#10 lore raw

PATCH 10/17 usb: xhci: bail out of setup if the controller is inaccessible

In reply to Mathias Nyman

From: Breno Leitao <[email protected]>

xhci_gen_setup() locates the operational registers using the capability length read from the very first register:

	xhci->op_regs = hcd->regs +
		HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));

If the controller is dead or has dropped off the bus, that read returns ~0, HC_LENGTH() truncates it to 0xff, and op_regs ends up 0xff bytes past the page-aligned MMIO base, i.e. unaligned. The first access through it, xhci_halt() -> xhci_handshake() reading op_regs->status, is then an unaligned readl() on device memory. arm64 faults on unaligned device accesses, so instead of xhci_handshake() catching the all-ones value and returning -ENODEV, setup oopses:

xhci-pci-renesas 0005:08:00.0: Unable to change power state from D3cold to D0, device inaccessible xhci-pci-renesas 0005:08:00.0: xHCI Host Controller
xhci-pci-renesas 0005:08:00.0: new USB bus registered, assigned bus number 1

  Unable to handle kernel paging request at virtual address ffff80030a770103
    ESR = 0x0000000096000021
    FSC = 0x21: alignment fault
  Internal error: Oops: 0000000096000021 [#1]  SMP
  pc : xhci_halt [xhci_hcd]
  Call trace:
   xhci_halt
   xhci_gen_setup
   xhci_pci_setup
   usb_add_hcd
   usb_hcd_pci_probe
   xhci_pci_common_probe
   xhci_pci_renesas_probe

This was hit with a Renesas uPD720201 that failed to power up ("Unable to change power state from D3cold to D0, device inaccessible") yet still reached the HCD probe path.

Read the capability register once, and if it reads back the all-ones value (as xhci_handshake() and xhci_reset() already test for), abort setup with -ENODEV before op_regs is derived from it. Reading it once also avoids re-reading a register that may change under a concurrent hot-removal.

Fixes 66d4eadd8d06 ("USB: xhci: BIOS handoff and HW initialization.")
---
 drivers/usb/host/xhci.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/usb/host/xhci.c +8 -3
index 71c25c2e71b9..4473b3cd1d36 100644
@@ -5434,6 +5434,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 	struct device		*dev = hcd->self.sysdev;
 	int			retval;
 	u32			hcs_params1;
+	u32			hc_capbase;
 
 	/* Accept arbitrarily long scatter-gather lists */
 	hcd->self.sg_tablesize = ~0;
@@ -5454,15 +5455,19 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 	mutex_init(&xhci->mutex);
 	xhci->main_hcd = hcd;
 	xhci->cap_regs = hcd->regs;
-	xhci->op_regs = hcd->regs +
-		HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
+	hc_capbase = readl(&xhci->cap_regs->hc_capbase);
+	if (hc_capbase == U32_MAX) {
+		xhci_warn(xhci, "Host controller not accessible, removed?\n");
+		return -ENODEV;
+	}
+	xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase);
 	xhci->run_regs = hcd->regs +
 		(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
 	/* Cache read-only capability registers */
 	hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
 	xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
 	xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
-	xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase));
+	xhci->hci_version = HC_VERSION(hc_capbase);
 	xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
 	if (xhci->hci_version > 0x100)
 		xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
Signature · 1 line
2.43.0
#11 lore raw

PATCH 11/17 usb: xhci: standardize multi bit-field macros

In reply to Mathias Nyman

From: Niklas Neronin <[email protected]>

This patch aims to unify the format of register macros and masks within the xHCI driver. Currently, register macros have inconsistent bit-field masks, get macros, and set macros, with varying naming conventions and functionalities.

==================== Proposal ====================
* Introduce a standardized approach by using only mask macros for each bit field, leveraging GENMASK() for enhanced clarity.

  #define HCC_MAX_PSA		GENMASK(15, 12)

* Utilize FIELD_GET() and FIELD_PREP() macros directly in the C code for getting and setting values, ensuring consistency and readability.

  u32 psa = FIELD_GET(HCC_MAX_PSA, reg);

* Maintain exceptions for macros that perform custom operations.

  #define CTX_SIZE(_hcc)	(_hcc & HCC_64BYTE_CONTEXT ? 64 : 32)

* Note, while FIELD_*() macros are beneficial, I am not suggesting that they should always be used. Instead, use them where they simplify the code and eliminate the necessity for custom get/set macros. In the example below, additional FIELD_PREP() or FIELD_MODIFY() is not beneficial.

  #define HCS_MAX_SCRATCHPAD(p)   (FIELD_GET(HCS_MAX_SP_HI, (p)) << 5 | \
				   FIELD_GET(HCS_MAX_SP_LO, (p)))

==================== Improvements ==================== Simplified Macros:
By reducing custom macros, the code becomes more straightforward. Macros FIELD_GET() and FIELD_PREP() are commonly used, which contributes to the code readability and consistency.

  $ git grep -n 'FIELD_GET' | wc -l
  9027
  $ git grep -n 'FIELD_PREP' | wc -l
  15407

Consistent Return Type:
All bit macros will return unsigned 64-bit values, mitigating potential cross-architecture issues.

Unified Bit Range Definition:
The mask macro will define bit ranges, eliminating separate definitions for get/set macros. Because, FIELD_GET() & FIELD_PREP() use mask macro.

Cleaner header file with less macros:
Fewer macros result in a cleaner and more manageable header file.

---
 drivers/usb/dwc3/host.c         |  5 ++--
 drivers/usb/host/xhci-caps.h    | 41 ++++++++++++++++++---------------
 drivers/usb/host/xhci-debugfs.c |  3 ++-
 drivers/usb/host/xhci-histb.c   |  2 +-
 drivers/usb/host/xhci-hub.c     |  4 ++--
 drivers/usb/host/xhci-mem.c     |  3 ++-
 drivers/usb/host/xhci-mtk.c     |  4 ++--
 drivers/usb/host/xhci-pci.c     |  4 +++-
 drivers/usb/host/xhci-plat.c    |  2 +-
 drivers/usb/host/xhci-ring.c    |  4 +++-
 drivers/usb/host/xhci-tegra.c   |  5 ++--
 drivers/usb/host/xhci.c         | 23 ++++++++++--------
 12 files changed, 57 insertions(+), 43 deletions(-)
drivers/usb/dwc3/host.c +3 -2
index 96b588bd08cd..c5674161b2b0 100644
@@ -12,6 +12,7 @@
 #include <linux/platform_device.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
+#include <linux/bitfield.h>
 
 #include "../host/xhci-port.h"
 #include "../host/xhci-ext-caps.h"
@@ -46,9 +47,9 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
 			return;
 		}
 
-		op_regs_base = HC_LENGTH(readl(xhci_regs));
+		op_regs_base = FIELD_GET(HC_LENGTH, readl(xhci_regs));
 		reg = readl(xhci_regs + XHCI_HCSPARAMS1);
-		port_num = HCS_MAX_PORTS(reg);
+		port_num = FIELD_GET(HCS_MAX_PORTS, reg);
 
 		for (i = 1; i <= port_num; i++) {
 			offset = op_regs_base + XHCI_PORTSC_BASE + 0x10 * (i - 1);
drivers/usb/host/xhci-caps.h +22 -19

87 lines not on this page · raw message

drivers/usb/host/xhci-debugfs.c +2 -1
index d07276192256..2aa01d99f23d 100644
@@ -9,6 +9,7 @@
 
 #include <linux/slab.h>
 #include <linux/uaccess.h>
+#include <linux/bitfield.h>
 
 #include "xhci.h"
 #include "xhci-debugfs.h"
@@ -791,7 +792,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
 			    xhci->debugfs_root, "reg-cap");
 
 	xhci_debugfs_regset(xhci,
-			    HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)),
+			    FIELD_GET(HC_LENGTH, readl(&xhci->cap_regs->hc_capbase)),
 			    xhci_op_regs, ARRAY_SIZE(xhci_op_regs),
 			    xhci->debugfs_root, "reg-op");
 
drivers/usb/host/xhci-histb.c +1 -1
index 02396c8721dc..fddb43bf6323 100644
@@ -276,7 +276,7 @@ static int xhci_histb_probe(struct platform_device *pdev)
 	if (ret)
 		goto put_usb3_hcd;
 
-	if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+	if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4)
 		xhci->shared_hcd->can_do_streams = 1;
 
 	ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
drivers/usb/host/xhci-hub.c +2 -2
index b0264bd8577a..17ac05516642 100644
@@ -115,8 +115,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
 
 	if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
 		reg = readl(&xhci->cap_regs->hcs_params3);
-		ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg);
-		ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg));
+		ss_cap->bU1devExitLat = FIELD_GET(HCS_U1_LATENCY, reg);
+		ss_cap->bU2DevExitLat = cpu_to_le16(FIELD_GET(HCS_U2_LATENCY, reg));
 	}
 
 	if (wLength < le16_to_cpu(bos->wTotalLength))
drivers/usb/host/xhci-mem.c +2 -1
index 2d7a61b3aaf2..5717bd830189 100644
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/dmapool.h>
 #include <linux/dma-mapping.h>
+#include <linux/bitfield.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"
@@ -2301,7 +2302,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
 	if (!segs)
 		segs = ERST_DEFAULT_SEGS;
 
-	max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2));
+	max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2;
 	segs = min(segs, max_segs);
 
 	ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
drivers/usb/host/xhci-mtk.c +2 -2
index d9b865546a67..60af5fe29bd4 100644
@@ -468,7 +468,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
 	 * MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream,
 	 * and it's 3 when support it.
 	 */
-	if (xhci->hci_version < 0x100 && HCC_MAX_PSA(xhci->hcc_params) == 4)
+	if (xhci->hci_version < 0x100 && GET_MAX_PSA_SIZE(xhci->hcc_params) == 4)
 		xhci->quirks |= XHCI_BROKEN_STREAMS;
 }
 
@@ -650,7 +650,7 @@ static int xhci_mtk_probe(struct platform_device *pdev)
 	}
 
 	usb3_hcd = xhci_get_usb3_hcd(xhci);
-	if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 &&
+	if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 &&
 	    !(xhci->quirks & XHCI_BROKEN_STREAMS))
 		usb3_hcd->can_do_streams = 1;
 
drivers/usb/host/xhci-pci.c +3 -1
index 6b3fcba44b08..a8889081ae82 100644
@@ -14,6 +14,7 @@
 #include <linux/acpi.h>
 #include <linux/reset.h>
 #include <linux/suspend.h>
+#include <linux/bitfield.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"
@@ -662,7 +663,8 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	}
 
 	usb3_hcd = xhci_get_usb3_hcd(xhci);
-	if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4)
+	if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) &&
+	    GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4)
 		usb3_hcd->can_do_streams = 1;
 
 	/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
drivers/usb/host/xhci-plat.c +1 -1
index 074d9c731639..6fd595f81a30 100644
@@ -340,7 +340,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
 	}
 
 	usb3_hcd = xhci_get_usb3_hcd(xhci);
-	if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 &&
+	if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 &&
 	    !(xhci->quirks & XHCI_BROKEN_STREAMS))
 		usb3_hcd->can_do_streams = 1;
 
drivers/usb/host/xhci-ring.c +3 -1
index c868bf4deaef..80d53acc37fd 100644
@@ -57,6 +57,8 @@
 #include <linux/slab.h>
 #include <linux/string_choices.h>
 #include <linux/dma-mapping.h>
+#include <linux/bitfield.h>
+
 #include "xhci.h"
 #include "xhci-trace.h"
 
@@ -3954,7 +3956,7 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
 /* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */
 static int xhci_ist_microframes(struct xhci_hcd *xhci)
 {
-	int ist = HCS_IST_VALUE(xhci->hcs_params2);
+	int ist = FIELD_GET(HCS_IST_VALUE, xhci->hcs_params2);
 
 	if (xhci->hcs_params2 & HCS_IST_UNIT)
 		ist *= 8;
drivers/usb/host/xhci-tegra.c +3 -2
index e7e6d569f1db..6f235d1e117e 100644
@@ -6,6 +6,7 @@
  * Copyright (C) 2014 Google, Inc.
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
@@ -993,7 +994,7 @@ static int tegra_xusb_wait_for_falcon(struct tegra_xusb *tegra)
 	u32 value;
 
 	cap_regs = tegra->regs;
-	op_regs = tegra->regs + HC_LENGTH(readl(&cap_regs->hc_capbase));
+	op_regs = tegra->regs + FIELD_GET(HC_LENGTH, readl(&cap_regs->hc_capbase)),
 
 	ret = readl_poll_timeout(&op_regs->status, value, !(value & STS_CNR), 1000, 200000);
 
@@ -1895,7 +1896,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
 		goto remove_usb2;
 	}
 
-	if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+	if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4)
 		xhci->shared_hcd->can_do_streams = 1;
 
 	err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED);
drivers/usb/host/xhci.c +13 -10
index 4473b3cd1d36..2faf91966890 100644
@@ -21,6 +21,7 @@
 #include <linux/dmi.h>
 #include <linux/dma-mapping.h>
 #include <linux/usb/xhci-sideband.h>
+#include <linux/bitfield.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"
@@ -3507,7 +3508,7 @@ static void xhci_calculate_streams_entries(struct xhci_hcd *xhci,
 	 * level page entries), but that's an optional feature for xHCI host
 	 * controllers. xHCs must support at least 4 stream IDs.
 	 */
-	max_streams = HCC_MAX_PSA(xhci->hcc_params);
+	max_streams = GET_MAX_PSA_SIZE(xhci->hcc_params);
 	if (*num_stream_ctxs > max_streams) {
 		xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n",
 				max_streams);
@@ -3637,7 +3638,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
 
 	/* MaxPSASize value 0 (2 streams) means streams are not supported */
 	if ((xhci->quirks & XHCI_BROKEN_STREAMS) ||
-			HCC_MAX_PSA(xhci->hcc_params) < 4) {
+			GET_MAX_PSA_SIZE(xhci->hcc_params) < 4) {
 		xhci_dbg(xhci, "xHCI controller does not support streams.\n");
 		return -ENOSYS;
 	}
@@ -4608,7 +4609,7 @@ static int xhci_calculate_hird_besl(struct xhci_hcd *xhci,
 	int besl_device = 0;
 	u32 field;
 
-	u2del = HCS_U2_LATENCY(xhci->hcs_params3);
+	u2del = FIELD_GET(HCS_U2_LATENCY, xhci->hcs_params3);
 	field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
 
 	if (field & USB_BESL_SUPPORT) {
@@ -5460,26 +5461,28 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 		xhci_warn(xhci, "Host controller not accessible, removed?\n");
 		return -ENODEV;
 	}
-	xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase);
+	xhci->op_regs = hcd->regs + FIELD_GET(HC_LENGTH, hc_capbase);
+
 	xhci->run_regs = hcd->regs +
 		(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
 	/* Cache read-only capability registers */
 	hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
 	xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
 	xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
-	xhci->hci_version = HC_VERSION(hc_capbase);
+	xhci->hci_version = FIELD_GET(HC_VERSION, hc_capbase);
 	xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
 	if (xhci->hci_version > 0x100)
 		xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
 
 	xhci->dma_mask_bits = 64;
-	xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS);
-	xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
+	xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS);
+	xhci->max_ports = min(FIELD_GET(HCS_MAX_PORTS, hcs_params1), MAX_HC_PORTS);
+
 	/* xhci-plat or xhci-pci might have set max_interrupters already */
 	if (!xhci->max_interrupters)
-		xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS);
-	else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
-		xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
+		xhci->max_interrupters = min(FIELD_GET(HCS_MAX_INTRS, hcs_params1), MAX_HC_INTRS);
+	else if (xhci->max_interrupters > FIELD_GET(HCS_MAX_INTRS, hcs_params1))
+		xhci->max_interrupters = FIELD_GET(HCS_MAX_INTRS, hcs_params1);
 
 	xhci->quirks |= quirks;
 
Signature · 1 line
2.43.0
#12 lore raw

PATCH 12/17 usb: xhci: use 64-bit Addressing Capability macro

In reply to Mathias Nyman

From: Niklas Neronin <[email protected]>

Simplify by replace BIT(0) call with its relevant macro.

---
 drivers/usb/host/xhci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/host/xhci.c +1 -1
index 2faf91966890..174fea16cd50 100644
@@ -5523,7 +5523,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 	 * DMA_BIT_MASK(32)) in this xhci_gen_setup().
 	 */
 	if (xhci->quirks & XHCI_NO_64BIT_SUPPORT)
-		xhci->hcc_params &= ~BIT(0);
+		xhci->hcc_params &= ~HCC_64BIT_ADDR;
 
 	/*
 	 * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports
Signature · 1 line
2.43.0
#13 lore raw

PATCH 13/17 usb: xhci: remove redundant function wrapper

In reply to Mathias Nyman

From: Niklas Neronin <[email protected]>

The function ring_doorbell_for_active_rings() rings the doorbell for any rings with pending URBs. It has a trivial wrapper, xhci_ring_doorbell_for_active_rings(), which takes the same arguments and simply calls the former.

Since the wrapper adds no functionality, remove it and rename ring_doorbell_for_active_rings() to xhci_ring_doorbell_for_active_rings().

---
 drivers/usb/host/xhci-ring.c | 20 ++++++--------------
 1 file changed, 6 insertions(+), 14 deletions(-)
drivers/usb/host/xhci-ring.c +6 -14
index 80d53acc37fd..21ef7284e957 100644
@@ -575,9 +575,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
 }
 
 /* Ring the doorbell for any rings with pending URBs */
-static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
-		unsigned int slot_id,
-		unsigned int ep_index)
+void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, unsigned int slot_id,
+					 unsigned int ep_index)
 {
 	unsigned int stream_id;
 	struct xhci_virt_ep *ep;
@@ -600,13 +599,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
 	}
 }
 
-void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
-		unsigned int slot_id,
-		unsigned int ep_index)
-{
-	ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
-}
-
 static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci,
 					     unsigned int slot_id,
 					     unsigned int ep_index)
@@ -1303,7 +1295,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
 
 	/* Otherwise ring the doorbell(s) to restart queued transfers */
 	xhci_giveback_invalidated_tds(ep);
-	ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+	xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 }
 
 static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
@@ -1546,13 +1538,13 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 			 __func__);
 		xhci_invalidate_cancelled_tds(ep);
 		/* Try to restart the endpoint if all is done */
-		ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+		xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 		/* Start giving back any TDs invalidated above */
 		xhci_giveback_invalidated_tds(ep);
 	} else {
 		/* Restart any rings with pending URBs */
 		xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__);
-		ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+		xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 	}
 }
 
@@ -1587,7 +1579,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
 
 	/* if this was a soft reset, then restart */
 	if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP)
-		ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+		xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 }
 
 static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command,
Signature · 1 line
2.43.0
#14 lore raw

PATCH 14/17 usb: xhci: remove redundant 'xhci' pointer from endpoint struct

In reply to Mathias Nyman

From: Niklas Neronin <[email protected]>

The 'xhci_virt_ep' struct currently contains a pointer to its parent 'xhci_hcd' struct. Since all endpoint-related structs are contained within 'xhci_hcd', this pointer is redundant.

Remove the 'xhci' pointer from 'xhci_virt_ep' and instead pass it explicitly to functions that require it, as some already do it. This change reduces unnecessary complexity and aligns the code with the rest of the xhci driver.

Memory impact:
For each device connected a struct 'xhci_virt_device' is allocated, this struct conatains a 31 slot array of struct 'xhci_virt_ep'. A USB hub consumes 1 slot, but every downstream device consumes another slot.
This means that the total memory saved buy this patch is:
Devices * 31 * 8 bytes

---
 drivers/usb/host/xhci-mem.c  |  1 -
 drivers/usb/host/xhci-ring.c | 53 +++++++++++++++++-------------------
 drivers/usb/host/xhci.c      |  2 +-
 drivers/usb/host/xhci.h      |  3 +-
 4 files changed, 27 insertions(+), 32 deletions(-)
drivers/usb/host/xhci-mem.c +0 -1
index 5717bd830189..cc916ee3cb71 100644
@@ -1002,7 +1002,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
 	for (i = 0; i < 31; i++) {
 		dev->eps[i].ep_index = i;
 		dev->eps[i].vdev = dev;
-		dev->eps[i].xhci = xhci;
 		INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
 		INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
 	}
drivers/usb/host/xhci-ring.c +25 -28

149 lines not on this page · raw message

drivers/usb/host/xhci.c +1 -1
index 174fea16cd50..a9e47e178c28 100644
@@ -1857,7 +1857,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 		/* and cancelled TDs can be given back right away */
 		xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n",
 				urb->dev->slot_id, ep_index, ep->ep_state);
-		xhci_process_cancelled_tds(ep);
+		xhci_process_cancelled_tds(xhci, ep);
 	} else {
 		/* Otherwise, queue a new Stop Endpoint command */
 		command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
drivers/usb/host/xhci.h +1 -2
index 670533dc1d97..66235f755f34 100644
@@ -685,7 +685,6 @@ struct xhci_virt_ep {
 #define EP_DROP_PENDING		BIT(9) /* port disconnect or link error, don't restart */
 	/* ----  Related to URB cancellation ---- */
 	struct list_head	cancelled_td_list;
-	struct xhci_hcd		*xhci;
 	/* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
 	 * command.  We'll need to update the ring's dequeue segment and dequeue
 	 * pointer after the command completes.
@@ -1961,7 +1960,7 @@ unsigned int count_trbs(u64 addr, u64 len);
 unsigned int xhci_num_trbs_free(struct xhci_ring *ring);
 int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
 			    int suspend, gfp_t gfp_flags);
-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep);
+void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep);
 void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
 			      struct xhci_interrupter *ir,
 			      bool clear_ehb);
Signature · 1 line
2.43.0
#15 lore raw

PATCH 15/17 usb: xhci: replace Unicode quotes with ASCII apostrophes

In reply to Mathias Nyman

From: Niklas Neronin <[email protected]>

Non-ASCII characters trigger git send-email to prompt for encoding on each modification near them, which is unnecessary and annoying. Using plain ASCII avoids these prompts and does not change its meaning.

This change only affects comments and has no functional impact.

---
 drivers/usb/host/xhci-hub.c | 2 +-
 drivers/usb/host/xhci-mem.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/usb/host/xhci-hub.c +1 -1
index 17ac05516642..470bafe1802b 100644
@@ -1295,7 +1295,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 			}
 			/* In spec software should not attempt to suspend
 			 * a port unless the port reports that it is in the
-			 * enabled (PED = ‘1’,PLS < ‘3’) state.
+			 * enabled (PED = '1',PLS < '3') state.
 			 */
 			portsc = xhci_portsc_readl(port);
 			if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) ||
drivers/usb/host/xhci-mem.c +1 -1
index cc916ee3cb71..7a21ac81f9c8 100644
@@ -2084,7 +2084,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
 		       addr, port_offset, port_count, major_revision);
 	/* Port count includes the current port offset */
 	if (port_offset == 0 || (port_offset + port_count - 1) > num_ports)
-		/* WTF? "Valid values are ‘1’ to MaxPorts" */
+		/* WTF? "Valid values are '1' to MaxPorts" */
 		return;
 
 	port_cap = &xhci->port_caps[xhci->num_port_caps++];
Signature · 1 line
2.43.0
#16 lore raw

PATCH 16/17 usb: xhci: Handle USB3 port events when there is one roothub

In reply to Mathias Nyman

From: Semih Baskan <[email protected]>

handle_port_status() drops every USB3 port event when xhci->shared_hcd is NULL. The check dates from a time when xhci-plat always created a shared hcd, so a NULL one could only mean the hcd had been removed.

Since commit 4736ebd7fcaf ("usb: host: xhci-plat: omit shared hcd if either root hub has no ports") that is no longer true. A controller whose USB2 root hub has no ports gets a single roothub, the USB3 rhub is served by the main hcd, and shared_hcd stays NULL for the lifetime of the device. Every SuperSpeed port event is then thrown away as bogus behind a debug message, so devices never enumerate even though the port sees the device and its change bits stay set:

0x006a1203 Powered Connected Enabled Link:U0 PortSpeed:4
Change: CSC WRC PRC PLC

Broadcom Northstar is such a controller. USB3 works there up to 5.15 and stops working from 5.19 onwards.

Ask xhci_get_usb3_hcd() instead. It returns the shared hcd when there is one, the main hcd when the USB2 root hub has no ports, and NULL once the shared hcd is gone, which keeps the original meaning of the check.

Tested on an Asus RT-N18U (BCM47081), which has a single roothub. Before the change nothing enumerates on the USB3 port; after it SuperSpeed devices enumerate normally over repeated connect and disconnect cycles, the change bits shown above clear, and USB2 is unaffected on both ports.

Fixes 4736ebd7fcaf ("usb: host: xhci-plat: omit shared hcd if either root hub has no ports")
---
 drivers/usb/host/xhci-ring.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/host/xhci-ring.c +1 -1
index 6e60959e3faa..69bd582c7e2b 100644
@@ -2019,7 +2019,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
 		vdev = xhci->devs[port->slot_id];
 
 	/* We might get interrupts after shared_hcd is removed */
-	if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) {
+	if (port->rhub == &xhci->usb3_rhub && xhci_get_usb3_hcd(xhci) == NULL) {
 		xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n");
 		bogus_port_status = true;
 		goto cleanup;
Signature · 1 line
2.43.0
#17 lore raw

PATCH 17/17 usb: xhci: Handle bogus TRB pointers in Missed Service Error events

In reply to Mathias Nyman

From: Michal Pecio <[email protected]>

xHCI 1.0 allowed these pointers to be zero. Some Intel chipsets from the era usually set it to zero, but sometimes (apparently) to the next TRB after the one referenced by the previous transfer event on the endpoint.

Usually that's indeed the missed TD, but it may also be the last TRB of a two-TRB TD already completed with Short Packet on its first TRB. Then the driver skips all pending TDs, failing to find a match.

When handling Missed Service Error, scan TD list twice and only really skip TDs in the second pass if the first pass found a match. This won't catch bogus pointers to wrong TDs, but such a bug would be practically impossible to detect automatically and isn't known to exist.

Fixes d0b619599e52 ("usb: xhci: Expedite skipping missed isoch TDs on modern HCs")
---
 drivers/usb/host/xhci-ring.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/usb/host/xhci-ring.c +16 -2
index 69bd582c7e2b..97a1b53c18ef 100644
@@ -2597,6 +2597,17 @@ static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci,
 	}
 }
 
+static struct xhci_td *find_td_by_dma(struct xhci_ring *ep_ring, dma_addr_t dma)
+{
+	struct xhci_td *td;
+
+	if (dma)
+		list_for_each_entry(td, &ep_ring->td_list, td_list)
+			if (trb_in_td(td, dma))
+				return td;
+	return NULL;
+}
+
 /*
  * If this function returns an error condition, it means it got a Transfer
  * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2791,8 +2802,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		xhci_dequeue_td(xhci, td, ep_ring, td->status);
 	}
 
-	/* If the TRB pointer is NULL, missed TDs will be skipped on the next event */
-	if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
+	/*
+	 * We don't know how many TDs were missed when ep_trb_dma is zero (as permitted by
+	 * xHCI 1.0) or bogus. Bail out leaving ep->skip set, next event will sort it out.
+	 */
+	if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !find_td_by_dma(ep_ring, ep_trb_dma))
 		return 0;
 
 	if (list_empty(&ep_ring->td_list)) {
Signature · 1 line
2.43.0