Skip to content

Commit dcfe437

Browse files
Thinh Nguyengregkh
authored andcommitted
usb: dwc3: gadget: Reinitiate stream for all host NoStream behavior
There are too many different host behaviors when it comes to NoStream handling, and not everyone follows the USB and xHCI spec. The DWC3 driver attempts to do some guess work to interop with different hosts, but it can't cover everything. Let's keep it simple and treat every host the same: just retry on NoStream after a 100ms of no response delay. Note that some hosts cannot handle frequent retries. This may affect performance on certain defective hosts, but NoStream is a rare occurrence and interoperability is more important. Signed-off-by: Thinh Nguyen <[email protected]> Link: https://lore.kernel.org/r/b92ae94c86f01f165d5f178b7767898573b6dc75.1736819308.git.Thinh.Nguyen@synopsys.com Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f386bfa commit dcfe437

File tree

2 files changed

+59
-53
lines changed

2 files changed

+59
-53
lines changed

drivers/usb/dwc3/core.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ struct dwc3_event_buffer {
743743
*/
744744
struct dwc3_ep {
745745
struct usb_ep endpoint;
746+
struct delayed_work nostream_work;
746747
struct list_head cancelled_list;
747748
struct list_head pending_list;
748749
struct list_head started_list;
@@ -765,7 +766,7 @@ struct dwc3_ep {
765766
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
766767
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
767768
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
768-
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
769+
#define DWC3_EP_STREAM_PRIMED BIT(10)
769770
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
770771
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
771772
#define DWC3_EP_DELAY_STOP BIT(13)

drivers/usb/dwc3/gadget.c

Lines changed: 57 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -996,8 +996,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
996996

997997
/*
998998
* All stream eps will reinitiate stream on NoStream
999-
* rejection until we can determine that the host can
1000-
* prime after the first transfer.
999+
* rejection.
10011000
*
10021001
* However, if the controller is capable of
10031002
* TXF_FLUSH_BYPASS, then IN direction endpoints will
@@ -3300,6 +3299,50 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
33003299
return dwc3_alloc_trb_pool(dep);
33013300
}
33023301

3302+
#define nostream_work_to_dep(w) (container_of(to_delayed_work(w), struct dwc3_ep, nostream_work))
3303+
static void dwc3_nostream_work(struct work_struct *work)
3304+
{
3305+
struct dwc3_ep *dep = nostream_work_to_dep(work);
3306+
struct dwc3 *dwc = dep->dwc;
3307+
unsigned long flags;
3308+
3309+
spin_lock_irqsave(&dwc->lock, flags);
3310+
if (dep->flags & DWC3_EP_STREAM_PRIMED)
3311+
goto out;
3312+
3313+
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
3314+
(!DWC3_MST_CAPABLE(&dwc->hwparams) &&
3315+
!(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
3316+
goto out;
3317+
/*
3318+
* If the host rejects a stream due to no active stream, by the
3319+
* USB and xHCI spec, the endpoint will be put back to idle
3320+
* state. When the host is ready (buffer added/updated), it will
3321+
* prime the endpoint to inform the usb device controller. This
3322+
* triggers the device controller to issue ERDY to restart the
3323+
* stream. However, some hosts don't follow this and keep the
3324+
* endpoint in the idle state. No prime will come despite host
3325+
* streams are updated, and the device controller will not be
3326+
* triggered to generate ERDY to move the next stream data. To
3327+
* workaround this and maintain compatibility with various
3328+
* hosts, force to reinitiate the stream until the host is ready
3329+
* instead of waiting for the host to prime the endpoint.
3330+
*/
3331+
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
3332+
unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
3333+
3334+
dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
3335+
} else {
3336+
dep->flags |= DWC3_EP_DELAY_START;
3337+
dwc3_stop_active_transfer(dep, true, true);
3338+
spin_unlock_irqrestore(&dwc->lock, flags);
3339+
return;
3340+
}
3341+
out:
3342+
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
3343+
spin_unlock_irqrestore(&dwc->lock, flags);
3344+
}
3345+
33033346
static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
33043347
{
33053348
struct dwc3_ep *dep;
@@ -3345,6 +3388,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
33453388
INIT_LIST_HEAD(&dep->pending_list);
33463389
INIT_LIST_HEAD(&dep->started_list);
33473390
INIT_LIST_HEAD(&dep->cancelled_list);
3391+
INIT_DELAYED_WORK(&dep->nostream_work, dwc3_nostream_work);
33483392

33493393
dwc3_debugfs_create_endpoint_dir(dep);
33503394

@@ -3744,66 +3788,27 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
37443788
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
37453789
const struct dwc3_event_depevt *event)
37463790
{
3747-
struct dwc3 *dwc = dep->dwc;
3748-
37493791
if (event->status == DEPEVT_STREAMEVT_FOUND) {
3750-
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
3751-
goto out;
3792+
cancel_delayed_work(&dep->nostream_work);
3793+
dep->flags |= DWC3_EP_STREAM_PRIMED;
3794+
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
3795+
return;
37523796
}
37533797

37543798
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
37553799
switch (event->parameters) {
37563800
case DEPEVT_STREAM_PRIME:
3757-
/*
3758-
* If the host can properly transition the endpoint state from
3759-
* idle to prime after a NoStream rejection, there's no need to
3760-
* force restarting the endpoint to reinitiate the stream. To
3761-
* simplify the check, assume the host follows the USB spec if
3762-
* it primed the endpoint more than once.
3763-
*/
3764-
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
3765-
if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
3766-
dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
3767-
else
3768-
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
3769-
}
3770-
3801+
cancel_delayed_work(&dep->nostream_work);
3802+
dep->flags |= DWC3_EP_STREAM_PRIMED;
3803+
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
37713804
break;
37723805
case DEPEVT_STREAM_NOSTREAM:
3773-
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
3774-
!(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
3775-
(!DWC3_MST_CAPABLE(&dwc->hwparams) &&
3776-
!(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
3777-
break;
3778-
3779-
/*
3780-
* If the host rejects a stream due to no active stream, by the
3781-
* USB and xHCI spec, the endpoint will be put back to idle
3782-
* state. When the host is ready (buffer added/updated), it will
3783-
* prime the endpoint to inform the usb device controller. This
3784-
* triggers the device controller to issue ERDY to restart the
3785-
* stream. However, some hosts don't follow this and keep the
3786-
* endpoint in the idle state. No prime will come despite host
3787-
* streams are updated, and the device controller will not be
3788-
* triggered to generate ERDY to move the next stream data. To
3789-
* workaround this and maintain compatibility with various
3790-
* hosts, force to reinitiate the stream until the host is ready
3791-
* instead of waiting for the host to prime the endpoint.
3792-
*/
3793-
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
3794-
unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
3795-
3796-
dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
3797-
} else {
3798-
dep->flags |= DWC3_EP_DELAY_START;
3799-
dwc3_stop_active_transfer(dep, true, true);
3800-
return;
3801-
}
3806+
dep->flags &= ~DWC3_EP_STREAM_PRIMED;
3807+
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
3808+
queue_delayed_work(system_wq, &dep->nostream_work,
3809+
msecs_to_jiffies(100));
38023810
break;
38033811
}
3804-
3805-
out:
3806-
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
38073812
}
38083813

38093814
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,

0 commit comments

Comments
 (0)