[PATCH v1] usb: host: xhci: reliable endpoint reset after halt

2017-05-26 Thread gavinli
From: Gavin Li 

If a stalling TRB is cancelled and NOOP'ed in xhci_handle_cmd_stop_ep(),
finish_td() never gets called to reset the halted endpoint and the
endpoint remains indefinitely stalled. This patch ensures that
xhci_cleanup_halted_endpoint() is called after a TRB completion if the
endpoint is halted, irregardless of the status of any pending TRBs.

Signed-off-by: Gavin Li 
---
 drivers/usb/host/xhci-ring.c | 34 +-
 drivers/usb/host/xhci.h  |  1 +
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 03f63f50afb6..85bc53d0d43e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1113,11 +1113,13 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
 {
struct xhci_virt_device *vdev;
struct xhci_ep_ctx *ep_ctx;
+   struct xhci_virt_ep *ep;
unsigned int ep_index;
 
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
vdev = xhci->devs[slot_id];
ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
+   ep = >devs[slot_id]->eps[ep_index];
trace_xhci_handle_cmd_reset_ep(ep_ctx);
 
/* This command will only fail if the endpoint wasn't halted,
@@ -1130,6 +1132,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
 * command complete before the endpoint can be used.  Queue that here
 * because the HW can't handle two commands being queued in a row.
 */
+   ep->ep_state &= ~EP_RESET_PENDING;
if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
struct xhci_command *command;
 
@@ -1145,7 +1148,11 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
xhci_ring_cmd_db(xhci);
} else {
/* Clear our internal halted state */
-   xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
+   ep->ep_state &= ~EP_HALTED;
+   if (!(ep->ep_state & SET_DEQ_PENDING)) {
+   /* Restart any rings with pending URBs */
+   ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+   }
}
 }
 
@@ -1800,19 +1807,25 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
 static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id,
-   struct xhci_td *td, union xhci_trb *ep_trb)
+   struct xhci_td *td)
 {
struct xhci_virt_ep *ep = >devs[slot_id]->eps[ep_index];
struct xhci_command *command;
+
+   if (ep->ep_state & EP_RESET_PENDING)
+   return;
+
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
if (!command)
return;
 
-   ep->ep_state |= EP_HALTED;
+   ep->ep_state |= EP_HALTED | EP_RESET_PENDING;
ep->stopped_stream = stream_id;
 
xhci_queue_reset_ep(xhci, command, slot_id, ep_index);
-   xhci_cleanup_stalled_ring(xhci, ep_index, td);
+   if (td != NULL && !(ep->ep_state & SET_DEQ_PENDING)) {
+   xhci_cleanup_stalled_ring(xhci, ep_index, td);
+   }
 
ep->stopped_stream = 0;
 
@@ -1947,7 +1960,7 @@ static int finish_td(struct xhci_hcd *xhci, struct 
xhci_td *td,
 * The class driver clears the device side halt later.
 */
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
-   ep_ring->stream_id, td, ep_trb);
+   ep_ring->stream_id, td);
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
@@ -2588,6 +2601,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 */
} while (handling_skipped_tds);
 
+   /*
+* If a cancelled TRB halts the endpoint, reset it here.
+*/
+   if (trb_comp_code == COMP_STALL_ERROR ||
+   xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+   trb_comp_code)) {
+   /* No harm in calling this twice; second call will be a no-op */
+   xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
+   ep_ring->stream_id, td);
+   }
+
return 0;
 }
 
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a986d5e..0f7439f4d414 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -922,6 +922,7 @@ struct xhci_virt_ep {
 #define EP_HAS_STREAMS (1 << 4)
 /* Transitioning the endpoint to not using streams, don't enqueue URBs */
 #define EP_GETTING_NO_STREAMS  (1 << 5)
+#define EP_RESET_PENDING  (1 << 6) /* For stall recovery */
/*   Related to URB cancellation 

[PATCH] usb: host: xhci: reliable endpoint reset after halt

2017-05-25 Thread gavinli
From: Gavin Li 

If a stalling TRB is cancelled and NOOP'ed in xhci_handle_cmd_stop_ep(),
finish_td() never gets called to reset the halted endpoint and the
endpoint remains indefinitely stalled. This patch ensures that
xhci_cleanup_halted_endpoint() is called after a TRB completion if the
endpoint is halted, irregardless of the status of any pending TRBs.
---
 drivers/usb/host/xhci-ring.c | 34 +-
 drivers/usb/host/xhci.h  |  1 +
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 03f63f50afb6..85bc53d0d43e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1113,11 +1113,13 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
 {
struct xhci_virt_device *vdev;
struct xhci_ep_ctx *ep_ctx;
+   struct xhci_virt_ep *ep;
unsigned int ep_index;
 
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
vdev = xhci->devs[slot_id];
ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
+   ep = >devs[slot_id]->eps[ep_index];
trace_xhci_handle_cmd_reset_ep(ep_ctx);
 
/* This command will only fail if the endpoint wasn't halted,
@@ -1130,6 +1132,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
 * command complete before the endpoint can be used.  Queue that here
 * because the HW can't handle two commands being queued in a row.
 */
+   ep->ep_state &= ~EP_RESET_PENDING;
if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
struct xhci_command *command;
 
@@ -1145,7 +1148,11 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
xhci_ring_cmd_db(xhci);
} else {
/* Clear our internal halted state */
-   xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
+   ep->ep_state &= ~EP_HALTED;
+   if (!(ep->ep_state & SET_DEQ_PENDING)) {
+   /* Restart any rings with pending URBs */
+   ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+   }
}
 }
 
@@ -1800,19 +1807,25 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
 static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id,
-   struct xhci_td *td, union xhci_trb *ep_trb)
+   struct xhci_td *td)
 {
struct xhci_virt_ep *ep = >devs[slot_id]->eps[ep_index];
struct xhci_command *command;
+
+   if (ep->ep_state & EP_RESET_PENDING)
+   return;
+
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
if (!command)
return;
 
-   ep->ep_state |= EP_HALTED;
+   ep->ep_state |= EP_HALTED | EP_RESET_PENDING;
ep->stopped_stream = stream_id;
 
xhci_queue_reset_ep(xhci, command, slot_id, ep_index);
-   xhci_cleanup_stalled_ring(xhci, ep_index, td);
+   if (td != NULL && !(ep->ep_state & SET_DEQ_PENDING)) {
+   xhci_cleanup_stalled_ring(xhci, ep_index, td);
+   }
 
ep->stopped_stream = 0;
 
@@ -1947,7 +1960,7 @@ static int finish_td(struct xhci_hcd *xhci, struct 
xhci_td *td,
 * The class driver clears the device side halt later.
 */
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
-   ep_ring->stream_id, td, ep_trb);
+   ep_ring->stream_id, td);
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
@@ -2588,6 +2601,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 */
} while (handling_skipped_tds);
 
+   /*
+* If a cancelled TRB halts the endpoint, reset it here.
+*/
+   if (trb_comp_code == COMP_STALL_ERROR ||
+   xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+   trb_comp_code)) {
+   /* No harm in calling this twice; second call will be a no-op */
+   xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
+   ep_ring->stream_id, td);
+   }
+
return 0;
 }
 
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a986d5e..0f7439f4d414 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -922,6 +922,7 @@ struct xhci_virt_ep {
 #define EP_HAS_STREAMS (1 << 4)
 /* Transitioning the endpoint to not using streams, don't enqueue URBs */
 #define EP_GETTING_NO_STREAMS  (1 << 5)
+#define EP_RESET_PENDING  (1 << 6) /* For stall recovery */
/*   Related to URB cancellation  */
struct list_head

[PATCH] cdc-acm: fix wrong pipe type on rx interrupt xfers

2016-08-12 Thread gavinli
From: Gavin Li 

This fixes the "BOGUS urb xfer" warning logged by usb_submit_urb().

Signed-off-by: Gavin Li 
---
 drivers/usb/class/cdc-acm.c | 5 ++---
 drivers/usb/class/cdc-acm.h | 1 -
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index a6c4a1b..2ff86f5 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1405,7 +1405,6 @@ made_compressed_probe:
spin_lock_init(>write_lock);
spin_lock_init(>read_lock);
mutex_init(>mutex);
-   acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
acm->is_int_ep = usb_endpoint_xfer_int(epread);
if (acm->is_int_ep)
acm->bInterval = epread->bInterval;
@@ -1445,14 +1444,14 @@ made_compressed_probe:
urb->transfer_dma = rb->dma;
if (acm->is_int_ep) {
usb_fill_int_urb(urb, acm->dev,
-acm->rx_endpoint,
+usb_rcvintpipe(usb_dev, 
epread->bEndpointAddress),
 rb->base,
 acm->readsize,
 acm_read_bulk_callback, rb,
 acm->bInterval);
} else {
usb_fill_bulk_urb(urb, acm->dev,
- acm->rx_endpoint,
+ usb_rcvbulkpipe(usb_dev, 
epread->bEndpointAddress),
  rb->base,
  acm->readsize,
  acm_read_bulk_callback, rb);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 05ce308..1f1eabf 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -96,7 +96,6 @@ struct acm {
struct acm_rb read_buffers[ACM_NR];
struct acm_wb *putbuffer;   /* for 
acm_tty_put_char() */
int rx_buflimit;
-   int rx_endpoint;
spinlock_t read_lock;
int write_used; /* number of non-empty 
write buffers */
int transmitting;
-- 
2.9.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html