Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html