diff options
Diffstat (limited to 'drivers/usb/gadget/udc/core.c')
-rw-r--r-- | drivers/usb/gadget/udc/core.c | 52 |
1 files changed, 43 insertions, 9 deletions
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 6a62bbd01324..493ff93f7dda 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -29,6 +29,7 @@ * @list: for use by the udc class driver * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true + * @started: the UDC's started state. True if the UDC had started. * * This represents the internal data structure which is used by the UDC-class * to hold information about udc driver and gadget together. @@ -39,6 +40,7 @@ struct usb_udc { struct device dev; struct list_head list; bool vbus; + bool started; }; static struct class *udc_class; @@ -1082,7 +1084,18 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); */ static inline int usb_gadget_udc_start(struct usb_udc *udc) { - return udc->gadget->ops->udc_start(udc->gadget, udc->driver); + int ret; + + if (udc->started) { + dev_err(&udc->dev, "UDC had already started\n"); + return -EBUSY; + } + + ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver); + if (!ret) + udc->started = true; + + return ret; } /** @@ -1098,7 +1111,13 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc) */ static inline void usb_gadget_udc_stop(struct usb_udc *udc) { + if (!udc->started) { + dev_err(&udc->dev, "UDC had already stopped\n"); + return; + } + udc->gadget->ops->udc_stop(udc->gadget); + udc->started = false; } /** @@ -1114,12 +1133,18 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc) static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, enum usb_device_speed speed) { - if (udc->gadget->ops->udc_set_speed) { - enum usb_device_speed s; + struct usb_gadget *gadget = udc->gadget; + enum usb_device_speed s; - s = min(speed, udc->gadget->max_speed); - udc->gadget->ops->udc_set_speed(udc->gadget, s); - } + if (speed == USB_SPEED_UNKNOWN) + s = gadget->max_speed; + else + s = min(speed, gadget->max_speed); + + if (s == USB_SPEED_SUPER_PLUS && gadget->ops->udc_set_ssp_rate) + gadget->ops->udc_set_ssp_rate(gadget, gadget->max_ssp_rate); + else if (gadget->ops->udc_set_speed) + gadget->ops->udc_set_speed(gadget, s); } /** @@ -1222,6 +1247,8 @@ int usb_add_gadget(struct usb_gadget *gadget) udc->gadget = gadget; gadget->udc = udc; + udc->started = false; + mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -1529,10 +1556,13 @@ static ssize_t soft_connect_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + ssize_t ret; + mutex_lock(&udc_lock); if (!udc->driver) { dev_err(dev, "soft-connect without a gadget driver\n"); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto out; } if (sysfs_streq(buf, "connect")) { @@ -1543,10 +1573,14 @@ static ssize_t soft_connect_store(struct device *dev, usb_gadget_udc_stop(udc); } else { dev_err(dev, "unsupported command '%s'\n", buf); - return -EINVAL; + ret = -EINVAL; + goto out; } - return n; + ret = n; +out: + mutex_unlock(&udc_lock); + return ret; } static DEVICE_ATTR_WO(soft_connect); |