Skip to contentSkip to conversation

usb: dwc3: gadget: Handle streams

PATCH Superseded Thinh Nguyen 4 participants 18 messages Apr 27, 2020 – May 15, 2020

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Thinh Nguyen

author maintainer
#0 lore raw

cover usb: dwc3: gadget: Handle streams

The dwc3 driver wasn't implemented to handle streams. This series provide a few updates to dwc3 and usb request for that:
* Introduce usb_request->is_last field
* Handle dwc3 transfer completion
* Handle dwc3 stream events

To use stream, the dwc3 driver must know which request is the last request of a transfer. The controller uses this information to reallocate its resources for different streams as transfers are completed. Function drivers that support stream must indicate this via usb_request->is_last field.

Please note, I also included the patch
"usb: dwc3: gadget: Continue to process pending requests" in this series to avoid dependency issue when applying to your git tree.

https://patchwork.kernel.org/patch/11466967/

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

Thinh Nguyen (5):
usb: gadget: Introduce usb_request->is_last field
usb: gadget: f_tcm: Inform last transfer request
usb: dwc3: gadget: Continue to process pending requests
usb: dwc3: gadget: Handle transfer completion
usb: dwc3: gadget: Handle stream transfers

 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          |   2 +
 5 files changed, 211 insertions(+), 33 deletions(-)
Signature · 1 line
2.11.0
author maintainer
#1 lore raw

PATCH 1/5 usb: gadget: Introduce usb_request->is_last field

A transfer is composed of one or more usb_requests. Currently, only the function driver knows this based on its implementation and its class protocol. However, some usb controllers need to know this to update its resources. For example, the DWC3 controller needs this info to update its internal resources and initiate different streams.

Introduce a new field is_last to usb_request to inform the controller driver whether the request is the last of its transfer.

---
 include/linux/usb/gadget.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/usb/gadget.h +2 -0
index e959c09a97c9..742c52f7e470 100644
@@ -50,6 +50,7 @@ struct usb_ep;
  * @short_not_ok: When reading data, makes short packets be
  *     treated as errors (queue stops advancing till cleanup).
  * @dma_mapped: Indicates if request has been mapped to DMA (internal)
+ * @is_last: Indicates if this request is the last of a transfer.
  * @complete: Function called when request completes, so this request and
  *	its buffer may be re-used.  The function will always be called with
  *	interrupts disabled, and it must not sleep.
@@ -108,6 +109,7 @@ struct usb_request {
 	unsigned		zero:1;
 	unsigned		short_not_ok:1;
 	unsigned		dma_mapped:1;
+	unsigned		is_last:1;
 
 	void			(*complete)(struct usb_ep *ep,
 					struct usb_request *req);
Signature · 1 line
2.11.0
#2 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Quoted 19 lines · Thinh Nguyen

A transfer is composed of one or more usb_requests. Currently, only the function driver knows this based on its implementation and its class protocol. However, some usb controllers need to know this to update its resources. For example, the DWC3 controller needs this info to update its internal resources and initiate different streams.

Introduce a new field is_last to usb_request to inform the controller driver whether the request is the last of its transfer.

---
 include/linux/usb/gadget.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/usb/gadget.h +0 -0
index e959c09a97c9..742c52f7e470 100644

include/linux/usb/gadget.h struct usb_ep; · line 53

Quoted 4 lines
include/linux/usb/gadget.h +1 -0
@@ -50,6 +50,7 @@ struct usb_ep;
  * @short_not_ok: When reading data, makes short packets be
  *     treated as errors (queue stops advancing till cleanup).
  * @dma_mapped: Indicates if request has been mapped to DMA (internal)
+ * @is_last: Indicates if this request is the last of a transfer.

Would you please describe the use case for it, and what's 'transfer' and 'request' in your use case?

Peter

Quoted 14 lines
include/linux/usb/gadget.h +1 -0
  * @complete: Function called when request completes, so this request and
  *	its buffer may be re-used.  The function will always be called with
  *	interrupts disabled, and it must not sleep.
@@ -108,6 +109,7 @@ struct usb_request {
 	unsigned		zero:1;
 	unsigned		short_not_ok:1;
 	unsigned		dma_mapped:1;
+	unsigned		is_last:1;
 
 	void			(*complete)(struct usb_ep *ep,
 					struct usb_request *req);
Signature · 1 line
2.11.0
Signature · 2 lines
Thanks,
Peter Chen
author maintainer
#3 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Hi,

Quoted 15 lines · Peter Chen
Quoted 14 lines · Thinh Nguyen

A transfer is composed of one or more usb_requests. Currently, only the function driver knows this based on its implementation and its class protocol. However, some usb controllers need to know this to update its resources. For example, the DWC3 controller needs this info to update its internal resources and initiate different streams.

Introduce a new field is_last to usb_request to inform the controller driver whether the request is the last of its transfer.

---
  include/linux/usb/gadget.h | 2 ++
  1 file changed, 2 insertions(+)
Quoted 12 lines

include/linux/usb/gadget.h struct usb_ep; · line 53

Quoted 9 lines
include/linux/usb/gadget.h +1 -0
index e959c09a97c9..742c52f7e470 100644
@@ -50,6 +50,7 @@ struct usb_ep;
   * @short_not_ok: When reading data, makes short packets be
   *     treated as errors (queue stops advancing till cleanup).
   * @dma_mapped: Indicates if request has been mapped to DMA (internal)
+ * @is_last: Indicates if this request is the last of a transfer.

Would you please describe the use case for it, and what's 'transfer' and 'request' in your use case?

The transfer size is defined by a class protocol. The function driver will determine how many usb_requests are needed to fulfill that transfer.

For example, in MSC, a READ/WRITE command can request for a transfer size up to (512 * 2^31-1) bytes. It'd be too large for a single usb_request, which is why the mass_storage function driver limits each request to 16KB max by default. A MSC command itself is a transfer, same with its status.

This is a similar case for UASP. However, the f_tcm and the target drivers current implementation only use a single request per transfer. (This needs to be fixed, along with some other issues in f_tcm).

The use case here is that DWC3x controller needs to update its resources whenever a transfer is completed. This is a requirement for streams when it needs to free up some resources for different stream transfers. The function driver must pass this information to the controller driver for streams to work properly.

Potentially, another use case for this is to update the documentation on usb_dequeue_request(). By providing the concept of a transfer, we can say that dequeuing an in-flight request will cancel the transfer, and any incomplete request within the transfer must be given back to the function driver. This would make sense for controllers that use TRB ring buffer and dequeue/enqueue TRB pointers. But this idea still needs more thoughts.

BR,
Thinh

#4 lore raw

RE: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Quoted 30 lines
Quoted 16 lines · Thinh Nguyen
Quoted 14 lines

A transfer is composed of one or more usb_requests. Currently, only the function driver knows this based on its implementation and its class protocol. However, some usb controllers need to know this to update its resources. For example, the DWC3 controller needs this info to update its internal resources and initiate different streams.

Introduce a new field is_last to usb_request to inform the controller driver whether the request is the last of its transfer.

---
  include/linux/usb/gadget.h | 2 ++
  1 file changed, 2 insertions(+)
Quoted 13 lines

include/linux/usb/gadget.h struct usb_ep; · line 54

Quoted 10 lines
include/linux/usb/gadget.h +1 -0
index e959c09a97c9..742c52f7e470 100644
@@ -50,6 +50,7 @@ struct usb_ep;
   * @short_not_ok: When reading data, makes short packets be
   *     treated as errors (queue stops advancing till cleanup).
   * @dma_mapped: Indicates if request has been mapped to DMA
 (internal)
+ * @is_last: Indicates if this request is the last of a transfer.

Would you please describe the use case for it, and what's 'transfer' and 'request' in your use case?

Quoted 3 lines

The transfer size is defined by a class protocol. The function driver will determine how many usb_requests are needed to fulfill that transfer.

Thanks for your information.

If 'transfer size' here is software concept, why controller needs to know? The general controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

Quoted 5 lines

For example, in MSC, a READ/WRITE command can request for a transfer size up to (512 * 2^31-1) bytes. It'd be too large for a single usb_request, which is why the mass_storage function driver limits each request to 16KB max by default. A MSC command itself is a transfer, same with its status.

Quoted 9 lines

This is a similar case for UASP. However, the f_tcm and the target drivers current implementation only use a single request per transfer.
(This needs to be fixed, along with some other issues in f_tcm).

The use case here is that DWC3x controller needs to update its resources whenever a transfer is completed. This is a requirement for streams when it needs to free up some resources for different stream transfers. The function driver must pass this information to the controller driver for streams to work properly.

Does the controller case about stream information or the transfer information?

Quoted 7 lines

Potentially, another use case for this is to update the documentation on usb_dequeue_request(). By providing the concept of a transfer, we can say that dequeuing an in-flight request will cancel the transfer, and any incomplete request within the transfer must be given back to the function driver. This would make sense for controllers that use TRB ring buffer and dequeue/enqueue TRB pointers. But this idea still needs more thoughts.

I think class driver needs to consider that, the controller driver only needs to handle request and related TRBs.

Peter

author maintainer
#5 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Quoted 29 lines · Peter Chen
Quoted 29 lines
Quoted 29 lines · Thinh Nguyen
Quoted 19 lines

A transfer is composed of one or more usb_requests. Currently, only the function driver knows this based on its implementation and its class protocol. However, some usb controllers need to know this to update its resources. For example, the DWC3 controller needs this info to update its internal resources and initiate different streams.

Introduce a new field is_last to usb_request to inform the controller driver whether the request is the last of its transfer.

---
   include/linux/usb/gadget.h | 2 ++
   1 file changed, 2 insertions(+)
include/linux/usb/gadget.h +0 -0
index e959c09a97c9..742c52f7e470 100644

include/linux/usb/gadget.h struct usb_ep; · line 54

Quoted 5 lines
include/linux/usb/gadget.h +1 -0
@@ -50,6 +50,7 @@ struct usb_ep;
    * @short_not_ok: When reading data, makes short packets be
    *     treated as errors (queue stops advancing till cleanup).
    * @dma_mapped: Indicates if request has been mapped to DMA
 (internal)
+ * @is_last: Indicates if this request is the last of a transfer.

Would you please describe the use case for it, and what's 'transfer' and 'request' in your use case?

Quoted 7 lines
Quoted 3 lines

The transfer size is defined by a class protocol. The function driver will determine how many usb_requests are needed to fulfill that transfer.

Thanks for your information.

If 'transfer size' here is software concept, why controller needs to know? The general controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

While some controllers don't have the concept of this, DWC_usb3x does. It has a notion of starting and ending a transfer. While a transfer is started, the endpoint uses a resource. It releases that resource when the transfer completes. So far, dwc3 implemented in such a way that bulk transfers are always in-progress and don't complete. That's fine so far, but it's not the case with streams.

Quoted 10 lines
Quoted 10 lines

For example, in MSC, a READ/WRITE command can request for a transfer size up to (512 * 2^31-1) bytes. It'd be too large for a single usb_request, which is why the mass_storage function driver limits each request to 16KB max by default. A MSC command itself is a transfer, same with its status.

This is a similar case for UASP. However, the f_tcm and the target drivers current implementation only use a single request per transfer.
(This needs to be fixed, along with some other issues in f_tcm).

Quoted 6 lines
Quoted 5 lines

The use case here is that DWC3x controller needs to update its resources whenever a transfer is completed. This is a requirement for streams when it needs to free up some resources for different stream transfers. The function driver must pass this information to the controller driver for streams to work properly.

Does the controller case about stream information or the transfer information?

For streams, each stransfer has a stream ID, and each transfer uses a resource.

Quoted 11 lines
Quoted 8 lines

Potentially, another use case for this is to update the documentation on usb_dequeue_request(). By providing the concept of a transfer, we can say that dequeuing an in-flight request will cancel the transfer, and any incomplete request within the transfer must be given back to the function driver. This would make sense for controllers that use TRB ring buffer and dequeue/enqueue TRB pointers. But this idea still needs more thoughts.

I think class driver needs to consider that, the controller driver only needs to handle request and related TRBs.

It maybe true for some controllers, but DWC_usb3x needs this information.

BR,
Thinh

#6 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Quoted 11 lines · Thinh Nguyen
Quoted 2 lines · Peter Chen

If 'transfer size' here is software concept, why controller needs to know? The general controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

While some controllers don't have the concept of this, DWC_usb3x does. It has a notion of starting and ending a transfer. While a transfer is started, the endpoint uses a resource. It releases that resource when the transfer completes. So far, dwc3 implemented in such a way that bulk transfers are always in-progress and don't complete. That's fine so far, but it's not the case with streams.

This is peculiar. I haven't heard of any other controller doing this.

What does the controller use this resource for? Would anything go wrong if you told the controller that each transfer was a single usb_request?

Alan Stern

author maintainer
#7 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Hi,

Quoted 11 lines · Alan Stern
Quoted 9 lines · Thinh Nguyen
Quoted 2 lines · Peter Chen

If 'transfer size' here is software concept, why controller needs to know? The general controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

While some controllers don't have the concept of this, DWC_usb3x does. It has a notion of starting and ending a transfer. While a transfer is started, the endpoint uses a resource. It releases that resource when the transfer completes. So far, dwc3 implemented in such a way that bulk transfers are always in-progress and don't complete. That's fine so far, but it's not the case with streams.

Quoted 5 lines

This is peculiar. I haven't heard of any other controller doing this.

What does the controller use this resource for? Would anything go wrong if you told the controller that each transfer was a single usb_request?

It's no problem. Each transfer can be a single request. Just set the request->is_last. (Refer to [patch 2/5] for f_tcm).

The issue here is that the controller needs to know when a stream completes so it can start on a different stream. In the controller driver, this is done by setting a certain control bit in the TRB indicating the last TRB of a transfer. This knowledge can only come from the function driver, which is why we need this "is_last" field.

BR,
Thinh

#8 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Quoted 20 lines · Thinh Nguyen

Hi,

Quoted 11 lines · Alan Stern
Quoted 9 lines · Thinh Nguyen
Quoted 2 lines · Peter Chen

If 'transfer size' here is software concept, why controller needs to know? The general controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

While some controllers don't have the concept of this, DWC_usb3x does. It has a notion of starting and ending a transfer. While a transfer is started, the endpoint uses a resource. It releases that resource when the transfer completes. So far, dwc3 implemented in such a way that bulk transfers are always in-progress and don't complete. That's fine so far, but it's not the case with streams.

Quoted 5 lines

This is peculiar. I haven't heard of any other controller doing this.

What does the controller use this resource for? Would anything go wrong if you told the controller that each transfer was a single usb_request?

Quoted 5 lines

It's no problem. Each transfer can be a single request. Just set the request->is_last. (Refer to [patch 2/5] for f_tcm).

The issue here is that the controller needs to know when a stream completes so it can start on a different stream. In the controller

Why does it need to know this? Why can't it start on a different stream at any time?

Quoted 3 lines

driver, this is done by setting a certain control bit in the TRB indicating the last TRB of a transfer. This knowledge can only come from the function driver, which is why we need this "is_last" field.

What's wrong with just assuming _every_ usb_request is the last one of its transfer? Then you wouldn't have to add a new flag or change the existing function drivers.

Alan Stern

author maintainer
#9 lore raw

Re: [PATCH 1/5] usb: gadget: Introduce usb_request->is_last field

Quoted 21 lines · Alan Stern
Quoted 19 lines · Thinh Nguyen

Hi,

Quoted 16 lines · Alan Stern
Quoted 9 lines · Thinh Nguyen
Quoted 2 lines · Peter Chen

If 'transfer size' here is software concept, why controller needs to know? The general controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

While some controllers don't have the concept of this, DWC_usb3x does. It has a notion of starting and ending a transfer. While a transfer is started, the endpoint uses a resource. It releases that resource when the transfer completes. So far, dwc3 implemented in such a way that bulk transfers are always in-progress and don't complete. That's fine so far, but it's not the case with streams.

This is peculiar. I haven't heard of any other controller doing this.

What does the controller use this resource for? Would anything go wrong if you told the controller that each transfer was a single usb_request?

Quoted 7 lines
Quoted 5 lines

It's no problem. Each transfer can be a single request. Just set the request->is_last. (Refer to [patch 2/5] for f_tcm).

The issue here is that the controller needs to know when a stream completes so it can start on a different stream. In the controller

Why does it need to know this? Why can't it start on a different stream at any time?

By design, whenever the controller needs to start on a different stream, it will issue a START_TRANSFER command along with a stream ID. It cannot issue START_TRANSFER command again until the previous transfer completes or ended.

Quoted 7 lines
Quoted 4 lines

driver, this is done by setting a certain control bit in the TRB indicating the last TRB of a transfer. This knowledge can only come from the function driver, which is why we need this "is_last" field.

What's wrong with just assuming _every_ usb_request is the last one of its transfer? Then you wouldn't have to add a new flag or change the existing function drivers.

That will affect performance. The driver will then need to service multiple interrupts and restart the transfer multiple times.

BR,
Thinh

author maintainer
#10 lore raw

PATCH 2/5 usb: gadget: f_tcm: Inform last transfer request

In reply to Thinh Nguyen

Set 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 allocate resource for different streams. The current implementation of f_tcm uses a single request per transfer, so every stream request is the last of its transfer.

---
 drivers/usb/gadget/function/f_tcm.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/usb/gadget/function/f_tcm.c +3 -0
index 36504931b2d1..2979cbe4d95f 100644
@@ -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
author maintainer
#11 lore raw

PATCH 3/5 usb: dwc3: gadget: Continue to process pending requests

In reply to Thinh Nguyen

If 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
index 4ca3e197bee4..865e6fbb7360 100644
@@ -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
author maintainer
#12 lore raw

PATCH 4/5 usb: dwc3: gadget: Handle transfer completion

In reply to Thinh Nguyen

With the new usb_request->is_last field, now the function drivers can inform which request is the end of a transfer, dwc3 can program its TRBs to let the controller know when to free its resources when a transfer completes. This is required for stream transfers. The controller needs to know where one stream starts and ends to properly allocate resources for different streams.

---
 drivers/usb/dwc3/core.h   |   1 +
 drivers/usb/dwc3/gadget.c | 107 ++++++++++++++++++++++++++++++++--------------
 2 files changed, 77 insertions(+), 31 deletions(-)
drivers/usb/dwc3/core.h +1 -0
index 7204a838ec06..b11183a715a7 100644
@@ -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 +76 -31

205 lines not on this page · raw message

Signature · 1 line
2.11.0
#13 lore raw

Re: [PATCH 4/5] usb: dwc3: gadget: Handle transfer completion

Hi,

Quoted 8 lines · Thinh Nguyen

With the new usb_request->is_last field, now the function drivers can inform which request is the end of a transfer, dwc3 can program its TRBs to let the controller know when to free its resources when a transfer completes. This is required for stream transfers. The controller needs to know where one stream starts and ends to properly allocate resources for different streams.

from a quick read, it looks like this can be broken down into smaller patches.

drivers/usb/dwc3/core.h struct dwc3_ep { · line 704

Quoted 9 lines
drivers/usb/dwc3/core.h +1 -0
index 7204a838ec06..b11183a715a7 100644
@@ -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 could be its own patch with proper description (and usage)

drivers/usb/dwc3/gadget.c static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) · line 582

Quoted 12 lines
drivers/usb/dwc3/core.h +0 -0
 	/* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN		BIT(31)
drivers/usb/dwc3/gadget.c +1 -0
index 865e6fbb7360..628f9d142876 100644
@@ -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

adding this bit for stream endpoints could be a separate commit.

drivers/usb/dwc3/gadget.c static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) · line 923

Quoted 12 lines
drivers/usb/dwc3/gadget.c +3 -2
 			| DWC3_DEPCFG_STREAM_EVENT_EN;
 		dep->stream_capable = true;
 	}
@@ -917,8 +918,9 @@ 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)
+		dma_addr_t dma, unsigned length, unsigned chain,
+		unsigned is_last, unsigned node, unsigned stream_id,
+		unsigned short_not_ok, unsigned no_interrupt)

if you add "is_last" as the last argument, this hunk will look smaller. Nitpicking, I know.

drivers/usb/dwc3/gadget.c static void dwc3_prepare_trbs(struct dwc3_ep *dep) · line 1237

Quoted 8 lines
drivers/usb/dwc3/gadget.c +4 -0
@@ -1223,6 +1231,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 
 		if (!dwc3_calc_trbs_left(dep))
 			return;
+
+		/* Don't prepare ahead. This is not an option for DWC_usb32. */
+		if (req->request.is_last)
+			return;

this requires some better description. Why isn't it an option for dwc_usb32?

drivers/usb/dwc3/gadget.c static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep) · line 2655

Quoted 9 lines
drivers/usb/dwc3/gadget.c +0 -5
@@ -2648,37 +2666,22 @@ static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
 	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)
-{
-	dep->frame_number = event->parameters;
-}

moving these functions around could be a separate patch. The way it's done now takes away from review.

Quoted 47 lines
drivers/usb/dwc3/gadget.c +39 -0
@@ -2704,6 +2707,45 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
 
 		dwc->u1u2 = 0;
 	}
+
+	return no_started_trb;
+}
+
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
+		const struct dwc3_event_depevt *event)
+{
+	dep->frame_number = event->parameters;
+}
+
+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_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;
+
+	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,

drivers/usb/dwc3/gadget.c static void dwc3_endpoint_interrupt(struct dwc3 *dwc, · line 2815

Quoted 4 lines
drivers/usb/dwc3/gadget.c +1 -0
@@ -2770,7 +2812,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 		}
 		break;
 	case DWC3_DEPEVT_STREAMEVT:
+		break;

Swap these around. Keep all the "nothing to do here" cases together.

Signature · 1 line
balbi
author maintainer
#14 lore raw

Re: [PATCH 4/5] usb: dwc3: gadget: Handle transfer completion

Hi,

Quoted 11 lines · Felipe Balbi

Hi,

Quoted 7 lines · Thinh Nguyen

With the new usb_request->is_last field, now the function drivers can inform which request is the end of a transfer, dwc3 can program its TRBs to let the controller know when to free its resources when a transfer completes. This is required for stream transfers. The controller needs to know where one stream starts and ends to properly allocate resources for different streams.

Quoted 3 lines
Quoted 1 line

from a quick read, it looks like this can be broken down into smaller patches.

Ok.

Quoted 11 lines

drivers/usb/dwc3/core.h struct dwc3_ep { · line 704

Quoted 10 lines
drivers/usb/dwc3/core.h +1 -0
index 7204a838ec06..b11183a715a7 100644
@@ -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 could be its own patch with proper description (and usage)

Will do.

Quoted 9 lines
Quoted 9 lines
drivers/usb/dwc3/core.h +0 -0
  	/* This last one is specific to EP0 */
  #define DWC3_EP0_DIR_IN		BIT(31)
drivers/usb/dwc3/gadget.c +0 -0
index 865e6fbb7360..628f9d142876 100644
@@ -579,6 +579,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
 
Quoted 4 lines

drivers/usb/dwc3/gadget.c static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) · line 582

Quoted 3 lines
drivers/usb/dwc3/gadget.c +1 -0
  	if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
  		params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+			| DWC3_DEPCFG_XFER_COMPLETE_EN

adding this bit for stream endpoints could be a separate commit.

Will do.

Quoted 7 lines
Quoted 7 lines
drivers/usb/dwc3/gadget.c +0 -0
  			| DWC3_DEPCFG_STREAM_EVENT_EN;
  		dep->stream_capable = true;
  	}
@@ -917,8 +918,9 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
  }
 
Quoted 8 lines

drivers/usb/dwc3/gadget.c static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) · line 923

Quoted 6 lines
drivers/usb/dwc3/gadget.c +3 -2
  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)
+		dma_addr_t dma, unsigned length, unsigned chain,
+		unsigned is_last, unsigned node, unsigned stream_id,
+		unsigned short_not_ok, unsigned no_interrupt)

if you add "is_last" as the last argument, this hunk will look smaller. Nitpicking, I know.

No problem :)

Quoted 10 lines

drivers/usb/dwc3/gadget.c static void dwc3_prepare_trbs(struct dwc3_ep *dep) · line 1237

Quoted 9 lines
drivers/usb/dwc3/gadget.c +4 -0
@@ -1223,6 +1231,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 
  		if (!dwc3_calc_trbs_left(dep))
  			return;
+
+		/* Don't prepare ahead. This is not an option for DWC_usb32. */
+		if (req->request.is_last)
+			return;

this requires some better description. Why isn't it an option for dwc_usb32?

Internally, DWC_usb32 does some advance caching and burst that we should not prepare more TRB until the transfer is completed.
This doesn't apply for isoc, missed a check here. Need to apply on the next version.

Quoted 5 lines
Quoted 5 lines
drivers/usb/dwc3/gadget.c +0 -0
@@ -2648,37 +2666,22 @@ static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
  	return !dwc3_gadget_ep_request_completed(req);
  }
 
Quoted 7 lines

drivers/usb/dwc3/gadget.c static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep) · line 2655

Quoted 5 lines
drivers/usb/dwc3/gadget.c +0 -5
-static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
-		const struct dwc3_event_depevt *event)
-{
-	dep->frame_number = event->parameters;
-}

moving these functions around could be a separate patch. The way it's done now takes away from review.

You're right, it's a bit difficult to review as is.

Quoted 46 lines
Quoted 46 lines
drivers/usb/dwc3/gadget.c +39 -0
@@ -2704,6 +2707,45 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
 
  		dwc->u1u2 = 0;
  	}
+
+	return no_started_trb;
+}
+
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
+		const struct dwc3_event_depevt *event)
+{
+	dep->frame_number = event->parameters;
+}
+
+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_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;
+
+	if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
+		dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
  }
 
Quoted 9 lines

drivers/usb/dwc3/gadget.c static void dwc3_endpoint_interrupt(struct dwc3 *dwc, · line 2815

Quoted 6 lines
drivers/usb/dwc3/gadget.c +1 -0
  static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
@@ -2770,7 +2812,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
  		}
  		break;
  	case DWC3_DEPEVT_STREAMEVT:
+		break;

Swap these around. Keep all the "nothing to do here" cases together.

Thanks,
Thinh

#15 lore raw

Re: [PATCH 4/5] usb: dwc3: gadget: Handle transfer completion

Hi,

Quoted 10 lines · Thinh Nguyen
Quoted 9 lines

drivers/usb/dwc3/gadget.c static void dwc3_prepare_trbs(struct dwc3_ep *dep) · line 1237

Quoted 8 lines
drivers/usb/dwc3/gadget.c +4 -0
@@ -1223,6 +1231,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 
  		if (!dwc3_calc_trbs_left(dep))
  			return;
+
+		/* Don't prepare ahead. This is not an option for DWC_usb32. */
+		if (req->request.is_last)
+			return;

this requires some better description. Why isn't it an option for dwc_usb32?

Quoted 4 lines

Internally, DWC_usb32 does some advance caching and burst that we should not prepare more TRB until the transfer is completed.
This doesn't apply for isoc, missed a check here. Need to apply on the next version.

Do you mind re-wording this statement as a comment in the code?

Signature · 1 line
balbi
author maintainer
#16 lore raw

Re: [PATCH 4/5] usb: dwc3: gadget: Handle transfer completion

Quoted 12 lines · Felipe Balbi

Hi,

Quoted 9 lines · Thinh Nguyen
Quoted 9 lines

drivers/usb/dwc3/gadget.c static void dwc3_prepare_trbs(struct dwc3_ep *dep) · line 1237

Quoted 8 lines
drivers/usb/dwc3/gadget.c +4 -0
@@ -1223,6 +1231,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 
   		if (!dwc3_calc_trbs_left(dep))
   			return;
+
+		/* Don't prepare ahead. This is not an option for DWC_usb32. */
+		if (req->request.is_last)
+			return;

this requires some better description. Why isn't it an option for dwc_usb32?

Quoted 5 lines
Quoted 4 lines

Internally, DWC_usb32 does some advance caching and burst that we should not prepare more TRB until the transfer is completed.
This doesn't apply for isoc, missed a check here. Need to apply on the next version.

Do you mind re-wording this statement as a comment in the code?

Yes, I reworded it and added the comment in the code and commit message in the v2. You already have it on your testing/next branch.

Thanks,
Thinh

author maintainer
#17 lore raw

PATCH 5/5 usb: dwc3: gadget: Handle stream transfers

In reply to Thinh Nguyen

The dwc3 driver wasn't implemented to handle streams.

This implementation handles the followings:
* Handles device-initiated streams (e.g. UASP driver)
* Handles some hosts' quirky behavior where they only prime each endpoint once
* Does not use any timer for NoStream rejection

To use stream, the dwc3 driver must know which request is the last request of a transfer. The controller uses this information to reallocate its resources for different streams as transfers are completed. Function drivers that support stream must indicate this via usb_request->is_last field.

---
 drivers/usb/dwc3/core.h   |  11 +++++
 drivers/usb/dwc3/debug.h  |   2 +
 drivers/usb/dwc3/gadget.c | 102 ++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 112 insertions(+), 3 deletions(-)
drivers/usb/dwc3/core.h +11 -0
index b11183a715a7..013f42a2b5dc 100644
@@ -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)
@@ -1157,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
@@ -1301,6 +1308,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
index 0f95656c9622..d8f600e0e88f 100644
@@ -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 +99 -3

144 lines not on this page · raw message

Signature · 1 line
2.11.0