[RFC 3/3] usb/dummy_hcd: assign endpoint on enqeue on host side

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

 



dummy_urb_enqueue() now assigns the endpoint to the qh structure. If the
UDC disables the endpoint (on the device side) the endpoint information
is removed from the qh as well. I think real HW would timeout on
transfer (and return -EPROTO) so we do here the same except that we
might do this early at enqueue time.
Is this behaviour okay or should the transfer be accepted and -EPROTO should
be returned only from the timer while maintaining "last transfer" member in
qh for INT transfers so they don't come too quickly?
I have no issues for "rmmod g_ncm" anymore and I think it is because I
don't have any re-queues at complete time (due to the possible -EPROTO
at enqueue time).

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
 drivers/usb/gadget/dummy_hcd.c |  116 +++++++++++++++++++++++++---------------
 1 file changed, 74 insertions(+), 42 deletions(-)

diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 2c27566..6c91451 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -94,6 +94,12 @@ struct dummy_request {
 	struct usb_request		req;
 };
 
+struct dummy_qh {
+	struct list_head urbp_list;
+	struct list_head qh_list;
+	struct dummy_ep *ep;
+};
+
 static inline struct dummy_ep *usb_ep_to_dummy_ep(struct usb_ep *_ep)
 {
 	return container_of(_ep, struct dummy_ep, ep);
@@ -557,7 +563,9 @@ static int dummy_enable(struct usb_ep *_ep,
 static int dummy_disable(struct usb_ep *_ep)
 {
 	struct dummy_ep		*ep;
+	struct dummy_qh		*qh;
 	struct dummy		*dum;
+	struct dummy_hcd	*dum_hcd;
 	unsigned long		flags;
 	int			retval;
 
@@ -565,8 +573,17 @@ static int dummy_disable(struct usb_ep *_ep)
 	if (!_ep || !ep->desc || _ep->name == ep0name)
 		return -EINVAL;
 	dum = ep_to_dummy(ep);
+	dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
 
 	spin_lock_irqsave(&dum->lock, flags);
+
+	list_for_each_entry(qh, &dum_hcd->qh_list, qh_list) {
+		if (qh->ep == ep) {
+			qh->ep = NULL;
+			break;
+		}
+	}
+
 	ep->desc = NULL;
 	ep->stream_en = 0;
 	retval = 0;
@@ -1161,10 +1178,32 @@ static int dummy_validate_stream(struct dummy_hcd *dum_hcd, struct urb *urb)
 	return 0;
 }
 
-struct dummy_qh {
-	struct list_head urbp_list;
-	struct list_head qh_list;
-};
+#define is_active(dum_hcd)	((dum_hcd->port_status & \
+		(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \
+			USB_PORT_STAT_SUSPEND)) \
+		== (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))
+
+static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address)
+{
+	int		i;
+
+	if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
+			dum->ss_hcd : dum->hs_hcd)))
+		return NULL;
+	if ((address & ~USB_DIR_IN) == 0)
+		return &dum->ep[0];
+	for (i = 1; i < DUMMY_ENDPOINTS; i++) {
+		struct dummy_ep	*ep = &dum->ep[i];
+
+		if (!ep->desc)
+			continue;
+		if (ep->desc->bEndpointAddress == address)
+			return ep;
+	}
+	return NULL;
+}
+
+#undef is_active
 
 struct dummy_qh *qh_append_urb(struct dummy_hcd *dum_hcd, struct urb *urb)
 {
@@ -1180,6 +1219,8 @@ struct dummy_qh *qh_append_urb(struct dummy_hcd *dum_hcd, struct urb *urb)
 	list_add_tail(&qh->qh_list, &dum_hcd->qh_list);
 	INIT_LIST_HEAD(&qh->urbp_list);
 	urb->ep->hcpriv = qh;
+	qh->ep = NULL;
+
 	return qh;
 }
 
@@ -1196,9 +1237,15 @@ static void dummy_disable_ep(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 		goto out;
 
 	qh = ep->hcpriv;
-	list_del(&qh->qh_list);
-	kfree(ep->hcpriv);
-	ep->hcpriv = NULL;
+	if (qh) {
+		/*
+		 * the URBs which might be pending here will be nuked by the
+		 * timer.
+		 */
+		list_del(&qh->qh_list);
+		kfree(ep->hcpriv);
+		ep->hcpriv = NULL;
+	}
 out:
 	spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
 }
@@ -1212,6 +1259,7 @@ static int dummy_urb_enqueue(
 	struct dummy_qh *qh;
 	struct urbp	*urbp;
 	unsigned long	flags;
+	u8		address;
 	int		rc;
 
 	urbp = kmalloc(sizeof *urbp, mem_flags);
@@ -1235,6 +1283,20 @@ static int dummy_urb_enqueue(
 	if (!qh)
 		goto err_qh;
 
+	if (!qh->ep) {
+		struct dummy	*dum = dum_hcd->dum;
+
+		address = usb_pipeendpoint(urb->pipe);
+		if (usb_pipein(urb->pipe))
+			address |= USB_DIR_IN;
+		qh->ep = find_endpoint(dum, address);
+	}
+
+	if (!qh->ep) {
+		rc = -EPROTO;
+		goto err_qh;
+	}
+
 	if (!dum_hcd->udev) {
 		dum_hcd->udev = urb->dev;
 		usb_get_dev(dum_hcd->udev);
@@ -1499,32 +1561,6 @@ static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep)
 	return limit;
 }
 
-#define is_active(dum_hcd)	((dum_hcd->port_status & \
-		(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \
-			USB_PORT_STAT_SUSPEND)) \
-		== (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))
-
-static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address)
-{
-	int		i;
-
-	if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
-			dum->ss_hcd : dum->hs_hcd)))
-		return NULL;
-	if ((address & ~USB_DIR_IN) == 0)
-		return &dum->ep[0];
-	for (i = 1; i < DUMMY_ENDPOINTS; i++) {
-		struct dummy_ep	*ep = &dum->ep[i];
-
-		if (!ep->desc)
-			continue;
-		if (ep->desc->bEndpointAddress == address)
-			return ep;
-	}
-	return NULL;
-}
-
-#undef is_active
 
 #define Dev_Request	(USB_TYPE_STANDARD | USB_RECIP_DEVICE)
 #define Dev_InRequest	(Dev_Request | USB_DIR_IN)
@@ -1709,13 +1745,12 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
 	return ret_val;
 }
 
-static int handle_one_urb(struct urbp *urbp, struct dummy_hcd *dum_hcd, int *total)
+static int handle_one_urb(struct urbp *urbp, struct dummy_ep *ep,
+		struct dummy_hcd *dum_hcd, int *total)
 {
 	struct dummy		*dum = dum_hcd->dum;
 	struct urb		*urb;
 	struct dummy_request	*req;
-	u8			address;
-	struct dummy_ep		*ep = NULL;
 	int			type;
 	int			status = -EINPROGRESS;
 	int			limit;
@@ -1734,11 +1769,6 @@ static int handle_one_urb(struct urbp *urbp, struct dummy_hcd *dum_hcd, int *tot
 	if (*total <= 0 && type == PIPE_BULK)
 		return 0;
 
-	/* find the gadget's ep for this request (if configured) */
-	address = usb_pipeendpoint (urb->pipe);
-	if (usb_pipein(urb->pipe))
-		address |= USB_DIR_IN;
-	ep = find_endpoint(dum, address);
 	if (!ep) {
 		/* set_configuration() disagreement */
 		dev_dbg(dummy_dev(dum_hcd),
@@ -1922,10 +1952,12 @@ static void dummy_timer(unsigned long _dum_hcd)
 	list_for_each_entry_safe(qh, qh_tmp, &dum_hcd->qh_list, qh_list) {
 		int ret;
 		struct urbp *urbp, *urbp_tmp;
+		struct dummy_ep *ep;
 
 		list_for_each_entry_safe(urbp, urbp_tmp, &qh->urbp_list, urbp_list) {
 
-			ret = handle_one_urb(urbp, dum_hcd, &total);
+			ep = qh->ep;
+			ret = handle_one_urb(urbp, ep, dum_hcd, &total);
 			if (!ret)
 				continue;
 			goto restart;
-- 
1.7.10.4

--
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