usb gadget: function activation/deactivation

Add a new mechanism to the composite gadget framework, letting
functions deactivate (and reactivate) themselves.  Think of it
as a refcounted wrapper for the software pullup control.

A key example of why to use this mechanism involves functions that
require a userspace daemon.  Those functions shuld use this new
mechanism to prevent the gadget from enumerating until those daemons
are activated.  Without this mechanism, hosts would see devices that
malfunction until the relevant daemons start.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
David Brownell 2008-08-18 17:38:22 -07:00 committed by Greg Kroah-Hartman
parent 8066134ff8
commit 60beed95e3
2 changed files with 73 additions and 2 deletions

View file

@ -127,6 +127,70 @@ done:
return value;
}
/**
* usb_function_deactivate - prevent function and gadget enumeration
* @function: the function that isn't yet ready to respond
*
* Blocks response of the gadget driver to host enumeration by
* preventing the data line pullup from being activated. This is
* normally called during @bind() processing to change from the
* initial "ready to respond" state, or when a required resource
* becomes available.
*
* For example, drivers that serve as a passthrough to a userspace
* daemon can block enumeration unless that daemon (such as an OBEX,
* MTP, or print server) is ready to handle host requests.
*
* Not all systems support software control of their USB peripheral
* data pullups.
*
* Returns zero on success, else negative errno.
*/
int usb_function_deactivate(struct usb_function *function)
{
struct usb_composite_dev *cdev = function->config->cdev;
int status = 0;
spin_lock(&cdev->lock);
if (cdev->deactivations == 0)
status = usb_gadget_disconnect(cdev->gadget);
if (status == 0)
cdev->deactivations++;
spin_unlock(&cdev->lock);
return status;
}
/**
* usb_function_activate - allow function and gadget enumeration
* @function: function on which usb_function_activate() was called
*
* Reverses effect of usb_function_deactivate(). If no more functions
* are delaying their activation, the gadget driver will respond to
* host enumeration procedures.
*
* Returns zero on success, else negative errno.
*/
int usb_function_activate(struct usb_function *function)
{
struct usb_composite_dev *cdev = function->config->cdev;
int status = 0;
spin_lock(&cdev->lock);
if (WARN_ON(cdev->deactivations == 0))
status = -EINVAL;
else {
cdev->deactivations--;
if (cdev->deactivations == 0)
status = usb_gadget_connect(cdev->gadget);
}
spin_unlock(&cdev->lock);
return status;
}
/**
* usb_interface_id() - allocate an unused interface ID
* @config: configuration associated with the interface

View file

@ -130,6 +130,9 @@ struct usb_function {
int usb_add_function(struct usb_configuration *, struct usb_function *);
int usb_function_deactivate(struct usb_function *);
int usb_function_activate(struct usb_function *);
int usb_interface_id(struct usb_configuration *, struct usb_function *);
/**
@ -316,9 +319,13 @@ struct usb_composite_dev {
struct usb_composite_driver *driver;
u8 next_string_id;
spinlock_t lock;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
/* REVISIT use and existence of lock ... */
/* protects at least deactivation count */
spinlock_t lock;
};
extern int usb_string_id(struct usb_composite_dev *c);