diff options
Diffstat (limited to 'drivers/usb/gadget/udc-core.c')
| -rw-r--r-- | drivers/usb/gadget/udc-core.c | 90 | 
1 files changed, 80 insertions, 10 deletions
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index f8f62c3ed65..ffd8fa54110 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -101,6 +101,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);  /* ------------------------------------------------------------------------- */ +void usb_gadget_set_state(struct usb_gadget *gadget, +		enum usb_device_state state) +{ +	gadget->state = state; +	sysfs_notify(&gadget->dev.kobj, NULL, "status"); +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ +  /**   * usb_gadget_udc_start - tells usb device controller to start up   * @gadget: The gadget we want to get started @@ -156,15 +166,23 @@ static void usb_udc_release(struct device *dev)  }  static const struct attribute_group *usb_udc_attr_groups[]; + +static void usb_udc_nop_release(struct device *dev) +{ +	dev_vdbg(dev, "%s\n", __func__); +} +  /** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function.   *   * Returns zero on success, negative errno otherwise.   */ -int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, +		void (*release)(struct device *dev))  {  	struct usb_udc		*udc;  	int			ret = -ENOMEM; @@ -173,6 +191,22 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)  	if (!udc)  		goto err1; +	dev_set_name(&gadget->dev, "gadget"); +	gadget->dev.parent = parent; + +	dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask); +	gadget->dev.dma_parms = parent->dma_parms; +	gadget->dev.dma_mask = parent->dma_mask; + +	if (release) +		gadget->dev.release = release; +	else +		gadget->dev.release = usb_udc_nop_release; + +	ret = device_register(&gadget->dev); +	if (ret) +		goto err2; +  	device_initialize(&udc->dev);  	udc->dev.release = usb_udc_release;  	udc->dev.class = udc_class; @@ -180,7 +214,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)  	udc->dev.parent = parent;  	ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));  	if (ret) -		goto err2; +		goto err3;  	udc->gadget = gadget; @@ -189,21 +223,42 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)  	ret = device_add(&udc->dev);  	if (ret) -		goto err3; +		goto err4; + +	usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);  	mutex_unlock(&udc_lock);  	return 0; -err3: + +err4:  	list_del(&udc->list);  	mutex_unlock(&udc_lock); -err2: +err3:  	put_device(&udc->dev); +err2: +	put_device(&gadget->dev); +	kfree(udc); +  err1:  	return ret;  } +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ +	return usb_add_gadget_udc_release(parent, gadget, NULL); +}  EXPORT_SYMBOL_GPL(usb_add_gadget_udc);  static void usb_gadget_remove_driver(struct usb_udc *udc) @@ -220,6 +275,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)  	udc->driver = NULL;  	udc->dev.driver = NULL; +	udc->gadget->dev.driver = NULL;  }  /** @@ -254,6 +310,7 @@ found:  	kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);  	device_unregister(&udc->dev); +	device_unregister(&gadget->dev);  }  EXPORT_SYMBOL_GPL(usb_del_gadget_udc); @@ -268,6 +325,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri  	udc->driver = driver;  	udc->dev.driver = &driver->driver; +	udc->gadget->dev.driver = &driver->driver;  	ret = driver->bind(udc->gadget, driver);  	if (ret) @@ -286,6 +344,7 @@ err1:  			udc->driver->function, ret);  	udc->driver = NULL;  	udc->dev.driver = NULL; +	udc->gadget->dev.driver = NULL;  	return ret;  } @@ -395,6 +454,16 @@ static ssize_t usb_udc_softconn_store(struct device *dev,  }  static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); +static ssize_t usb_gadget_state_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct usb_udc		*udc = container_of(dev, struct usb_udc, dev); +	struct usb_gadget	*gadget = udc->gadget; + +	return sprintf(buf, "%s\n", usb_state_string(gadget->state)); +} +static DEVICE_ATTR(state, S_IRUGO, usb_gadget_state_show, NULL); +  #define USB_UDC_SPEED_ATTR(name, param)					\  ssize_t usb_udc_##param##_show(struct device *dev,			\  		struct device_attribute *attr, char *buf)		\ @@ -403,7 +472,7 @@ ssize_t usb_udc_##param##_show(struct device *dev,			\  	return snprintf(buf, PAGE_SIZE, "%s\n",				\  			usb_speed_string(udc->gadget->param));		\  }									\ -static DEVICE_ATTR(name, S_IRUSR, usb_udc_##param##_show, NULL) +static DEVICE_ATTR(name, S_IRUGO, usb_udc_##param##_show, NULL)  static USB_UDC_SPEED_ATTR(current_speed, speed);  static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); @@ -428,6 +497,7 @@ static USB_UDC_ATTR(a_alt_hnp_support);  static struct attribute *usb_udc_attrs[] = {  	&dev_attr_srp.attr,  	&dev_attr_soft_connect.attr, +	&dev_attr_state.attr,  	&dev_attr_current_speed.attr,  	&dev_attr_maximum_speed.attr,  |