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 <bige...@linutronix.de>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to