[PATCH 7/7] usb/dummy_hcd: complete stream support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



dummy_hcd provides (alloc|free)_stream() callbacks but there are not
doing anything. This patch changes this and implements stream allocation
/ de-allocation support. Dummy supports 15 streams on host side but this
value has no real restrictions. Error checking is simple and limited not
to allocate a streams on one endpoint twice. The check for SuperSpeed
has been removed because USB-core does this already.
The transfer code has been modified so we match the proper stream once
we found an urb and usb_request which match.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
 drivers/usb/gadget/dummy_hcd.c |  107 +++++++++++++++++++++++++++++++++++-----
 1 files changed, 94 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 887a2d2..c84032a 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -142,6 +142,7 @@ static const char *const ep_name [] = {
 #define DUMMY_ENDPOINTS	ARRAY_SIZE(ep_name)
 
 /*-------------------------------------------------------------------------*/
+#define DUMMY_MAX_STREAMS	15
 
 struct urbp {
 	struct urb		*urb;
@@ -167,6 +168,7 @@ struct dummy_hcd {
 
 	struct usb_device		*udev;
 	struct list_head		urbp_list;
+	u32				stream_en_ep;
 
 	unsigned			active:1;
 	unsigned			old_active:1;
@@ -1030,6 +1032,16 @@ static struct platform_driver dummy_udc_driver = {
 
 /*-------------------------------------------------------------------------*/
 
+static u32 dummy_get_ep_idx(const struct usb_endpoint_descriptor *desc)
+{
+	u32 index;
+
+	index = usb_endpoint_num(desc) << 1;
+	if (usb_endpoint_dir_in(desc))
+		index |= 1;
+	return index;
+}
+
 /* MASTER/HOST SIDE DRIVER
  *
  * this uses the hcd framework to hook up to host side drivers.
@@ -1051,6 +1063,7 @@ static int dummy_urb_enqueue (
 	struct urbp	*urbp;
 	unsigned long	flags;
 	int		rc;
+	int		ep_idx;
 
 	urbp = kmalloc (sizeof *urbp, mem_flags);
 	if (!urbp)
@@ -1060,6 +1073,28 @@ static int dummy_urb_enqueue (
 
 	dum_hcd = hcd_to_dummy_hcd(hcd);
 	spin_lock_irqsave(&dum_hcd->dum->lock, flags);
+
+	if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
+		ep_idx = dummy_get_ep_idx(&urb->ep->desc);
+		if ((1 << ep_idx) & dum_hcd->stream_en_ep) {
+			if (urb->stream_id == 0) {
+				dev_err(dummy_dev(dum_hcd),
+				"Stream id 0 on stream enabled endpoint\n");
+				rc = -EINVAL;
+				kfree(urbp);
+				goto done;
+			}
+			if (urb->stream_id > DUMMY_MAX_STREAMS) {
+				dev_err(dummy_dev(dum_hcd),
+					"Stream id %d is out of range.\n",
+					urb->stream_id);
+				rc = -EINVAL;
+				kfree(urbp);
+				goto done;
+			}
+		}
+	}
+
 	rc = usb_hcd_link_urb_to_ep(hcd, urb);
 	if (rc) {
 		kfree(urbp);
@@ -1163,11 +1198,25 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req,
 	return trans;
 }
 
+static int dummy_ep_stream_en(struct dummy_hcd *dum, struct urb *urb)
+{
+	const struct usb_endpoint_descriptor *desc = &urb->ep->desc;
+	u32 index;
+
+	if (!usb_endpoint_xfer_bulk(desc))
+		return 0;
+
+	index = dummy_get_ep_idx(desc);
+	if ((1 << index) & dum->stream_en_ep)
+		return 1;
+	return 0;
+}
+
 /* transfer up to a frame's worth; caller must own lock */
-static int
-transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit,
-		int *status)
+static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
+		struct dummy_ep *ep, int limit, int *status)
 {
+	struct dummy		*dum = dum_hcd->dum;
 	struct dummy_request	*req;
 
 top:
@@ -1177,6 +1226,11 @@ top:
 		int		is_short, to_host;
 		int		rescan = 0;
 
+		if (dummy_ep_stream_en(dum_hcd, urb)) {
+			if ((urb->stream_id != req->req.stream_id))
+				continue;
+		}
+
 		/* 1..N packets of ep->ep.maxpacket each ... the last one
 		 * may be short (including zero length).
 		 *
@@ -1704,7 +1758,7 @@ restart:
 		default:
 		treat_control_like_bulk:
 			ep->last_io = jiffies;
-			total = transfer(dum, urb, ep, limit, &status);
+			total = transfer(dum_hcd, urb, ep, limit, &status);
 			break;
 		}
 
@@ -2161,6 +2215,7 @@ static int dummy_start_ss(struct dummy_hcd *dum_hcd)
 	dum_hcd->timer.function = dummy_timer;
 	dum_hcd->timer.data = (unsigned long)dum_hcd;
 	dum_hcd->rh_state = DUMMY_RH_RUNNING;
+	dum_hcd->stream_en_ep = 0;
 	INIT_LIST_HEAD(&dum_hcd->urbp_list);
 	dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET;
 	dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING;
@@ -2250,11 +2305,25 @@ static int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
 	struct usb_host_endpoint **eps, unsigned int num_eps,
 	unsigned int num_streams, gfp_t mem_flags)
 {
-	if (hcd->speed != HCD_USB3)
-		dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
-			"%s() - ERROR! Not supported for USB2.0 roothub\n",
-			__func__);
-	return 0;
+	struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
+	int max_stream;
+	u32 index;
+	int i;
+
+	for (i = 0; i < num_eps; i++) {
+		if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
+			return -EINVAL;
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		if ((1 << index) & dum_hcd->stream_en_ep)
+			return -EINVAL;
+	}
+
+	for (i = 0; i < num_eps; i++) {
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		dum_hcd->stream_en_ep |= 1 << index;
+	}
+	max_stream = min_t(u32, num_streams, DUMMY_MAX_STREAMS);
+	return max_stream;
 }
 
 /* Reverts a group of bulk endpoints back to not using stream IDs. */
@@ -2262,10 +2331,22 @@ static int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
 	struct usb_host_endpoint **eps, unsigned int num_eps,
 	gfp_t mem_flags)
 {
-	if (hcd->speed != HCD_USB3)
-		dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
-			"%s() - ERROR! Not supported for USB2.0 roothub\n",
-			__func__);
+	struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
+	u32 index;
+	u32 i;
+
+	for (i = 0; i < num_eps; i++) {
+		if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
+			return -EINVAL;
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		if (!((1 << index) & dum_hcd->stream_en_ep))
+			return -EINVAL;
+	}
+
+	for (i = 0; i < num_eps; i++) {
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		dum_hcd->stream_en_ep &= ~(1 << index);
+	}
 	return 0;
 }
 
-- 
1.7.7.3

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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux