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