Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/dummy_hcd.c | 115 ++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 27 deletions(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index ea702c6..2c27566 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -167,7 +167,8 @@ struct dummy_hcd { unsigned long re_timeout; struct usb_device *udev; - struct list_head urbp_list; + int active_urbs; + struct list_head qh_list; u32 stream_en_ep; u8 num_stream[30 / 2]; @@ -1160,12 +1161,55 @@ 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; +}; + +struct dummy_qh *qh_append_urb(struct dummy_hcd *dum_hcd, struct urb *urb) +{ + struct dummy_qh *qh; + + qh = urb->ep->hcpriv; + if (qh) + return qh; + + qh = kmalloc(sizeof(*qh), GFP_ATOMIC); + if (!qh) + return qh; + list_add_tail(&qh->qh_list, &dum_hcd->qh_list); + INIT_LIST_HEAD(&qh->urbp_list); + urb->ep->hcpriv = qh; + return qh; +} + +static void dummy_disable_ep(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct dummy_hcd *dum_hcd; + struct dummy_qh *qh; + unsigned long flags; + + dum_hcd = hcd_to_dummy_hcd(hcd); + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + + if (!ep->hcpriv) + goto out; + + qh = ep->hcpriv; + list_del(&qh->qh_list); + kfree(ep->hcpriv); + ep->hcpriv = NULL; +out: + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); +} + static int dummy_urb_enqueue( struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags ) { struct dummy_hcd *dum_hcd; + struct dummy_qh *qh; struct urbp *urbp; unsigned long flags; int rc; @@ -1180,16 +1224,16 @@ static int dummy_urb_enqueue( spin_lock_irqsave(&dum_hcd->dum->lock, flags); rc = dummy_validate_stream(dum_hcd, urb); - if (rc) { - kfree(urbp); - goto done; - } + if (rc) + goto err; rc = usb_hcd_link_urb_to_ep(hcd, urb); - if (rc) { - kfree(urbp); - goto done; - } + if (rc) + goto err; + + qh = qh_append_urb(dum_hcd, urb); + if (!qh) + goto err_qh; if (!dum_hcd->udev) { dum_hcd->udev = urb->dev; @@ -1197,7 +1241,8 @@ static int dummy_urb_enqueue( } else if (unlikely(dum_hcd->udev != urb->dev)) dev_err(dummy_dev(dum_hcd), "usb_device address has changed!\n"); - list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list); + list_add_tail(&urbp->urbp_list, &qh->urbp_list); + dum_hcd->active_urbs++; urb->hcpriv = urbp; if (usb_pipetype(urb->pipe) == PIPE_CONTROL) urb->error_count = 1; /* mark as a new urb */ @@ -1209,6 +1254,11 @@ static int dummy_urb_enqueue( done: spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; +err_qh: + usb_hcd_unlink_urb_from_ep(hcd, urb); +err: + kfree(urbp); + goto done; } static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) @@ -1224,7 +1274,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) rc = usb_hcd_check_unlink_urb(hcd, urb, status); if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING && - !list_empty(&dum_hcd->urbp_list)) + dum_hcd->active_urbs) mod_timer(&dum_hcd->timer, jiffies); spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); @@ -1811,6 +1861,7 @@ static int handle_one_urb(struct urbp *urbp, struct dummy_hcd *dum_hcd, int *tot ep->already_seen = ep->setup_stage = 0; usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb); + dum_hcd->active_urbs--; spin_unlock(&dum->lock); usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status); spin_lock(&dum->lock); @@ -1824,7 +1875,7 @@ static void dummy_timer(unsigned long _dum_hcd) { struct dummy_hcd *dum_hcd = (struct dummy_hcd *) _dum_hcd; struct dummy *dum = dum_hcd->dum; - struct urbp *urbp, *tmp; + struct dummy_qh *qh, *qh_tmp; unsigned long flags; int total; int i; @@ -1868,16 +1919,20 @@ static void dummy_timer(unsigned long _dum_hcd) } restart: - list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) { + list_for_each_entry_safe(qh, qh_tmp, &dum_hcd->qh_list, qh_list) { int ret; + struct urbp *urbp, *urbp_tmp; - ret = handle_one_urb(urbp, dum_hcd, &total); - if (!ret) - continue; - goto restart; + list_for_each_entry_safe(urbp, urbp_tmp, &qh->urbp_list, urbp_list) { + + ret = handle_one_urb(urbp, dum_hcd, &total); + if (!ret) + continue; + goto restart; + } } - if (list_empty(&dum_hcd->urbp_list)) { + if (!dum_hcd->active_urbs) { usb_put_dev(dum_hcd->udev); dum_hcd->udev = NULL; } else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { @@ -2260,7 +2315,7 @@ static int dummy_bus_resume(struct usb_hcd *hcd) } else { dum_hcd->rh_state = DUMMY_RH_RUNNING; set_link_state(dum_hcd); - if (!list_empty(&dum_hcd->urbp_list)) + if (dum_hcd->active_urbs) mod_timer(&dum_hcd->timer, jiffies); hcd->state = HC_STATE_RUNNING; } @@ -2319,17 +2374,22 @@ static ssize_t show_urbs(struct device *dev, struct device_attribute *attr, { struct usb_hcd *hcd = dev_get_drvdata(dev); struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); - struct urbp *urbp; + struct dummy_qh *qh; size_t size = 0; unsigned long flags; spin_lock_irqsave(&dum_hcd->dum->lock, flags); - list_for_each_entry(urbp, &dum_hcd->urbp_list, urbp_list) { - size_t temp; - temp = show_urb(buf, PAGE_SIZE - size, urbp->urb); - buf += temp; - size += temp; + list_for_each_entry(qh, &dum_hcd->qh_list, qh_list) { + struct urbp *urbp; + + list_for_each_entry(urbp, &qh->urbp_list, urbp_list) { + size_t temp; + + temp = show_urb(buf, PAGE_SIZE - size, urbp->urb); + buf += temp; + size += temp; + } } spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); @@ -2344,7 +2404,7 @@ static int dummy_start_ss(struct dummy_hcd *dum_hcd) 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); + INIT_LIST_HEAD(&dum_hcd->qh_list); dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET; dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING; dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1; @@ -2375,7 +2435,7 @@ static int dummy_start(struct usb_hcd *hcd) dum_hcd->timer.data = (unsigned long)dum_hcd; dum_hcd->rh_state = DUMMY_RH_RUNNING; - INIT_LIST_HEAD(&dum_hcd->urbp_list); + INIT_LIST_HEAD(&dum_hcd->qh_list); hcd->power_budget = POWER_BUDGET; hcd->state = HC_STATE_RUNNING; @@ -2520,6 +2580,7 @@ static struct hc_driver dummy_hcd = { .urb_enqueue = dummy_urb_enqueue, .urb_dequeue = dummy_urb_dequeue, + .endpoint_disable = dummy_disable_ep, .get_frame_number = dummy_h_get_frame, -- 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