Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: dwc3: Fix ep0 handling when getting reset while doing control transfer

According to the databook ep0 should be in setup phase during reset.
If host issues reset between control transfers, ep0 will be in an
invalid state. Fix this by issuing stall and restart on ep0 if it
is not in setup phase.

Also SW needs to complete pending control transfer and setup core for
next setup stage as per data book. Hence check ep0 state during reset
interrupt handling and make sure active transfers on ep0 out/in
endpoint are stopped by queuing ENDXFER command for that endpoint and
restart ep0 out again to receive next setup packet.

Signed-off-by: Mayank Rana <quic_mrana@quicinc.com>
Link: https://lore.kernel.org/r/1651693001-29891-1-git-send-email-quic_mrana@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Mayank Rana and committed by
Greg Kroah-Hartman
9d778f0c 69a1c9a9

+35 -5
+8 -3
drivers/usb/dwc3/ep0.c
··· 218 218 return ret; 219 219 } 220 220 221 - static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) 221 + void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) 222 222 { 223 223 struct dwc3_ep *dep; 224 224 ··· 1088 1088 __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); 1089 1089 } 1090 1090 1091 - static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) 1091 + void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) 1092 1092 { 1093 1093 struct dwc3_gadget_ep_cmd_params params; 1094 1094 u32 cmd; 1095 1095 int ret; 1096 1096 1097 - if (!dep->resource_index) 1097 + /* 1098 + * For status/DATA OUT stage, TRB will be queued on ep0 out 1099 + * endpoint for which resource index is zero. Hence allow 1100 + * queuing ENDXFER command for ep0 out endpoint. 1101 + */ 1102 + if (!dep->resource_index && dep->number) 1098 1103 return; 1099 1104 1100 1105 cmd = DWC3_DEPCMD_ENDTRANSFER;
+25 -2
drivers/usb/dwc3/gadget.c
··· 882 882 reg |= DWC3_DALEPENA_EP(dep->number); 883 883 dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); 884 884 885 + dep->trb_dequeue = 0; 886 + dep->trb_enqueue = 0; 887 + 885 888 if (usb_endpoint_xfer_control(desc)) 886 889 goto out; 887 890 888 891 /* Initialize the TRB ring */ 889 - dep->trb_dequeue = 0; 890 - dep->trb_enqueue = 0; 891 892 memset(dep->trb_pool, 0, 892 893 sizeof(struct dwc3_trb) * DWC3_TRB_NUM); 893 894 ··· 2742 2741 2743 2742 /* begin to receive SETUP packets */ 2744 2743 dwc->ep0state = EP0_SETUP_PHASE; 2744 + dwc->ep0_bounced = false; 2745 2745 dwc->link_state = DWC3_LINK_STATE_SS_DIS; 2746 2746 dwc->delayed_status = false; 2747 2747 dwc3_ep0_out_start(dwc); ··· 3822 3820 } 3823 3821 3824 3822 dwc3_reset_gadget(dwc); 3823 + 3824 + /* 3825 + * From SNPS databook section 8.1.2, the EP0 should be in setup 3826 + * phase. So ensure that EP0 is in setup phase by issuing a stall 3827 + * and restart if EP0 is not in setup phase. 3828 + */ 3829 + if (dwc->ep0state != EP0_SETUP_PHASE) { 3830 + unsigned int dir; 3831 + 3832 + dir = !!dwc->ep0_expect_in; 3833 + if (dwc->ep0state == EP0_DATA_PHASE) 3834 + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); 3835 + else 3836 + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); 3837 + 3838 + dwc->eps[0]->trb_enqueue = 0; 3839 + dwc->eps[1]->trb_enqueue = 0; 3840 + 3841 + dwc3_ep0_stall_and_restart(dwc); 3842 + } 3843 + 3825 3844 /* 3826 3845 * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a 3827 3846 * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
+2
drivers/usb/dwc3/gadget.h
··· 110 110 void dwc3_ep0_interrupt(struct dwc3 *dwc, 111 111 const struct dwc3_event_depevt *event); 112 112 void dwc3_ep0_out_start(struct dwc3 *dwc); 113 + void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep); 114 + void dwc3_ep0_stall_and_restart(struct dwc3 *dwc); 113 115 int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); 114 116 int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); 115 117 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,