diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-05-25 10:11:19 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-05-25 10:11:19 +0300 |
commit | c2312ff575fcaed96889d5bb5392afcc604c9442 (patch) | |
tree | 475d63360ee17732fac41c7c1e4a1636ed92e7cb /drivers/usb/gadget/legacy | |
parent | a3975dea1696b7c81319dc4b66e3c378dd47ccfb (diff) | |
parent | 9cb1fd0efd195590b828b9b865421ad345a4a145 (diff) | |
download | linux-c2312ff575fcaed96889d5bb5392afcc604c9442.tar.xz |
Merge 5.7-rc7 into staging-next
We need the staging/iio fixes in here as well.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/gadget/legacy')
-rw-r--r-- | drivers/usb/gadget/legacy/audio.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/cdc2.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/inode.c | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/ncm.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/raw_gadget.c | 315 |
5 files changed, 262 insertions, 68 deletions
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index dd81fd538cb8..a748ed0842e8 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -300,8 +300,10 @@ static int audio_bind(struct usb_composite_dev *cdev) struct usb_descriptor_header *usb_desc; usb_desc = usb_otg_descriptor_alloc(cdev->gadget); - if (!usb_desc) + if (!usb_desc) { + status = -ENOMEM; goto fail; + } usb_otg_descriptor_init(cdev->gadget, usb_desc); otg_desc[0] = usb_desc; otg_desc[1] = NULL; diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index 8d7a556ece30..563363aba48f 100644 --- a/drivers/usb/gadget/legacy/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -179,8 +179,10 @@ static int cdc_bind(struct usb_composite_dev *cdev) struct usb_descriptor_header *usb_desc; usb_desc = usb_otg_descriptor_alloc(gadget); - if (!usb_desc) + if (!usb_desc) { + status = -ENOMEM; goto fail1; + } usb_otg_descriptor_init(gadget, usb_desc); otg_desc[0] = usb_desc; otg_desc[1] = NULL; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index aa0de9e35afa..3afddd3bea6e 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1361,7 +1361,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) req->buf = dev->rbuf; req->context = NULL; - value = -EOPNOTSUPP; switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: @@ -1784,7 +1783,7 @@ static ssize_t dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) { struct dev_data *dev = fd->private_data; - ssize_t value = len, length = len; + ssize_t value, length = len; unsigned total; u32 tag; char *kbuf; diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c index c61e71ba7045..0f1b45e3abd1 100644 --- a/drivers/usb/gadget/legacy/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -156,8 +156,10 @@ static int gncm_bind(struct usb_composite_dev *cdev) struct usb_descriptor_header *usb_desc; usb_desc = usb_otg_descriptor_alloc(gadget); - if (!usb_desc) + if (!usb_desc) { + status = -ENOMEM; goto fail; + } usb_otg_descriptor_init(gadget, usb_desc); otg_desc[0] = usb_desc; otg_desc[1] = NULL; diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index ca7d95bf7397..e01e366d89cd 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -7,6 +7,7 @@ */ #include <linux/compiler.h> +#include <linux/ctype.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/kref.h> @@ -123,8 +124,6 @@ static void raw_event_queue_destroy(struct raw_event_queue *queue) struct raw_dev; -#define USB_RAW_MAX_ENDPOINTS 32 - enum ep_state { STATE_EP_DISABLED, STATE_EP_ENABLED, @@ -134,6 +133,7 @@ struct raw_ep { struct raw_dev *dev; enum ep_state state; struct usb_ep *ep; + u8 addr; struct usb_request *req; bool urb_queued; bool disabling; @@ -168,7 +168,8 @@ struct raw_dev { bool ep0_out_pending; bool ep0_urb_queued; ssize_t ep0_status; - struct raw_ep eps[USB_RAW_MAX_ENDPOINTS]; + struct raw_ep eps[USB_RAW_EPS_NUM_MAX]; + int eps_num; struct completion ep0_done; struct raw_event_queue queue; @@ -202,8 +203,8 @@ static void dev_free(struct kref *kref) usb_ep_free_request(dev->gadget->ep0, dev->req); } raw_event_queue_destroy(&dev->queue); - for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) { - if (dev->eps[i].state != STATE_EP_ENABLED) + for (i = 0; i < dev->eps_num; i++) { + if (dev->eps[i].state == STATE_EP_DISABLED) continue; usb_ep_disable(dev->eps[i].ep); usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req); @@ -249,12 +250,26 @@ static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req) complete(&dev->ep0_done); } +static u8 get_ep_addr(const char *name) +{ + /* If the endpoint has fixed function (named as e.g. "ep12out-bulk"), + * parse the endpoint address from its name. We deliberately use + * deprecated simple_strtoul() function here, as the number isn't + * followed by '\0' nor '\n'. + */ + if (isdigit(name[2])) + return simple_strtoul(&name[2], NULL, 10); + /* Otherwise the endpoint is configurable (named as e.g. "ep-a"). */ + return USB_RAW_EP_ADDR_ANY; +} + static int gadget_bind(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - int ret = 0; + int ret = 0, i = 0; struct raw_dev *dev = container_of(driver, struct raw_dev, driver); struct usb_request *req; + struct usb_ep *ep; unsigned long flags; if (strcmp(gadget->name, dev->udc_name) != 0) @@ -273,6 +288,13 @@ static int gadget_bind(struct usb_gadget *gadget, dev->req->context = dev; dev->req->complete = gadget_ep0_complete; dev->gadget = gadget; + gadget_for_each_ep(ep, dev->gadget) { + dev->eps[i].ep = ep; + dev->eps[i].addr = get_ep_addr(ep->name); + dev->eps[i].state = STATE_EP_DISABLED; + i++; + } + dev->eps_num = i; spin_unlock_irqrestore(&dev->lock, flags); /* Matches kref_put() in gadget_unbind(). */ @@ -555,7 +577,7 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr, if (copy_from_user(io, ptr, sizeof(*io))) return ERR_PTR(-EFAULT); - if (io->ep >= USB_RAW_MAX_ENDPOINTS) + if (io->ep >= USB_RAW_EPS_NUM_MAX) return ERR_PTR(-EINVAL); if (!usb_raw_io_flags_valid(io->flags)) return ERR_PTR(-EINVAL); @@ -669,43 +691,61 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value) if (IS_ERR(data)) return PTR_ERR(data); ret = raw_process_ep0_io(dev, &io, data, false); - if (ret) + if (ret < 0) goto free; length = min(io.length, (unsigned int)ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; + else + ret = length; free: kfree(data); return ret; } -static bool check_ep_caps(struct usb_ep *ep, - struct usb_endpoint_descriptor *desc) +static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value) { - switch (usb_endpoint_type(desc)) { - case USB_ENDPOINT_XFER_ISOC: - if (!ep->caps.type_iso) - return false; - break; - case USB_ENDPOINT_XFER_BULK: - if (!ep->caps.type_bulk) - return false; - break; - case USB_ENDPOINT_XFER_INT: - if (!ep->caps.type_int) - return false; - break; - default: - return false; + int ret = 0; + unsigned long flags; + + if (value) + return -EINVAL; + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + if (dev->ep0_urb_queued) { + dev_dbg(&dev->gadget->dev, "fail, urb already queued\n"); + ret = -EBUSY; + goto out_unlock; + } + if (!dev->ep0_in_pending && !dev->ep0_out_pending) { + dev_dbg(&dev->gadget->dev, "fail, no request pending\n"); + ret = -EBUSY; + goto out_unlock; } - if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) - return false; - if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) - return false; + ret = usb_ep_set_halt(dev->gadget->ep0); + if (ret < 0) + dev_err(&dev->gadget->dev, + "fail, usb_ep_set_halt returned %d\n", ret); + + if (dev->ep0_in_pending) + dev->ep0_in_pending = false; + else + dev->ep0_out_pending = false; - return true; +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; } static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value) @@ -713,7 +753,7 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value) int ret = 0, i; unsigned long flags; struct usb_endpoint_descriptor *desc; - struct usb_ep *ep = NULL; + struct raw_ep *ep; desc = memdup_user((void __user *)value, sizeof(*desc)); if (IS_ERR(desc)) @@ -741,41 +781,32 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value) goto out_free; } - for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) { - if (dev->eps[i].state == STATE_EP_ENABLED) + for (i = 0; i < dev->eps_num; i++) { + ep = &dev->eps[i]; + if (ep->state != STATE_EP_DISABLED) continue; - break; - } - if (i == USB_RAW_MAX_ENDPOINTS) { - dev_dbg(&dev->gadget->dev, - "fail, no device endpoints available\n"); - ret = -EBUSY; - goto out_free; - } - - gadget_for_each_ep(ep, dev->gadget) { - if (ep->enabled) + if (ep->addr != usb_endpoint_num(desc) && + ep->addr != USB_RAW_EP_ADDR_ANY) continue; - if (!check_ep_caps(ep, desc)) + if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL)) continue; - ep->desc = desc; - ret = usb_ep_enable(ep); + ep->ep->desc = desc; + ret = usb_ep_enable(ep->ep); if (ret < 0) { dev_err(&dev->gadget->dev, "fail, usb_ep_enable returned %d\n", ret); goto out_free; } - dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (!dev->eps[i].req) { + ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC); + if (!ep->req) { dev_err(&dev->gadget->dev, "fail, usb_ep_alloc_request failed\n"); - usb_ep_disable(ep); + usb_ep_disable(ep->ep); ret = -ENOMEM; goto out_free; } - dev->eps[i].ep = ep; - dev->eps[i].state = STATE_EP_ENABLED; - ep->driver_data = &dev->eps[i]; + ep->state = STATE_EP_ENABLED; + ep->ep->driver_data = ep; ret = i; goto out_unlock; } @@ -794,10 +825,6 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value) { int ret = 0, i = value; unsigned long flags; - const void *desc; - - if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS) - return -EINVAL; spin_lock_irqsave(&dev->lock, flags); if (dev->state != STATE_DEV_RUNNING) { @@ -810,7 +837,12 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value) ret = -EBUSY; goto out_unlock; } - if (dev->eps[i].state != STATE_EP_ENABLED) { + if (i < 0 || i >= dev->eps_num) { + dev_dbg(dev->dev, "fail, invalid endpoint\n"); + ret = -EBUSY; + goto out_unlock; + } + if (dev->eps[i].state == STATE_EP_DISABLED) { dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); ret = -EINVAL; goto out_unlock; @@ -834,10 +866,8 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value) spin_lock_irqsave(&dev->lock, flags); usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req); - desc = dev->eps[i].ep->desc; - dev->eps[i].ep = NULL; + kfree(dev->eps[i].ep->desc); dev->eps[i].state = STATE_EP_DISABLED; - kfree(desc); dev->eps[i].disabling = false; out_unlock: @@ -845,6 +875,74 @@ out_unlock: return ret; } +static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev, + unsigned long value, bool set, bool halt) +{ + int ret = 0, i = value; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + if (i < 0 || i >= dev->eps_num) { + dev_dbg(dev->dev, "fail, invalid endpoint\n"); + ret = -EBUSY; + goto out_unlock; + } + if (dev->eps[i].state == STATE_EP_DISABLED) { + dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); + ret = -EINVAL; + goto out_unlock; + } + if (dev->eps[i].disabling) { + dev_dbg(&dev->gadget->dev, + "fail, disable is in progress\n"); + ret = -EINVAL; + goto out_unlock; + } + if (dev->eps[i].urb_queued) { + dev_dbg(&dev->gadget->dev, + "fail, waiting for urb completion\n"); + ret = -EINVAL; + goto out_unlock; + } + if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) { + dev_dbg(&dev->gadget->dev, + "fail, can't halt/wedge ISO endpoint\n"); + ret = -EINVAL; + goto out_unlock; + } + + if (set && halt) { + ret = usb_ep_set_halt(dev->eps[i].ep); + if (ret < 0) + dev_err(&dev->gadget->dev, + "fail, usb_ep_set_halt returned %d\n", ret); + } else if (!set && halt) { + ret = usb_ep_clear_halt(dev->eps[i].ep); + if (ret < 0) + dev_err(&dev->gadget->dev, + "fail, usb_ep_clear_halt returned %d\n", ret); + } else if (set && !halt) { + ret = usb_ep_set_wedge(dev->eps[i].ep); + if (ret < 0) + dev_err(&dev->gadget->dev, + "fail, usb_ep_set_wedge returned %d\n", ret); + } + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req) { struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data; @@ -866,7 +964,7 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io, { int ret = 0; unsigned long flags; - struct raw_ep *ep = &dev->eps[io->ep]; + struct raw_ep *ep; DECLARE_COMPLETION_ONSTACK(done); spin_lock_irqsave(&dev->lock, flags); @@ -880,6 +978,12 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io, ret = -EBUSY; goto out_unlock; } + if (io->ep >= dev->eps_num) { + dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n"); + ret = -EINVAL; + goto out_unlock; + } + ep = &dev->eps[io->ep]; if (ep->state != STATE_EP_ENABLED) { dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); ret = -EBUSY; @@ -964,12 +1068,14 @@ static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value) if (IS_ERR(data)) return PTR_ERR(data); ret = raw_process_ep_io(dev, &io, data, false); - if (ret) + if (ret < 0) goto free; length = min(io.length, (unsigned int)ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; + else + ret = length; free: kfree(data); return ret; @@ -1023,6 +1129,71 @@ out_unlock: return ret; } +static void fill_ep_caps(struct usb_ep_caps *caps, + struct usb_raw_ep_caps *raw_caps) +{ + raw_caps->type_control = caps->type_control; + raw_caps->type_iso = caps->type_iso; + raw_caps->type_bulk = caps->type_bulk; + raw_caps->type_int = caps->type_int; + raw_caps->dir_in = caps->dir_in; + raw_caps->dir_out = caps->dir_out; +} + +static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits) +{ + limits->maxpacket_limit = ep->maxpacket_limit; + limits->max_streams = ep->max_streams; +} + +static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value) +{ + int ret = 0, i; + unsigned long flags; + struct usb_raw_eps_info *info; + struct raw_ep *ep; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto out; + } + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + spin_unlock_irqrestore(&dev->lock, flags); + goto out_free; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + spin_unlock_irqrestore(&dev->lock, flags); + goto out_free; + } + + memset(info, 0, sizeof(*info)); + for (i = 0; i < dev->eps_num; i++) { + ep = &dev->eps[i]; + strscpy(&info->eps[i].name[0], ep->ep->name, + USB_RAW_EP_NAME_MAX); + info->eps[i].addr = ep->addr; + fill_ep_caps(&ep->ep->caps, &info->eps[i].caps); + fill_ep_limits(ep->ep, &info->eps[i].limits); + } + ret = dev->eps_num; + spin_unlock_irqrestore(&dev->lock, flags); + + if (copy_to_user((void __user *)value, info, sizeof(*info))) + ret = -EFAULT; + +out_free: + kfree(info); +out: + return ret; +} + static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value) { struct raw_dev *dev = fd->private_data; @@ -1065,6 +1236,24 @@ static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value) case USB_RAW_IOCTL_VBUS_DRAW: ret = raw_ioctl_vbus_draw(dev, value); break; + case USB_RAW_IOCTL_EPS_INFO: + ret = raw_ioctl_eps_info(dev, value); + break; + case USB_RAW_IOCTL_EP0_STALL: + ret = raw_ioctl_ep0_stall(dev, value); + break; + case USB_RAW_IOCTL_EP_SET_HALT: + ret = raw_ioctl_ep_set_clear_halt_wedge( + dev, value, true, true); + break; + case USB_RAW_IOCTL_EP_CLEAR_HALT: + ret = raw_ioctl_ep_set_clear_halt_wedge( + dev, value, false, true); + break; + case USB_RAW_IOCTL_EP_SET_WEDGE: + ret = raw_ioctl_ep_set_clear_halt_wedge( + dev, value, true, false); + break; default: ret = -EINVAL; } |