usb: dwc3: gadget: Handle streams
Subsystems (2)
Thinh Nguyen
maintainerv2 cover usb: dwc3: gadget: Handle streams
Overview of stream transfer requirement:
* A transfer will have a set of TRBs of the same stream ID.
* A transfer is started with a stream ID in START_TRANSFER command.
* A new stream will only start when the previous completes.
Overview of stream events:
* A "prime" from host indicates that its endpoints are active (buffers prepared and ready to receive/transmit data). The controller automatically initiates stream if it sees this.
* A "NoStream" rejection event indicates that the host isn't ready. Host will put the endpoint back to idle state. Device may need to reinitiate the stream to start transfer again.
* A Stream Found event means host accepted device initiated stream. Nothing needs to be done from driver.
To initiate a stream, the driver will issue START_TRANSFER command with a stream ID. To reinitiate the stream, the driver must issue END_TRANSFER and restart the transfer with START_TRANSFER command with the same stream ID.
This implementation handles device-initated streams (e.g. UASP driver). It also handles some hosts' quirky behavior where they only prime each endpoint once.
Prerequisite:
This series requires DWC_usb32 patch series
https://patchwork.kernel.org/project/linux-usb/list/?series=269641
[PATCH 1/2] usb: dwc3: Add support for DWC_usb32 IP [PATCH 2/2] usb: dwc3: Get MDWIDTH for DWC_usb32
Changes in v2:
- Update cover letter
- Split handling of stream and of transfer completion into smaller patches
- Reword usb_request->is_last to strictly use for streams
- Enforce transfer completion handling to only for stream capable endpoints
Thinh Nguyen (11):
usb: gadget: Introduce usb_request->is_last
usb: gadget: f_tcm: Inform last stream request
usb: dwc3: gadget: Continue to process pending requests
usb: dwc3: gadget: Check for in-progress END_TRANSFER
usb: dwc3: gadget: Refactor TRB completion handler
usb: dwc3: gadget: Enable XferComplete event
usb: dwc3: gadget: Handle XferComplete for streams
usb: dwc3: gadget: Wait for transfer completion
usb: dwc3: gadget: Don't prepare beyond a transfer
usb: dwc3: gadget: Handle stream transfers
usb: dwc3: gadget: Use SET_EP_PRIME for NoStream
drivers/usb/dwc3/core.h | 12 ++ drivers/usb/dwc3/debug.h | 2 + drivers/usb/dwc3/gadget.c | 225 +++++++++++++++++++++++++++++++----- drivers/usb/gadget/function/f_tcm.c | 3 + include/linux/usb/gadget.h | 3 + 5 files changed, 215 insertions(+), 30 deletions(-)
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 1/11 usb: gadget: Introduce usb_request->is_last
To take advantage of DWC3 internal TRB prefetch and cache for performance, inform the controller the last request with stream_id before switching to a different stream transfer. This allows the controller to maintain its transfer burst within the stream ID.
Introduce the usb-request is_last field to help inform the DWC3 controller of this.
--- include/linux/usb/gadget.h | 3 +++ 1 file changed, 3 insertions(+)
include/linux/usb/gadget.h +3 -0
@@ -42,6 +42,8 @@ struct usb_ep;* @num_mapped_sgs: number of SG entries mapped to DMA (internal) * @length: Length of that data * @stream_id: The stream id, when USB3.0 bulk streams are being used +* @is_last: Indicates if this is the last request of a stream_id before +* switching to a different stream (required for DWC3 controllers). * @no_interrupt: If true, hints that no completion irq is needed. * Helpful sometimes with deep request queues that are handled * directly by DMA controllers.
@@ -104,6 +106,7 @@ struct usb_request {unsigned num_mapped_sgs; unsigned stream_id:16; + unsigned is_last:1; unsigned no_interrupt:1; unsigned zero:1; unsigned short_not_ok:1;
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 2/11 usb: gadget: f_tcm: Inform last stream request
In reply to Thinh NguyenSet the request->is_last to each stream request to indicate that the request is the last stream request of a transfer. The DWC3 controller needs to know this info to properly switch streams. The current implementation of f_tcm uses a single request per transfer, so every stream request is the last of its stream.
--- drivers/usb/gadget/function/f_tcm.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/usb/gadget/function/f_tcm.c +3 -0
@@ -531,6 +531,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd) stream->req_in->sg = se_cmd->t_data_sg; } + stream->req_in->is_last =1 ; stream->req_in->complete = uasp_status_data_cmpl; stream->req_in->length = se_cmd->data_length; stream->req_in->context = cmd;
@@ -554,6 +555,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)*/ iu->len =cpu_to_be16 (se_cmd->scsi_sense_length); iu->status = se_cmd->scsi_status; + stream->req_status->is_last =1 ; stream->req_status->context = cmd; stream->req_status->length = se_cmd->scsi_sense_length +16 ; stream->req_status->buf = iu;
@@ -991,6 +993,7 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req) req->sg = se_cmd->t_data_sg; } + req->is_last =1 ; req->complete = usbg_data_write_cmpl; req->length = se_cmd->data_length; req->context = cmd;
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 3/11 usb: dwc3: gadget: Continue to process pending requests
In reply to Thinh NguyenIf there are still pending requests because no TRB was available, prepare more when started requests are completed.
Introduce dwc3_gadget_ep_should_continue() to check for incomplete and pending requests to resume updating new TRBs to the controller's TRB cache.
--- drivers/usb/dwc3/gadget.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
drivers/usb/dwc3/gadget.c +21 -3
@@ -2605,10 +2605,8 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, req->request.actual = req->request.length - req->remaining; -if (!dwc3_gadget_ep_request_completed (req)) { -__dwc3_gadget_kick_transfer (dep); +if (!dwc3_gadget_ep_request_completed (req)) goto out; - }dwc3_gadget_giveback (dep, req, status);
@@ -2632,6 +2630,24 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, } } +static bool dwc3_gadget_ep_should_continue (struct dwc3_ep *dep) +{ +struct dwc3_request *req; + +if (!list_empty (&dep->pending_list)) +return true; + +/* +* We only need to check the first entry of the started list. We can +* assume the completed requests are removed from the started list. +*/ + req =next_request (&dep->started_list); +if (!req) +return false; + +return !dwc3_gadget_ep_request_completed (req); +} +static void dwc3_gadget_endpoint_frame_from_event (struct dwc3_ep *dep,const struct dwc3_event_depevt *event) {
@@ -2661,6 +2677,8 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,if (stop)dwc3_stop_active_transfer (dep, true, true); +else if (dwc3_gadget_ep_should_continue (dep)) +__dwc3_gadget_kick_transfer (dep);/* * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 4/11 usb: dwc3: gadget: Check for in-progress END_TRANSFER
In reply to Thinh NguyenWhile handling TRBs completion, if a END_TRANSFER command isn't completed, don't kick new transfer or issue END_TRANSFER command.
--- drivers/usb/dwc3/gadget.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/dwc3/gadget.c +4 -0
@@ -2675,11 +2675,15 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,dwc3_gadget_ep_cleanup_completed_requests (dep, event, status); +if (dep->flags &DWC3_EP_END_TRANSFER_PENDING ) + goto out; +if (stop)dwc3_stop_active_transfer (dep, true, true);else if (dwc3_gadget_ep_should_continue (dep))__dwc3_gadget_kick_transfer (dep); +out:/* * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. * See dwc3_gadget_linksts_change_interrupt() for 1st half.
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 5/11 usb: dwc3: gadget: Refactor TRB completion handler
In reply to Thinh NguyenTo prepare for handling of XferComplete event, let's refactor and split up dwc3_gadget_endpoint_transfer_in_progress() to handle TRBs completion for different events. The handling of TRBs completion will be the same, but the status of XferComplete event is different than XferInProgress. No functional change in this commit.
--- drivers/usb/dwc3/gadget.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-)
drivers/usb/dwc3/gadget.c +26 -19
@@ -2654,34 +2654,22 @@ static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep, dep->frame_number = event->parameters; } -static void dwc3_gadget_endpoint_transfer_in_progress (struct dwc3_ep *dep, -const struct dwc3_event_depevt *event) +static bool dwc3_gadget_endpoint_trbs_complete (struct dwc3_ep *dep, +const struct dwc3_event_depevt *event,int status) {struct dwc3 *dwc = dep->dwc; -unsigned status =0 ; -bool stop = false; - -dwc3_gadget_endpoint_frame_from_event (dep, event); - -if (event->status &DEPEVT_STATUS_BUSERR ) - status = -ECONNRESET ; - -if (event->status &DEPEVT_STATUS_MISSED_ISOC ) { - status = -EXDEV ; - -if (list_empty (&dep->started_list)) - stop = true; - } +bool no_started_trb = true;dwc3_gadget_ep_cleanup_completed_requests (dep, event, status);if (dep->flags &DWC3_EP_END_TRANSFER_PENDING ) goto out; -if (stop) +if (status == -EXDEV &&list_empty (&dep->started_list))dwc3_stop_active_transfer (dep, true, true);else if (dwc3_gadget_ep_should_continue (dep)) -__dwc3_gadget_kick_transfer (dep); +if (__dwc3_gadget_kick_transfer (dep) ==0 ) + no_started_trb = false; out:/*
@@ -2699,7 +2687,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,continue ;if (!list_empty (&dep->started_list)) -return ; +return no_started_trb; } reg =dwc3_readl (dwc->regs,DWC3_DCTL );
@@ -2708,6 +2696,25 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, dwc->u1u2 =0 ; } + +return no_started_trb; +} + +static void dwc3_gadget_endpoint_transfer_in_progress (struct dwc3_ep *dep, +const struct dwc3_event_depevt *event) +{ +int status =0 ; + +if (usb_endpoint_xfer_isoc (dep->endpoint.desc)) +dwc3_gadget_endpoint_frame_from_event (dep, event); + +if (event->status &DEPEVT_STATUS_BUSERR ) + status = -ECONNRESET ; + +if (event->status &DEPEVT_STATUS_MISSED_ISOC ) + status = -EXDEV ; + +dwc3_gadget_endpoint_trbs_complete (dep, event, status); } static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 6/11 usb: dwc3: gadget: Enable XferComplete event
In reply to Thinh NguyenTo switch from one stream to another, this requires the driver to start a new transfer with a specific stream ID. For a transfer to complete, the driver needs to indicate the last TRB of a transfer, and it needs to enable XferComplete event to handle completed TRBs of a transfer. Let's enable this event only for stream capable endpoints.
--- drivers/usb/dwc3/gadget.c | 1 + 1 file changed, 1 insertion(+)
drivers/usb/dwc3/gadget.c +1 -0
@@ -579,6 +579,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)if (usb_ss_max_streams (comp_desc) &&usb_endpoint_xfer_bulk (desc)) { params.param1 |=DWC3_DEPCFG_STREAM_CAPABLE + |DWC3_DEPCFG_XFER_COMPLETE_EN |DWC3_DEPCFG_STREAM_EVENT_EN ; dep->stream_capable = true; }
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 7/11 usb: dwc3: gadget: Handle XferComplete for streams
In reply to Thinh NguyenIn DWC3, to prepare TRBs for streams, all the TRBs of a transfer will use the same stream ID. To start a new stream, the driver needs to wait for the current transfer to complete or ended (by END_TRANFER command). As a result, inform the controller of the last TRB of a transfer so that it knows when a transfer completes and start a new transfer of a new stream.
Even though the transfer completion handling can be applied for other non-isoc endpoints, only do it for streams due to its requirement. It's better to keep the controller's TRB cache full than waiting for transfer completion and starting a new transfer.
--- drivers/usb/dwc3/gadget.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
drivers/usb/dwc3/gadget.c +28 -6
@@ -919,7 +919,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)static void __dwc3_prepare_one_trb (struct dwc3_ep *dep,struct dwc3_trb *trb,dma_addr_t dma,unsigned length,unsigned chain,unsigned node, -unsigned stream_id,unsigned short_not_ok,unsigned no_interrupt) +unsigned stream_id,unsigned short_not_ok, +unsigned no_interrupt,unsigned is_last) {struct dwc3 *dwc = dep->dwc;struct usb_gadget *gadget = &dwc->gadget;
@@ -1012,6 +1013,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,if (chain) trb->ctrl |=DWC3_TRB_CTRL_CHN ; +else if (dep->stream_capable && is_last) + trb->ctrl |=DWC3_TRB_CTRL_LST ;if (usb_endpoint_xfer_bulk (dep->endpoint.desc) && dep->stream_capable) trb->ctrl |=DWC3_TRB_CTRL_SID_SOFN (stream_id);
@@ -1039,6 +1042,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,unsigned stream_id = req->request.stream_id;unsigned short_not_ok = req->request.short_not_ok;unsigned no_interrupt = req->request.no_interrupt; +unsigned is_last = req->request.is_last;if (req->request.num_sgs >0 ) { length =sg_dma_len (req->start_sg);
@@ -1059,7 +1063,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, req->num_trbs++;__dwc3_prepare_one_trb (dep, trb, dma, length, chain, node, - stream_id, short_not_ok, no_interrupt); + stream_id, short_not_ok, no_interrupt, is_last); } static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
@@ -1104,7 +1108,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, maxp - rem, false,1 , req->request.stream_id, req->request.short_not_ok, - req->request.no_interrupt); + req->request.no_interrupt, + req->request.is_last); }else {dwc3_prepare_one_trb (dep, req, chain, i); }
@@ -1148,7 +1153,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,__dwc3_prepare_one_trb (dep, trb, dwc->bounce_addr, maxp - rem, false,1 , req->request.stream_id, req->request.short_not_ok, - req->request.no_interrupt); + req->request.no_interrupt, + req->request.is_last); }else if (req->request.zero && req->request.length && (IS_ALIGNED (req->request.length, maxp))) {struct dwc3 *dwc = dep->dwc;
@@ -1165,7 +1171,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,__dwc3_prepare_one_trb (dep, trb, dwc->bounce_addr,0 , false,1 , req->request.stream_id, req->request.short_not_ok, - req->request.no_interrupt); + req->request.no_interrupt, + req->request.is_last); }else {dwc3_prepare_one_trb (dep, req, false,0 ); }
@@ -2718,6 +2725,19 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,dwc3_gadget_endpoint_trbs_complete (dep, event, status); } +static void dwc3_gadget_endpoint_transfer_complete (struct dwc3_ep *dep, +const struct dwc3_event_depevt *event) +{ +int status =0 ; + + dep->flags &= ~DWC3_EP_TRANSFER_STARTED ; + +if (event->status &DEPEVT_STATUS_BUSERR ) + status = -ECONNRESET ; + +dwc3_gadget_endpoint_trbs_complete (dep, event, status); +} +static void dwc3_gadget_endpoint_transfer_not_ready (struct dwc3_ep *dep,const struct dwc3_event_depevt *event) {
@@ -2781,8 +2801,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dep->flags &= ~DWC3_EP_DELAY_START ; }break ; -case DWC3_DEPEVT_STREAMEVT :case DWC3_DEPEVT_XFERCOMPLETE : +dwc3_gadget_endpoint_transfer_complete (dep, event); +break ; +case DWC3_DEPEVT_STREAMEVT :case DWC3_DEPEVT_RXTXFIFOEVT :break ; }
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 8/11 usb: dwc3: gadget: Wait for transfer completion
In reply to Thinh NguyenIf a transfer is in-progress, any new request should not kick off another transfer. The driver needs to wait for the current transfer to complete before starting off the next transfer. Introduce a new flag DWC3_EP_WAIT_TRANSFER_COMPLETE for this.
--- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/gadget.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/usb/dwc3/core.h +1 -0
@@ -701,6 +701,7 @@ struct dwc3_ep {#define DWC3_EP_END_TRANSFER_PENDING BIT(4)#define DWC3_EP_PENDING_REQUEST BIT(5)#define DWC3_EP_DELAY_START BIT(6) +#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)/* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN BIT(31)
drivers/usb/dwc3/gadget.c +8 -1
@@ -1292,6 +1292,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)return ret; } +if (dep->stream_capable && req->request.is_last) + dep->flags |=DWC3_EP_WAIT_TRANSFER_COMPLETE ; +return 0 ; }
@@ -1498,6 +1501,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)list_add_tail (&req->list, &dep->pending_list); req->status =DWC3_REQUEST_STATUS_QUEUED ; +if (dep->flags &DWC3_EP_WAIT_TRANSFER_COMPLETE ) +return 0 ; +/* Start the transfer only after the END_TRANSFER is completed */ if (dep->flags &DWC3_EP_END_TRANSFER_PENDING ) { dep->flags |=DWC3_EP_DELAY_START ;
@@ -2735,7 +2741,8 @@ static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,if (event->status &DEPEVT_STATUS_BUSERR ) status = -ECONNRESET ; -dwc3_gadget_endpoint_trbs_complete (dep, event, status); +if (dwc3_gadget_endpoint_trbs_complete (dep, event, status)) + dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE ; } static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 9/11 usb: dwc3: gadget: Don't prepare beyond a transfer
In reply to Thinh NguyenDon't prepare TRBs beyond a transfer. In DWC_usb32, its transfer burst capability may try to read and use TRBs beyond the active transfer. For other controllers, they don't process the next transfer TRBs until the current transfer is completed. Explicitly prevent preparing TRBs ahead for all controllers.
--- drivers/usb/dwc3/gadget.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/usb/dwc3/gadget.c +8 -0
@@ -1231,6 +1231,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)if (!dwc3_calc_trbs_left (dep))return ; + +/* +* Don't prepare beyond a transfer. In DWC_usb32, its transfer +* burst capability may try to read and use TRBs beyond the +* active transfer instead of stopping. +*/ +if (dep->stream_capable && req->request.is_last) +return ; } }
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 10/11 usb: dwc3: gadget: Handle stream transfers
In reply to Thinh NguyenOverview of stream transfer requirement:
* A transfer will have a set of TRBs of the same stream ID.
* A transfer is started with a stream ID in START_TRANSFER command.
* A new stream will only start when the previous completes.
Overview of stream events:
* A "prime" from host indicates that its endpoints are active (buffers prepared and ready to receive/transmit data). The controller automatically initiates stream if it sees this.
* A "NoStream" rejection event indicates that the host isn't ready. Host will put the endpoint back to idle state. Device may need to reinitiate the stream to start transfer again.
* A Stream Found event means host accepted device initiated stream. Nothing needs to be done from driver.
To initiate a stream, the driver will issue START_TRANSFER command with a stream ID. To reinitiate the stream, the driver must issue END_TRANSFER and restart the transfer with START_TRANSFER command with the same stream ID.
This implementation handles device-initated streams (e.g. UASP driver). It also handles some hosts' quirky behavior where they only prime each endpoint once.
--- drivers/usb/dwc3/core.h | 8 ++++ drivers/usb/dwc3/debug.h | 2 + drivers/usb/dwc3/gadget.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 104 insertions(+), 3 deletions(-)
drivers/usb/dwc3/core.h +8 -0
@@ -495,6 +495,7 @@#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10#define DWC3_DGCMD_STATUS (n) (((n) >> 12) & 0x0F)
@@ -702,6 +703,9 @@ struct dwc3_ep {#define DWC3_EP_PENDING_REQUEST BIT(5)#define DWC3_EP_DELAY_START BIT(6)#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7) +#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) +#define DWC3_EP_FORCE_RESTART_STREAM BIT(9) +#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)/* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN BIT(31)
@@ -1301,6 +1305,10 @@ struct dwc3_event_depevt {#define DEPEVT_STREAMEVT_FOUND 1#define DEPEVT_STREAMEVT_NOTFOUND 2 +/* Stream event parameter */ +#define DEPEVT_STREAM_PRIME 0xfffe +#define DEPEVT_STREAM_NOSTREAM 0x0 +/* Control-only Status */ #define DEPEVT_STATUS_CONTROL_DATA 1#define DEPEVT_STATUS_CONTROL_STATUS 2
drivers/usb/dwc3/debug.h +2 -0
@@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)return "All FIFO Flush" ;case DWC3_DGCMD_SET_ENDPOINT_NRDY :return "Set Endpoint NRDY" ; +case DWC3_DGCMD_SET_ENDPOINT_PRIME : +return "Set Endpoint Prime" ;case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK :return "Run SoC Bus Loopback Test" ;default :
drivers/usb/dwc3/gadget.c +94 -3
139 lines not on this page · raw message
Signature · 1 line
2.11.0
Thinh Nguyen
maintainerPATCH v2 11/11 usb: dwc3: gadget: Use SET_EP_PRIME for NoStream
In reply to Thinh NguyenDWC_usb32 v1.00a and later can use SET_EP_PRIME command to reinitiate a stream. Use the command to handle NoStream rejection instead of ending and restarting the endpoint.
--- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/gadget.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-)
drivers/usb/dwc3/core.h +3 -0
@@ -1161,6 +1161,9 @@ struct dwc3 {#define DWC31_REVISION_180A 0x3138302a#define DWC31_REVISION_190A 0x3139302a +#define DWC32_REVISION_ANY 0x0 +#define DWC32_REVISION_100A 0x3130302a +u32 version_type;#define DWC31_VERSIONTYPE_ANY 0x0
drivers/usb/dwc3/gadget.c +10 -3
@@ -2844,9 +2844,16 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,* hosts, force to reinitate the stream until the host is ready * instead of waiting for the host to prime the endpoint. */ - dep->flags |=DWC3_EP_DELAY_START ; -dwc3_stop_active_transfer (dep, true, true); -return ; + if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) { +unsigned int cmd =DWC3_DGCMD_SET_ENDPOINT_PRIME ; + +dwc3_send_gadget_generic_command (dwc, cmd, dep->number); + }else { + dep->flags |=DWC3_EP_DELAY_START ; +dwc3_stop_active_transfer (dep, true, true); +return ; + } +break ; } out:
Signature · 1 line
2.11.0
Hi,
Quoted 38 lines · Thinh Nguyen
Overview of stream transfer requirement:
* A transfer will have a set of TRBs of the same stream ID.
* A transfer is started with a stream ID inSTART_TRANSFERcommand.
* A new stream will only start when the previous completes.Overview of stream events:
* A "prime" from host indicates that its endpoints are active (buffers prepared and ready to receive/transmit data). The controller automatically initiates stream if it sees this.
* A "NoStream" rejection event indicates that the host isn't ready. Host will put the endpoint back to idle state. Device may need to reinitiate the stream to start transfer again.
* A Stream Found event means host accepted device initiated stream. Nothing needs to be done from driver.To initiate a stream, the driver will issue
START_TRANSFERcommand with a stream ID. To reinitiate the stream, the driver must issueEND_TRANSFERand restart the transfer withSTART_TRANSFERcommand with the same stream ID.This implementation handles device-initated streams (e.g. UASP driver). It also handles some hosts' quirky behavior where they only prime each endpoint once.
Prerequisite:
This series requiresDWC_usb32patch series
https://patchwork.kernel.org/project/linux-usb/list/?series=269641[PATCH 1/2] usb: dwc3: Add support for
DWC_usb32IP [PATCH 2/2] usb: dwc3: Get MDWIDTH forDWC_usb32Changes in v2:
- Update cover letter
- Split handling of stream and of transfer completion into smaller patches
- Rewordusb_request->is_lastto strictly use for streams
- Enforce transfer completion handling to only for stream capable endpoints
Quoted 12 lines
Thinh Nguyen (11):
usb: gadget: Introduceusb_request->is_last
usb: gadget:f_tcm: Inform last stream request
usb: dwc3: gadget: Continue to process pending requests
usb: dwc3: gadget: Check for in-progressEND_TRANSFER
usb: dwc3: gadget: Refactor TRB completion handler
usb: dwc3: gadget: Enable XferComplete event
usb: dwc3: gadget: Handle XferComplete for streams
usb: dwc3: gadget: Wait for transfer completion
usb: dwc3: gadget: Don't prepare beyond a transfer
usb: dwc3: gadget: Handle stream transfers
usb: dwc3: gadget: UseSET_EP_PRIMEfor NoStream
This seris is now in testing/next. Can you check that everything looks fine for you? I had to manually apply one patch.
Signature · 1 line
balbi