diff options
Diffstat (limited to 'drivers/usb/gadget/legacy/raw_gadget.c')
-rw-r--r-- | drivers/usb/gadget/legacy/raw_gadget.c | 91 |
1 files changed, 71 insertions, 20 deletions
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index e549022642e5..399fca32a8ac 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -25,6 +25,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/ch11.h> #include <linux/usb/gadget.h> +#include <linux/usb/composite.h> #include <uapi/linux/usb/raw_gadget.h> @@ -64,7 +65,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue, struct usb_raw_event *event; spin_lock_irqsave(&queue->lock, flags); - if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) { + if (queue->size >= RAW_EVENT_QUEUE_SIZE) { spin_unlock_irqrestore(&queue->lock, flags); return -ENOMEM; } @@ -310,9 +311,10 @@ static int gadget_bind(struct usb_gadget *gadget, dev->eps_num = i; spin_unlock_irqrestore(&dev->lock, flags); + dev_dbg(&gadget->dev, "gadget connected\n"); ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL); if (ret < 0) { - dev_err(&gadget->dev, "failed to queue event\n"); + dev_err(&gadget->dev, "failed to queue connect event\n"); set_gadget_data(gadget, NULL); return ret; } @@ -357,20 +359,65 @@ static int gadget_setup(struct usb_gadget *gadget, ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl); if (ret < 0) - dev_err(&gadget->dev, "failed to queue event\n"); + dev_err(&gadget->dev, "failed to queue control event\n"); goto out; out_unlock: spin_unlock_irqrestore(&dev->lock, flags); out: + if (ret == 0 && ctrl->wLength == 0) { + /* + * Return USB_GADGET_DELAYED_STATUS as a workaround to stop + * some UDC drivers (e.g. dwc3) from automatically proceeding + * with the status stage for 0-length transfers. + * Should be removed once all UDC drivers are fixed to always + * delay the status stage until a response is queued to EP0. + */ + return USB_GADGET_DELAYED_STATUS; + } return ret; } -/* These are currently unused but present in case UDC driver requires them. */ -static void gadget_disconnect(struct usb_gadget *gadget) { } -static void gadget_suspend(struct usb_gadget *gadget) { } -static void gadget_resume(struct usb_gadget *gadget) { } -static void gadget_reset(struct usb_gadget *gadget) { } +static void gadget_disconnect(struct usb_gadget *gadget) +{ + struct raw_dev *dev = get_gadget_data(gadget); + int ret; + + dev_dbg(&gadget->dev, "gadget disconnected\n"); + ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL); + if (ret < 0) + dev_err(&gadget->dev, "failed to queue disconnect event\n"); +} +static void gadget_suspend(struct usb_gadget *gadget) +{ + struct raw_dev *dev = get_gadget_data(gadget); + int ret; + + dev_dbg(&gadget->dev, "gadget suspended\n"); + ret = raw_queue_event(dev, USB_RAW_EVENT_SUSPEND, 0, NULL); + if (ret < 0) + dev_err(&gadget->dev, "failed to queue suspend event\n"); +} +static void gadget_resume(struct usb_gadget *gadget) +{ + struct raw_dev *dev = get_gadget_data(gadget); + int ret; + + dev_dbg(&gadget->dev, "gadget resumed\n"); + ret = raw_queue_event(dev, USB_RAW_EVENT_RESUME, 0, NULL); + if (ret < 0) + dev_err(&gadget->dev, "failed to queue resume event\n"); +} +static void gadget_reset(struct usb_gadget *gadget) +{ + struct raw_dev *dev = get_gadget_data(gadget); + int ret; + + dev_dbg(&gadget->dev, "gadget reset\n"); + ret = raw_queue_event(dev, USB_RAW_EVENT_RESET, 0, NULL); + if (ret < 0) + dev_err(&gadget->dev, "failed to queue reset event\n"); +} /*----------------------------------------------------------------------*/ @@ -663,12 +710,12 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, if (WARN_ON(in && dev->ep0_out_pending)) { ret = -ENODEV; dev->state = STATE_DEV_FAILED; - goto out_done; + goto out_unlock; } if (WARN_ON(!in && dev->ep0_in_pending)) { ret = -ENODEV; dev->state = STATE_DEV_FAILED; - goto out_done; + goto out_unlock; } dev->req->buf = data; @@ -682,8 +729,7 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret); spin_lock_irqsave(&dev->lock, flags); - dev->state = STATE_DEV_FAILED; - goto out_done; + goto out_queue_failed; } ret = wait_for_completion_interruptible(&dev->ep0_done); @@ -692,13 +738,16 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, usb_ep_dequeue(dev->gadget->ep0, dev->req); wait_for_completion(&dev->ep0_done); spin_lock_irqsave(&dev->lock, flags); - goto out_done; + if (dev->ep0_status == -ECONNRESET) + dev->ep0_status = -EINTR; + goto out_interrupted; } spin_lock_irqsave(&dev->lock, flags); - ret = dev->ep0_status; -out_done: +out_interrupted: + ret = dev->ep0_status; +out_queue_failed: dev->ep0_urb_queued = false; out_unlock: spin_unlock_irqrestore(&dev->lock, flags); @@ -1066,8 +1115,7 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io, dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret); spin_lock_irqsave(&dev->lock, flags); - dev->state = STATE_DEV_FAILED; - goto out_done; + goto out_queue_failed; } ret = wait_for_completion_interruptible(&done); @@ -1076,13 +1124,16 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io, usb_ep_dequeue(ep->ep, ep->req); wait_for_completion(&done); spin_lock_irqsave(&dev->lock, flags); - goto out_done; + if (ep->status == -ECONNRESET) + ep->status = -EINTR; + goto out_interrupted; } spin_lock_irqsave(&dev->lock, flags); - ret = ep->status; -out_done: +out_interrupted: + ret = ep->status; +out_queue_failed: ep->urb_queued = false; out_unlock: spin_unlock_irqrestore(&dev->lock, flags); |